github.com/aykevl/tinygo@v0.5.0/interp/frame.go (about)

     1  package interp
     2  
     3  // This file implements the core interpretation routines, interpreting single
     4  // functions.
     5  
     6  import (
     7  	"errors"
     8  	"strings"
     9  
    10  	"tinygo.org/x/go-llvm"
    11  )
    12  
    13  type frame struct {
    14  	*Eval
    15  	fn      llvm.Value
    16  	pkgName string
    17  	locals  map[llvm.Value]Value
    18  }
    19  
    20  var ErrUnreachable = errors.New("interp: unreachable executed")
    21  
    22  // evalBasicBlock evaluates a single basic block, returning the return value (if
    23  // ending with a ret instruction), a list of outgoing basic blocks (if not
    24  // ending with a ret instruction), or an error on failure.
    25  // Most of it works at compile time. Some calls get translated into calls to be
    26  // executed at runtime: calls to functions with side effects, external calls,
    27  // and operations on the result of such instructions.
    28  func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (retval Value, outgoing []llvm.Value, err error) {
    29  	for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
    30  		if fr.Debug {
    31  			print(indent)
    32  			inst.Dump()
    33  			println()
    34  		}
    35  		switch {
    36  		case !inst.IsABinaryOperator().IsNil():
    37  			lhs := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
    38  			rhs := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
    39  
    40  			switch inst.InstructionOpcode() {
    41  			// Standard binary operators
    42  			case llvm.Add:
    43  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateAdd(lhs, rhs, "")}
    44  			case llvm.FAdd:
    45  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFAdd(lhs, rhs, "")}
    46  			case llvm.Sub:
    47  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSub(lhs, rhs, "")}
    48  			case llvm.FSub:
    49  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFSub(lhs, rhs, "")}
    50  			case llvm.Mul:
    51  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateMul(lhs, rhs, "")}
    52  			case llvm.FMul:
    53  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFMul(lhs, rhs, "")}
    54  			case llvm.UDiv:
    55  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateUDiv(lhs, rhs, "")}
    56  			case llvm.SDiv:
    57  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSDiv(lhs, rhs, "")}
    58  			case llvm.FDiv:
    59  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFDiv(lhs, rhs, "")}
    60  			case llvm.URem:
    61  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateURem(lhs, rhs, "")}
    62  			case llvm.SRem:
    63  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSRem(lhs, rhs, "")}
    64  			case llvm.FRem:
    65  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFRem(lhs, rhs, "")}
    66  
    67  			// Logical operators
    68  			case llvm.Shl:
    69  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateShl(lhs, rhs, "")}
    70  			case llvm.LShr:
    71  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateLShr(lhs, rhs, "")}
    72  			case llvm.AShr:
    73  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateAShr(lhs, rhs, "")}
    74  			case llvm.And:
    75  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateAnd(lhs, rhs, "")}
    76  			case llvm.Or:
    77  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateOr(lhs, rhs, "")}
    78  			case llvm.Xor:
    79  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateXor(lhs, rhs, "")}
    80  
    81  			default:
    82  				return nil, nil, &Unsupported{inst}
    83  			}
    84  
    85  		// Memory operators
    86  		case !inst.IsAAllocaInst().IsNil():
    87  			allocType := inst.Type().ElementType()
    88  			alloca := llvm.AddGlobal(fr.Mod, allocType, fr.pkgName+"$alloca")
    89  			alloca.SetInitializer(getZeroValue(allocType))
    90  			alloca.SetLinkage(llvm.InternalLinkage)
    91  			fr.locals[inst] = &LocalValue{
    92  				Underlying: alloca,
    93  				Eval:       fr.Eval,
    94  			}
    95  		case !inst.IsALoadInst().IsNil():
    96  			operand := fr.getLocal(inst.Operand(0)).(*LocalValue)
    97  			var value llvm.Value
    98  			if !operand.IsConstant() || inst.IsVolatile() || (!operand.Underlying.IsAConstantExpr().IsNil() && operand.Underlying.Opcode() == llvm.BitCast) {
    99  				value = fr.builder.CreateLoad(operand.Value(), inst.Name())
   100  			} else {
   101  				value = operand.Load()
   102  			}
   103  			if value.Type() != inst.Type() {
   104  				panic("interp: load: type does not match")
   105  			}
   106  			fr.locals[inst] = fr.getValue(value)
   107  		case !inst.IsAStoreInst().IsNil():
   108  			value := fr.getLocal(inst.Operand(0))
   109  			ptr := fr.getLocal(inst.Operand(1))
   110  			if inst.IsVolatile() {
   111  				fr.builder.CreateStore(value.Value(), ptr.Value())
   112  			} else {
   113  				ptr.Store(value.Value())
   114  			}
   115  		case !inst.IsAGetElementPtrInst().IsNil():
   116  			value := fr.getLocal(inst.Operand(0))
   117  			llvmIndices := make([]llvm.Value, inst.OperandsCount()-1)
   118  			for i := range llvmIndices {
   119  				llvmIndices[i] = inst.Operand(i + 1)
   120  			}
   121  			indices := make([]uint32, len(llvmIndices))
   122  			for i, llvmIndex := range llvmIndices {
   123  				operand := fr.getLocal(llvmIndex)
   124  				if !operand.IsConstant() {
   125  					// Not a constant operation.
   126  					// This should be detected by the scanner, but isn't at the
   127  					// moment.
   128  					panic("todo: non-const gep")
   129  				}
   130  				indices[i] = uint32(operand.Value().ZExtValue())
   131  			}
   132  			result := value.GetElementPtr(indices)
   133  			if result.Type() != inst.Type() {
   134  				println(" expected:", inst.Type().String())
   135  				println(" actual:  ", result.Type().String())
   136  				panic("interp: gep: type does not match")
   137  			}
   138  			fr.locals[inst] = result
   139  
   140  		// Cast operators
   141  		case !inst.IsATruncInst().IsNil():
   142  			value := fr.getLocal(inst.Operand(0))
   143  			fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateTrunc(value.(*LocalValue).Value(), inst.Type(), "")}
   144  		case !inst.IsAZExtInst().IsNil():
   145  			value := fr.getLocal(inst.Operand(0))
   146  			fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateZExt(value.(*LocalValue).Value(), inst.Type(), "")}
   147  		case !inst.IsASExtInst().IsNil():
   148  			value := fr.getLocal(inst.Operand(0))
   149  			fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSExt(value.(*LocalValue).Value(), inst.Type(), "")}
   150  		case !inst.IsAFPToUIInst().IsNil():
   151  			value := fr.getLocal(inst.Operand(0))
   152  			fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFPToUI(value.(*LocalValue).Value(), inst.Type(), "")}
   153  		case !inst.IsAFPToSIInst().IsNil():
   154  			value := fr.getLocal(inst.Operand(0))
   155  			fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFPToSI(value.(*LocalValue).Value(), inst.Type(), "")}
   156  		case !inst.IsAUIToFPInst().IsNil():
   157  			value := fr.getLocal(inst.Operand(0))
   158  			fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateUIToFP(value.(*LocalValue).Value(), inst.Type(), "")}
   159  		case !inst.IsASIToFPInst().IsNil():
   160  			value := fr.getLocal(inst.Operand(0))
   161  			fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSIToFP(value.(*LocalValue).Value(), inst.Type(), "")}
   162  		case !inst.IsAFPTruncInst().IsNil():
   163  			value := fr.getLocal(inst.Operand(0))
   164  			fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFPTrunc(value.(*LocalValue).Value(), inst.Type(), "")}
   165  		case !inst.IsAFPExtInst().IsNil():
   166  			value := fr.getLocal(inst.Operand(0))
   167  			fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFPExt(value.(*LocalValue).Value(), inst.Type(), "")}
   168  		case !inst.IsAPtrToIntInst().IsNil():
   169  			value := fr.getLocal(inst.Operand(0))
   170  			fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreatePtrToInt(value.Value(), inst.Type(), "")}
   171  		case !inst.IsABitCastInst().IsNil() && inst.Type().TypeKind() == llvm.PointerTypeKind:
   172  			operand := inst.Operand(0)
   173  			if !operand.IsACallInst().IsNil() {
   174  				fn := operand.CalledValue()
   175  				if !fn.IsAFunction().IsNil() && fn.Name() == "runtime.alloc" {
   176  					continue // special case: bitcast of alloc
   177  				}
   178  			}
   179  			value := fr.getLocal(operand).(*LocalValue)
   180  			fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateBitCast(value.Value(), inst.Type(), "")}
   181  
   182  		// Other operators
   183  		case !inst.IsAICmpInst().IsNil():
   184  			lhs := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
   185  			rhs := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
   186  			predicate := inst.IntPredicate()
   187  			fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateICmp(predicate, lhs, rhs, "")}
   188  		case !inst.IsAFCmpInst().IsNil():
   189  			lhs := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
   190  			rhs := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
   191  			predicate := inst.FloatPredicate()
   192  			fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFCmp(predicate, lhs, rhs, "")}
   193  		case !inst.IsAPHINode().IsNil():
   194  			for i := 0; i < inst.IncomingCount(); i++ {
   195  				if inst.IncomingBlock(i) == incoming {
   196  					fr.locals[inst] = fr.getLocal(inst.IncomingValue(i))
   197  				}
   198  			}
   199  		case !inst.IsACallInst().IsNil():
   200  			callee := inst.CalledValue()
   201  			switch {
   202  			case callee.Name() == "runtime.alloc":
   203  				// heap allocation
   204  				users := getUses(inst)
   205  				var resultInst = inst
   206  				if len(users) == 1 && !users[0].IsABitCastInst().IsNil() {
   207  					// happens when allocating something other than i8*
   208  					resultInst = users[0]
   209  				}
   210  				size := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying.ZExtValue()
   211  				allocType := resultInst.Type().ElementType()
   212  				typeSize := fr.TargetData.TypeAllocSize(allocType)
   213  				elementCount := 1
   214  				if size != typeSize {
   215  					// allocate an array
   216  					if size%typeSize != 0 {
   217  						return nil, nil, &Unsupported{inst}
   218  					}
   219  					elementCount = int(size / typeSize)
   220  					allocType = llvm.ArrayType(allocType, elementCount)
   221  				}
   222  				alloc := llvm.AddGlobal(fr.Mod, allocType, fr.pkgName+"$alloc")
   223  				alloc.SetInitializer(getZeroValue(allocType))
   224  				alloc.SetLinkage(llvm.InternalLinkage)
   225  				result := &LocalValue{
   226  					Underlying: alloc,
   227  					Eval:       fr.Eval,
   228  				}
   229  				if elementCount == 1 {
   230  					fr.locals[resultInst] = result
   231  				} else {
   232  					fr.locals[resultInst] = result.GetElementPtr([]uint32{0, 0})
   233  				}
   234  			case callee.Name() == "runtime.hashmapMake":
   235  				// create a map
   236  				keySize := inst.Operand(0).ZExtValue()
   237  				valueSize := inst.Operand(1).ZExtValue()
   238  				fr.locals[inst] = &MapValue{
   239  					Eval:      fr.Eval,
   240  					PkgName:   fr.pkgName,
   241  					KeySize:   int(keySize),
   242  					ValueSize: int(valueSize),
   243  				}
   244  			case callee.Name() == "runtime.hashmapStringSet":
   245  				// set a string key in the map
   246  				m := fr.getLocal(inst.Operand(0)).(*MapValue)
   247  				// "key" is a Go string value, which in the TinyGo calling convention is split up
   248  				// into separate pointer and length parameters.
   249  				keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
   250  				keyLen := fr.getLocal(inst.Operand(2)).(*LocalValue)
   251  				valPtr := fr.getLocal(inst.Operand(3)).(*LocalValue)
   252  				m.PutString(keyBuf, keyLen, valPtr)
   253  			case callee.Name() == "runtime.hashmapBinarySet":
   254  				// set a binary (int etc.) key in the map
   255  				m := fr.getLocal(inst.Operand(0)).(*MapValue)
   256  				keyBuf := fr.getLocal(inst.Operand(1)).(*LocalValue)
   257  				valPtr := fr.getLocal(inst.Operand(2)).(*LocalValue)
   258  				m.PutBinary(keyBuf, valPtr)
   259  			case callee.Name() == "runtime.stringConcat":
   260  				// adding two strings together
   261  				buf1Ptr := fr.getLocal(inst.Operand(0))
   262  				buf1Len := fr.getLocal(inst.Operand(1))
   263  				buf2Ptr := fr.getLocal(inst.Operand(2))
   264  				buf2Len := fr.getLocal(inst.Operand(3))
   265  				buf1 := getStringBytes(buf1Ptr, buf1Len.Value())
   266  				buf2 := getStringBytes(buf2Ptr, buf2Len.Value())
   267  				result := []byte(string(buf1) + string(buf2))
   268  				vals := make([]llvm.Value, len(result))
   269  				for i := range vals {
   270  					vals[i] = llvm.ConstInt(fr.Mod.Context().Int8Type(), uint64(result[i]), false)
   271  				}
   272  				globalType := llvm.ArrayType(fr.Mod.Context().Int8Type(), len(result))
   273  				globalValue := llvm.ConstArray(fr.Mod.Context().Int8Type(), vals)
   274  				global := llvm.AddGlobal(fr.Mod, globalType, fr.pkgName+"$stringconcat")
   275  				global.SetInitializer(globalValue)
   276  				global.SetLinkage(llvm.InternalLinkage)
   277  				global.SetGlobalConstant(true)
   278  				global.SetUnnamedAddr(true)
   279  				stringType := fr.Mod.GetTypeByName("runtime._string")
   280  				retPtr := llvm.ConstGEP(global, getLLVMIndices(fr.Mod.Context().Int32Type(), []uint32{0, 0}))
   281  				retLen := llvm.ConstInt(stringType.StructElementTypes()[1], uint64(len(result)), false)
   282  				ret := getZeroValue(stringType)
   283  				ret = llvm.ConstInsertValue(ret, retPtr, []uint32{0})
   284  				ret = llvm.ConstInsertValue(ret, retLen, []uint32{1})
   285  				fr.locals[inst] = &LocalValue{fr.Eval, ret}
   286  			case callee.Name() == "runtime.stringToBytes":
   287  				// convert a string to a []byte
   288  				bufPtr := fr.getLocal(inst.Operand(0))
   289  				bufLen := fr.getLocal(inst.Operand(1))
   290  				result := getStringBytes(bufPtr, bufLen.Value())
   291  				vals := make([]llvm.Value, len(result))
   292  				for i := range vals {
   293  					vals[i] = llvm.ConstInt(fr.Mod.Context().Int8Type(), uint64(result[i]), false)
   294  				}
   295  				globalType := llvm.ArrayType(fr.Mod.Context().Int8Type(), len(result))
   296  				globalValue := llvm.ConstArray(fr.Mod.Context().Int8Type(), vals)
   297  				global := llvm.AddGlobal(fr.Mod, globalType, fr.pkgName+"$bytes")
   298  				global.SetInitializer(globalValue)
   299  				global.SetLinkage(llvm.InternalLinkage)
   300  				global.SetGlobalConstant(true)
   301  				global.SetUnnamedAddr(true)
   302  				sliceType := inst.Type()
   303  				retPtr := llvm.ConstGEP(global, getLLVMIndices(fr.Mod.Context().Int32Type(), []uint32{0, 0}))
   304  				retLen := llvm.ConstInt(sliceType.StructElementTypes()[1], uint64(len(result)), false)
   305  				ret := getZeroValue(sliceType)
   306  				ret = llvm.ConstInsertValue(ret, retPtr, []uint32{0}) // ptr
   307  				ret = llvm.ConstInsertValue(ret, retLen, []uint32{1}) // len
   308  				ret = llvm.ConstInsertValue(ret, retLen, []uint32{2}) // cap
   309  				fr.locals[inst] = &LocalValue{fr.Eval, ret}
   310  			case callee.Name() == "runtime.interfaceImplements":
   311  				typecode := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
   312  				interfaceMethodSet := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
   313  				if typecode.IsAConstantExpr().IsNil() || typecode.Opcode() != llvm.PtrToInt {
   314  					panic("interp: expected typecode to be a ptrtoint")
   315  				}
   316  				typecode = typecode.Operand(0)
   317  				if interfaceMethodSet.IsAConstantExpr().IsNil() || interfaceMethodSet.Opcode() != llvm.GetElementPtr {
   318  					panic("interp: expected method set in runtime.interfaceImplements to be a constant gep")
   319  				}
   320  				interfaceMethodSet = interfaceMethodSet.Operand(0).Initializer()
   321  				methodSet := llvm.ConstExtractValue(typecode.Initializer(), []uint32{1})
   322  				if methodSet.IsAConstantExpr().IsNil() || methodSet.Opcode() != llvm.GetElementPtr {
   323  					panic("interp: expected method set to be a constant gep")
   324  				}
   325  				methodSet = methodSet.Operand(0).Initializer()
   326  
   327  				// Make a set of all the methods on the concrete type, for
   328  				// easier checking in the next step.
   329  				definedMethods := map[string]struct{}{}
   330  				for i := 0; i < methodSet.Type().ArrayLength(); i++ {
   331  					methodInfo := llvm.ConstExtractValue(methodSet, []uint32{uint32(i)})
   332  					name := llvm.ConstExtractValue(methodInfo, []uint32{0}).Name()
   333  					definedMethods[name] = struct{}{}
   334  				}
   335  				// Check whether all interface methods are also in the list
   336  				// of defined methods calculated above.
   337  				implements := uint64(1) // i1 true
   338  				for i := 0; i < interfaceMethodSet.Type().ArrayLength(); i++ {
   339  					name := llvm.ConstExtractValue(interfaceMethodSet, []uint32{uint32(i)}).Name()
   340  					if _, ok := definedMethods[name]; !ok {
   341  						// There is a method on the interface that is not
   342  						// implemented by the type.
   343  						implements = 0 // i1 false
   344  						break
   345  					}
   346  				}
   347  				fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), implements, false)}
   348  			case callee.Name() == "runtime.nanotime":
   349  				fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int64Type(), 0, false)}
   350  			case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic":
   351  				// This are all print instructions, which necessarily have side
   352  				// effects but no results.
   353  				// TODO: print an error when executing runtime._panic (with the
   354  				// exact error message it would print at runtime).
   355  				var params []llvm.Value
   356  				for i := 0; i < inst.OperandsCount()-1; i++ {
   357  					operand := fr.getLocal(inst.Operand(i)).Value()
   358  					fr.markDirty(operand)
   359  					params = append(params, operand)
   360  				}
   361  				// TODO: accurate debug info, including call chain
   362  				fr.builder.CreateCall(callee, params, inst.Name())
   363  			case !callee.IsAFunction().IsNil() && callee.IsDeclaration():
   364  				// external functions
   365  				var params []llvm.Value
   366  				for i := 0; i < inst.OperandsCount()-1; i++ {
   367  					operand := fr.getLocal(inst.Operand(i)).Value()
   368  					fr.markDirty(operand)
   369  					params = append(params, operand)
   370  				}
   371  				// TODO: accurate debug info, including call chain
   372  				result := fr.builder.CreateCall(callee, params, inst.Name())
   373  				if inst.Type().TypeKind() != llvm.VoidTypeKind {
   374  					fr.markDirty(result)
   375  					fr.locals[inst] = &LocalValue{fr.Eval, result}
   376  				}
   377  			case !callee.IsAFunction().IsNil():
   378  				// regular function
   379  				var params []Value
   380  				dirtyParams := false
   381  				for i := 0; i < inst.OperandsCount()-1; i++ {
   382  					local := fr.getLocal(inst.Operand(i))
   383  					if !local.IsConstant() {
   384  						dirtyParams = true
   385  					}
   386  					params = append(params, local)
   387  				}
   388  				var ret Value
   389  				scanResult := fr.Eval.hasSideEffects(callee)
   390  				if scanResult.severity == sideEffectLimited || dirtyParams && scanResult.severity != sideEffectAll {
   391  					// Side effect is bounded. This means the operation invokes
   392  					// side effects (like calling an external function) but it
   393  					// is known at compile time which side effects it invokes.
   394  					// This means the function can be called at runtime and the
   395  					// affected globals can be marked dirty at compile time.
   396  					llvmParams := make([]llvm.Value, len(params))
   397  					for i, param := range params {
   398  						llvmParams[i] = param.Value()
   399  					}
   400  					result := fr.builder.CreateCall(callee, llvmParams, inst.Name())
   401  					ret = &LocalValue{fr.Eval, result}
   402  					// mark all mentioned globals as dirty
   403  					for global := range scanResult.mentionsGlobals {
   404  						fr.markDirty(global)
   405  					}
   406  				} else {
   407  					// Side effect is one of:
   408  					//   * None: no side effects, can be fully interpreted at
   409  					//     compile time.
   410  					//   * Unbounded: cannot call at runtime so we'll try to
   411  					//     interpret anyway and hope for the best.
   412  					ret, err = fr.function(callee, params, fr.pkgName, indent+"    ")
   413  					if err != nil {
   414  						return nil, nil, err
   415  					}
   416  				}
   417  				if inst.Type().TypeKind() != llvm.VoidTypeKind {
   418  					fr.locals[inst] = ret
   419  				}
   420  			default:
   421  				// function pointers, etc.
   422  				return nil, nil, &Unsupported{inst}
   423  			}
   424  		case !inst.IsAExtractValueInst().IsNil():
   425  			agg := fr.getLocal(inst.Operand(0)).(*LocalValue) // must be constant
   426  			indices := inst.Indices()
   427  			if agg.Underlying.IsConstant() {
   428  				newValue := llvm.ConstExtractValue(agg.Underlying, indices)
   429  				fr.locals[inst] = fr.getValue(newValue)
   430  			} else {
   431  				if len(indices) != 1 {
   432  					return nil, nil, errors.New("cannot handle extractvalue with not exactly 1 index")
   433  				}
   434  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateExtractValue(agg.Underlying, int(indices[0]), inst.Name())}
   435  			}
   436  		case !inst.IsAInsertValueInst().IsNil():
   437  			agg := fr.getLocal(inst.Operand(0)).(*LocalValue) // must be constant
   438  			val := fr.getLocal(inst.Operand(1))
   439  			indices := inst.Indices()
   440  			if agg.IsConstant() && val.IsConstant() {
   441  				newValue := llvm.ConstInsertValue(agg.Underlying, val.Value(), indices)
   442  				fr.locals[inst] = &LocalValue{fr.Eval, newValue}
   443  			} else {
   444  				if len(indices) != 1 {
   445  					return nil, nil, errors.New("cannot handle insertvalue with not exactly 1 index")
   446  				}
   447  				fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateInsertValue(agg.Underlying, val.Value(), int(indices[0]), inst.Name())}
   448  			}
   449  
   450  		case !inst.IsAReturnInst().IsNil() && inst.OperandsCount() == 0:
   451  			return nil, nil, nil // ret void
   452  		case !inst.IsAReturnInst().IsNil() && inst.OperandsCount() == 1:
   453  			return fr.getLocal(inst.Operand(0)), nil, nil
   454  		case !inst.IsABranchInst().IsNil() && inst.OperandsCount() == 3:
   455  			// conditional branch (if/then/else)
   456  			cond := fr.getLocal(inst.Operand(0)).Value()
   457  			if cond.Type() != fr.Mod.Context().Int1Type() {
   458  				panic("expected an i1 in a branch instruction")
   459  			}
   460  			thenBB := inst.Operand(1)
   461  			elseBB := inst.Operand(2)
   462  			if !cond.IsConstant() {
   463  				return nil, nil, errors.New("interp: branch on a non-constant")
   464  			} else {
   465  				switch cond.ZExtValue() {
   466  				case 0: // false
   467  					return nil, []llvm.Value{thenBB}, nil // then
   468  				case 1: // true
   469  					return nil, []llvm.Value{elseBB}, nil // else
   470  				default:
   471  					panic("branch was not true or false")
   472  				}
   473  			}
   474  		case !inst.IsABranchInst().IsNil() && inst.OperandsCount() == 1:
   475  			// unconditional branch (goto)
   476  			return nil, []llvm.Value{inst.Operand(0)}, nil
   477  		case !inst.IsAUnreachableInst().IsNil():
   478  			// Unreachable was reached (e.g. after a call to panic()).
   479  			// Report this as an error, as it is not supposed to happen.
   480  			return nil, nil, ErrUnreachable
   481  
   482  		default:
   483  			return nil, nil, &Unsupported{inst}
   484  		}
   485  	}
   486  
   487  	panic("interp: reached end of basic block without terminator")
   488  }
   489  
   490  // Get the Value for an operand, which is a constant value of some sort.
   491  func (fr *frame) getLocal(v llvm.Value) Value {
   492  	if ret, ok := fr.locals[v]; ok {
   493  		return ret
   494  	} else if value := fr.getValue(v); value != nil {
   495  		return value
   496  	} else {
   497  		panic("cannot find value")
   498  	}
   499  }