interp: add support for constant icmp instructions
These instructions sometimes pop up in LLVM 15, but they never occured in LLVM 14. Implementing them is relatively straightforward: simply generalize the code that already exists for llvm.ICmp interpreting.
Этот коммит содержится в:
родитель
0ddcf4af96
коммит
08a51535d4
4 изменённых файлов: 73 добавлений и 38 удалений
|
@ -723,46 +723,9 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||||
locals[inst.localIndex] = newagg
|
locals[inst.localIndex] = newagg
|
||||||
case llvm.ICmp:
|
case llvm.ICmp:
|
||||||
predicate := llvm.IntPredicate(operands[2].(literalValue).value.(uint8))
|
predicate := llvm.IntPredicate(operands[2].(literalValue).value.(uint8))
|
||||||
var result bool
|
|
||||||
lhs := operands[0]
|
lhs := operands[0]
|
||||||
rhs := operands[1]
|
rhs := operands[1]
|
||||||
switch predicate {
|
result := r.interpretICmp(lhs, rhs, predicate)
|
||||||
case llvm.IntEQ, llvm.IntNE:
|
|
||||||
lhsPointer, lhsErr := lhs.asPointer(r)
|
|
||||||
rhsPointer, rhsErr := rhs.asPointer(r)
|
|
||||||
if (lhsErr == nil) != (rhsErr == nil) {
|
|
||||||
// Fast path: only one is a pointer, so they can't be equal.
|
|
||||||
result = false
|
|
||||||
} else if lhsErr == nil {
|
|
||||||
// Both must be nil, so both are pointers.
|
|
||||||
// Compare them directly.
|
|
||||||
result = lhsPointer.equal(rhsPointer)
|
|
||||||
} else {
|
|
||||||
// Fall back to generic comparison.
|
|
||||||
result = lhs.asRawValue(r).equal(rhs.asRawValue(r))
|
|
||||||
}
|
|
||||||
if predicate == llvm.IntNE {
|
|
||||||
result = !result
|
|
||||||
}
|
|
||||||
case llvm.IntUGT:
|
|
||||||
result = lhs.Uint() > rhs.Uint()
|
|
||||||
case llvm.IntUGE:
|
|
||||||
result = lhs.Uint() >= rhs.Uint()
|
|
||||||
case llvm.IntULT:
|
|
||||||
result = lhs.Uint() < rhs.Uint()
|
|
||||||
case llvm.IntULE:
|
|
||||||
result = lhs.Uint() <= rhs.Uint()
|
|
||||||
case llvm.IntSGT:
|
|
||||||
result = lhs.Int() > rhs.Int()
|
|
||||||
case llvm.IntSGE:
|
|
||||||
result = lhs.Int() >= rhs.Int()
|
|
||||||
case llvm.IntSLT:
|
|
||||||
result = lhs.Int() < rhs.Int()
|
|
||||||
case llvm.IntSLE:
|
|
||||||
result = lhs.Int() <= rhs.Int()
|
|
||||||
default:
|
|
||||||
return nil, mem, r.errorAt(inst, errors.New("interp: unsupported icmp"))
|
|
||||||
}
|
|
||||||
if result {
|
if result {
|
||||||
locals[inst.localIndex] = literalValue{uint8(1)}
|
locals[inst.localIndex] = literalValue{uint8(1)}
|
||||||
} else {
|
} else {
|
||||||
|
@ -948,6 +911,51 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||||
return nil, mem, r.errorAt(bb.instructions[len(bb.instructions)-1], errors.New("interp: reached end of basic block without terminator"))
|
return nil, mem, r.errorAt(bb.instructions[len(bb.instructions)-1], errors.New("interp: reached end of basic block without terminator"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Interpret an icmp instruction. Doesn't have side effects, only returns the
|
||||||
|
// output of the comparison.
|
||||||
|
func (r *runner) interpretICmp(lhs, rhs value, predicate llvm.IntPredicate) bool {
|
||||||
|
switch predicate {
|
||||||
|
case llvm.IntEQ, llvm.IntNE:
|
||||||
|
var result bool
|
||||||
|
lhsPointer, lhsErr := lhs.asPointer(r)
|
||||||
|
rhsPointer, rhsErr := rhs.asPointer(r)
|
||||||
|
if (lhsErr == nil) != (rhsErr == nil) {
|
||||||
|
// Fast path: only one is a pointer, so they can't be equal.
|
||||||
|
result = false
|
||||||
|
} else if lhsErr == nil {
|
||||||
|
// Both must be nil, so both are pointers.
|
||||||
|
// Compare them directly.
|
||||||
|
result = lhsPointer.equal(rhsPointer)
|
||||||
|
} else {
|
||||||
|
// Fall back to generic comparison.
|
||||||
|
result = lhs.asRawValue(r).equal(rhs.asRawValue(r))
|
||||||
|
}
|
||||||
|
if predicate == llvm.IntNE {
|
||||||
|
result = !result
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
case llvm.IntUGT:
|
||||||
|
return lhs.Uint() > rhs.Uint()
|
||||||
|
case llvm.IntUGE:
|
||||||
|
return lhs.Uint() >= rhs.Uint()
|
||||||
|
case llvm.IntULT:
|
||||||
|
return lhs.Uint() < rhs.Uint()
|
||||||
|
case llvm.IntULE:
|
||||||
|
return lhs.Uint() <= rhs.Uint()
|
||||||
|
case llvm.IntSGT:
|
||||||
|
return lhs.Int() > rhs.Int()
|
||||||
|
case llvm.IntSGE:
|
||||||
|
return lhs.Int() >= rhs.Int()
|
||||||
|
case llvm.IntSLT:
|
||||||
|
return lhs.Int() < rhs.Int()
|
||||||
|
case llvm.IntSLE:
|
||||||
|
return lhs.Int() <= rhs.Int()
|
||||||
|
default:
|
||||||
|
// _should_ be unreachable, until LLVM adds new icmp operands (unlikely)
|
||||||
|
panic("interp: unsupported icmp")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, mem *memoryView, indent string) *Error {
|
func (r *runner) runAtRuntime(fn *function, inst instruction, locals []value, mem *memoryView, indent string) *Error {
|
||||||
numOperands := inst.llvmInst.OperandsCount()
|
numOperands := inst.llvmInst.OperandsCount()
|
||||||
operands := make([]llvm.Value, numOperands)
|
operands := make([]llvm.Value, numOperands)
|
||||||
|
|
|
@ -1031,6 +1031,17 @@ func (v *rawValue) set(llvmValue llvm.Value, r *runner) {
|
||||||
for i := uint32(0); i < ptrSize; i++ {
|
for i := uint32(0); i < ptrSize; i++ {
|
||||||
v.buf[i] = ptrValue.pointer
|
v.buf[i] = ptrValue.pointer
|
||||||
}
|
}
|
||||||
|
case llvm.ICmp:
|
||||||
|
size := r.targetData.TypeAllocSize(llvmValue.Operand(0).Type())
|
||||||
|
lhs := newRawValue(uint32(size))
|
||||||
|
rhs := newRawValue(uint32(size))
|
||||||
|
lhs.set(llvmValue.Operand(0), r)
|
||||||
|
rhs.set(llvmValue.Operand(1), r)
|
||||||
|
if r.interpretICmp(lhs, rhs, llvmValue.IntPredicate()) {
|
||||||
|
v.buf[0] = 1 // result is true
|
||||||
|
} else {
|
||||||
|
v.buf[0] = 0 // result is false
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
llvmValue.Dump()
|
llvmValue.Dump()
|
||||||
println()
|
println()
|
||||||
|
|
15
interp/testdata/consteval.ll
предоставленный
15
interp/testdata/consteval.ll
предоставленный
|
@ -3,6 +3,7 @@ target triple = "x86_64--linux"
|
||||||
|
|
||||||
@intToPtrResult = global i8 0
|
@intToPtrResult = global i8 0
|
||||||
@ptrToIntResult = global i8 0
|
@ptrToIntResult = global i8 0
|
||||||
|
@icmpResult = global i8 0
|
||||||
@someArray = internal global {i16, i8, i8} zeroinitializer
|
@someArray = internal global {i16, i8, i8} zeroinitializer
|
||||||
@someArrayPointer = global i8* zeroinitializer
|
@someArrayPointer = global i8* zeroinitializer
|
||||||
|
|
||||||
|
@ -15,6 +16,7 @@ define internal void @main.init() {
|
||||||
call void @testIntToPtr()
|
call void @testIntToPtr()
|
||||||
call void @testPtrToInt()
|
call void @testPtrToInt()
|
||||||
call void @testConstGEP()
|
call void @testConstGEP()
|
||||||
|
call void @testICmp()
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,3 +50,16 @@ define internal void @testConstGEP() {
|
||||||
store i8* getelementptr inbounds (i8, i8* bitcast ({i16, i8, i8}* @someArray to i8*), i32 2), i8** @someArrayPointer
|
store i8* getelementptr inbounds (i8, i8* bitcast ({i16, i8, i8}* @someArray to i8*), i32 2), i8** @someArrayPointer
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define internal void @testICmp() {
|
||||||
|
br i1 icmp eq (i64 ptrtoint (i8* @ptrToIntResult to i64), i64 0), label %equal, label %unequal
|
||||||
|
equal:
|
||||||
|
; should not be reached
|
||||||
|
store i8 1, i8* @icmpResult
|
||||||
|
ret void
|
||||||
|
unequal:
|
||||||
|
; should be reached
|
||||||
|
store i8 2, i8* @icmpResult
|
||||||
|
ret void
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
1
interp/testdata/consteval.out.ll
предоставленный
1
interp/testdata/consteval.out.ll
предоставленный
|
@ -3,6 +3,7 @@ target triple = "x86_64--linux"
|
||||||
|
|
||||||
@intToPtrResult = local_unnamed_addr global i8 2
|
@intToPtrResult = local_unnamed_addr global i8 2
|
||||||
@ptrToIntResult = local_unnamed_addr global i8 2
|
@ptrToIntResult = local_unnamed_addr global i8 2
|
||||||
|
@icmpResult = local_unnamed_addr global i8 2
|
||||||
@someArray = internal global { i16, i8, i8 } zeroinitializer
|
@someArray = internal global { i16, i8, i8 } zeroinitializer
|
||||||
@someArrayPointer = local_unnamed_addr global i8* getelementptr inbounds ({ i16, i8, i8 }, { i16, i8, i8 }* @someArray, i64 0, i32 1)
|
@someArrayPointer = local_unnamed_addr global i8* getelementptr inbounds ({ i16, i8, i8 }, { i16, i8, i8 }* @someArray, i64 0, i32 1)
|
||||||
|
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче