github.com/llir/llvm@v0.3.6/ir/helper.go (about)

     1  package ir
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/llir/llvm/internal/enc"
    10  	"github.com/llir/llvm/ir/enum"
    11  	"github.com/llir/llvm/ir/metadata"
    12  	"github.com/llir/llvm/ir/types"
    13  	"github.com/llir/llvm/ir/value"
    14  )
    15  
    16  // Align is a memory alignment attribute.
    17  type Align uint64
    18  
    19  // String returns the string representation of the alignment attribute.
    20  func (align Align) String() string {
    21  	// Note, alignment is printed as `align = 8` in attribute groups.
    22  	return fmt.Sprintf("align %d", uint64(align))
    23  }
    24  
    25  // AlignStack is a stack alignment attribute.
    26  type AlignStack uint64
    27  
    28  // String returns the string representation of the stack alignment attribute.
    29  func (align AlignStack) String() string {
    30  	// Note, stack alignment is printed as `alignstack = 8` in attribute groups.
    31  	return fmt.Sprintf("alignstack(%d)", uint64(align))
    32  }
    33  
    34  // AllocSize is an attribute for functions like malloc. If the second parameter
    35  // is omitted, NElemsIndex will be -1.
    36  type AllocSize struct {
    37  	// Element size parameter index.
    38  	ElemSizeIndex int
    39  	// Number of elements parameter index; -1 if not present.
    40  	NElemsIndex int
    41  }
    42  
    43  // String returns the string representation of the allocsize attribute.
    44  func (a AllocSize) String() string {
    45  	if a.NElemsIndex == -1 {
    46  		return fmt.Sprintf("allocsize(%d)", a.ElemSizeIndex)
    47  	}
    48  	return fmt.Sprintf("allocsize(%d, %d)", a.ElemSizeIndex, a.NElemsIndex)
    49  }
    50  
    51  // Arg is a function argument with optional parameter attributes.
    52  type Arg struct {
    53  	// Argument value.
    54  	value.Value
    55  	// (optional) Parameter attributes.
    56  	Attrs []ParamAttribute
    57  }
    58  
    59  // NewArg returns a new function argument based on the given value and parameter
    60  // attributes.
    61  func NewArg(x value.Value, attrs ...ParamAttribute) *Arg {
    62  	return &Arg{Value: x, Attrs: attrs}
    63  }
    64  
    65  // String returns a string representation of the function argument.
    66  func (arg *Arg) String() string {
    67  	// Typ=ConcreteType Attrs=ParamAttribute* Val=Value
    68  	buf := &strings.Builder{}
    69  	buf.WriteString(arg.Type().String())
    70  	for _, attr := range arg.Attrs {
    71  		fmt.Fprintf(buf, " %s", attr)
    72  	}
    73  	fmt.Fprintf(buf, " %s", arg.Ident())
    74  	return buf.String()
    75  }
    76  
    77  // AttrPair is an attribute key-value pair (used in function, parameter and
    78  // return attributes).
    79  type AttrPair struct {
    80  	Key   string
    81  	Value string
    82  }
    83  
    84  // String returns the string representation of the attribute key-value pair.
    85  func (a AttrPair) String() string {
    86  	// Key=StringLit '=' Val=StringLit
    87  	return fmt.Sprintf("%s=%s", quote(a.Key), quote(a.Value))
    88  }
    89  
    90  // AttrString is an attribute string (used in function, parameter and return
    91  // attributes).
    92  type AttrString string
    93  
    94  // String returns the string representation of the attribute string.
    95  func (a AttrString) String() string {
    96  	return quote(string(a))
    97  }
    98  
    99  // ByRef is a byref parameter attribute.
   100  type ByRef struct {
   101  	// Parameter type.
   102  	Typ types.Type
   103  }
   104  
   105  // String returns the string representation of the byref parameter attribute.
   106  func (b ByRef) String() string {
   107  	// 'byref' '(' Typ=Type ')'
   108  	return fmt.Sprintf("byref(%s)", b.Typ)
   109  }
   110  
   111  // Byval is a byval parameter attribute.
   112  type Byval struct {
   113  	// (optional) Parameter type.
   114  	Typ types.Type
   115  }
   116  
   117  // String returns the string representation of the byval parameter attribute.
   118  func (b Byval) String() string {
   119  	// 'byval'
   120  	//
   121  	// 'byval' '(' Typ=Type ')'
   122  	if b.Typ != nil {
   123  		return fmt.Sprintf("byval(%s)", b.Typ)
   124  	}
   125  	return "byval"
   126  }
   127  
   128  // Dereferenceable is a dereferenceable memory attribute.
   129  type Dereferenceable struct {
   130  	// Number of bytes known to be dereferenceable.
   131  	N uint64
   132  	// (optional) Either dereferenceable or null if set.
   133  	DerefOrNull bool
   134  }
   135  
   136  // String returns the string representation of the dereferenceable memory
   137  // attribute.
   138  func (d Dereferenceable) String() string {
   139  	// 'dereferenceable' '(' N=UintLit ')'
   140  	//
   141  	// 'dereferenceable_or_null' '(' N=UintLit ')'
   142  	if d.DerefOrNull {
   143  		return fmt.Sprintf("dereferenceable_or_null(%d)", d.N)
   144  	}
   145  	return fmt.Sprintf("dereferenceable(%d)", d.N)
   146  }
   147  
   148  // ElementType is a elementtype parameter attribute.
   149  type ElementType struct {
   150  	// Parameter type.
   151  	Typ types.Type
   152  }
   153  
   154  // String returns the string representation of the elementtype parameter
   155  // attribute.
   156  func (b ElementType) String() string {
   157  	// 'elementtype' '(' Typ=Type ')'
   158  	return fmt.Sprintf("elementtype(%s)", b.Typ)
   159  }
   160  
   161  // InAlloca is a param attribute.
   162  type InAlloca struct {
   163  	Typ types.Type
   164  }
   165  
   166  // String returns a string representation of the InAlloca attribute.
   167  func (p InAlloca) String() string {
   168  	return fmt.Sprintf("inalloca(%v)", p.Typ)
   169  }
   170  
   171  // Preallocated is a func/param attribute.
   172  type Preallocated struct {
   173  	Typ types.Type
   174  }
   175  
   176  // String returns a string representation of the Preallocated attribute.
   177  func (p Preallocated) String() string {
   178  	return fmt.Sprintf("preallocated(%v)", p.Typ)
   179  }
   180  
   181  // VectorScaleRange denotes the min/max vector scale value of a given function. If
   182  // the second parameter is omitted, Min will be -1.
   183  type VectorScaleRange struct {
   184  	// Min value.
   185  	Min int
   186  	// Max value.
   187  	Max int
   188  }
   189  
   190  // String returns the string representation of the vscale_range attribute.
   191  func (a VectorScaleRange) String() string {
   192  	if a.Min == -1 {
   193  		return fmt.Sprintf("vscale_range(%d)", a.Max)
   194  	}
   195  	return fmt.Sprintf("vscale_range(%d, %d)", a.Min, a.Max)
   196  }
   197  
   198  // TODO: check if *ir.InstLandingPad is a valid ExceptionPad.
   199  
   200  // ExceptionPad is an exception pad or the none token.
   201  //
   202  // An ExceptionPad has one of the following underlying types.
   203  //
   204  //    *ir.InstCatchPad
   205  //    *ir.InstCleanupPad
   206  //    *constant.NoneToken
   207  type ExceptionPad interface {
   208  	value.Value
   209  }
   210  
   211  // FuncAttribute is a function attribute.
   212  //
   213  // A FuncAttribute has one of the following underlying types.
   214  //
   215  //    ir.AttrString
   216  //    ir.AttrPair
   217  //    *ir.AttrGroupDef
   218  //    ir.Align
   219  //    ir.AlignStack
   220  //    ir.AllocSize
   221  //    enum.FuncAttr
   222  type FuncAttribute interface {
   223  	fmt.Stringer
   224  	// IsFuncAttribute ensures that only function attributes can be assigned to
   225  	// the ir.FuncAttribute interface.
   226  	IsFuncAttribute()
   227  }
   228  
   229  // GlobalIdent is a global identifier.
   230  type GlobalIdent struct {
   231  	GlobalName string
   232  	GlobalID   int64
   233  }
   234  
   235  // Ident returns the identifier associated with the global identifier.
   236  func (i GlobalIdent) Ident() string {
   237  	if i.IsUnnamed() {
   238  		return enc.GlobalID(i.GlobalID)
   239  	}
   240  	return enc.GlobalName(i.GlobalName)
   241  }
   242  
   243  // Name returns the name of the global identifier.
   244  //
   245  // If unnamed, the global ID is returned. To distinguish numeric names from
   246  // unnamed IDs, numeric names are quoted.
   247  func (i GlobalIdent) Name() string {
   248  	if i.IsUnnamed() {
   249  		return strconv.FormatInt(i.GlobalID, 10)
   250  	}
   251  	if x, err := strconv.ParseInt(i.GlobalName, 10, 64); err == nil {
   252  		// Print GlobalName with quotes if it is a number; e.g. "42".
   253  		return fmt.Sprintf(`"%d"`, x)
   254  	}
   255  	return i.GlobalName
   256  }
   257  
   258  // SetName sets the name of the global identifier.
   259  func (i *GlobalIdent) SetName(name string) {
   260  	i.GlobalName = name
   261  	i.GlobalID = 0
   262  }
   263  
   264  // ID returns the ID of the global identifier.
   265  func (i GlobalIdent) ID() int64 {
   266  	return i.GlobalID
   267  }
   268  
   269  // SetID sets the ID of the global identifier.
   270  func (i *GlobalIdent) SetID(id int64) {
   271  	i.GlobalID = id
   272  }
   273  
   274  // IsUnnamed reports whether the global identifier is unnamed.
   275  func (i GlobalIdent) IsUnnamed() bool {
   276  	return len(i.GlobalName) == 0
   277  }
   278  
   279  // LocalIdent is a local identifier.
   280  type LocalIdent struct {
   281  	LocalName string
   282  	LocalID   int64
   283  }
   284  
   285  // NewLocalIdent returns a new local identifier based on the given string. An
   286  // unnamed local ID is used if ident is an integer, and a local name otherwise.
   287  func NewLocalIdent(ident string) LocalIdent {
   288  	if id, err := strconv.ParseInt(ident, 10, 64); err == nil {
   289  		return LocalIdent{LocalID: id}
   290  	}
   291  	return LocalIdent{LocalName: ident}
   292  }
   293  
   294  // Ident returns the identifier associated with the local identifier.
   295  func (i LocalIdent) Ident() string {
   296  	if i.IsUnnamed() {
   297  		return enc.LocalID(i.LocalID)
   298  	}
   299  	return enc.LocalName(i.LocalName)
   300  }
   301  
   302  // Name returns the name of the local identifier.
   303  //
   304  // If unnamed, the local ID is returned. To distinguish numeric names from
   305  // unnamed IDs, numeric names are quoted.
   306  func (i LocalIdent) Name() string {
   307  	if i.IsUnnamed() {
   308  		return strconv.FormatInt(i.LocalID, 10)
   309  	}
   310  	if x, err := strconv.ParseInt(i.LocalName, 10, 64); err == nil {
   311  		// Print LocalName with quotes if it is a number; e.g. "42".
   312  		return fmt.Sprintf(`"%d"`, x)
   313  	}
   314  	return i.LocalName
   315  }
   316  
   317  // SetName sets the name of the local identifier.
   318  func (i *LocalIdent) SetName(name string) {
   319  	i.LocalName = name
   320  	i.LocalID = 0
   321  }
   322  
   323  // ID returns the ID of the local identifier.
   324  func (i LocalIdent) ID() int64 {
   325  	return i.LocalID
   326  }
   327  
   328  // SetID sets the ID of the local identifier.
   329  func (i *LocalIdent) SetID(id int64) {
   330  	i.LocalID = id
   331  }
   332  
   333  // IsUnnamed reports whether the local identifier is unnamed.
   334  func (i LocalIdent) IsUnnamed() bool {
   335  	return len(i.LocalName) == 0
   336  }
   337  
   338  // Metadata is a list of metadata attachments.
   339  type Metadata []*metadata.Attachment
   340  
   341  // MDAttachments returns the metadata attachments of the value.
   342  func (mds Metadata) MDAttachments() []*metadata.Attachment {
   343  	return mds
   344  }
   345  
   346  // OperandBundle is a tagged set of SSA values associated with a call-site.
   347  type OperandBundle struct {
   348  	Tag    string
   349  	Inputs []value.Value
   350  }
   351  
   352  // NewOperandBundle returns a new operand bundle based on the given tag and
   353  // input values.
   354  func NewOperandBundle(tag string, inputs ...value.Value) *OperandBundle {
   355  	return &OperandBundle{Tag: tag, Inputs: inputs}
   356  }
   357  
   358  // String returns a string representation of the operand bundle.
   359  func (o *OperandBundle) String() string {
   360  	// Tag=StringLit '(' Inputs=(TypeValue separator ',')* ')'
   361  	buf := &strings.Builder{}
   362  	fmt.Fprintf(buf, "%s(", quote(o.Tag))
   363  	for i, input := range o.Inputs {
   364  		if i != 0 {
   365  			buf.WriteString(", ")
   366  		}
   367  		buf.WriteString(input.String())
   368  	}
   369  	buf.WriteString(")")
   370  	return buf.String()
   371  }
   372  
   373  // ParamAttribute is a parameter attribute.
   374  //
   375  // A ParamAttribute has one of the following underlying types.
   376  //
   377  //    ir.AttrString
   378  //    ir.AttrPair
   379  //    ir.Align
   380  //    ir.Dereferenceable
   381  //    enum.ParamAttr
   382  type ParamAttribute interface {
   383  	fmt.Stringer
   384  	// IsParamAttribute ensures that only parameter attributes can be assigned to
   385  	// the ir.ParamAttribute interface.
   386  	IsParamAttribute()
   387  }
   388  
   389  // ReturnAttribute is a return attribute.
   390  //
   391  // A ReturnAttribute has one of the following underlying types.
   392  //
   393  //    ir.AttrString
   394  //    ir.AttrPair
   395  //    ir.Align
   396  //    ir.Dereferenceable
   397  //    enum.ReturnAttr
   398  type ReturnAttribute interface {
   399  	fmt.Stringer
   400  	// IsReturnAttribute ensures that only return attributes can be assigned to
   401  	// the ir.ReturnAttribute interface.
   402  	IsReturnAttribute()
   403  }
   404  
   405  // ___ [ Function parameter ] __________________________________________________
   406  
   407  // Param is an LLVM IR function parameter.
   408  type Param struct {
   409  	// (optional) Parameter name (without '%' prefix).
   410  	LocalIdent
   411  	// Parameter type.
   412  	Typ types.Type
   413  
   414  	// extra.
   415  
   416  	// (optional) Parameter attributes.
   417  	Attrs []ParamAttribute
   418  }
   419  
   420  // NewParam returns a new function parameter based on the given name and type.
   421  func NewParam(name string, typ types.Type) *Param {
   422  	return &Param{
   423  		LocalIdent: LocalIdent{LocalName: name},
   424  		Typ:        typ,
   425  	}
   426  }
   427  
   428  // String returns the LLVM syntax representation of the function parameter as a
   429  // type-value pair.
   430  func (p *Param) String() string {
   431  	return fmt.Sprintf("%s %s", p.Type(), p.Ident())
   432  }
   433  
   434  // Type returns the type of the function parameter.
   435  func (p *Param) Type() types.Type {
   436  	return p.Typ
   437  }
   438  
   439  // LLString returns the LLVM syntax representation of the function parameter.
   440  //
   441  // Typ=Type Attrs=ParamAttribute* Name=LocalIdent?
   442  func (p *Param) LLString() string {
   443  	buf := &strings.Builder{}
   444  	buf.WriteString(p.Typ.String())
   445  	for _, attr := range p.Attrs {
   446  		fmt.Fprintf(buf, " %s", attr)
   447  	}
   448  	fmt.Fprintf(buf, " %s", p.Ident())
   449  	return buf.String()
   450  }
   451  
   452  // SRet is an sret parameter attribute.
   453  type SRet struct {
   454  	Typ types.Type
   455  }
   456  
   457  // String returns the string representation of the sret parameter attribute.
   458  func (s SRet) String() string {
   459  	// 'sret' '(' Typ=Type ')'
   460  	return fmt.Sprintf("sret(%s)", s.Typ)
   461  }
   462  
   463  // ### [ Helper functions ] ####################################################
   464  
   465  // quote returns s as a double-quoted string literal.
   466  func quote(s string) string {
   467  	return enc.Quote([]byte(s))
   468  }
   469  
   470  // callingConvString returns the string representation of the given calling
   471  // convention.
   472  func callingConvString(callingConv enum.CallingConv) string {
   473  	s := callingConv.String()
   474  	cc := uint(callingConv)
   475  	if unknown := fmt.Sprintf("CallingConv(%d)", cc); s == unknown {
   476  		return fmt.Sprintf("cc %d", cc)
   477  	}
   478  	return s
   479  }
   480  
   481  // tlsModelString returns the string representation of the given thread local
   482  // storage model.
   483  func tlsModelString(model enum.TLSModel) string {
   484  	if model == enum.TLSModelGeneric {
   485  		return "thread_local"
   486  	}
   487  	return fmt.Sprintf("thread_local(%s)", model)
   488  }
   489  
   490  // --- [ Formatted I/O writer ] ------------------------------------------------
   491  
   492  // fmtWriter is a formatted I/O writer.
   493  //
   494  // A formatted I/O writer keeps track of the total number of bytes written to w
   495  // and the first non-nil error encountered.
   496  type fmtWriter struct {
   497  	// underlying io.Writer.
   498  	w io.Writer
   499  	// Number of bytes written to w.
   500  	size int64
   501  	// First non-nil error encountered.
   502  	err error
   503  }
   504  
   505  // Fprint formats using the default formats for its operands and writes to w.
   506  // Spaces are added between operands when neither is a string. It returns the
   507  // number of bytes written and any write error encountered.
   508  func (fw *fmtWriter) Fprint(a ...interface{}) (n int, err error) {
   509  	if fw.err != nil {
   510  		// early return if a previous error has been encountered.
   511  		return 0, nil
   512  	}
   513  	n, err = fmt.Fprint(fw.w, a...)
   514  	fw.size += int64(n)
   515  	fw.err = err
   516  	return n, err
   517  }
   518  
   519  // Fprintf formats according to a format specifier and writes to w. It returns
   520  // the number of bytes written and any write error encountered.
   521  func (fw *fmtWriter) Fprintf(format string, a ...interface{}) (n int, err error) {
   522  	if fw.err != nil {
   523  		// early return if a previous error has been encountered.
   524  		return 0, nil
   525  	}
   526  	n, err = fmt.Fprintf(fw.w, format, a...)
   527  	fw.size += int64(n)
   528  	fw.err = err
   529  	return n, err
   530  }
   531  
   532  // Fprintln formats using the default formats for its operands and writes to w.
   533  // Spaces are always added between operands and a newline is appended. It
   534  // returns the number of bytes written and any write error encountered.
   535  func (fw *fmtWriter) Fprintln(a ...interface{}) (n int, err error) {
   536  	if fw.err != nil {
   537  		// early return if a previous error has been encountered.
   538  		return 0, nil
   539  	}
   540  	n, err = fmt.Fprintln(fw.w, a...)
   541  	fw.size += int64(n)
   542  	fw.err = err
   543  	return n, err
   544  }
   545  
   546  // namedVar is a named variable.
   547  type namedVar interface {
   548  	value.Named
   549  	// ID returns the ID of the local identifier.
   550  	ID() int64
   551  	// SetID sets the ID of the local identifier.
   552  	SetID(id int64)
   553  	// IsUnnamed reports whether the local identifier is unnamed.
   554  	IsUnnamed() bool
   555  }