fix an assorted bugs in byval implementation exposed by LLVM's unit tests
MODIFY
ir/attrs.cpp
ir/attrs.cpp
@@ -10,7 +10,7 @@ ostream& operator<<(ostream &os, const ParamAttrs &attr) {
if (attr.has(ParamAttrs::NonNull))
os << "nonnull ";
if (attr.has(ParamAttrs::ByVal))
- os << "byval ";
+ os << "byval(" << attr.blockSize << ") ";
if (attr.has(ParamAttrs::NoCapture))
os << "nocapture ";
if (attr.has(ParamAttrs::ReadOnly))
@@ -21,6 +21,8 @@ ostream& operator<<(ostream &os, const ParamAttrs &attr) {
os << "dereferenceable(" << attr.derefBytes << ") ";
if (attr.has(ParamAttrs::NoUndef))
os << "noundef ";
+ if (attr.has(ParamAttrs::Align))
+ os << "align(" << attr.align << ") ";
return os;
}

MODIFY
ir/attrs.h
ir/attrs.h
@@ -9,19 +9,20 @@ namespace IR {
class ParamAttrs final {
unsigned bits;
- uint64_t derefBytes;
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 };
+ NoUndef = 1<<6, Align = 1<<7 };
ParamAttrs(unsigned bits = None) : bits(bits) {}
+ uint64_t derefBytes; // Dereferenceable
+ uint64_t blockSize; // exact block size for e.g. byval args
+ uint64_t align; // power-of-2 in bytes
+
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 ParamAttrs &attr);
};

MODIFY
ir/instr.cpp
ir/instr.cpp
@@ -1518,7 +1518,7 @@ static void unpack_inputs(State &s, Value &argv, Type &ty,
p.stripAttrs();
if (is_deref)
s.addUB(
- p.isDereferenceable(argflag.getDerefBytes(), bits_byte / 8, false));
+ p.isDereferenceable(argflag.derefBytes, bits_byte / 8, false));
if (is_nonnull)
s.addUB(p.isNonZero());

MODIFY
ir/memory.cpp
ir/memory.cpp
@@ -981,7 +981,6 @@ static const array<uint64_t, 5> alias_buckets_vals = { 1, 2, 3, 5, 10 };
static array<uint64_t, 6> alias_buckets_hits = { 0 };
static uint64_t only_local = 0, only_nonlocal = 0;
-
void Memory::AliasSet::computeAccessStats() const {
auto nlocal = numMayAlias(true);
auto nnonlocal = numMayAlias(false);
@@ -1045,7 +1044,8 @@ void Memory::AliasSet::print(ostream &os) const {
if (numMayAlias(false) > 0) {
if (has_local) os << " / ";
print("non-local: ", non_local);
- }
+ } else if (!has_local)
+ os << "(empty)";
}
@@ -1554,6 +1554,7 @@ void Memory::syncWithSrc(const Memory &src) {
}
void Memory::markByVal(unsigned bid) {
+ assert(bid < has_null_block + num_globals_src);
byval_blks.emplace_back(bid);
}

