interp: fix several bugs related to constant vs dirty values
* Loading from a dirty global must be done at runtime (!). For some reason this wasn't already the case. * Global variables somehow had IsConstant() the wrong way round, returning the inverse from what they should. * Do binary and logical operations at runtime if necessary, relying on const propagation in the IR builder. * Don't try to interpret functions that take a dirty parameter. Call them at runtime.
Этот коммит содержится в:
родитель
eccbd572eb
коммит
7d8b269f2e
2 изменённых файлов: 28 добавлений и 23 удалений
|
@ -38,43 +38,43 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
switch inst.InstructionOpcode() {
|
switch inst.InstructionOpcode() {
|
||||||
// Standard binary operators
|
// Standard binary operators
|
||||||
case llvm.Add:
|
case llvm.Add:
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstAdd(lhs, rhs)}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateAdd(lhs, rhs, "")}
|
||||||
case llvm.FAdd:
|
case llvm.FAdd:
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstFAdd(lhs, rhs)}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFAdd(lhs, rhs, "")}
|
||||||
case llvm.Sub:
|
case llvm.Sub:
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstSub(lhs, rhs)}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSub(lhs, rhs, "")}
|
||||||
case llvm.FSub:
|
case llvm.FSub:
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstFSub(lhs, rhs)}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFSub(lhs, rhs, "")}
|
||||||
case llvm.Mul:
|
case llvm.Mul:
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstMul(lhs, rhs)}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateMul(lhs, rhs, "")}
|
||||||
case llvm.FMul:
|
case llvm.FMul:
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstFMul(lhs, rhs)}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFMul(lhs, rhs, "")}
|
||||||
case llvm.UDiv:
|
case llvm.UDiv:
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstUDiv(lhs, rhs)}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateUDiv(lhs, rhs, "")}
|
||||||
case llvm.SDiv:
|
case llvm.SDiv:
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstSDiv(lhs, rhs)}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSDiv(lhs, rhs, "")}
|
||||||
case llvm.FDiv:
|
case llvm.FDiv:
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstFDiv(lhs, rhs)}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFDiv(lhs, rhs, "")}
|
||||||
case llvm.URem:
|
case llvm.URem:
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstURem(lhs, rhs)}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateURem(lhs, rhs, "")}
|
||||||
case llvm.SRem:
|
case llvm.SRem:
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstSRem(lhs, rhs)}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSRem(lhs, rhs, "")}
|
||||||
case llvm.FRem:
|
case llvm.FRem:
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstFRem(lhs, rhs)}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFRem(lhs, rhs, "")}
|
||||||
|
|
||||||
// Logical operators
|
// Logical operators
|
||||||
case llvm.Shl:
|
case llvm.Shl:
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstShl(lhs, rhs)}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateShl(lhs, rhs, "")}
|
||||||
case llvm.LShr:
|
case llvm.LShr:
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstLShr(lhs, rhs)}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateLShr(lhs, rhs, "")}
|
||||||
case llvm.AShr:
|
case llvm.AShr:
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstAShr(lhs, rhs)}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateAShr(lhs, rhs, "")}
|
||||||
case llvm.And:
|
case llvm.And:
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstAnd(lhs, rhs)}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateAnd(lhs, rhs, "")}
|
||||||
case llvm.Or:
|
case llvm.Or:
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstOr(lhs, rhs)}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateOr(lhs, rhs, "")}
|
||||||
case llvm.Xor:
|
case llvm.Xor:
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstXor(lhs, rhs)}
|
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateXor(lhs, rhs, "")}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, nil, &Unsupported{inst}
|
return nil, nil, &Unsupported{inst}
|
||||||
|
@ -90,7 +90,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
case !inst.IsALoadInst().IsNil():
|
case !inst.IsALoadInst().IsNil():
|
||||||
operand := fr.getLocal(inst.Operand(0))
|
operand := fr.getLocal(inst.Operand(0))
|
||||||
var value llvm.Value
|
var value llvm.Value
|
||||||
if inst.IsVolatile() {
|
if !operand.IsConstant() || inst.IsVolatile() {
|
||||||
value = fr.builder.CreateLoad(operand.Value(), inst.Name())
|
value = fr.builder.CreateLoad(operand.Value(), inst.Name())
|
||||||
} else {
|
} else {
|
||||||
value = operand.Load()
|
value = operand.Load()
|
||||||
|
@ -325,12 +325,17 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
||||||
case !callee.IsAFunction().IsNil():
|
case !callee.IsAFunction().IsNil():
|
||||||
// regular function
|
// regular function
|
||||||
var params []Value
|
var params []Value
|
||||||
|
dirtyParams := false
|
||||||
for i := 0; i < inst.OperandsCount()-1; i++ {
|
for i := 0; i < inst.OperandsCount()-1; i++ {
|
||||||
params = append(params, fr.getLocal(inst.Operand(i)))
|
local := fr.getLocal(inst.Operand(i))
|
||||||
|
if !local.IsConstant() {
|
||||||
|
dirtyParams = true
|
||||||
|
}
|
||||||
|
params = append(params, local)
|
||||||
}
|
}
|
||||||
var ret Value
|
var ret Value
|
||||||
scanResult := fr.Eval.hasSideEffects(callee)
|
scanResult := fr.Eval.hasSideEffects(callee)
|
||||||
if scanResult.severity == sideEffectLimited {
|
if scanResult.severity == sideEffectLimited || dirtyParams && scanResult.severity != sideEffectAll {
|
||||||
// Side effect is bounded. This means the operation invokes
|
// Side effect is bounded. This means the operation invokes
|
||||||
// side effects (like calling an external function) but it
|
// side effects (like calling an external function) but it
|
||||||
// is known at compile time which side effects it invokes.
|
// is known at compile time which side effects it invokes.
|
||||||
|
|
|
@ -127,9 +127,9 @@ func (v *GlobalValue) Type() llvm.Type {
|
||||||
// IsConstant returns true if this global is not dirty, false otherwise.
|
// IsConstant returns true if this global is not dirty, false otherwise.
|
||||||
func (v *GlobalValue) IsConstant() bool {
|
func (v *GlobalValue) IsConstant() bool {
|
||||||
if _, ok := v.Eval.dirtyGlobals[v.Underlying]; ok {
|
if _, ok := v.Eval.dirtyGlobals[v.Underlying]; ok {
|
||||||
return true
|
return false
|
||||||
}
|
}
|
||||||
return false
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load returns the initializer of the global variable.
|
// Load returns the initializer of the global variable.
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче