fix crash in unit tests due to incorrect calculation of # poison bits
MODIFY
ir/globals.cpp
ir/globals.cpp
@@ -18,6 +18,7 @@ unsigned num_ptrinputs;
unsigned num_extra_nonconst_tgt;
unsigned num_nonlocals;
unsigned num_nonlocals_src;
+unsigned bits_poison_per_byte;
unsigned bits_for_ptrattrs;
unsigned bits_for_bid;
unsigned bits_for_offset;
@@ -41,7 +42,6 @@ bool has_null_block;
bool does_int_mem_access;
bool does_ptr_mem_access;
bool does_ptr_store;
-bool does_sub_byte_access;
unsigned heap_block_alignment;

MODIFY
ir/globals.h
ir/globals.h
@@ -26,6 +26,8 @@ extern unsigned num_nonlocals;
// Upperbound of the number of nonlocal blocks in src (<= num_nonlocals)
extern unsigned num_nonlocals_src;
+extern unsigned bits_poison_per_byte;
+
/// Number of bits needed for attributes of pointers (e.g. nocapture).
extern unsigned bits_for_ptrattrs;
@@ -78,9 +80,6 @@ extern bool does_int_mem_access;
extern bool does_ptr_mem_access;
extern bool does_ptr_store;
-/// Whether the programs do memory accesses of less than bits_byte
-extern bool does_sub_byte_access;
-
extern unsigned heap_block_alignment;

