add support for align() attribute in malloc
this is now used by clang (rev 2cae7025c1d9e83d971f670c9a66497a8c1094ec)
MODIFY
ir/instr.cpp
ir/instr.cpp
@@ -2704,13 +2704,17 @@ DEFINE_AS_RETZERO(Malloc, getMaxGEPOffset);
DEFINE_AS_EMPTYACCESS(Malloc);
pair<uint64_t, unsigned> Malloc::getMaxAllocSize() const {
- return { getIntOr(*size, UINT64_MAX), heap_block_alignment };
+ return { getIntOr(*size, UINT64_MAX), getAlign() };
}
bool Malloc::canFree() const {
return ptr != nullptr;
}
+unsigned Malloc::getAlign() const {
+ return align ? align : heap_block_alignment;
+}
+
vector<Value*> Malloc::operands() const {
if (!ptr)
return { size };
@@ -2730,6 +2734,8 @@ void Malloc::print(ostream &os) const {
else
os << " = realloc " << *ptr << ", ";
os << *size;
+ if (align)
+ os << ", align " << align;
if (isNonNull)
os << ", nonnull";
}
@@ -2738,10 +2744,9 @@ StateValue Malloc::toSMT(State &s) const {
auto &m = s.getMemory();
auto &[sz, np_size] = s.getAndAddPoisonUB(*size, true);
- unsigned align = heap_block_alignment;
expr nonnull = expr::mkBoolVar("malloc_never_fails");
auto [p_new, allocated]
- = m.alloc(sz, align, Memory::MALLOC, np_size, nonnull);
+ = m.alloc(sz, getAlign(), Memory::MALLOC, np_size, nonnull);
expr nullp = Pointer::mkNullPointer(m)();
expr ret = expr::mkIf(allocated, p_new, nullp);
@@ -2792,20 +2797,24 @@ DEFINE_AS_RETFALSE(Calloc, canFree);
pair<uint64_t, unsigned> Calloc::getMaxAllocSize() const {
if (auto sz = getInt(*size)) {
if (auto n = getInt(*num))
- return { *sz * *n, heap_block_alignment };
+ return { *sz * *n, getAlign() };
}
- return { UINT64_MAX, heap_block_alignment };
+ return { UINT64_MAX, getAlign() };
}
Calloc::ByteAccessInfo Calloc::getByteAccessInfo() const {
auto info = ByteAccessInfo::intOnly(1);
if (auto n = getInt(*num))
if (auto sz = getInt(*size)) {
- info.byteSize = gcd(heap_block_alignment, *n * *sz);
+ info.byteSize = gcd(getAlign(), *n * *sz);
}
return info;
}
+unsigned Calloc::getAlign() const {
+ return align ? align : heap_block_alignment;
+}
+
vector<Value*> Calloc::operands() const {
return { num, size };
}
@@ -2817,21 +2826,22 @@ void Calloc::rauw(const Value &what, Value &with) {
void Calloc::print(ostream &os) const {
os << getName() << " = calloc " << *num << ", " << *size;
+ if (align)
+ os << ", align " << align;
}
StateValue Calloc::toSMT(State &s) const {
auto &[nm, np_num] = s.getAndAddPoisonUB(*num, true);
auto &[sz, np_sz] = s.getAndAddPoisonUB(*size, true);
- unsigned align = heap_block_alignment;
auto np = np_num && np_sz;
expr size = nm * sz;
expr nonnull = expr::mkBoolVar("malloc_never_fails");
auto &m = s.getMemory();
- auto [p, allocated] = m.alloc(size, align, Memory::MALLOC,
+ auto [p, allocated] = m.alloc(size, getAlign(), Memory::MALLOC,
np && nm.mul_no_uoverflow(sz), nonnull);
- m.memset(p, { expr::mkUInt(0, 8), true }, size, align, {}, false);
+ m.memset(p, { expr::mkUInt(0, 8), true }, size, getAlign(), {}, false);
return { expr::mkIf(allocated, p, Pointer::mkNullPointer(m)()), true };
}

MODIFY
ir/instr.h
ir/instr.h
@@ -515,20 +515,27 @@ public:
class Malloc final : public MemInstr {
Value *ptr = nullptr, *size;
+ unsigned align;
// Is this malloc (or equivalent operation, like new()) never returning
// null?
bool isNonNull = false;
public:
- Malloc(Type &type, std::string &&name, Value &size, bool isNonNull)
- : MemInstr(type, std::move(name)), size(&size), isNonNull(isNonNull) {}
+ Malloc(Type &type, std::string &&name, Value &size, bool isNonNull,
+ unsigned align = 0)
+ : MemInstr(type, std::move(name)), size(&size), align(align),
+ isNonNull(isNonNull) {}
- Malloc(Type &type, std::string &&name, Value &ptr, Value &size)
- : MemInstr(type, std::move(name)), ptr(&ptr), size(&size) {}
+ Malloc(Type &type, std::string &&name, Value &ptr, Value &size,
+ unsigned align = 0)
+ : MemInstr(type, std::move(name)), ptr(&ptr), size(&size), align(align) {}
Value& getSize() const { return *size; }
+ unsigned getAlign() const;
bool isRealloc() const { return ptr != nullptr; }
+ void setAlign(unsigned val) { align = val; }
+
std::pair<uint64_t, unsigned> getMaxAllocSize() const override;
uint64_t getMaxAccessSize() const override;
uint64_t getMaxGEPOffset() const override;
@@ -546,12 +553,17 @@ public:
class Calloc final : public MemInstr {
Value *num, *size;
+ unsigned align;
public:
- Calloc(Type &type, std::string &&name, Value &num, Value &size)
- : MemInstr(type, std::move(name)), num(&num), size(&size) {}
+ Calloc(Type &type, std::string &&name, Value &num, Value &size,
+ unsigned align = 0)
+ : MemInstr(type, std::move(name)), num(&num), size(&size), align(align) {}
Value& getNum() const { return *num; }
Value& getSize() const { return *size; }
+ unsigned getAlign() const;
+
+ void setAlign(unsigned val) { align = val; }
std::pair<uint64_t, unsigned> getMaxAllocSize() const override;
uint64_t getMaxAccessSize() const override;

MODIFY
llvm_util/known_fns.cpp
llvm_util/known_fns.cpp
@@ -20,6 +20,11 @@ using namespace std;
#define RETURN_APPROX() \
return { nullptr, move(attrs), move(param_attrs), true }
+static unsigned align(llvm::CallInst &i) {
+ auto a = i.getRetAlign();
+ return a.hasValue() ? a.getValue().value() : 0;
+}
+
namespace llvm_util {
tuple<unique_ptr<Instr>, FnAttrs, vector<ParamAttrs>, bool>
@@ -35,12 +40,18 @@ known_call(llvm::CallInst &i, const llvm::TargetLibraryInfo &TLI,
// TODO: add support for checking mismatch of C vs C++ alloc fns
if (llvm::isMallocLikeFn(&i, &TLI, false)) {
bool isNonNull = i.getCalledFunction()->getName() != "malloc";
- RETURN_VAL(make_unique<Malloc>(*ty, value_name(i), *args[0], isNonNull));
- } else if (llvm::isCallocLikeFn(&i, &TLI, false)) {
- RETURN_VAL(make_unique<Calloc>(*ty, value_name(i), *args[0], *args[1]));
- } else if (llvm::isReallocLikeFn(&i, &TLI, false)) {
- RETURN_VAL(make_unique<Malloc>(*ty, value_name(i), *args[0], *args[1]));
- } else if (llvm::isFreeCall(&i, &TLI)) {
+ RETURN_VAL(
+ make_unique<Malloc>(*ty, value_name(i), *args[0], isNonNull, align(i)));
+ }
+ else if (llvm::isCallocLikeFn(&i, &TLI, false)) {
+ RETURN_VAL(
+ make_unique<Calloc>(*ty, value_name(i), *args[0], *args[1], align(i)));
+ }
+ else if (llvm::isReallocLikeFn(&i, &TLI, false)) {
+ RETURN_VAL(
+ make_unique<Malloc>(*ty, value_name(i), *args[0], *args[1], align(i)));
+ }
+ else if (llvm::isFreeCall(&i, &TLI)) {
if (i.hasFnAttr(llvm::Attribute::NoFree)) {
auto zero = make_intconst(0, 1);
RETURN_VAL(make_unique<Assume>(*zero, Assume::AndNonPoison));

ADD
tests/alive-tv/memory/malloc-align.srctgt.ll
+define i8* @src() {
+ %call = call align 16 i8* @malloc(i64 8)
+ store i8 0, i8* %call, align 1
+ ret i8* %call
+}
+
+define i8* @tgt() {
+ %call = call align 16 i8* @malloc(i64 8)
+ store i8 0, i8* %call, align 16
+ ret i8* %call
+}
+
+declare i8* @malloc(i64)

ADD
tests/alive-tv/memory/malloc-align2.srctgt.ll
+define i8* @src() {
+ %call = call i8* @malloc(i64 8)
+ store i8 0, i8* %call, align 1
+ ret i8* %call
+}
+
+define i8* @tgt() {
+ %call = call i8* @malloc(i64 8)
+ store i8 0, i8* %call, align 8
+ ret i8* %call
+}
+
+declare i8* @malloc(i64)

add support for align() attribute in malloc
this is now used by clang (rev 2cae7025c1d9e83d971f670c9a66497a8c1094ec)