Skip to content

Commit 24ff2d1

Browse files
aykevldeadprogram
authored andcommitted
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.
1 parent 0db26b0 commit 24ff2d1

File tree

5 files changed

+40
-27
lines changed

5 files changed

+40
-27
lines changed

interp/errors.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ func errorAt(inst llvm.Value, msg string) scanner.Error {
7878
// getPosition returns the position information for the given instruction, as
7979
// far as it is available.
8080
func getPosition(inst llvm.Value) token.Position {
81+
if inst.IsAInstruction().IsNil() {
82+
return token.Position{}
83+
}
8184
loc := inst.InstructionDebugLoc()
8285
if loc.IsNil() {
8386
return token.Position{}

interp/frame.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
9797
value = operand.Load()
9898
}
9999
if value.Type() != inst.Type() {
100-
panic("interp: load: type does not match")
100+
return nil, nil, fr.errorAt(inst, "interp: load: type does not match")
101101
}
102102
fr.locals[inst] = fr.getValue(value)
103103
case !inst.IsAStoreInst().IsNil():
@@ -121,15 +121,13 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
121121
// Not a constant operation.
122122
// This should be detected by the scanner, but isn't at the
123123
// moment.
124-
panic("todo: non-const gep")
124+
return nil, nil, fr.errorAt(inst, "todo: non-const gep")
125125
}
126126
indices[i] = uint32(operand.Value().ZExtValue())
127127
}
128128
result := value.GetElementPtr(indices)
129129
if result.Type() != inst.Type() {
130-
println(" expected:", inst.Type().String())
131-
println(" actual: ", result.Type().String())
132-
panic("interp: gep: type does not match")
130+
return nil, nil, fr.errorAt(inst, "interp: gep: type does not match")
133131
}
134132
fr.locals[inst] = result
135133

@@ -184,7 +182,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
184182
}
185183
}
186184
// It is not possible in Go to bitcast a map value to a pointer.
187-
panic("unimplemented: bitcast of map")
185+
return nil, nil, fr.errorAt(inst, "unimplemented: bitcast of map")
188186
}
189187
value := fr.getLocal(operand).(*LocalValue)
190188
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
397395
typecode := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying
398396
interfaceMethodSet := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying
399397
if typecode.IsAConstantExpr().IsNil() || typecode.Opcode() != llvm.PtrToInt {
400-
panic("interp: expected typecode to be a ptrtoint")
398+
return nil, nil, fr.errorAt(inst, "interp: expected typecode to be a ptrtoint")
401399
}
402400
typecode = typecode.Operand(0)
403401
if interfaceMethodSet.IsAConstantExpr().IsNil() || interfaceMethodSet.Opcode() != llvm.GetElementPtr {
404-
panic("interp: expected method set in runtime.interfaceImplements to be a constant gep")
402+
return nil, nil, fr.errorAt(inst, "interp: expected method set in runtime.interfaceImplements to be a constant gep")
405403
}
406404
interfaceMethodSet = interfaceMethodSet.Operand(0).Initializer()
407405
methodSet := llvm.ConstExtractValue(typecode.Initializer(), []uint32{1})
408406
if methodSet.IsAConstantExpr().IsNil() || methodSet.Opcode() != llvm.GetElementPtr {
409-
panic("interp: expected method set to be a constant gep")
407+
return nil, nil, fr.errorAt(inst, "interp: expected method set to be a constant gep")
410408
}
411409
methodSet = methodSet.Operand(0).Initializer()
412410

@@ -476,7 +474,10 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
476474
params = append(params, local)
477475
}
478476
var ret Value
479-
scanResult := fr.Eval.hasSideEffects(callee)
477+
scanResult, err := fr.hasSideEffects(callee)
478+
if err != nil {
479+
return nil, nil, err
480+
}
480481
if scanResult.severity == sideEffectLimited || dirtyParams && scanResult.severity != sideEffectAll {
481482
// Side effect is bounded. This means the operation invokes
482483
// side effects (like calling an external function) but it
@@ -545,7 +546,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
545546
// conditional branch (if/then/else)
546547
cond := fr.getLocal(inst.Operand(0)).Value()
547548
if cond.Type() != fr.Mod.Context().Int1Type() {
548-
panic("expected an i1 in a branch instruction")
549+
return nil, nil, fr.errorAt(inst, "expected an i1 in a branch instruction")
549550
}
550551
thenBB := inst.Operand(1)
551552
elseBB := inst.Operand(2)
@@ -563,7 +564,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
563564
case llvm.ConstInt(fr.Mod.Context().Int1Type(), 1, false): // true
564565
return nil, []llvm.Value{elseBB}, nil // else
565566
default:
566-
panic("branch was not true or false")
567+
return nil, nil, fr.errorAt(inst, "branch was not true or false")
567568
}
568569
case !inst.IsABranchInst().IsNil() && inst.OperandsCount() == 1:
569570
// unconditional branch (goto)
@@ -589,6 +590,7 @@ func (fr *frame) getLocal(v llvm.Value) Value {
589590
} else if value := fr.getValue(v); value != nil {
590591
return value
591592
} else {
593+
// This should not happen under normal circumstances.
592594
panic("cannot find value")
593595
}
594596
}

interp/interp.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ type Eval struct {
2727
type evalPackage struct {
2828
*Eval
2929
packagePath string
30-
errors []error
3130
}
3231

3332
// Run evaluates the function with the given name and then eliminates all

interp/scan.go

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,33 +38,33 @@ type sideEffectResult struct {
3838
// hasSideEffects scans this function and all descendants, recursively. It
3939
// returns whether this function has side effects and if it does, which globals
4040
// it mentions anywhere in this function or any called functions.
41-
func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
41+
func (e *evalPackage) hasSideEffects(fn llvm.Value) (*sideEffectResult, error) {
4242
switch fn.Name() {
4343
case "runtime.alloc":
4444
// Cannot be scanned but can be interpreted.
45-
return &sideEffectResult{severity: sideEffectNone}
45+
return &sideEffectResult{severity: sideEffectNone}, nil
4646
case "runtime.nanotime":
4747
// Fixed value at compile time.
48-
return &sideEffectResult{severity: sideEffectNone}
48+
return &sideEffectResult{severity: sideEffectNone}, nil
4949
case "runtime._panic":
50-
return &sideEffectResult{severity: sideEffectLimited}
50+
return &sideEffectResult{severity: sideEffectLimited}, nil
5151
case "runtime.interfaceImplements":
52-
return &sideEffectResult{severity: sideEffectNone}
52+
return &sideEffectResult{severity: sideEffectNone}, nil
5353
case "runtime.sliceCopy":
54-
return &sideEffectResult{severity: sideEffectNone}
54+
return &sideEffectResult{severity: sideEffectNone}, nil
5555
case "runtime.trackPointer":
56-
return &sideEffectResult{severity: sideEffectNone}
56+
return &sideEffectResult{severity: sideEffectNone}, nil
5757
case "llvm.dbg.value":
58-
return &sideEffectResult{severity: sideEffectNone}
58+
return &sideEffectResult{severity: sideEffectNone}, nil
5959
}
6060
if fn.IsDeclaration() {
61-
return &sideEffectResult{severity: sideEffectLimited}
61+
return &sideEffectResult{severity: sideEffectLimited}, nil
6262
}
6363
if e.sideEffectFuncs == nil {
6464
e.sideEffectFuncs = make(map[llvm.Value]*sideEffectResult)
6565
}
6666
if se, ok := e.sideEffectFuncs[fn]; ok {
67-
return se
67+
return se, nil
6868
}
6969
result := &sideEffectResult{
7070
severity: sideEffectInProgress,
@@ -75,6 +75,7 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
7575
for bb := fn.EntryBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
7676
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
7777
if inst.IsAInstruction().IsNil() {
78+
// Should not happen in valid IR.
7879
panic("not an instruction")
7980
}
8081

@@ -91,7 +92,7 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
9192
switch inst.InstructionOpcode() {
9293
case llvm.IndirectBr, llvm.Invoke:
9394
// Not emitted by the compiler.
94-
panic("unknown instructions")
95+
return nil, e.errorAt(inst, "unknown instructions")
9596
case llvm.Call:
9697
child := inst.CalledValue()
9798
if !child.IsAInlineAsm().IsNil() {
@@ -117,7 +118,10 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
117118
}
118119
continue
119120
}
120-
childSideEffects := e.hasSideEffects(child)
121+
childSideEffects, err := e.hasSideEffects(child)
122+
if err != nil {
123+
return nil, err
124+
}
121125
switch childSideEffects.severity {
122126
case sideEffectInProgress, sideEffectNone:
123127
// no side effects or recursive function - continue scanning
@@ -159,7 +163,7 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
159163
// No side effect was reported for this function.
160164
result.severity = sideEffectNone
161165
}
162-
return result
166+
return result, nil
163167
}
164168

165169
// 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
174178
for use := inst.FirstUse(); !use.IsNil(); use = use.NextUse() {
175179
user := use.User()
176180
if user.IsAInstruction().IsNil() {
181+
// Should not happen in valid IR.
177182
panic("user not an instruction")
178183
}
179184
switch user.InstructionOpcode() {

interp/scan_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ func TestScan(t *testing.T) {
5959
t.Errorf("scan test: could not find tested function %s in the IR", tc.name)
6060
continue
6161
}
62-
result := e.hasSideEffects(fn)
62+
evalPkg := &evalPackage{e, "testdata"}
63+
result, err := evalPkg.hasSideEffects(fn)
64+
if err != nil {
65+
t.Errorf("scan test: failed to scan %s for side effects: %v", fn.Name(), err)
66+
}
6367

6468
// Check whether the result is what we expect.
6569
if result.severity != tc.severity {

0 commit comments

Comments
 (0)