Support readnone arg attribute
MODIFY
ir/globals.cpp
ir/globals.cpp
@@ -27,6 +27,7 @@ bool has_free;
bool has_fncall;
bool has_nocapture;
bool has_readonly;
+bool has_readnone;
bool has_dead_allocas;
bool does_int_mem_access;
bool does_ptr_mem_access;

MODIFY
ir/globals.h
ir/globals.h
@@ -49,6 +49,7 @@ extern bool has_fncall;
/// Whether any function argument (not function call arg) has the attribute
extern bool has_nocapture;
extern bool has_readonly;
+extern bool has_readnone;
/// Whether there are allocas that are initially dead (need start_lifetime)
extern bool has_dead_allocas;

MODIFY
ir/memory.cpp
ir/memory.cpp
@@ -318,6 +318,7 @@ static expr attr_to_bitvec(unsigned attributes) {
};
bits |= to_bit(has_nocapture, Input::NoCapture);
bits |= to_bit(has_readonly, Input::ReadOnly);
+ bits |= to_bit(has_readnone, Input::ReadNone);
return expr::mkUInt(bits, bits_for_ptrattrs);
}
@@ -563,6 +564,7 @@ static pair<expr, expr> is_dereferenceable(const Pointer &p,
cond &= offset.add_no_uoverflow(bytes_off);
cond &= p.is_block_alive();
+ cond &= !p.is_readnone();
if (iswrite)
cond &= p.is_writable() && !p.is_readonly();
@@ -808,6 +810,13 @@ expr Pointer::is_readonly() const {
return p.extract(has_nocapture, has_nocapture) == 1;
}
+expr Pointer::is_readnone() const {
+ if (!has_readnone)
+ return false;
+ unsigned idx = (unsigned)has_nocapture + (unsigned)has_readonly;
+ return p.extract(idx, idx) == 1;
+}
+
void Pointer::strip_attrs() {
p = p.extract(total_bits()-1, bits_for_ptrattrs)
.concat_zeros(bits_for_ptrattrs);

MODIFY
ir/memory.h
ir/memory.h
@@ -163,6 +163,7 @@ public:
smt::expr is_heap_allocated() const;
smt::expr is_nocapture() const;
smt::expr is_readonly() const;
+ smt::expr is_readnone() const;
void strip_attrs();

MODIFY
ir/value.cpp
ir/value.cpp
@@ -166,6 +166,8 @@ static string attr_str(unsigned attributes) {
ret += "nocapture ";
if (attributes & Input::ReadOnly)
ret += "readonly ";
+ if (attributes & Input::ReadNone)
+ ret += "readnone ";
return ret;
}

MODIFY
ir/value.h
ir/value.h
@@ -110,7 +110,7 @@ public:
class Input final : public Value {
public:
enum Attribute { None = 0, NonNull = 1<<0, ByVal = 1<<1, NoCapture = 1<<2,
- ReadOnly = 1 << 3 };
+ ReadOnly = 1 << 3, ReadNone = 1 << 4 };
private:
std::string smt_name;
unsigned attributes;

MODIFY
llvm_util/llvm2alive.cpp
llvm_util/llvm2alive.cpp
@@ -762,6 +762,10 @@ public:
attrs |= Input::ReadOnly;
continue;
+ case llvm::Attribute::ReadNone:
+ attrs |= Input::ReadNone;
+ continue;
+
default:
*out << "ERROR: Unsupported attribute: " << attr.getAsString() << '\n';
return {};

ADD
tests/alive-tv/attrs/readnone-fail.src.ll
+; This test checks LangRef's following statement:
+; """
+; this attribute indicates that the function does not dereference that pointer
+; argument, even though it may read or write the memory that the pointer points
+; to if accessed through other pointers.
+; """
+define i8 @f(i8* readnone %x, i8* %y) {
+ %c = icmp eq i8* %x, %y
+ br i1 %c, label %A, label %EXIT
+A:
+ %ret = load i8, i8* %y
+ ret i8 %ret
+EXIT:
+ ret i8 0
+}
+
+; ERROR: Value mismatch

ADD
tests/alive-tv/attrs/readnone-fail.tgt.ll
+define i8 @f(i8* readnone %x, i8* %y) {
+ ret i8 0
+}

ADD
tests/alive-tv/attrs/readnone.src.ll
+define void @f(i8* readnone %x) {
+ load i8, i8* %x
+ ret void
+}

ADD
tests/alive-tv/attrs/readnone.tgt.ll
+define void @f(i8* readnone %x) {
+ unreachable
+}

MODIFY
tools/transform.cpp
tools/transform.cpp
@@ -724,7 +724,8 @@ static void calculateAndInitConstants(Transform &t) {
bool has_byval = has_attr(Input::ByVal);
has_nocapture = has_attr(Input::NoCapture);
has_readonly = has_attr(Input::ReadOnly);
- bits_for_ptrattrs = has_nocapture + has_readonly;
+ has_readnone = has_attr(Input::ReadNone);
+ bits_for_ptrattrs = has_nocapture + has_readonly + has_readnone;
// ceil(log2(maxblks)) + 1 for local bit
bits_for_bid = max(1u, ilog2_ceil(max(num_locals, num_nonlocals), false))

Support readnone arg attribute (#288)