Add support for dereferenceable attribute in return value of fn calls
MODIFY
ir/attrs.cpp
ir/attrs.cpp
@@ -34,6 +34,8 @@ ostream& operator<<(ostream &os, const FnAttrs &attr) {
os << " NNaN";
if (attr.has(FnAttrs::NoReturn))
os << " noreturn";
+ if (attr.has(FnAttrs::Dereferenceable))
+ os << " dereferenceable(" << attr.derefBytes << ")";
return os;
}

MODIFY
ir/attrs.h
ir/attrs.h
@@ -28,15 +28,19 @@ public:
class FnAttrs final {
unsigned bits;
+ uint64_t derefBytes;
public:
enum Attribute { None = 0, NoRead = 1 << 0, NoWrite = 1 << 1,
- ArgMemOnly = 1 << 2, NNaN = 1 << 3, NoReturn = 1 << 4 };
+ ArgMemOnly = 1 << 2, NNaN = 1 << 3, NoReturn = 1 << 4,
+ Dereferenceable = 1 << 5 };
FnAttrs(unsigned bits = None) : bits(bits) {}
bool has(Attribute a) const { return (bits & a) != 0; }
void set(Attribute a) { bits |= (unsigned)a; }
+ uint64_t getDerefBytes() const { return derefBytes; }
+ void setDerefBytes(uint64_t bytes) { derefBytes = bytes; }
friend std::ostream& operator<<(std::ostream &os, const FnAttrs &attr);
};

MODIFY
ir/instr.cpp
ir/instr.cpp
@@ -1360,12 +1360,13 @@ static void unpack_ret_ty (vector<Type*> &out_types, Type &ty) {
}
}
-static StateValue pack_return(Type &ty, vector<StateValue> &vals,
- const FnAttrs &attrs, unsigned &idx) {
+static StateValue
+pack_return(State &s, Type &ty, vector<StateValue> &vals, const FnAttrs &attrs,
+ unsigned &idx) {
if (auto agg = ty.getAsAggregateType()) {
vector<StateValue> vs;
for (unsigned i = 0, e = agg->numElementsConst(); i != e; ++i) {
- vs.emplace_back(pack_return(agg->getChild(i), vals, attrs, idx));
+ vs.emplace_back(pack_return(s, agg->getChild(i), vals, attrs, idx));
}
return agg->aggregateVals(vs);
}
@@ -1373,6 +1374,11 @@ static StateValue pack_return(Type &ty, vector<StateValue> &vals,
auto &ret = vals[idx++];
if (ty.isFloatType() && attrs.has(FnAttrs::NNaN))
ret.non_poison &= !ret.value.isNaN();
+ if (ty.isPtrType() && attrs.has(FnAttrs::Dereferenceable)) {
+ Pointer p(s.getMemory(), ret.value);
+ s.addUB(ret.non_poison);
+ s.addUB(p.isDereferenceable(attrs.getDerefBytes(), bits_byte / 8, false));
+ }
return ret;
}
@@ -1405,7 +1411,7 @@ StateValue FnCall::toSMT(State &s) const {
// exit. Relevant bug: https://bugs.llvm.org/show_bug.cgi?id=27953
s.addNoReturn();
}
- return isVoid() ? StateValue() : pack_return(getType(), ret, attrs, idx);
+ return isVoid() ? StateValue() : pack_return(s, getType(), ret, attrs, idx);
}
expr FnCall::getTypeConstraints(const Function &f) const {

MODIFY
llvm_util/llvm2alive.cpp
llvm_util/llvm2alive.cpp
@@ -266,6 +266,12 @@ public:
if (op->hasNoNaNs())
attrs.set(FnAttrs::NNaN);
}
+ const auto &ret = llvm::AttributeList::ReturnIndex;
+ if (uint64_t b = max(i.getDereferenceableBytes(ret),
+ i.getCalledFunction()->getDereferenceableBytes(ret))) {
+ attrs.set(FnAttrs::Dereferenceable);
+ attrs.setDerefBytes(b);
+ }
string fn_name = '@' + fn->getName().str();
auto call =

ADD
tests/alive-tv/attrs/dereferenceable-ret-callsite.srctgt.ll
+define i32 @src(i1 %c) {
+ENTRY:
+ %p = call dereferenceable(4) i32* @f()
+ br i1 %c, label %A, label %EXIT
+A:
+ %v1 = load i32, i32* %p
+ br label %EXIT
+EXIT:
+ %val = phi i32 [%v1, %A], [0, %ENTRY]
+ ret i32 %val
+}
+
+define i32 @tgt(i1 %c) {
+ %p = call dereferenceable(4) i32* @f()
+ %v1 = load i32, i32* %p
+ %val = select i1 %c, i32 %v1, i32 0
+ ret i32 %val
+}
+
+declare i32* @f()

ADD
tests/alive-tv/attrs/dereferenceable-ret-callsite2.srctgt.ll
+define i32 @src(i1 %c) {
+ENTRY:
+ %p = call i32* @f()
+ br i1 %c, label %A, label %EXIT
+A:
+ %v1 = load i32, i32* %p
+ br label %EXIT
+EXIT:
+ %val = phi i32 [%v1, %A], [0, %ENTRY]
+ ret i32 %val
+}
+
+define i32 @tgt(i1 %c) {
+ %p = call i32* @f()
+ %v1 = load i32, i32* %p
+ %val = select i1 %c, i32 %v1, i32 0
+ ret i32 %val
+}
+
+declare dereferenceable(4) i32* @f()

Add support for dereferenceable attribute in return value of fn calls (#380)