diff options
author | Ayke van Laethem <[email protected]> | 2019-11-26 14:49:44 +0100 |
---|---|---|
committer | Ron Evans <[email protected]> | 2019-11-26 15:45:39 +0100 |
commit | 24ff2d1ee228a6dc78ea61a228743c04e37e972e (patch) | |
tree | 08fea7b9b2adf082153bbfe4bd4765e9bf22a527 | |
parent | 0db26b06624f566790eb799f0252d6a50817e7fd (diff) | |
download | tinygo-24ff2d1ee228a6dc78ea61a228743c04e37e972e.tar.gz tinygo-24ff2d1ee228a6dc78ea61a228743c04e37e972e.zip |
interp: replace many panics with error messages
This commit replaces most panics in interp/frame.go and interp/scan.go
with real error messages. The remaining ones are panics that should not
happen when working with valid IR.
-rw-r--r-- | interp/errors.go | 3 | ||||
-rw-r--r-- | interp/frame.go | 26 | ||||
-rw-r--r-- | interp/interp.go | 1 | ||||
-rw-r--r-- | interp/scan.go | 31 | ||||
-rw-r--r-- | interp/scan_test.go | 6 |
5 files changed, 40 insertions, 27 deletions
diff --git a/interp/errors.go b/interp/errors.go index c3cf0a1ad..26b3ea377 100644 --- a/interp/errors.go +++ b/interp/errors.go @@ -78,6 +78,9 @@ func errorAt(inst llvm.Value, msg string) scanner.Error { // getPosition returns the position information for the given instruction, as // far as it is available. func getPosition(inst llvm.Value) token.Position { + if inst.IsAInstruction().IsNil() { + return token.Position{} + } loc := inst.InstructionDebugLoc() if loc.IsNil() { return token.Position{} diff --git a/interp/frame.go b/interp/frame.go index c42dcf253..04a9fa0cd 100644 --- a/interp/frame.go +++ b/interp/frame.go @@ -97,7 +97,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re value = operand.Load() } if value.Type() != inst.Type() { - panic("interp: load: type does not match") + return nil, nil, fr.errorAt(inst, "interp: load: type does not match") } fr.locals[inst] = fr.getValue(value) case !inst.IsAStoreInst().IsNil(): @@ -121,15 +121,13 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re // Not a constant operation. // This should be detected by the scanner, but isn't at the // moment. - panic("todo: non-const gep") + return nil, nil, fr.errorAt(inst, "todo: non-const gep") } indices[i] = uint32(operand.Value().ZExtValue()) } result := value.GetElementPtr(indices) if result.Type() != inst.Type() { - println(" expected:", inst.Type().String()) - println(" actual: ", result.Type().String()) - panic("interp: gep: type does not match") + return nil, nil, fr.errorAt(inst, "interp: gep: type does not match") } fr.locals[inst] = result @@ -184,7 +182,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re } } // It is not possible in Go to bitcast a map value to a pointer. - panic("unimplemented: bitcast of map") + return nil, nil, fr.errorAt(inst, "unimplemented: bitcast of map") } value := fr.getLocal(operand).(*LocalValue) fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateBitCast(value.Value(), inst.Type(), "")} @@ -397,16 +395,16 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re typecode := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying interfaceMethodSet := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying if typecode.IsAConstantExpr().IsNil() || typecode.Opcode() != llvm.PtrToInt { - panic("interp: expected typecode to be a ptrtoint") + return nil, nil, fr.errorAt(inst, "interp: expected typecode to be a ptrtoint") } typecode = typecode.Operand(0) if interfaceMethodSet.IsAConstantExpr().IsNil() || interfaceMethodSet.Opcode() != llvm.GetElementPtr { - panic("interp: expected method set in runtime.interfaceImplements to be a constant gep") + return nil, nil, fr.errorAt(inst, "interp: expected method set in runtime.interfaceImplements to be a constant gep") } interfaceMethodSet = interfaceMethodSet.Operand(0).Initializer() methodSet := llvm.ConstExtractValue(typecode.Initializer(), []uint32{1}) if methodSet.IsAConstantExpr().IsNil() || methodSet.Opcode() != llvm.GetElementPtr { - panic("interp: expected method set to be a constant gep") + return nil, nil, fr.errorAt(inst, "interp: expected method set to be a constant gep") } methodSet = methodSet.Operand(0).Initializer() @@ -476,7 +474,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re params = append(params, local) } var ret Value - scanResult := fr.Eval.hasSideEffects(callee) + scanResult, err := fr.hasSideEffects(callee) + if err != nil { + return nil, nil, err + } if scanResult.severity == sideEffectLimited || dirtyParams && scanResult.severity != sideEffectAll { // Side effect is bounded. This means the operation invokes // side effects (like calling an external function) but it @@ -545,7 +546,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re // conditional branch (if/then/else) cond := fr.getLocal(inst.Operand(0)).Value() if cond.Type() != fr.Mod.Context().Int1Type() { - panic("expected an i1 in a branch instruction") + return nil, nil, fr.errorAt(inst, "expected an i1 in a branch instruction") } thenBB := inst.Operand(1) elseBB := inst.Operand(2) @@ -563,7 +564,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re case llvm.ConstInt(fr.Mod.Context().Int1Type(), 1, false): // true return nil, []llvm.Value{elseBB}, nil // else default: - panic("branch was not true or false") + return nil, nil, fr.errorAt(inst, "branch was not true or false") } case !inst.IsABranchInst().IsNil() && inst.OperandsCount() == 1: // unconditional branch (goto) @@ -589,6 +590,7 @@ func (fr *frame) getLocal(v llvm.Value) Value { } else if value := fr.getValue(v); value != nil { return value } else { + // This should not happen under normal circumstances. panic("cannot find value") } } diff --git a/interp/interp.go b/interp/interp.go index f91c31f3e..cea2316f5 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -27,7 +27,6 @@ type Eval struct { type evalPackage struct { *Eval packagePath string - errors []error } // Run evaluates the function with the given name and then eliminates all diff --git a/interp/scan.go b/interp/scan.go index b8c68117f..7a256c27c 100644 --- a/interp/scan.go +++ b/interp/scan.go @@ -38,33 +38,33 @@ type sideEffectResult struct { // hasSideEffects scans this function and all descendants, recursively. It // returns whether this function has side effects and if it does, which globals // it mentions anywhere in this function or any called functions. -func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult { +func (e *evalPackage) hasSideEffects(fn llvm.Value) (*sideEffectResult, error) { switch fn.Name() { case "runtime.alloc": // Cannot be scanned but can be interpreted. - return &sideEffectResult{severity: sideEffectNone} + return &sideEffectResult{severity: sideEffectNone}, nil case "runtime.nanotime": // Fixed value at compile time. - return &sideEffectResult{severity: sideEffectNone} + return &sideEffectResult{severity: sideEffectNone}, nil case "runtime._panic": - return &sideEffectResult{severity: sideEffectLimited} + return &sideEffectResult{severity: sideEffectLimited}, nil case "runtime.interfaceImplements": - return &sideEffectResult{severity: sideEffectNone} + return &sideEffectResult{severity: sideEffectNone}, nil case "runtime.sliceCopy": - return &sideEffectResult{severity: sideEffectNone} + return &sideEffectResult{severity: sideEffectNone}, nil case "runtime.trackPointer": - return &sideEffectResult{severity: sideEffectNone} + return &sideEffectResult{severity: sideEffectNone}, nil case "llvm.dbg.value": - return &sideEffectResult{severity: sideEffectNone} + return &sideEffectResult{severity: sideEffectNone}, nil } if fn.IsDeclaration() { - return &sideEffectResult{severity: sideEffectLimited} + return &sideEffectResult{severity: sideEffectLimited}, nil } if e.sideEffectFuncs == nil { e.sideEffectFuncs = make(map[llvm.Value]*sideEffectResult) } if se, ok := e.sideEffectFuncs[fn]; ok { - return se + return se, nil } result := &sideEffectResult{ severity: sideEffectInProgress, @@ -75,6 +75,7 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult { for bb := fn.EntryBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) { for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) { if inst.IsAInstruction().IsNil() { + // Should not happen in valid IR. panic("not an instruction") } @@ -91,7 +92,7 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult { switch inst.InstructionOpcode() { case llvm.IndirectBr, llvm.Invoke: // Not emitted by the compiler. - panic("unknown instructions") + return nil, e.errorAt(inst, "unknown instructions") case llvm.Call: child := inst.CalledValue() if !child.IsAInlineAsm().IsNil() { @@ -117,7 +118,10 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult { } continue } - childSideEffects := e.hasSideEffects(child) + childSideEffects, err := e.hasSideEffects(child) + if err != nil { + return nil, err + } switch childSideEffects.severity { case sideEffectInProgress, sideEffectNone: // no side effects or recursive function - continue scanning @@ -159,7 +163,7 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult { // No side effect was reported for this function. result.severity = sideEffectNone } - return result + return result, nil } // hasLocalSideEffects checks whether the given instruction flows into a branch @@ -174,6 +178,7 @@ func (e *Eval) hasLocalSideEffects(dirtyLocals map[llvm.Value]struct{}, inst llv for use := inst.FirstUse(); !use.IsNil(); use = use.NextUse() { user := use.User() if user.IsAInstruction().IsNil() { + // Should not happen in valid IR. panic("user not an instruction") } switch user.InstructionOpcode() { diff --git a/interp/scan_test.go b/interp/scan_test.go index 2150f108e..9d3e15dbe 100644 --- a/interp/scan_test.go +++ b/interp/scan_test.go @@ -59,7 +59,11 @@ func TestScan(t *testing.T) { t.Errorf("scan test: could not find tested function %s in the IR", tc.name) continue } - result := e.hasSideEffects(fn) + evalPkg := &evalPackage{e, "testdata"} + result, err := evalPkg.hasSideEffects(fn) + if err != nil { + t.Errorf("scan test: failed to scan %s for side effects: %v", fn.Name(), err) + } // Check whether the result is what we expect. if result.severity != tc.severity { |