change semantics of pointer comparison to use integer addresses
MODIFY
ir/instr.cpp
ir/instr.cpp
@@ -1876,6 +1876,13 @@ bool ICmp::propagatesPoison() const {
return true;
}
+bool ICmp::isPtrCmp() const {
+ auto &elem_ty = a->getType();
+ return elem_ty.isPtrType() ||
+ (elem_ty.isVectorType() &&
+ elem_ty.getAsAggregateType()->getChild(0).isPtrType());
+}
+
void ICmp::rauw(const Value &what, Value &with) {
RAUW(a);
RAUW(b);
@@ -1916,34 +1923,9 @@ static StateValue build_icmp_chain(const expr &var,
StateValue ICmp::toSMT(State &s) const {
auto &a_eval = s[*a];
auto &b_eval = s[*b];
- function<StateValue(const expr&, const expr&, Cond)> fn;
-
- auto &elem_ty = a->getType();
- if (elem_ty.isPtrType() ||
- (elem_ty.isVectorType() &&
- elem_ty.getAsAggregateType()->getChild(0).isPtrType())) {
- fn = [&](auto &av, auto &bv, Cond cond) {
- Pointer lhs(s.getMemory(), av);
- Pointer rhs(s.getMemory(), bv);
- switch (cond) {
- case EQ: return StateValue(lhs == rhs, true);
- case NE: return StateValue(lhs != rhs, true);
- case SLE: return lhs.sle(rhs);
- case SLT: return lhs.slt(rhs);
- case SGE: return lhs.sge(rhs);
- case SGT: return lhs.sgt(rhs);
- case ULE: return lhs.ule(rhs);
- case ULT: return lhs.ult(rhs);
- case UGE: return lhs.uge(rhs);
- case UGT: return lhs.ugt(rhs);
- case Any:
- UNREACHABLE();
- }
- UNREACHABLE();
- };
- } else { // integer comparison
- fn = [&](auto &av, auto &bv, Cond cond) {
+ function<StateValue(const expr&, const expr&, Cond)> fn =
+ [&](auto &av, auto &bv, Cond cond) {
switch (cond) {
case EQ: return StateValue(av == bv, true);
case NE: return StateValue(av != bv, true);
@@ -1960,6 +1942,13 @@ StateValue ICmp::toSMT(State &s) const {
}
UNREACHABLE();
};
+
+ if (isPtrCmp()) {
+ fn = [&](const expr &av, const expr &bv, Cond cond) {
+ Pointer lhs(s.getMemory(), av);
+ Pointer rhs(s.getMemory(), bv);
+ return fn(lhs.getAddress(), rhs.getAddress(), cond);
+ };
}
auto scalar = [&](const StateValue &a, const StateValue &b) -> StateValue {
@@ -1968,6 +1957,7 @@ StateValue ICmp::toSMT(State &s) const {
return { v.value.toBVBool(), a.non_poison && b.non_poison && v.non_poison };
};
+ auto &elem_ty = a->getType();
if (auto agg = elem_ty.getAsAggregateType()) {
vector<StateValue> vals;
for (unsigned i = 0, e = agg->numElementsConst(); i != e; ++i) {

MODIFY
ir/instr.h
ir/instr.h
@@ -239,6 +239,7 @@ private:
public:
ICmp(Type &type, std::string &&name, Cond cond, Value &a, Value &b);
+ bool isPtrCmp() const;
std::vector<Value*> operands() const override;
bool propagatesPoison() const override;
void rauw(const Value &what, Value &with) override;

MODIFY
ir/pointer.cpp
ir/pointer.cpp
@@ -269,24 +269,6 @@ expr Pointer::operator!=(const Pointer &rhs) const {
return !operator==(rhs);
}
-#define DEFINE_CMP(op) \
-StateValue Pointer::op(const Pointer &rhs) const { \
- /* Note that attrs are not compared. */ \
- expr nondet = expr::mkFreshVar("nondet", true); \
- m.state->addQuantVar(nondet); \
- return { expr::mkIf(getBid() == rhs.getBid(), \
- getOffset().op(rhs.getOffset()), nondet), true }; \
-}
-
-DEFINE_CMP(sle)
-DEFINE_CMP(slt)
-DEFINE_CMP(sge)
-DEFINE_CMP(sgt)
-DEFINE_CMP(ule)
-DEFINE_CMP(ult)
-DEFINE_CMP(uge)
-DEFINE_CMP(ugt)
-
static expr inbounds(const Pointer &p, bool strict) {
if (isUndef(p.getOffset()))
return false;

MODIFY
ir/pointer.h
ir/pointer.h
@@ -79,15 +79,6 @@ public:
smt::expr operator==(const Pointer &rhs) const;
smt::expr operator!=(const Pointer &rhs) const;
- StateValue sle(const Pointer &rhs) const;
- StateValue slt(const Pointer &rhs) const;
- StateValue sge(const Pointer &rhs) const;
- StateValue sgt(const Pointer &rhs) const;
- StateValue ule(const Pointer &rhs) const;
- StateValue ult(const Pointer &rhs) const;
- StateValue uge(const Pointer &rhs) const;
- StateValue ugt(const Pointer &rhs) const;
-
smt::expr inbounds(bool simplify_ptr = false, bool strict = false);
smt::expr blockAlignment() const; // log(bits)
smt::expr isBlockAligned(unsigned align, bool exact = false) const;

MODIFY
tests/alive-tv/attrs/nocapture-noundef.srctgt.ll
tests/alive-tv/attrs/nocapture-noundef.srctgt.ll
@@ -24,3 +24,6 @@ f:
declare i8* @g(i8*)
+
+; If %a = %b + n, this is wrong.
+; ERROR: Source is more defined than target

ADD
tests/alive-tv/ptrcmp/D99642.srctgt.ll
+define i1 @src() local_unnamed_addr {
+entry:
+ %a = alloca i64, align 8
+ store i64 0, i64* %a, align 8
+ %i = ptrtoint i64* %a to i64
+ %p = bitcast i64* %a to i8*
+ %0 = sub i64 0, %i
+ %q = getelementptr i8, i8* %p, i64 %0
+ %c = icmp eq i8* %q, null
+ ; `%a` is non-null at the end of the block, because we store through it.
+ ; However, `%q` is derived from `%a` via a GEP that is not `inbounds`, therefore we cannot judge `%q` is non-null as well
+ ; and must retain the `icmp` instruction.
+ ; CHECK: %c = icmp eq i8* %q, null
+ ret i1 %c
+}
+
+define i1 @tgt() local_unnamed_addr {
+if.end:
+ ret i1 0
+}
+
+; ERROR: Value mismatch

ADD
tests/alive-tv/ptrcmp/gep-inbounds-zero.srctgt.ll
+@glb = global i8 0
+
+define i1 @src(i64 %idx) {
+ %p = getelementptr inbounds i8, i8* @glb, i64 %idx
+ %c = icmp eq i8* %p, null
+ ret i1 %c
+}
+
+define i1 @tgt(i64 %idx) {
+ ret i1 false
+}

ADD
tests/alive-tv/ptrcmp/gep-zero.srctgt.ll
+@glb = global i8 0
+
+define i1 @src(i64 %idx) {
+ %p = getelementptr i8, i8* @glb, i64 %idx
+ %c = icmp eq i8* %p, null
+ ret i1 %c
+}
+
+define i1 @tgt(i64 %idx) {
+ ret i1 false
+}
+
+; ERROR: Value mismatch

RENAME
tests/alive-tv/memory/ptr-cmp.src.ll
tests/alive-tv/ptrcmp/ptr-cmp.src.ll
-; ERROR: Value mismatch
-
define i1 @f(i32** %a) {
%load = load i32*, i32** %a
%integral = ptrtoint i32* %load to i64

RENAME
tests/alive-tv/memory/ptr-cmp.tgt.ll
tests/alive-tv/ptrcmp/ptr-cmp.tgt.ll
MODIFY
tools/transform.cpp
tools/transform.cpp
@@ -827,6 +827,9 @@ static void calculateAndInitConstants(Transform &t) {
auto &t = bc->getType();
has_vector_bitcast |= t.isVectorType();
min_access_size = gcd(min_access_size, getCommonAccessSize(t));
+
+ } else if (auto *ic = dynamic_cast<const ICmp*>(&i)) {
+ has_ptr2int |= ic->isPtrCmp();
}
}
}

change semantics of pointer comparison to use integer addresses (#713)