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

     1  package ir
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"sync"
     7  
     8  	"github.com/llir/llvm/internal/enc"
     9  	"github.com/llir/llvm/ir/constant"
    10  	"github.com/llir/llvm/ir/enum"
    11  	"github.com/llir/llvm/ir/types"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  // === [ Functions ] ===========================================================
    16  
    17  // Func is an LLVM IR function. The body of a function definition consists of a
    18  // set of basic blocks, interconnected by terminator control flow instructions.
    19  type Func struct {
    20  	// Function name (without '@' prefix).
    21  	GlobalIdent
    22  	// Function signature.
    23  	Sig *types.FuncType
    24  	// Function parameters.
    25  	Params []*Param
    26  	// Basic blocks.
    27  	Blocks []*Block // nil if declaration.
    28  
    29  	// extra.
    30  
    31  	// Pointer type to function, including an optional address space. If Typ is
    32  	// nil, the first invocation of Type stores a pointer type with Sig as
    33  	// element.
    34  	Typ *types.PointerType
    35  	// (optional) Linkage.
    36  	Linkage enum.Linkage
    37  	// (optional) Preemption; zero value if not present.
    38  	Preemption enum.Preemption
    39  	// (optional) Visibility; zero value if not present.
    40  	Visibility enum.Visibility
    41  	// (optional) DLL storage class; zero value if not present.
    42  	DLLStorageClass enum.DLLStorageClass
    43  	// (optional) Calling convention; zero value if not present.
    44  	CallingConv enum.CallingConv
    45  	// (optional) Return attributes.
    46  	ReturnAttrs []ReturnAttribute
    47  	// (optional) Unnamed address.
    48  	UnnamedAddr enum.UnnamedAddr
    49  	// (optional) Address space; zero if not present.
    50  	AddrSpace types.AddrSpace
    51  	// (optional) Function attributes.
    52  	FuncAttrs []FuncAttribute
    53  	// (optional) Section name; empty if not present.
    54  	Section string
    55  	// (optional) Partition name; empty if not present.
    56  	Partition string
    57  	// (optional) Comdat; nil if not present.
    58  	Comdat *ComdatDef
    59  	// (optional) Alignment; zero if not present.
    60  	Align Align
    61  	// (optional) Garbage collection; empty if not present.
    62  	GC string
    63  	// (optional) Prefix; nil if not present.
    64  	Prefix constant.Constant
    65  	// (optional) Prologue; nil if not present.
    66  	Prologue constant.Constant
    67  	// (optional) Personality; nil if not present.
    68  	Personality constant.Constant
    69  	// (optional) Use list orders.
    70  	UseListOrders []*UseListOrder
    71  	// (optional) Metadata.
    72  	Metadata
    73  
    74  	// Parent module; field set by ir.Module.NewFunc.
    75  	Parent *Module `json:"-"`
    76  
    77  	// mu prevents races on AssignIDs.
    78  	mu sync.Mutex
    79  }
    80  
    81  // NewFunc returns a new function based on the given function name, return type
    82  // and function parameters.
    83  func NewFunc(name string, retType types.Type, params ...*Param) *Func {
    84  	paramTypes := make([]types.Type, len(params))
    85  	for i, param := range params {
    86  		paramTypes[i] = param.Type()
    87  	}
    88  	sig := types.NewFunc(retType, paramTypes...)
    89  	f := &Func{Sig: sig, Params: params}
    90  	f.SetName(name)
    91  	// Compute type.
    92  	f.Type()
    93  	return f
    94  }
    95  
    96  // String returns the LLVM syntax representation of the function as a type-value
    97  // pair.
    98  func (f *Func) String() string {
    99  	return fmt.Sprintf("%s %s", f.Type(), f.Ident())
   100  }
   101  
   102  // Type returns the type of the function.
   103  func (f *Func) Type() types.Type {
   104  	// Cache type if not present.
   105  	if f.Typ == nil {
   106  		f.Typ = types.NewPointer(f.Sig)
   107  		f.Typ.AddrSpace = f.AddrSpace
   108  	}
   109  	return f.Typ
   110  }
   111  
   112  // LLString returns the LLVM syntax representation of the function definition or
   113  // declaration.
   114  //
   115  // Function declaration.
   116  //
   117  //    'declare' Metadata=MetadataAttachment* Header=FuncHeader
   118  //
   119  // Function definition.
   120  //
   121  //    'define' Header=FuncHeader Metadata=MetadataAttachment* Body=FuncBody
   122  func (f *Func) LLString() string {
   123  	if err := f.AssignIDs(); err != nil {
   124  		panic(fmt.Errorf("unable to assign IDs of function %q; %v", f.Ident(), err))
   125  	}
   126  	buf := &strings.Builder{}
   127  	if len(f.Blocks) == 0 {
   128  		// Function declaration.
   129  		buf.WriteString("declare")
   130  		for _, md := range f.Metadata {
   131  			fmt.Fprintf(buf, " %s", md)
   132  		}
   133  		if f.Linkage != enum.LinkageNone {
   134  			fmt.Fprintf(buf, " %s", f.Linkage)
   135  		}
   136  		buf.WriteString(headerString(f))
   137  		return buf.String()
   138  	} else {
   139  		// Function definition.
   140  		buf.WriteString("define")
   141  		if f.Linkage != enum.LinkageNone {
   142  			fmt.Fprintf(buf, " %s", f.Linkage)
   143  		}
   144  		buf.WriteString(headerString(f))
   145  		for _, md := range f.Metadata {
   146  			fmt.Fprintf(buf, " %s", md)
   147  		}
   148  		fmt.Fprintf(buf, " %s", bodyString(f))
   149  		return buf.String()
   150  	}
   151  }
   152  
   153  // AssignIDs assigns IDs to unnamed local variables.
   154  func (f *Func) AssignIDs() error {
   155  	f.mu.Lock()
   156  	defer f.mu.Unlock()
   157  	id := int64(0)
   158  	setName := func(n namedVar) error {
   159  		if n.IsUnnamed() {
   160  			if n.ID() != 0 && id != n.ID() {
   161  				want := id
   162  				got := n.ID()
   163  				return errors.Errorf("invalid local ID in function %q, expected %s, got %s", f.Ident(), enc.LocalID(want), enc.LocalID(got))
   164  			}
   165  			n.SetID(id)
   166  			id++
   167  		}
   168  		return nil
   169  	}
   170  	for _, param := range f.Params {
   171  		// Assign local IDs to unnamed parameters of function definitions.
   172  		if err := setName(param); err != nil {
   173  			return errors.WithStack(err)
   174  		}
   175  	}
   176  	for _, block := range f.Blocks {
   177  		// Assign local IDs to unnamed basic blocks.
   178  		if err := setName(block); err != nil {
   179  			return errors.WithStack(err)
   180  		}
   181  		for _, inst := range block.Insts {
   182  			n, ok := inst.(namedVar)
   183  			if !ok {
   184  				continue
   185  			}
   186  			// Skip void instructions (call with void return).
   187  			if types.Equal(n.Type(), types.Void) {
   188  				continue
   189  			}
   190  			// Assign local IDs to unnamed local variables.
   191  			if err := setName(n); err != nil {
   192  				return errors.WithStack(err)
   193  			}
   194  		}
   195  		n, ok := block.Term.(namedVar)
   196  		if !ok {
   197  			continue
   198  		}
   199  		// Skip void terminators (invoke, callbr with void return).
   200  		if types.Equal(n.Type(), types.Void) {
   201  			continue
   202  		}
   203  		if err := setName(n); err != nil {
   204  			return errors.WithStack(err)
   205  		}
   206  	}
   207  	return nil
   208  }
   209  
   210  // ### [ Helper functions ] ####################################################
   211  
   212  // headerString returns the string representation of the function header.
   213  func headerString(f *Func) string {
   214  	// (Linkage | ExternLinkage)? Preemptionopt Visibilityopt DLLStorageClassopt CallingConvopt ReturnAttrs=ReturnAttribute* RetType=Type Name=GlobalIdent '(' Params ')' UnnamedAddropt AddrSpaceopt FuncAttrs=FuncAttribute* Sectionopt Partitionopt Comdatopt Alignopt GCopt Prefixopt Prologueopt Personalityopt
   215  	buf := &strings.Builder{}
   216  	if f.Preemption != enum.PreemptionNone {
   217  		fmt.Fprintf(buf, " %s", f.Preemption)
   218  	}
   219  	if f.Visibility != enum.VisibilityNone {
   220  		fmt.Fprintf(buf, " %s", f.Visibility)
   221  	}
   222  	if f.DLLStorageClass != enum.DLLStorageClassNone {
   223  		fmt.Fprintf(buf, " %s", f.DLLStorageClass)
   224  	}
   225  	if f.CallingConv != enum.CallingConvNone {
   226  		fmt.Fprintf(buf, " %s", callingConvString(f.CallingConv))
   227  	}
   228  	for _, attr := range f.ReturnAttrs {
   229  		fmt.Fprintf(buf, " %s", attr)
   230  	}
   231  	fmt.Fprintf(buf, " %s", f.Sig.RetType)
   232  	fmt.Fprintf(buf, " %s(", f.Ident())
   233  	for i, param := range f.Params {
   234  		if i != 0 {
   235  			buf.WriteString(", ")
   236  		}
   237  		buf.WriteString(param.LLString())
   238  	}
   239  	if f.Sig.Variadic {
   240  		if len(f.Params) > 0 {
   241  			buf.WriteString(", ")
   242  		}
   243  		buf.WriteString("...")
   244  	}
   245  	buf.WriteString(")")
   246  	if f.UnnamedAddr != enum.UnnamedAddrNone {
   247  		fmt.Fprintf(buf, " %s", f.UnnamedAddr)
   248  	}
   249  	if f.AddrSpace != 0 {
   250  		fmt.Fprintf(buf, " %s", f.AddrSpace)
   251  	}
   252  	for _, attr := range f.FuncAttrs {
   253  		fmt.Fprintf(buf, " %s", attr)
   254  	}
   255  	if len(f.Section) > 0 {
   256  		fmt.Fprintf(buf, " section %s", quote(f.Section))
   257  	}
   258  	if len(f.Partition) > 0 {
   259  		fmt.Fprintf(buf, " partition %s", quote(f.Partition))
   260  	}
   261  	if f.Comdat != nil {
   262  		if f.Comdat.Name == f.Name() {
   263  			buf.WriteString(" comdat")
   264  		} else {
   265  			fmt.Fprintf(buf, " %s", f.Comdat)
   266  		}
   267  	}
   268  	if f.Align != 0 {
   269  		fmt.Fprintf(buf, " %s", f.Align)
   270  	}
   271  	if len(f.GC) > 0 {
   272  		fmt.Fprintf(buf, " gc %s", quote(f.GC))
   273  	}
   274  	if f.Prefix != nil {
   275  		fmt.Fprintf(buf, " prefix %s", f.Prefix)
   276  	}
   277  	if f.Prologue != nil {
   278  		fmt.Fprintf(buf, " prologue %s", f.Prologue)
   279  	}
   280  	if f.Personality != nil {
   281  		fmt.Fprintf(buf, " personality %s", f.Personality)
   282  	}
   283  	return buf.String()
   284  }
   285  
   286  // bodyString returns the string representation of the function body.
   287  func bodyString(body *Func) string {
   288  	// '{' Blocks=Block+ UseListOrders=UseListOrder* '}'
   289  	buf := &strings.Builder{}
   290  	buf.WriteString("{\n")
   291  	for i, block := range body.Blocks {
   292  		if i != 0 {
   293  			buf.WriteString("\n")
   294  		}
   295  		fmt.Fprintf(buf, "%s\n", block.LLString())
   296  	}
   297  	if len(body.UseListOrders) > 0 {
   298  		buf.WriteString("\n")
   299  	}
   300  	for _, u := range body.UseListOrders {
   301  		fmt.Fprintf(buf, "\t%s\n", u)
   302  	}
   303  	buf.WriteString("}")
   304  	return buf.String()
   305  }