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

     1  // This example program parses testdata/eval.ll, evaluates the return value of
     2  // the @main function and prints the result to standard output. The result
     3  // should be 42.
     4  package ir_test
     5  
     6  import (
     7  	"fmt"
     8  	"log"
     9  
    10  	"github.com/llir/llvm/asm"
    11  	"github.com/llir/llvm/ir"
    12  	"github.com/llir/llvm/ir/constant"
    13  	"github.com/llir/llvm/ir/types"
    14  	"github.com/llir/llvm/ir/value"
    15  )
    16  
    17  func Example_evaluator() {
    18  	// Parse the LLVM IR assembly file `eval.ll`.
    19  	m, err := asm.ParseFile("testdata/eval.ll")
    20  	if err != nil {
    21  		log.Fatalf("%+v", err)
    22  	}
    23  	// Evalute and print the return value of the `@main` function.
    24  	for _, f := range m.Funcs {
    25  		if f.Name() == "main" {
    26  			e := newEvaluator(f)
    27  			fmt.Println("result:", e.eval())
    28  			break
    29  		}
    30  	}
    31  
    32  	// Output:
    33  	//
    34  	// result: 42
    35  }
    36  
    37  // evaluator is a function evaluator.
    38  type evaluator struct {
    39  	// Function being evaluated.
    40  	f *ir.Func
    41  	// Function arguments.
    42  	args []value.Value
    43  }
    44  
    45  // newEvaluator returns a new function evaluator, for evaluating the result of
    46  // invoking f with args.
    47  func newEvaluator(f *ir.Func, args ...value.Value) *evaluator {
    48  	return &evaluator{f: f, args: args}
    49  }
    50  
    51  // eval evalutes f and returns the corresponding 32-bit integer.
    52  func (e *evaluator) eval() uint32 {
    53  	f := e.f
    54  	if !types.Equal(f.Sig.RetType, types.I32) {
    55  		panic(fmt.Errorf("support for function return type %s not yet implemented", f.Sig.RetType))
    56  	}
    57  	for _, block := range f.Blocks {
    58  		switch term := block.Term.(type) {
    59  		case *ir.TermRet:
    60  			// Note: support for functions with more than one ret terminator not
    61  			// yet implemented.
    62  			if term.X != nil {
    63  				// The result of the first return value of a function is evaluated.
    64  				return e.evalValue(term.X)
    65  			}
    66  		}
    67  	}
    68  	panic(fmt.Errorf("unable to locate ret terminator in function %q", f.Ident()))
    69  }
    70  
    71  // evalInst evaluates inst and returns the corresponding 32-bit integer.
    72  func (e *evaluator) evalInst(inst ir.Instruction) uint32 {
    73  	switch inst := inst.(type) {
    74  	// Binary instructions.
    75  	case *ir.InstAdd:
    76  		return e.evalValue(inst.X) + e.evalValue(inst.Y)
    77  	case *ir.InstSub:
    78  		return e.evalValue(inst.X) - e.evalValue(inst.Y)
    79  	case *ir.InstMul:
    80  		return e.evalValue(inst.X) * e.evalValue(inst.Y)
    81  	case *ir.InstUDiv:
    82  		return e.evalValue(inst.X) / e.evalValue(inst.Y)
    83  	case *ir.InstSDiv:
    84  		return e.evalValue(inst.X) / e.evalValue(inst.Y)
    85  	case *ir.InstURem:
    86  		return e.evalValue(inst.X) % e.evalValue(inst.Y)
    87  	case *ir.InstSRem:
    88  		return e.evalValue(inst.X) % e.evalValue(inst.Y)
    89  	// Bitwise instructions.
    90  	case *ir.InstShl:
    91  		return e.evalValue(inst.X) << e.evalValue(inst.Y)
    92  	case *ir.InstLShr:
    93  		return e.evalValue(inst.X) >> e.evalValue(inst.Y)
    94  	case *ir.InstAShr:
    95  		x, y := e.evalValue(inst.X), e.evalValue(inst.Y)
    96  		result := x >> y
    97  		// sign extend.
    98  		if x&0x80000000 != 0 {
    99  			result = signExt(result)
   100  		}
   101  		return result
   102  	case *ir.InstAnd:
   103  		return e.evalValue(inst.X) & e.evalValue(inst.Y)
   104  	case *ir.InstOr:
   105  		return e.evalValue(inst.X) | e.evalValue(inst.Y)
   106  	case *ir.InstXor:
   107  		return e.evalValue(inst.X) ^ e.evalValue(inst.Y)
   108  	// Other instructions.
   109  	case *ir.InstCall:
   110  		callee, ok := inst.Callee.(*ir.Func)
   111  		if !ok {
   112  			panic(fmt.Errorf("support for callee type %T not yet implemented", inst.Callee))
   113  		}
   114  		ee := newEvaluator(callee, inst.Args...)
   115  		return ee.eval()
   116  	default:
   117  		panic(fmt.Errorf("support for instruction type %T not yet implemented", inst))
   118  	}
   119  }
   120  
   121  // evalValue evalutes v and returns the corresponding 32-bit integer.
   122  func (e *evaluator) evalValue(v value.Value) uint32 {
   123  	switch v := v.(type) {
   124  	case ir.Instruction:
   125  		return e.evalInst(v)
   126  	case *constant.Int:
   127  		return uint32(v.X.Int64())
   128  	case *ir.Param:
   129  		f := e.f
   130  		for i, param := range f.Params {
   131  			if v.Ident() == param.Ident() {
   132  				return e.evalValue(e.args[i])
   133  			}
   134  		}
   135  		panic(fmt.Errorf("unable to locate paramater %q of function %q", v.Ident(), f.Ident()))
   136  	default:
   137  		panic(fmt.Errorf("support for value type %T not yet implemented", v))
   138  	}
   139  }
   140  
   141  // signExt sign extends x.
   142  func signExt(x uint32) uint32 {
   143  	for i := uint32(31); i >= 0; i-- {
   144  		mask := uint32(1 << i)
   145  		if x&mask != 0 {
   146  			break
   147  		}
   148  		x |= mask
   149  	}
   150  	return x
   151  }