MODIFY
ir/value.cpp
ir/value.cpp
@@ -179,22 +179,23 @@ StateValue Input::toSMT(State &s) const {
// 00: normal, 01: undef, else: poison
expr type = getTyVar();
+ bool has_byval = hasAttribute(ParamAttrs::ByVal);
+ bool has_deref = hasAttribute(ParamAttrs::Dereferenceable);
+ bool has_nonnull = hasAttribute(ParamAttrs::NonNull);
+ bool has_noundef = hasAttribute(ParamAttrs::NoUndef);
+
expr val;
- if (hasAttribute(ParamAttrs::ByVal)) {
+ if (has_byval) {
unsigned bid;
- string sz_name = getName() + "#size";
- expr size = expr::mkVar(sz_name.c_str(), bits_size_t-1).zext(1);
- val = get_global(s, getName(), size, 1, false, bid);
+ expr size = expr::mkUInt(attrs.blockSize, bits_size_t);
+ val = get_global(s, getName(), size, attrs.align, false, bid);
s.getMemory().markByVal(bid);
} else {
val = getType().mkInput(s, smt_name.c_str(), attrs);
}
- bool has_deref = hasAttribute(ParamAttrs::Dereferenceable);
- bool has_nonnull = hasAttribute(ParamAttrs::NonNull);
- bool has_noundef = hasAttribute(ParamAttrs::NoUndef);
-
- bool never_undef = config::disable_undef_input || has_deref || has_noundef;
+ bool never_undef = config::disable_undef_input || has_byval || has_deref ||
+ has_noundef;
if (!never_undef) {
auto [undef, vars] = getType().mkUndefInput(s, attrs);
for (auto &v : vars) {
@@ -207,7 +208,7 @@ StateValue Input::toSMT(State &s) const {
Pointer p(s.getMemory(), val);
if (has_deref) {
s.addAxiom(type == 0);
- s.addAxiom(p.isDereferenceable(attrs.getDerefBytes(), bits_byte/8, false));
+ s.addAxiom(p.isDereferenceable(attrs.derefBytes, bits_byte/8, false));
}
if (has_nonnull && !has_deref) {
s.addAxiom(type.extract(1, 1) == 0);
@@ -216,7 +217,8 @@ StateValue Input::toSMT(State &s) const {
expr poison = getType().getDummyValue(false).non_poison;
expr non_poison = getType().getDummyValue(true).non_poison;
- bool never_poison = config::disable_poison_input || has_deref || has_noundef;
+ bool never_poison = config::disable_poison_input || has_byval || has_deref ||
+ has_noundef;
return { move(val),
never_poison

MODIFY
llvm_util/llvm2alive.cpp
llvm_util/llvm2alive.cpp
@@ -340,13 +340,17 @@ public:
derefb = max(derefb,
i.getCalledFunction()->getParamDereferenceableBytes(argidx));
assert(derefb);
- attr.setDerefBytes(derefb);
+ attr.derefBytes = derefb;
}
if (i.paramHasAttr(argidx, llvm::Attribute::ByVal)) {
- // A byval parameter cannot be captured.
- attr.set(ParamAttrs::NoCapture);
attr.set(ParamAttrs::ByVal);
+ auto ty = i.getParamByValType(argidx);
+ attr.blockSize = DL().getTypeAllocSize(ty);
+ if (!attr.has(ParamAttrs::Align)) {
+ attr.set(ParamAttrs::Align);
+ attr.align = DL().getABITypeAlignment(ty);
+ }
}
if (i.paramHasAttr(argidx, llvm::Attribute::NoCapture))
@@ -961,9 +965,16 @@ end:
// they don't change
continue;
- case llvm::Attribute::ByVal:
+ case llvm::Attribute::ByVal: {
attrs.set(ParamAttrs::ByVal);
+ auto ty = arg.getParamByValType();
+ attrs.blockSize = DL().getTypeAllocSize(ty);
+ if (!attrs.has(ParamAttrs::Align)) {
+ attrs.set(ParamAttrs::Align);
+ attrs.align = DL().getABITypeAlignment(ty);
+ }
continue;
+ }
case llvm::Attribute::NonNull:
attrs.set(ParamAttrs::NonNull);
@@ -983,7 +994,7 @@ end:
case llvm::Attribute::Dereferenceable:
attrs.set(ParamAttrs::Dereferenceable);
- attrs.setDerefBytes(attr.getDereferenceableBytes());
+ attrs.derefBytes = attr.getDereferenceableBytes();
continue;
case llvm::Attribute::NoUndef:

MODIFY
tools/transform.cpp
tools/transform.cpp
@@ -542,7 +542,12 @@ static void calculateAndInitConstants(Transform &t) {
num_ptrinputs = 0;
for (auto &arg : t.src.getInputs()) {
- num_ptrinputs += num_ptrs(arg.getType());
+ auto n = num_ptrs(arg.getType());
+ if (dynamic_cast<const Input*>(&arg)->hasAttribute(ParamAttrs::ByVal)) {
+ num_globals_src += n;
+ num_globals += n;
+ } else
+ num_ptrinputs += n;
}
// The number of instructions that can return a pointer to a non-local block.
@@ -584,10 +589,14 @@ static void calculateAndInitConstants(Transform &t) {
auto *i = dynamic_cast<const Input *>(&v);
if (i && i->hasAttribute(ParamAttrs::Dereferenceable)) {
does_mem_access = true;
- uint64_t deref_bytes = i->getAttributes().getDerefBytes();
+ uint64_t deref_bytes = i->getAttributes().derefBytes;
min_access_size = gcd(min_access_size, deref_bytes);
max_access_size = max(max_access_size, deref_bytes);
}
+ if (i && i->hasAttribute(ParamAttrs::ByVal)) {
+ does_mem_access = true;
+ max_access_size = max(max_access_size, i->getAttributes().blockSize);
+ }
}
auto update_min_vect_sz = [&](const Type &ty) {

fix an assorted bugs in byval implementation exposed by LLVM's unit tests