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 }