diff --git a/interp/frame.go b/interp/frame.go index 6f249d96..c42dcf25 100644 --- a/interp/frame.go +++ b/interp/frame.go @@ -194,20 +194,28 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re lhs := fr.getLocal(inst.Operand(0)).(*LocalValue).Underlying rhs := fr.getLocal(inst.Operand(1)).(*LocalValue).Underlying predicate := inst.IntPredicate() - if predicate == llvm.IntEQ && lhs.Type().TypeKind() == llvm.PointerTypeKind { - // Unfortunately, the const propagation in the IR builder - // doesn't handle pointer compares of inttoptr values. So we - // implement it manually here. - lhsNil, ok1 := isPointerNil(lhs) - rhsNil, ok2 := isPointerNil(rhs) + if predicate == llvm.IntEQ { + var lhsZero, rhsZero bool + var ok1, ok2 bool + if lhs.Type().TypeKind() == llvm.PointerTypeKind { + // Unfortunately, the const propagation in the IR builder + // doesn't handle pointer compares of inttoptr values. So we + // implement it manually here. + lhsZero, ok1 = isPointerNil(lhs) + rhsZero, ok2 = isPointerNil(rhs) + } + if lhs.Type().TypeKind() == llvm.IntegerTypeKind { + lhsZero, ok1 = isZero(lhs) + rhsZero, ok2 = isZero(rhs) + } if ok1 && ok2 { - if lhsNil && rhsNil { - // Both are nil, so this icmp is always evaluated to true. + if lhsZero && rhsZero { + // Both are zero, so this icmp is always evaluated to true. fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), 1, false)} continue } - if lhsNil != rhsNil { - // Only one of them is nil, so this comparison must return false. + if lhsZero != rhsZero { + // Only one of them is zero, so this comparison must return false. fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), 0, false)} continue } diff --git a/interp/testdata/consteval.ll b/interp/testdata/consteval.ll index 7b2b1e1c..b3f664de 100644 --- a/interp/testdata/consteval.ll +++ b/interp/testdata/consteval.ll @@ -2,6 +2,7 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64--linux" @intToPtrResult = global i8 0 +@ptrToIntResult = global i8 0 define void @runtime.initAll() { call void @main.init() @@ -10,6 +11,7 @@ define void @runtime.initAll() { define internal void @main.init() { call void @testIntToPtr() + call void @testPtrToInt() ret void } @@ -25,3 +27,16 @@ b: store i8 2, i8* @intToPtrResult ret void } + +define internal void @testPtrToInt() { + %zero = icmp eq i64 ptrtoint (i8* @ptrToIntResult to i64), 0 + br i1 %zero, label %a, label %b +a: + ; should not be reached + store i8 1, i8* @ptrToIntResult + ret void +b: + ; should be reached + store i8 2, i8* @ptrToIntResult + ret void +} diff --git a/interp/testdata/consteval.out.ll b/interp/testdata/consteval.out.ll index f1fc9155..175babbf 100644 --- a/interp/testdata/consteval.out.ll +++ b/interp/testdata/consteval.out.ll @@ -2,6 +2,7 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64--linux" @intToPtrResult = local_unnamed_addr global i8 2 +@ptrToIntResult = local_unnamed_addr global i8 2 define void @runtime.initAll() local_unnamed_addr { ret void diff --git a/interp/utils.go b/interp/utils.go index abf93103..8f0df4c7 100644 --- a/interp/utils.go +++ b/interp/utils.go @@ -60,9 +60,9 @@ func isPointerNil(v llvm.Value) (result bool, ok bool) { case llvm.IntToPtr: // Whether a constant inttoptr is nil is easy to // determine. - operand := v.Operand(0) - if operand.IsConstant() { - return operand.ZExtValue() == 0, true + result, ok = isZero(v.Operand(0)) + if ok { + return } case llvm.BitCast, llvm.GetElementPtr: // These const instructions are just a kind of wrappers for the @@ -74,6 +74,26 @@ func isPointerNil(v llvm.Value) (result bool, ok bool) { // A constant pointer null is always null, of course. return true, true } + if !v.IsAGlobalValue().IsNil() { + // A global value is never null. + return false, true + } + return false, false // not valid +} + +// isZero returns whether the value in v is the integer zero, and whether that +// can be known right now. +func isZero(v llvm.Value) (result bool, ok bool) { + if !v.IsAConstantExpr().IsNil() { + switch v.Opcode() { + case llvm.PtrToInt: + return isPointerNil(v.Operand(0)) + } + } + if !v.IsAConstantInt().IsNil() { + val := v.ZExtValue() + return val == 0, true + } return false, false // not valid }