approximate semantics of strcpy/strcat/strcmp
MODIFY
ir/attrs.cpp
ir/attrs.cpp
@@ -14,10 +14,10 @@ ostream& operator<<(ostream &os, const ParamAttrs &attr) {
os << "byval(" << attr.blockSize << ") ";
if (attr.has(ParamAttrs::NoCapture))
os << "nocapture ";
- if (attr.has(ParamAttrs::ReadOnly))
- os << "readonly ";
- if (attr.has(ParamAttrs::ReadNone))
- os << "readnone ";
+ if (attr.has(ParamAttrs::NoRead))
+ os << "noread ";
+ if (attr.has(ParamAttrs::NoWrite))
+ os << "nowrite ";
if (attr.has(ParamAttrs::Dereferenceable))
os << "dereferenceable(" << attr.derefBytes << ") ";
if (attr.has(ParamAttrs::NoUndef))
@@ -26,6 +26,8 @@ ostream& operator<<(ostream &os, const ParamAttrs &attr) {
os << "align(" << attr.align << ") ";
if (attr.has(ParamAttrs::Returned))
os << "returned ";
+if (attr.has(ParamAttrs::NoAlias))
+ os << "noalias ";
return os;
}
@@ -55,6 +57,8 @@ ostream& operator<<(ostream &os, const FnAttrs &attr) {
os << " nothrow";
if (attr.has(FnAttrs::NoAlias))
os << " noalias";
+ if (attr.has(FnAttrs::WillReturn))
+ os << " willreturn";
return os;
}

MODIFY
ir/attrs.h
ir/attrs.h
@@ -12,8 +12,9 @@ class ParamAttrs final {
public:
enum Attribute { None = 0, NonNull = 1<<0, ByVal = 1<<1, NoCapture = 1<<2,
- ReadOnly = 1<<3, ReadNone = 1<<4, Dereferenceable = 1<<5,
- NoUndef = 1<<6, Align = 1<<7, Returned = 1<<8 };
+ NoRead = 1<<3, NoWrite = 1<<4, Dereferenceable = 1<<5,
+ NoUndef = 1<<6, Align = 1<<7, Returned = 1<<8,
+ NoAlias = 1<<9 };
ParamAttrs(unsigned bits = None) : bits(bits) {}
@@ -45,7 +46,7 @@ public:
ArgMemOnly = 1 << 2, NNaN = 1 << 3, NoReturn = 1 << 4,
Dereferenceable = 1 << 5, NonNull = 1 << 6,
NoFree = 1 << 7, NoUndef = 1 << 8, Align = 1 << 9,
- NoThrow = 1 << 10, NoAlias = 1 << 11 };
+ NoThrow = 1 << 10, NoAlias = 1 << 11, WillReturn = 1 << 12 };
FnAttrs(unsigned bits = None) : bits(bits) {}

MODIFY
ir/globals.cpp
ir/globals.cpp
@@ -35,8 +35,8 @@ bool has_free;
bool has_alloca;
bool has_fncall;
bool has_nocapture;
-bool has_readonly;
-bool has_readnone;
+bool has_noread;
+bool has_nowrite;
bool has_dead_allocas;
bool has_null_block;
bool does_int_mem_access;

MODIFY
ir/globals.h
ir/globals.h
@@ -65,8 +65,8 @@ 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;
+extern bool has_noread;
+extern bool has_nowrite;
/// Whether there are allocas that are initially dead (need start_lifetime)
extern bool has_dead_allocas;

MODIFY
ir/memory.cpp
ir/memory.cpp
@@ -679,9 +679,9 @@ Memory::AliasSet Memory::computeAliasing(const Pointer &ptr, unsigned bytes,
// collect over-approximation of possible touched bids
for (auto &p : all_leaf_ptrs(*this, ptr())) {
- if (has_readnone && p.isReadnone().isTrue())
+ if (has_noread && !write && p.isNoRead().isTrue())
continue;
- if (has_readonly && write && p.isReadonly().isTrue())
+ if (has_nowrite && write && p.isNoWrite().isTrue())
continue;
AliasSet this_alias = aliasing;

MODIFY
ir/pointer.cpp
ir/pointer.cpp
@@ -46,8 +46,8 @@ static expr attr_to_bitvec(const ParamAttrs &attrs) {
return b ? ((attrs.has(a) ? 1 : 0) << idx++) : 0;
};
bits |= to_bit(has_nocapture, ParamAttrs::NoCapture);
- bits |= to_bit(has_readonly, ParamAttrs::ReadOnly);
- bits |= to_bit(has_readnone, ParamAttrs::ReadNone);
+ bits |= to_bit(has_noread, ParamAttrs::NoRead);
+ bits |= to_bit(has_nowrite, ParamAttrs::NoWrite);
return expr::mkUInt(bits, bits_for_ptrattrs);
}
@@ -341,10 +341,11 @@ static pair<expr, expr> is_dereferenceable(Pointer &p,
cond &= offset.add_no_uoverflow(bytes_off);
cond &= p.isBlockAlive();
- cond &= !p.isReadnone();
if (iswrite)
- cond &= p.isWritable() && !p.isReadonly();
+ cond &= p.isWritable() && !p.isNoWrite();
+ else
+ cond &= !p.isNoRead();
// try some constant folding; these are implied by the conditions above
if (bytes.ugt(block_sz).isTrue() ||
@@ -504,16 +505,16 @@ expr Pointer::isNocapture(bool simplify) const {
return p.extract(0, 0) == 1;
}
-expr Pointer::isReadonly() const {
- if (!has_readonly)
+expr Pointer::isNoRead() const {
+ if (!has_noread)
return false;
return p.extract(has_nocapture, has_nocapture) == 1;
}
-expr Pointer::isReadnone() const {
- if (!has_readnone)
+expr Pointer::isNoWrite() const {
+ if (!has_nowrite)
return false;
- unsigned idx = (unsigned)has_nocapture + (unsigned)has_readonly;
+ unsigned idx = (unsigned)has_nocapture + (unsigned)has_noread;
return p.extract(idx, idx) == 1;
}

MODIFY
ir/pointer.h
ir/pointer.h
@@ -110,8 +110,8 @@ public:
smt::expr getAllocType() const;
smt::expr isHeapAllocated() const;
smt::expr isNocapture(bool simplify = true) const;
- smt::expr isReadonly() const;
- smt::expr isReadnone() const;
+ smt::expr isNoRead() const;
+ smt::expr isNoWrite() const;
smt::expr refined(const Pointer &other) const;
smt::expr fninputRefined(const Pointer &other, std::set<smt::expr> &undef,

MODIFY
llvm_util/known_fns.cpp
llvm_util/known_fns.cpp
@@ -54,6 +54,11 @@ known_call(llvm::CallInst &i, const llvm::TargetLibraryInfo &TLI,
param_attrs[i].set(attr);
};
+ auto set_param_deref = [&](unsigned i, unsigned bytes) {
+ set_param(i, ParamAttrs::Dereferenceable);
+ param_attrs[i].derefBytes = bytes;
+ };
+
auto ret_and_args_no_undef = [&]() {
if (!dynamic_cast<VoidType*>(ty))
attrs.set(FnAttrs::NoUndef);
@@ -204,9 +209,9 @@ known_call(llvm::CallInst &i, const llvm::TargetLibraryInfo &TLI,
attrs.set(FnAttrs::NoFree);
attrs.set(FnAttrs::NoAlias);
set_param(0, ParamAttrs::NoCapture);
- set_param(0, ParamAttrs::ReadOnly);
+ set_param(0, ParamAttrs::NoWrite);
set_param(1, ParamAttrs::NoCapture);
- set_param(1, ParamAttrs::ReadOnly);
+ set_param(1, ParamAttrs::NoWrite);
RETURN_EXACT();
case llvm::LibFunc_fdopen:
@@ -215,7 +220,7 @@ known_call(llvm::CallInst &i, const llvm::TargetLibraryInfo &TLI,
attrs.set(FnAttrs::NoFree);
attrs.set(FnAttrs::NoAlias);
set_param(1, ParamAttrs::NoCapture);
- set_param(1, ParamAttrs::ReadOnly);
+ set_param(1, ParamAttrs::NoWrite);
RETURN_EXACT();
case llvm::LibFunc_fputc:
@@ -228,7 +233,7 @@ known_call(llvm::CallInst &i, const llvm::TargetLibraryInfo &TLI,
ret_and_args_no_undef();
attrs.set(FnAttrs::NoThrow);
attrs.set(FnAttrs::NoFree);
- set_param(0, ParamAttrs::ReadOnly);
+ set_param(0, ParamAttrs::NoWrite);
set_param(0, ParamAttrs::NoCapture);
set_param(1, ParamAttrs::NoCapture);
RETURN_EXACT();
@@ -265,7 +270,7 @@ known_call(llvm::CallInst &i, const llvm::TargetLibraryInfo &TLI,
ret_and_args_no_undef();
attrs.set(FnAttrs::NoFree);
set_param(0, ParamAttrs::NoCapture);
- set_param(0, ParamAttrs::ReadOnly);
+ set_param(0, ParamAttrs::NoWrite);
RETURN_EXACT();
case llvm::LibFunc_read:
@@ -278,7 +283,7 @@ known_call(llvm::CallInst &i, const llvm::TargetLibraryInfo &TLI,
ret_and_args_no_undef();
attrs.set(FnAttrs::NoFree);
set_param(1, ParamAttrs::NoCapture);
- set_param(1, ParamAttrs::ReadOnly);
+ set_param(1, ParamAttrs::NoWrite);
RETURN_EXACT();
case llvm::LibFunc_gets:
@@ -297,7 +302,7 @@ known_call(llvm::CallInst &i, const llvm::TargetLibraryInfo &TLI,
attrs.set(FnAttrs::NoThrow);
attrs.set(FnAttrs::NoFree);
set_param(0, ParamAttrs::NoCapture);
- set_param(0, ParamAttrs::ReadOnly);
+ set_param(0, ParamAttrs::NoWrite);
set_param(1, ParamAttrs::NoCapture);
RETURN_EXACT();
@@ -315,7 +320,7 @@ known_call(llvm::CallInst &i, const llvm::TargetLibraryInfo &TLI,
attrs.set(FnAttrs::NoThrow);
attrs.set(FnAttrs::NoFree);
set_param(0, ParamAttrs::NoCapture);
- set_param(0, ParamAttrs::ReadOnly);
+ set_param(0, ParamAttrs::NoWrite);
RETURN_EXACT();
case llvm::LibFunc_opendir:
@@ -324,7 +329,7 @@ known_call(llvm::CallInst &i, const llvm::TargetLibraryInfo &TLI,
attrs.set(FnAttrs::NoFree);
attrs.set(FnAttrs::NoAlias);
set_param(0, ParamAttrs::NoCapture);
- set_param(0, ParamAttrs::ReadOnly);
+ set_param(0, ParamAttrs::NoWrite);
RETURN_EXACT();
case llvm::LibFunc_rename:
@@ -335,9 +340,9 @@ known_call(llvm::CallInst &i, const llvm::TargetLibraryInfo &TLI,
attrs.set(FnAttrs::NoThrow);
attrs.set(FnAttrs::NoFree);
set_param(0, ParamAttrs::NoCapture);
- set_param(0, ParamAttrs::ReadOnly);
+ set_param(0, ParamAttrs::NoWrite);
set_param(1, ParamAttrs::NoCapture);
- set_param(1, ParamAttrs::ReadOnly);
+ set_param(1, ParamAttrs::NoWrite);
RETURN_EXACT();
case llvm::LibFunc_getc:
@@ -389,8 +394,45 @@ known_call(llvm::CallInst &i, const llvm::TargetLibraryInfo &TLI,
attrs.set(FnAttrs::NoThrow);
attrs.set(FnAttrs::NoFree);
attrs.set(FnAttrs::NoRead);
+ attrs.set(FnAttrs::WillReturn);
RETURN_EXACT();
+ case llvm::LibFunc_strcat:
+ case llvm::LibFunc_strcpy:
+ set_param_deref(0, 1);
+ set_param_deref(1, 1);
+ [[fallthrough]];
+ case llvm::LibFunc_strncat:
+ case llvm::LibFunc_strncpy:
+ ret_and_args_no_undef();
+ attrs.set(FnAttrs::ArgMemOnly);
+ attrs.set(FnAttrs::NoThrow);
+ attrs.set(FnAttrs::NoFree);
+ attrs.set(FnAttrs::WillReturn);
+ set_param(0, ParamAttrs::Returned);
+ set_param(0, ParamAttrs::NoRead);
+ set_param(1, ParamAttrs::NoWrite);
+ set_param(0, ParamAttrs::NoCapture);
+ set_param(1, ParamAttrs::NoCapture);
+ set_param(0, ParamAttrs::NoAlias);
+ set_param(1, ParamAttrs::NoAlias);
+ RETURN_APPROX();
+
+ case llvm::LibFunc_strcmp:
+ set_param_deref(0, 1);
+ set_param_deref(1, 1);
+ [[fallthrough]];
+ case llvm::LibFunc_strncmp:
+ ret_and_args_no_undef();
+ attrs.set(FnAttrs::ArgMemOnly);
+ attrs.set(FnAttrs::NoThrow);
+ attrs.set(FnAttrs::NoFree);
+ attrs.set(FnAttrs::NoWrite);
+ attrs.set(FnAttrs::WillReturn);
+ set_param(0, ParamAttrs::NoCapture);
+ set_param(1, ParamAttrs::NoCapture);
+ RETURN_APPROX();
+
default:
RETURN_APPROX();
}

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

MODIFY
tools/transform.cpp
tools/transform.cpp
@@ -876,9 +876,9 @@ static void calculateAndInitConstants(Transform &t) {
// The number of bits needed to encode pointer attributes
// nonnull and byval isn't encoded in ptr attribute bits
has_nocapture = has_attr(ParamAttrs::NoCapture);
- has_readonly = has_attr(ParamAttrs::ReadOnly);
- has_readnone = has_attr(ParamAttrs::ReadNone);
- bits_for_ptrattrs = has_nocapture + has_readonly + has_readnone;
+ has_noread = has_attr(ParamAttrs::NoRead);
+ has_nowrite = has_attr(ParamAttrs::NoWrite);
+ bits_for_ptrattrs = has_nocapture + has_noread + has_nowrite;
// ceil(log2(maxblks)) + 1 for local bit
bits_for_bid = max(1u, ilog2_ceil(max(num_locals, num_nonlocals), false))

approximate semantics of strcpy/strcat/strcmp