fix add support for readonly function arguments
MODIFY
ir/instr.cpp
ir/instr.cpp
@@ -2121,6 +2121,8 @@ static void unpack_inputs(State &s, Value &argv, Type &ty,
ptr_inputs.emplace_back(std::move(value),
argflag.blockSize,
+ argflag.has(ParamAttrs::NoRead),
+ argflag.has(ParamAttrs::NoWrite),
argflag.has(ParamAttrs::NoCapture));
} else {
inputs.emplace_back(std::move(value));

MODIFY
ir/memory.cpp
ir/memory.cpp
@@ -1271,8 +1271,15 @@ pair<expr, expr> Memory::mkUndefInput(const ParamAttrs &attrs) const {
return { p.release(), std::move(undef) };
}
+bool Memory::PtrInput::eq_attrs(const PtrInput &rhs) const {
+ return byval == rhs.byval &&
+ noread == rhs.noread &&
+ nowrite == rhs.nowrite &&
+ nocapture == rhs.nocapture;
+}
+
expr Memory::PtrInput::operator==(const PtrInput &rhs) const {
- if (byval != rhs.byval || nocapture != rhs.nocapture)
+ if (!eq_attrs(rhs))
return false;
return val == rhs.val;
}
@@ -1435,6 +1442,9 @@ void Memory::setState(const Memory::CallState &st,
for (unsigned bid = num_consts; bid < limit - has_write_fncall; ++bid) {
expr modifies(false);
for (auto &ptr_in : *ptr_inputs) {
+ if (ptr_in.nowrite)
+ continue;
+
if (!ptr_in.byval && bid < next_nonlocal_bid) {
modifies |= ptr_in.val.non_poison &&
Pointer(*this, ptr_in.val.value).getBid() == bid;

MODIFY
ir/memory.h
ir/memory.h
@@ -238,12 +238,13 @@ public:
struct PtrInput {
StateValue val;
- unsigned byval;
+ uint64_t byval;
+ bool noread;
+ bool nowrite;
bool nocapture;
- PtrInput(StateValue &&v, unsigned byval, bool nocapture) :
- val(std::move(v)), byval(byval), nocapture(nocapture) {}
smt::expr operator==(const PtrInput &rhs) const;
+ bool eq_attrs(const PtrInput &rhs) const;
auto operator<=>(const PtrInput &rhs) const = default;
};

MODIFY
ir/state.cpp
ir/state.cpp
@@ -717,17 +717,18 @@ expr State::FnCallInput::refinedBy(
if (!inaccessiblememonly) {
assert(args_ptr.size() == args_ptr2.size());
for (unsigned i = 0, e = args_ptr.size(); i != e; ++i) {
- // TODO: needs to take read/read2 as input to control if mem blocks
- // need to be compared
- auto &[ptr_in, byval, is_nocapture] = args_ptr[i];
- auto &[ptr_in2, byval2, is_nocapture2] = args_ptr2[i];
- if (byval != byval2 || is_nocapture != is_nocapture2)
+ auto &ptr1 = args_ptr[i];
+ auto &ptr2 = args_ptr2[i];
+ if (!ptr1.eq_attrs(ptr2))
return false;
- expr eq_val = Pointer(m, ptr_in.value)
- .fninputRefined(Pointer(m2, ptr_in2.value),
- undef_vars, byval2);
- refines.add(ptr_in.non_poison.implies(eq_val && ptr_in2.non_poison));
+ if (ptr1.noread)
+ continue;
+
+ expr eq_val = Pointer(m, ptr1.val.value)
+ .fninputRefined(Pointer(m2, ptr2.val.value),
+ undef_vars, ptr2.byval);
+ refines.add(ptr1.val.non_poison.implies(eq_val && ptr2.val.non_poison));
if (!refines)
return false;
@@ -743,9 +744,9 @@ expr State::FnCallInput::refinedBy(
auto restrict_ptrs2 = argmemonly ? &args_ptr2 : nullptr;
if (modifies_bid != -1u) {
dummy1.emplace_back(
- StateValue(Pointer(m, modifies_bid, false).release(), true), 0, false);
+ StateValue(Pointer(m, modifies_bid, false).release(), true));
dummy2.emplace_back(
- StateValue(Pointer(m2, modifies_bid, false).release(), true), 0, false);
+ StateValue(Pointer(m2, modifies_bid, false).release(), true));
restrict_ptrs = &dummy1;
restrict_ptrs2 = &dummy2;
}

MODIFY
llvm_util/llvm2alive.cpp
llvm_util/llvm2alive.cpp
@@ -1267,21 +1267,17 @@ public:
continue;
case llvm::Attribute::ReadOnly:
- if (!is_callsite)
attrs.set(ParamAttrs::NoWrite);
continue;
case llvm::Attribute::WriteOnly:
- if (!is_callsite)
attrs.set(ParamAttrs::NoRead);
continue;
case llvm::Attribute::ReadNone:
- if (!is_callsite) {
// TODO: can this pointer be freed?
attrs.set(ParamAttrs::NoRead);
attrs.set(ParamAttrs::NoWrite);
- }
continue;
case llvm::Attribute::Dereferenceable:

ADD
tests/alive-tv/attrs/nowrite-param.srctgt.ll
+@A = external global i32
+@B = external global i32
+
+define i32 @src(i1 %z) {
+ %l = call i32 @f_load_arg(ptr @A)
+ call void @f_readonly_arg(ptr @A, ptr @B)
+ br i1 %z, label %true, label %false
+
+true:
+ ret i32 %l
+
+false:
+ ret i32 0
+}
+
+define i32 @tgt(i1 %z) {
+ call void @f_readonly_arg(ptr @A, ptr @B)
+ br i1 %z, label %true, label %false
+true:
+ %l = call i32 @f_load_arg(ptr @A)
+ ret i32 %l
+
+false:
+ ret i32 0
+}
+
+declare i32 @f_load_arg(i32*) nounwind willreturn readonly argmemonly
+declare void @f_readonly_arg(i32* readonly, i32*) nounwind willreturn argmemonly

fix #787: add support for readonly function arguments