Browse Source

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.
pull/755/head
Ayke van Laethem 5 years ago
committed by Ron Evans
parent
commit
24ff2d1ee2
  1. 3
      interp/errors.go
  2. 26
      interp/frame.go
  3. 1
      interp/interp.go
  4. 31
      interp/scan.go
  5. 6
      interp/scan_test.go

3
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{}

26
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")
}
}

1
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

31
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() {

6
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 {

Loading…
Cancel
Save