MODIFY
ir/instr.cpp
ir/instr.cpp
@@ -2205,19 +2205,18 @@ MemInstr::ByteAccessInfo::intOnly(unsigned bytesz) {
MemInstr::ByteAccessInfo
MemInstr::ByteAccessInfo::get(const Type &t, bool store, unsigned align) {
+ bool ptr_access = hasPtr(t);
ByteAccessInfo info;
info.hasIntByteAccess = t.enforcePtrOrVectorType().isFalse();
- info.hasPtrByteAccess = hasPtr(t);
- info.doesPtrStore = info.hasPtrByteAccess && store;
- info.doesPtrLoad = info.hasPtrByteAccess && !store;
+ info.doesPtrStore = ptr_access && store;
+ info.doesPtrLoad = ptr_access && !store;
info.byteSize = gcd(align, getCommonAccessSize(t));
- info.hasSubByteAccess = hasSubByte(t);
return info;
}
MemInstr::ByteAccessInfo
-MemInstr::ByteAccessInfo::full(unsigned byteSize, bool subByte) {
- return { true, true, true, true, byteSize, subByte };
+MemInstr::ByteAccessInfo::full(unsigned byteSize) {
+ return { true, true, true, byteSize };
}
@@ -2774,7 +2773,7 @@ Memcpy::ByteAccessInfo Memcpy::getByteAccessInfo() const {
// FIXME: memcpy doesn't have multi-byte support
// Memcpy does not have sub-byte access, unless the sub-byte type appears
// at other instructions
- return ByteAccessInfo::full(byteSize, false);
+ return ByteAccessInfo::full(byteSize);
}
vector<Value*> Memcpy::operands() const {

MODIFY
ir/instr.h
ir/instr.h
@@ -445,27 +445,20 @@ public:
virtual uint64_t getMaxGEPOffset() const = 0;
struct ByteAccessInfo {
- // Does this instruction use integer (pointer) value of a byte?
- // If it stores poison value (e.g. uninitialized bytes of alloca), it is
- // okay for both variables to be false.
bool hasIntByteAccess = false;
- bool hasPtrByteAccess = false;
- // Does this intruction load / store pointers?
- // If hasPtrByteAccess is false, these cannot be true.
bool doesPtrLoad = false;
bool doesPtrStore = false;
+
// The maximum size of a byte that this instruction can support.
// If zero, this instruction does not read/write bytes.
// Otherwise, bytes of a memory can be widened to this size.
unsigned byteSize = 0;
- // Does this instruction have sub-byte access (less than 8 bits)?
- bool hasSubByteAccess = false;
bool doesMemAccess() const { return byteSize; }
static ByteAccessInfo intOnly(unsigned byteSize);
static ByteAccessInfo get(const Type &t, bool store, unsigned align);
- static ByteAccessInfo full(unsigned byteSize, bool subByte = false);
+ static ByteAccessInfo full(unsigned byteSize);
};
virtual ByteAccessInfo getByteAccessInfo() const = 0;

MODIFY
ir/memory.cpp
ir/memory.cpp
@@ -32,10 +32,6 @@ static bool byte_has_ptr_bit() {
return does_int_mem_access && does_ptr_mem_access;
}
-static unsigned bits_int_poison() {
- return does_sub_byte_access ? bits_byte : 1;
-}
-
static unsigned bits_ptr_byte_offset() {
assert(!does_ptr_mem_access || bits_byte <= bits_program_pointer);
return bits_byte < bits_program_pointer ? 3 : 0;
@@ -47,7 +43,8 @@ static unsigned padding_ptr_byte() {
}
static unsigned padding_nonptr_byte() {
- return Byte::bitsByte() - does_ptr_mem_access - bits_byte - bits_int_poison();
+ return
+ Byte::bitsByte() - does_ptr_mem_access - bits_byte - bits_poison_per_byte;
}
static expr concat_if(const expr &ifvalid, expr &&e) {
@@ -136,7 +133,7 @@ Byte::Byte(const Pointer &ptr, unsigned i, const expr &non_poison)
Byte::Byte(const Memory &m, const expr &data, const expr &non_poison) : m(m) {
assert(!data.isValid() || data.bits() == bits_byte);
- assert(!non_poison.isValid() || non_poison.bits() == bits_int_poison());
+ assert(!non_poison.isValid() || non_poison.bits() == bits_poison_per_byte);
if (!does_int_mem_access) {
p = expr::mkUInt(0, bitsByte());
@@ -189,9 +186,9 @@ expr Byte::ptrByteoffset() const {
expr Byte::nonptrNonpoison() const {
if (!does_int_mem_access)
- return expr::mkUInt(0, bits_int_poison());
+ return expr::mkUInt(0, bits_poison_per_byte);
unsigned start = padding_nonptr_byte() + bits_byte;
- return p.extract(start + bits_int_poison() - 1, start);
+ return p.extract(start + bits_poison_per_byte - 1, start);
}
expr Byte::nonptrValue() const {
@@ -203,7 +200,7 @@ expr Byte::nonptrValue() const {
expr Byte::isPoison(bool fullbit) const {
expr np = nonptrNonpoison();
- if (byte_has_ptr_bit() && bits_int_poison() == 1) {
+ if (byte_has_ptr_bit() && bits_poison_per_byte == 1) {
assert(!np.isValid() || ptrNonpoison().eq(np == 0));
return np == 1;
}
@@ -218,7 +215,7 @@ expr Byte::isZero() const {
unsigned Byte::bitsByte() {
unsigned ptr_bits = does_ptr_mem_access *
(1 + Pointer::totalBits() + bits_ptr_byte_offset());
- unsigned int_bits = does_int_mem_access * (bits_byte + bits_int_poison());
+ unsigned int_bits = does_int_mem_access * (bits_byte + bits_poison_per_byte);
// allow at least 1 bit if there's no memory access
return max(1u, byte_has_ptr_bit() + max(ptr_bits, int_bits));
}
@@ -261,8 +258,8 @@ ostream& operator<<(ostream &os, const Byte &byte) {
val.printHexadecimal(os);
} else {
os << "#b";
- for (unsigned i = 0; i < bits_int_poison(); ++i) {
- unsigned idx = bits_int_poison() - i - 1;
+ for (unsigned i = 0; i < bits_poison_per_byte; ++i) {
+ unsigned idx = bits_poison_per_byte - i - 1;
auto is_poison = (np.extract(idx, idx) == 1).isTrue();
auto v = (val.extract(idx, idx) == 1).isTrue();
os << (is_poison ? 'p' : (v ? '1' : '0'));
@@ -289,7 +286,7 @@ static vector<Byte> valueToBytes(const StateValue &val, const Type &fromType,
unsigned bytesize = divide_up(bitsize, bits_byte);
bvval = bvval.zext(bytesize * bits_byte - bitsize);
- unsigned np_mul = does_sub_byte_access ? bits_byte : 1;
+ unsigned np_mul = bits_poison_per_byte;
for (unsigned i = 0; i < bytesize; ++i) {
expr data = bvval.value.extract((i + 1) * bits_byte - 1, i * bits_byte);
@@ -362,7 +359,7 @@ static StateValue bytesToValue(const Memory &m, const vector<Byte> &bytes,
val = first ? move(v) : v.concat(val);
first = false;
}
- return toType.fromInt(val.trunc(bitsize));
+ return toType.fromInt(val.trunc(bitsize, toType.np_bits()));
}
}
@@ -2062,11 +2059,10 @@ expr Memory::blockValRefined(const Memory &other, unsigned bid, bool local,
// refinement if offset had non-ptr value
expr np1 = val.nonptrNonpoison();
expr np2 = val2.nonptrNonpoison();
- bool np_eqs = np1.eq(np2);
- expr int_cnstr = does_sub_byte_access
+ expr int_cnstr = bits_poison_per_byte > 1
? (np2 | np1) == np1 &&
(val.nonptrValue() | np1) == (val2.nonptrValue() | np1)
- : (np_eqs ? true : np2 == 0) &&
+ : (np1.eq(np2) ? true : np2 == 0) &&
val.nonptrValue() == val2.nonptrValue();
// fast path: if we didn't do any ptr store, then all ptrs in memory were

MODIFY
ir/state_value.cpp
ir/state_value.cpp
@@ -19,20 +19,18 @@ StateValue StateValue::mkIf(const expr &cond, const StateValue &then,
StateValue StateValue::zext(unsigned amount) const {
return { value.zext(amount),
- (!does_sub_byte_access || non_poison.isBool())
+ (bits_poison_per_byte == 1 || non_poison.isBool())
? expr(non_poison) : non_poison.zext(amount) };
}
-StateValue StateValue::trunc(unsigned amount) const {
- return { value.trunc(amount),
- (!does_sub_byte_access || non_poison.isBool())
- ? expr(non_poison) : non_poison.trunc(amount) };
+StateValue StateValue::trunc(unsigned bw_val, unsigned bw_np) const {
+ return { value.trunc(bw_val), non_poison.trunc(bw_np) };
}
StateValue StateValue::zextOrTrunc(unsigned tobw) const {
return
{ value.zextOrTrunc(tobw),
- (!does_sub_byte_access || non_poison.isBool())
+ (bits_poison_per_byte == 1 || non_poison.isBool())
? expr(non_poison) : non_poison.zextOrTrunc(tobw) };
}

MODIFY
ir/state_value.h
ir/state_value.h
@@ -23,7 +23,7 @@ struct StateValue {
auto bits() const { return value.bits(); }
StateValue zext(unsigned amount) const;
- StateValue trunc(unsigned amount) const;
+ StateValue trunc(unsigned bw_val, unsigned bw_np) const;
StateValue zextOrTrunc(unsigned tobw) const;
StateValue concat(const StateValue &other) const;

MODIFY
ir/type.cpp
ir/type.cpp
@@ -25,7 +25,7 @@ VoidType Type::voidTy;
unsigned Type::np_bits() const {
auto bw = bits();
- return does_sub_byte_access ? bw : (unsigned)divide_up(bw, bits_byte);
+ return min(bw, (unsigned)divide_up(bw * bits_poison_per_byte, bits_byte));
}
expr Type::var(const char *var, unsigned bits) const {
@@ -1064,6 +1064,12 @@ StateValue VectorType::update(const StateValue &vector,
(vector.non_poison & mask_np) | np_shifted});
}
+unsigned VectorType::np_bits() const {
+ if (getChild(0).isPtrType())
+ return elements;
+ return Type::np_bits();
+}
+
expr VectorType::getTypeConstraints() const {
auto &elementTy = *children[0];
expr r = AggregateType::getTypeConstraints() &&
@@ -1407,24 +1413,27 @@ bool isNonPtrVector(const Type &t) {
return vty && !vty->getChild(0).isPtrType();
}
-bool hasSubByte(const Type &t) {
+unsigned minVectorElemSize(const Type &t) {
if (auto agg = t.getAsAggregateType()) {
if (t.isVectorType()) {
auto &elemTy = agg->getChild(0);
- return elemTy.isPtrType() ? false : (elemTy.bits() % 8);
+ return elemTy.isPtrType() ? IR::bits_program_pointer : elemTy.bits();
}
+ unsigned val = 0;
for (unsigned i = 0, e = agg->numElementsConst(); i != e; ++i) {
- if (hasSubByte(agg->getChild(i)))
- return true;
+ if (auto ch = minVectorElemSize(agg->getChild(i))) {
+ val = val ? gcd(val, ch) : ch;
}
}
- return false;
+ return val;
+ }
+ return 0;
}
uint64_t getCommonAccessSize(const IR::Type &ty) {
if (auto agg = ty.getAsAggregateType()) {
- // non-pointer vectors are store/loaded all at once
+ // non-pointer vectors are stored/loaded all at once
if (agg->isVectorType()) {
auto &elemTy = agg->getChild(0);
if (!elemTy.isPtrType())

MODIFY
ir/type.h
ir/type.h
@@ -328,6 +328,7 @@ public:
IR::StateValue update(const IR::StateValue &vector,
const IR::StateValue &val,
const smt::expr &idx) const;
+ unsigned np_bits() const override;
smt::expr getTypeConstraints() const override;
smt::expr scalarSize() const override;
bool isVectorType() const override;
@@ -416,6 +417,6 @@ public:
bool hasPtr(const Type &t);
bool isNonPtrVector(const Type &t);
-bool hasSubByte(const Type &t);
+unsigned minVectorElemSize(const Type &t);
uint64_t getCommonAccessSize(const Type &ty);
}

MODIFY
tools/transform.cpp
tools/transform.cpp
@@ -569,9 +569,9 @@ static void calculateAndInitConstants(Transform &t) {
// Mininum access size (in bytes)
uint64_t min_access_size = 8;
+ unsigned min_vect_elem_sz = 0;
bool does_mem_access = false;
bool has_ptr_load = false;
- does_sub_byte_access = false;
bool has_vector_bitcast = false;
for (auto fn : { &t.src, &t.tgt }) {
@@ -590,6 +590,14 @@ static void calculateAndInitConstants(Transform &t) {
}
}
+ auto update_min_vect_sz = [&](const Type &ty) {
+ auto elemsz = minVectorElemSize(ty);
+ if (min_vect_elem_sz && elemsz)
+ min_vect_elem_sz = gcd(min_vect_elem_sz, elemsz);
+ else if (elemsz)
+ min_vect_elem_sz = elemsz;
+ };
+
set<pair<Value*, uint64_t>> nonlocal_cache;
for (auto BB : fn->getBBs()) {
for (auto &i : BB->instrs()) {
@@ -600,8 +608,11 @@ static void calculateAndInitConstants(Transform &t) {
for (auto op : i.operands()) {
nullptr_is_used |= has_nullptr(op);
+ update_min_vect_sz(op->getType());
}
+ update_min_vect_sz(i.getType());
+
if (auto *fc = dynamic_cast<const FnCall*>(&i)) {
has_fncall |= true;
has_free |= !fc->getAttributes().has(FnAttrs::NoFree);
@@ -616,8 +627,6 @@ static void calculateAndInitConstants(Transform &t) {
has_ptr_load |= info.doesPtrLoad;
does_ptr_store |= info.doesPtrStore;
does_int_mem_access |= info.hasIntByteAccess;
- does_ptr_mem_access |= info.hasPtrByteAccess;
- does_sub_byte_access |= info.hasSubByteAccess;
does_mem_access |= info.doesMemAccess();
min_access_size = gcd(min_access_size, info.byteSize);
@@ -642,12 +651,14 @@ static void calculateAndInitConstants(Transform &t) {
} else if (auto *bc = isCast(ConversionOp::BitCast, i)) {
auto &t = bc->getType();
has_vector_bitcast |= t.isVectorType();
- does_sub_byte_access |= hasSubByte(t);
min_access_size = gcd(min_access_size, getCommonAccessSize(t));
}
}
}
}
+
+ does_ptr_mem_access = has_ptr_load || does_ptr_store;
+
unsigned num_locals = max(num_locals_src, num_locals_tgt);
uint64_t min_global_size = UINT64_MAX;
@@ -729,6 +740,11 @@ static void calculateAndInitConstants(Transform &t) {
bits_byte = 8 * ((does_mem_access || num_globals != 0)
? (unsigned)min_access_size : 1);
+ bits_poison_per_byte = 1;
+ if (min_vect_elem_sz > 0)
+ bits_poison_per_byte = (min_vect_elem_sz % 8) ? bits_byte :
+ bits_byte / gcd(bits_byte, min_vect_elem_sz);
+
strlen_unroll_cnt = 10;
memcmp_unroll_cnt = 10;
@@ -747,6 +763,7 @@ static void calculateAndInitConstants(Transform &t) {
<< "\nmin_access_size: " << min_access_size
<< "\nmax_access_size: " << max_access_size
<< "\nbits_byte: " << bits_byte
+ << "\nbits_poison_per_byte: " << bits_poison_per_byte
<< "\nstrlen_unroll_cnt: " << strlen_unroll_cnt
<< "\nmemcmp_unroll_cnt: " << memcmp_unroll_cnt
<< "\nlittle_endian: " << little_endian
@@ -760,7 +777,6 @@ static void calculateAndInitConstants(Transform &t) {
<< "\ndoes_mem_access: " << does_mem_access
<< "\ndoes_ptr_mem_access: " << does_ptr_mem_access
<< "\ndoes_int_mem_access: " << does_int_mem_access
- << "\ndoes_sub_byte_access: " << does_sub_byte_access
<< '\n';
}

fix crash in unit tests due to incorrect calculation of # poison bits