fix implement support for null_pointer_is_valid functions
Note that this is a per-function toggle, but we implement it globally.
Needs revisiting when IPO support is added
MODIFY
ir/attrs.cpp
ir/attrs.cpp
@@ -70,6 +70,8 @@ ostream& operator<<(ostream &os, const FnAttrs &attr) {
os << " dereferenceable_or_null(" << attr.derefOrNullBytes << ')';
if (attr.has(FnAttrs::InaccessibleMemOnly))
os << " inaccessiblememonly";
+ if (attr.has(FnAttrs::NullPointerIsValid))
+ os << " null_pointer_is_valid";
return os;
}

MODIFY
ir/attrs.h
ir/attrs.h
@@ -58,7 +58,8 @@ public:
NoFree = 1 << 7, NoUndef = 1 << 8, Align = 1 << 9,
NoThrow = 1 << 10, NoAlias = 1 << 11, WillReturn = 1 << 12,
DereferenceableOrNull = 1 << 13,
- InaccessibleMemOnly = 1 << 14 };
+ InaccessibleMemOnly = 1 << 14,
+ NullPointerIsValid = 1 << 15 };
FnAttrs(unsigned bits = None) : bits(bits) {}

MODIFY
ir/globals.cpp
ir/globals.cpp
@@ -39,6 +39,7 @@ bool has_nocapture;
bool has_noread;
bool has_nowrite;
bool has_dead_allocas;
+bool has_null_pointer;
bool has_null_block;
bool does_int_mem_access;
bool does_ptr_mem_access;

MODIFY
ir/globals.h
ir/globals.h
@@ -78,8 +78,11 @@ extern bool has_nowrite;
/// Whether there are allocas that are initially dead (need start_lifetime)
extern bool has_dead_allocas;
-/// Whether there is a pointer that can point to the null block
-/// ex) undef ptr constant, fn arg
+/// Whether there null pointers appear in the program
+extern bool has_null_pointer;
+
+/// Whether the null block should be allocated, i.e., null is not
+/// dereferenceable
extern bool has_null_block;
/// Whether the programs do memory accesses that load/store int/ptrs

