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.
Этот коммит содержится в:
Ayke van Laethem 2018-11-14 12:20:06 +01:00
родитель eccbd572eb
коммит 7d8b269f2e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E97FF5335DFDFDED
2 изменённых файлов: 28 добавлений и 23 удалений

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

@ -38,43 +38,43 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
switch inst.InstructionOpcode() {
// Standard binary operators
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:
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstFAdd(lhs, rhs)}
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFAdd(lhs, rhs, "")}
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:
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstFSub(lhs, rhs)}
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFSub(lhs, rhs, "")}
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:
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstFMul(lhs, rhs)}
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFMul(lhs, rhs, "")}
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:
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstSDiv(lhs, rhs)}
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateSDiv(lhs, rhs, "")}
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:
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstURem(lhs, rhs)}
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateURem(lhs, rhs, "")}
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:
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstFRem(lhs, rhs)}
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateFRem(lhs, rhs, "")}
// Logical operators
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:
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstLShr(lhs, rhs)}
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateLShr(lhs, rhs, "")}
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:
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstAnd(lhs, rhs)}
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateAnd(lhs, rhs, "")}
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:
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstXor(lhs, rhs)}
fr.locals[inst] = &LocalValue{fr.Eval, fr.builder.CreateXor(lhs, rhs, "")}
default:
return nil, nil, &Unsupported{inst}
@ -90,7 +90,7 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
case !inst.IsALoadInst().IsNil():
operand := fr.getLocal(inst.Operand(0))
var value llvm.Value
if inst.IsVolatile() {
if !operand.IsConstant() || inst.IsVolatile() {
value = fr.builder.CreateLoad(operand.Value(), inst.Name())
} else {
value = operand.Load()
@ -325,12 +325,17 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
case !callee.IsAFunction().IsNil():
// regular function
var params []Value
dirtyParams := false
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
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 effects (like calling an external function) but it
// 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.
func (v *GlobalValue) IsConstant() bool {
if _, ok := v.Eval.dirtyGlobals[v.Underlying]; ok {
return true
return false
}
return false
return true
}
// Load returns the initializer of the global variable.