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.
Этот коммит содержится в:
родитель
0db26b0662
коммит
24ff2d1ee2
5 изменённых файлов: 40 добавлений и 27 удалений
|
@ -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{}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче