interp: fix recursive scanning

There were a few issues that made interp not perform as it should:

  * The scan was non-recursive due to a bug.
  * Recursive scanning would always return the severity level, which is
    not always the best strategy.
Этот коммит содержится в:
Ayke van Laethem 2019-02-05 00:20:38 +01:00 коммит произвёл Ron Evans
родитель bece6b9648
коммит 6ae4b43eb2

Просмотреть файл

@ -24,6 +24,13 @@ type sideEffectResult struct {
// returns whether this function has side effects and if it does, which globals // returns whether this function has side effects and if it does, which globals
// it mentions anywhere in this function or any called functions. // it mentions anywhere in this function or any called functions.
func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult { func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
switch fn.Name() {
case "runtime.alloc":
// Cannot be scanned but can be interpreted.
return &sideEffectResult{severity: sideEffectNone}
case "runtime._panic":
return &sideEffectResult{severity: sideEffectLimited}
}
if e.sideEffectFuncs == nil { if e.sideEffectFuncs == nil {
e.sideEffectFuncs = make(map[llvm.Value]*sideEffectResult) e.sideEffectFuncs = make(map[llvm.Value]*sideEffectResult)
} }
@ -73,25 +80,32 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
result.updateSeverity(sideEffectAll) result.updateSeverity(sideEffectAll)
continue continue
} }
name := child.Name()
if child.IsDeclaration() { if child.IsDeclaration() {
if name == "runtime.makeInterface" { switch child.Name() {
case "runtime.makeInterface":
// Can be interpreted so does not have side effects. // Can be interpreted so does not have side effects.
continue continue
} }
// External function call. Assume only limited side effects // External function call. Assume only limited side effects
// (no affected globals, etc.). // (no affected globals, etc.).
if result.hasLocalSideEffects(dirtyLocals, inst) { if e.hasLocalSideEffects(dirtyLocals, inst) {
result.updateSeverity(sideEffectLimited) result.updateSeverity(sideEffectLimited)
} }
continue continue
} }
childSideEffects := e.hasSideEffects(fn) childSideEffects := e.hasSideEffects(child)
switch childSideEffects.severity { switch childSideEffects.severity {
case sideEffectInProgress, sideEffectNone: case sideEffectInProgress, sideEffectNone:
// no side effects or recursive function - continue scanning // no side effects or recursive function - continue scanning
case sideEffectLimited:
// The return value may be problematic.
if e.hasLocalSideEffects(dirtyLocals, inst) {
result.updateSeverity(sideEffectLimited)
}
case sideEffectAll:
result.updateSeverity(sideEffectAll)
default: default:
result.update(childSideEffects) panic("unreachable")
} }
case llvm.Load, llvm.Store: case llvm.Load, llvm.Store:
if inst.IsVolatile() { if inst.IsVolatile() {
@ -118,7 +132,7 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
// hasLocalSideEffects checks whether the given instruction flows into a branch // hasLocalSideEffects checks whether the given instruction flows into a branch
// or return instruction, in which case the whole function must be marked as // or return instruction, in which case the whole function must be marked as
// having side effects and be called at runtime. // having side effects and be called at runtime.
func (r *sideEffectResult) hasLocalSideEffects(dirtyLocals map[llvm.Value]struct{}, inst llvm.Value) bool { func (e *Eval) hasLocalSideEffects(dirtyLocals map[llvm.Value]struct{}, inst llvm.Value) bool {
if _, ok := dirtyLocals[inst]; ok { if _, ok := dirtyLocals[inst]; ok {
// It is already known that this local is dirty. // It is already known that this local is dirty.
return true return true
@ -156,7 +170,7 @@ func (r *sideEffectResult) hasLocalSideEffects(dirtyLocals map[llvm.Value]struct
// For a list: // For a list:
// https://godoc.org/github.com/llvm-mirror/llvm/bindings/go/llvm#Opcode // https://godoc.org/github.com/llvm-mirror/llvm/bindings/go/llvm#Opcode
dirtyLocals[user] = struct{}{} dirtyLocals[user] = struct{}{}
if r.hasLocalSideEffects(dirtyLocals, user) { if e.hasLocalSideEffects(dirtyLocals, user) {
return true return true
} }
} }