github.com/llir/llvm@v0.3.6/asm/local.go (about)

     1  // Problems to solve.
     2  //
     3  // phi instructions can reference local variables defined in basic blocks not
     4  // yet visited when translating basic blocks in linear order.
     5  //
     6  // Terminator instructions can reference basic blocks not yet visited when
     7  // translating basic blocks in linear order.
     8  //
     9  // The function parameters, basic blocks and local variables (produced by the
    10  // result of instructions) of a function may be unnamed. They are assigned the
    11  // first unused local ID (e.g. %42) when traversing the body of the function in
    12  // linear order; where function parameters are assigned first, then for each
    13  // basic block, assign an ID to the basic block and then to the result of its
    14  // instructions. Note, instructions that produce void results are ignored.
    15  // Non-value instructions (e.g. store) are always ignored. Notably, the call
    16  // instruction may be ignored if the callee has a void return.
    17  
    18  // TODO: make concurrent :)
    19  
    20  package asm
    21  
    22  import (
    23  	"github.com/llir/ll/ast"
    24  	"github.com/llir/llvm/ir"
    25  	"github.com/llir/llvm/ir/types"
    26  	"github.com/llir/llvm/ir/value"
    27  	"github.com/pkg/errors"
    28  )
    29  
    30  // funcGen is a generator for a given IR function.
    31  type funcGen struct {
    32  	// Module generator.
    33  	gen *generator
    34  	// LLVM IR function being generated.
    35  	f *ir.Func
    36  	// locals maps from local identifier (without '%' prefix) to corresponding IR
    37  	// value.
    38  	locals map[ir.LocalIdent]value.Value
    39  }
    40  
    41  // newFuncGen returns a new generator for the given IR function.
    42  func newFuncGen(gen *generator, f *ir.Func) *funcGen {
    43  	return &funcGen{
    44  		gen:    gen,
    45  		f:      f,
    46  		locals: make(map[ir.LocalIdent]value.Value),
    47  	}
    48  }
    49  
    50  // resolveLocals resolves the local variables (function parameters, basic
    51  // blocks, results of instructions and terminators) of the given function body.
    52  func (fgen *funcGen) resolveLocals(old ast.FuncBody) error {
    53  	// Index local identifiers and create scaffolding IR local variables (without
    54  	// bodies but with types).
    55  	oldBlocks := old.Blocks()
    56  	if err := fgen.createLocals(oldBlocks); err != nil {
    57  		return errors.WithStack(err)
    58  	}
    59  	// Translate AST instructions to IR.
    60  	if err := fgen.translateInsts(oldBlocks); err != nil {
    61  		return errors.WithStack(err)
    62  	}
    63  	// Translate AST terminators to IR.
    64  	return fgen.translateTerms(oldBlocks)
    65  }
    66  
    67  // === [ Create and index IR ] =================================================
    68  
    69  // local is a local variable.
    70  type local interface {
    71  	value.Named
    72  	// ID returns the ID of the local identifier.
    73  	ID() int64
    74  	// SetID sets the ID of the local identifier.
    75  	SetID(id int64)
    76  	// IsUnnamed reports whether the local identifier is unnamed.
    77  	IsUnnamed() bool
    78  }
    79  
    80  // createLocals indexes local identifiers and creates scaffolding IR local
    81  // variables (without bodies but with types) of the given function.
    82  //
    83  // post-condition: fgen.locals maps from local identifier (without '%' prefix)
    84  // to corresponding skeleton IR value.
    85  func (fgen *funcGen) createLocals(oldBlocks []ast.BasicBlock) error {
    86  	// Create local variable skeletons (without bodies but with types).
    87  	if err := fgen.newLocals(oldBlocks); err != nil {
    88  		return errors.WithStack(err)
    89  	}
    90  	// Assign local IDs.
    91  	//
    92  	// Note: the type of call instructions and invoke terminators must be
    93  	// determined before assigning local IDs, as they may be values or non-values
    94  	// based on return type. This is done by fgen.newLocals.
    95  	if err := fgen.f.AssignIDs(); err != nil {
    96  		return errors.WithStack(err)
    97  	}
    98  	// Index local identifiers.
    99  	return fgen.indexLocals()
   100  }
   101  
   102  // newLocals creates scaffolding IR local variables (without bodies but with
   103  // types) of the given function.
   104  func (fgen *funcGen) newLocals(oldBlocks []ast.BasicBlock) error {
   105  	// Note: function parameters are already translated in gen.irFuncHeader.
   106  	f := fgen.f
   107  	f.Blocks = make([]*ir.Block, len(oldBlocks))
   108  	for i, oldBlock := range oldBlocks {
   109  		block := &ir.Block{}
   110  		if n, ok := oldBlock.Name(); ok {
   111  			block.LocalIdent = labelIdent(n)
   112  		}
   113  		if oldInsts := oldBlock.Insts(); len(oldInsts) > 0 {
   114  			block.Insts = make([]ir.Instruction, len(oldInsts))
   115  			for j, oldInst := range oldInsts {
   116  				inst, err := fgen.newInst(oldInst)
   117  				if err != nil {
   118  					return errors.WithStack(err)
   119  				}
   120  				block.Insts[j] = inst
   121  			}
   122  		}
   123  		term, err := fgen.newTerm(oldBlock.Term())
   124  		if err != nil {
   125  			return errors.WithStack(err)
   126  		}
   127  		block.Term = term
   128  		block.Parent = f
   129  		f.Blocks[i] = block
   130  	}
   131  	return nil
   132  }
   133  
   134  // indexLocals indexes local identifiers of the given function.
   135  func (fgen *funcGen) indexLocals() error {
   136  	// Index function parameters.
   137  	f := fgen.f
   138  	for _, param := range f.Params {
   139  		if err := fgen.addLocal(param.LocalIdent, param); err != nil {
   140  			return errors.WithStack(err)
   141  		}
   142  	}
   143  	// Index basic blocks.
   144  	for _, block := range f.Blocks {
   145  		if err := fgen.addLocal(block.LocalIdent, block); err != nil {
   146  			return errors.WithStack(err)
   147  		}
   148  		// Index instructions.
   149  		for _, inst := range block.Insts {
   150  			v, ok := inst.(local)
   151  			if !ok || v.Type().Equal(types.Void) {
   152  				// Skip non-value instructions.
   153  				continue
   154  			}
   155  			ident := localIdentOfValue(v)
   156  			if err := fgen.addLocal(ident, v); err != nil {
   157  				return errors.WithStack(err)
   158  			}
   159  		}
   160  		// Index terminator.
   161  		v, ok := block.Term.(local)
   162  		if !ok || v.Type().Equal(types.Void) {
   163  			// Skip non-value terminators.
   164  			continue
   165  		}
   166  		ident := localIdentOfValue(v)
   167  		if err := fgen.addLocal(ident, v); err != nil {
   168  			return errors.WithStack(err)
   169  		}
   170  	}
   171  	return nil
   172  }
   173  
   174  // ### [ Helper functions ] ####################################################
   175  
   176  // addLocal adds the local variable with the given local identifier to the map
   177  // of local variables of the function.
   178  func (fgen *funcGen) addLocal(ident ir.LocalIdent, v value.Value) error {
   179  	if prev, ok := fgen.locals[ident]; ok {
   180  		return errors.Errorf("local identifier %q already present; prev `%s`, new `%s`", ident.Ident(), prev, v)
   181  	}
   182  	fgen.locals[ident] = v
   183  	return nil
   184  }
   185  
   186  // localIdentOfValue returns the local identifier of the given local variable.
   187  func localIdentOfValue(v local) ir.LocalIdent {
   188  	if v.IsUnnamed() {
   189  		return ir.LocalIdent{LocalID: v.ID()}
   190  	}
   191  	return ir.LocalIdent{LocalName: v.Name()}
   192  }