MODIFY
ir/memory.cpp
ir/memory.cpp
@@ -1100,8 +1100,8 @@ Memory::Memory(State &state) : state(&state), escaped_local_blks(*this) {
if (memory_unused())
return;
- next_nonlocal_bid
- = has_null_block + num_globals_src + num_ptrinputs + has_fncall;
+ next_nonlocal_bid = (has_null_pointer || has_null_block) + num_globals_src +
+ num_ptrinputs + has_fncall;
if (has_null_block)
non_local_block_val.emplace_back();
@@ -1233,7 +1233,8 @@ void Memory::markByVal(unsigned bid) {
}
expr Memory::mkInput(const char *name, const ParamAttrs &attrs) {
- unsigned max_bid = has_null_block + num_globals_src + next_ptr_input++;
+ unsigned max_bid
+ = (has_null_pointer || has_null_block) + num_globals_src + next_ptr_input++;
assert(max_bid < num_nonlocals_src);
Pointer p(*this, name, false, false, false, attrs);
auto bid = p.getShortBid();

MODIFY
ir/pointer.cpp
ir/pointer.cpp
@@ -564,8 +564,7 @@ expr Pointer::isNoWrite() const {
}
Pointer Pointer::mkNullPointer(const Memory &m) {
- // Null pointer exists if either source or target uses it.
- assert(has_null_block);
+ assert(has_null_block || has_null_pointer);
// A null pointer points to block 0 without any attribute.
return { m, 0, false };
}

MODIFY
llvm_util/llvm2alive.cpp
llvm_util/llvm2alive.cpp
@@ -1390,6 +1390,10 @@ public:
attrs.set(FnAttrs::WillReturn);
break;
+ case llvm::Attribute::NullPointerIsValid:
+ attrs.set(FnAttrs::NullPointerIsValid);
+ break;
+
default:
break;
}

ADD
tests/alive-tv/attrs/null_is_valid.srctgt.ll
+; ERROR: Source is more defined
+
+define i8 @src() null_pointer_is_valid {
+ %v = load i8, ptr null
+ ret i8 %v
+}
+
+define i8 @tgt() null_pointer_is_valid {
+ unreachable
+}

ADD
tests/alive-tv/attrs/null_is_valid2.srctgt.ll
+; ERROR: Value mismatch
+
+define i8 @src(ptr %p) null_pointer_is_valid {
+ store i8 2, ptr null
+ store i8 3, ptr %p
+ %v = load i8, ptr null
+ ret i8 %v
+}
+
+define i8 @tgt(ptr %p) null_pointer_is_valid {
+ store i8 2, ptr null
+ store i8 3, ptr %p
+ ret i8 2
+}

MODIFY
tools/transform.cpp
tools/transform.cpp
@@ -817,7 +817,6 @@ static void calculateAndInitConstants(Transform &t) {
uint64_t max_access_size = 0;
uint64_t min_global_size = UINT64_MAX;
- bool nullptr_is_used = false;
bool has_int2ptr = false;
bool has_ptr2int = false;
has_alloca = false;
@@ -826,6 +825,7 @@ static void calculateAndInitConstants(Transform &t) {
has_free = false;
has_fncall = false;
has_write_fncall = false;
+ has_null_pointer = false;
has_null_block = false;
does_ptr_store = false;
does_ptr_mem_access = false;
@@ -863,8 +863,8 @@ static void calculateAndInitConstants(Transform &t) {
}
if (i->hasAttribute(ParamAttrs::DereferenceableOrNull)) {
// Optimization: unless explicitly compared with a null pointer, don't
- // set nullptr_is_used to true.
- // Null constant pointer will set nullptr_is_used to true anyway.
+ // set has_null_pointer to true.
+ // Null constant pointer will set has_null_pointer to true anyway.
// Note that dereferenceable_or_null implies num_ptrinputs > 0,
// which may turn has_null_block on.
does_mem_access = true;
@@ -894,7 +894,7 @@ static void calculateAndInitConstants(Transform &t) {
++cur_num_locals;
for (auto op : i.operands()) {
- nullptr_is_used |= has_nullptr(op);
+ has_null_pointer |= has_nullptr(op);
update_min_vect_sz(op->getType());
}
@@ -981,12 +981,14 @@ static void calculateAndInitConstants(Transform &t) {
// check if null block is needed
// Global variables cannot be null pointers
- has_null_block = num_null_ptrinputs > 0 || nullptr_is_used || has_malloc ||
+ has_null_block = num_null_ptrinputs > 0 || has_null_pointer || has_malloc ||
has_ptr_load || has_fncall || has_int2ptr;
num_nonlocals_src = num_globals_src + num_ptrinputs + num_nonlocals_inst_src +
num_inaccessiblememonly_fns + has_null_block;
+ has_null_block &= !t.src.getFnAttrs().has(FnAttrs::NullPointerIsValid);
+
// Allow at least one non-const global for calls to change
num_nonlocals_src += has_write_fncall;
@@ -1084,7 +1086,7 @@ static void calculateAndInitConstants(Transform &t) {
<< "\nstrlen_unroll_cnt: " << strlen_unroll_cnt
<< "\nmemcmp_unroll_cnt: " << memcmp_unroll_cnt
<< "\nlittle_endian: " << little_endian
- << "\nnullptr_is_used: " << nullptr_is_used
+ << "\nnullptr_is_used: " << has_null_pointer
<< "\nobserves_addresses: " << observes_addresses
<< "\nhas_malloc: " << has_malloc
<< "\nhas_free: " << has_free

fix #806: implement support for null_pointer_is_valid functions
Note that this is a per-function toggle, but we implement it globally.
Needs revisiting when IPO support is added