Add support for align attribute
MODIFY
ir/attrs.cpp
ir/attrs.cpp
@@ -23,7 +23,7 @@ ostream& operator<<(ostream &os, const ParamAttrs &attr) {
if (attr.has(ParamAttrs::NoUndef))
os << "noundef ";
if (attr.has(ParamAttrs::Align))
- os << "align(" << attr.align << ") ";
+ os << "align(" << (1ull << attr.align) << ") ";
if (attr.has(ParamAttrs::Returned))
os << "returned ";
return os;
@@ -49,6 +49,8 @@ ostream& operator<<(ostream &os, const FnAttrs &attr) {
os << " nofree";
if (attr.has(FnAttrs::NoUndef))
os << " noundef";
+ if (attr.has(FnAttrs::Align))
+ os << " align(" << (1ull << attr.align) << ")";
return os;
}
@@ -82,7 +84,9 @@ bool FnAttrs::operator==(const FnAttrs &rhs) const {
if (bits != rhs.bits)
return false;
- if (has(Dereferenceable) && getDerefBytes() != rhs.getDerefBytes())
+ if (has(Dereferenceable) && derefBytes != rhs.derefBytes)
+ return false;
+ if (has(Align) && align != rhs.align)
return false;
return true;

MODIFY
ir/attrs.h
ir/attrs.h
@@ -26,7 +26,8 @@ public:
// Returns true if it's UB for the argument to be poison / have a poison elem.
bool poisonImpliesUB() const
- { return has(NonNull) || has(Dereferenceable) || has(NoUndef) || has(ByVal); }
+ { return has(NonNull) || has(Dereferenceable) || has(NoUndef) || has(ByVal) ||
+ has(Align); }
// Returns true if it is UB for the argument to be (partially) undef.
bool undefImpliesUB() const;
@@ -40,24 +41,24 @@ 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,
Dereferenceable = 1 << 5, NonNull = 1 << 6,
- NoFree = 1 << 7, NoUndef = 1 << 8 };
+ NoFree = 1 << 7, NoUndef = 1 << 8, Align = 1 << 9 };
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; }
+
+ uint64_t derefBytes; // Dereferenceable
+ uint64_t align; // power-of-2 in bytes
// Returns true if returning poison or an aggregate having a poison is UB
bool poisonImpliesUB() const
- { return has(NonNull) || has(Dereferenceable) || has(NoUndef); }
+ { return has(NonNull) || has(Dereferenceable) || has(NoUndef) || has(Align); }
// Returns true if returning (partially) undef is UB
bool undefImpliesUB() const;

MODIFY
ir/instr.cpp
ir/instr.cpp
@@ -1556,7 +1556,7 @@ bool FnCall::canFree() const {
}
uint64_t FnCall::getMaxAccessSize() const {
- uint64_t sz = attrs.has(FnAttrs::Dereferenceable) ? attrs.getDerefBytes() : 0;
+ uint64_t sz = attrs.has(FnAttrs::Dereferenceable) ? attrs.derefBytes : 0;
for (auto &arg : args) {
if (arg.second.has(ParamAttrs::Dereferenceable))
sz = max(sz, arg.second.derefBytes);
@@ -1642,6 +1642,9 @@ static void unpack_inputs(State &s, Value &argv, Type &ty,
if (argflag.has(ParamAttrs::NonNull))
s.addUB(p.isNonZero());
+ if (argflag.has(ParamAttrs::Align))
+ s.addUB(p.isAligned(1ull << argflag.align));
+
ptr_inputs.emplace_back(StateValue(p.release(), move(value.non_poison)),
argflag.has(ParamAttrs::ByVal),
argflag.has(ParamAttrs::NoCapture));
@@ -1686,13 +1689,17 @@ pack_return(State &s, Type &ty, vector<StateValue> &vals, const FnAttrs &attrs,
bool isDeref = attrs.has(FnAttrs::Dereferenceable);
bool isNonNull = attrs.has(FnAttrs::NonNull);
- if (ty.isPtrType() && (isDeref || isNonNull)) {
+ bool isAlign = attrs.has(FnAttrs::Align);
+
+ if (ty.isPtrType() && (isDeref || isNonNull || isAlign)) {
Pointer p(s.getMemory(), ret.value);
s.addUB(ret.non_poison);
if (isDeref)
- s.addUB(p.isDereferenceable(attrs.getDerefBytes()));
+ s.addUB(p.isDereferenceable(attrs.derefBytes));
if (isNonNull)
s.addUB(p.isNonZero());
+ if (isAlign)
+ s.addUB(p.isAligned(1ull << attrs.align));
}
return ret;
@@ -2322,18 +2329,23 @@ StateValue Return::toSMT(State &s) const {
bool isDeref = attrs.has(FnAttrs::Dereferenceable);
bool isNonNull = attrs.has(FnAttrs::NonNull);
- if (isDeref || isNonNull) {
+ bool isAlign = attrs.has(FnAttrs::Align);
+
+ if (isDeref || isNonNull || isAlign) {
assert(val->getType().isPtrType());
Pointer p(s.getMemory(), retval.value);
if (isDeref) {
- s.addUB(p.isDereferenceable(attrs.getDerefBytes()));
+ s.addUB(p.isDereferenceable(attrs.derefBytes));
if (has_alloca)
s.addUB(p.getAllocType() != Pointer::STACK);
}
if (isNonNull) {
s.addUB(p.isNonZero());
}
+ if (isAlign) {
+ s.addUB(p.isAligned(1ull << attrs.align));
+ }
}
if (attrs.has(FnAttrs::NoReturn))

MODIFY
ir/memory.cpp
ir/memory.cpp
@@ -1581,8 +1581,12 @@ expr Memory::mkInput(const char *name, const ParamAttrs &attrs) {
assert(max_bid < num_nonlocals_src);
Pointer p(*this, name, false, false, false, attr_to_bitvec(attrs));
auto bid = p.getShortBid();
+
if (attrs.has(ParamAttrs::NonNull))
state->addAxiom(p.isNonZero());
+ if (attrs.has(ParamAttrs::Align))
+ state->addAxiom(p.isAligned(1ull << attrs.align));
+
state->addAxiom(bid.ule(max_bid));
AliasSet alias(*this);

MODIFY
llvm_util/llvm2alive.cpp
llvm_util/llvm2alive.cpp
@@ -281,9 +281,26 @@ public:
const auto &ret = llvm::AttributeList::ReturnIndex;
if (uint64_t b = max(i.getDereferenceableBytes(ret),
- i.getCalledFunction()->getDereferenceableBytes(ret))) {
+ fn->getDereferenceableBytes(ret))) {
attrs.set(FnAttrs::Dereferenceable);
- attrs.setDerefBytes(b);
+ attrs.derefBytes = b;
+ }
+
+ {
+ uint64_t align = 0;
+ llvm::MaybeAlign ra = i.getRetAlign();
+ if (ra)
+ align = ra->value();
+
+ if (fn->hasAttribute(ret, llvm::Attribute::Alignment))
+ align = max(align,
+ fn->getAttribute(ret, llvm::Attribute::Alignment)
+ .getAlignment()->value());
+
+ if (align) {
+ attrs.set(FnAttrs::Align);
+ attrs.align = ilog2(align);
+ }
}
string fn_name = '@' + fn->getName().str();
@@ -309,28 +326,43 @@ public:
// call handleAttributes().
if (i.paramHasAttr(argidx, llvm::Attribute::Dereferenceable)) {
- attr.set(ParamAttrs::Dereferenceable);
-
uint64_t derefb = 0;
// dereferenceable at caller
if (i.getAttributes()
.hasParamAttr(argidx, llvm::Attribute::Dereferenceable))
derefb = i.getParamAttr(argidx, llvm::Attribute::Dereferenceable)
.getDereferenceableBytes();
- if (argidx < i.getCalledFunction()->arg_size())
+ if (argidx < fn->arg_size())
derefb = max(derefb,
- i.getCalledFunction()->getParamDereferenceableBytes(argidx));
+ fn->getParamDereferenceableBytes(argidx));
assert(derefb);
+ attr.set(ParamAttrs::Dereferenceable);
attr.derefBytes = derefb;
}
+ if (i.paramHasAttr(argidx, llvm::Attribute::Alignment)) {
+ uint64_t a = 0;
+ // alignment at caller
+ if (i.getAttributes()
+ .hasParamAttr(argidx, llvm::Attribute::Alignment))
+ a = i.getParamAttr(argidx, llvm::Attribute::Alignment)
+ .getAlignment()->value();
+ if (argidx < fn->arg_size() &&
+ fn->hasParamAttribute(argidx, llvm::Attribute::Alignment))
+ a = max(a, fn->getParamAlign(argidx)->value());
+
+ assert(a);
+ attr.set(ParamAttrs::Align);
+ attr.align = ilog2(a);
+ }
+
if (i.paramHasAttr(argidx, llvm::Attribute::ByVal)) {
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);
+ attr.align = ilog2(DL().getABITypeAlignment(ty));
}
}
@@ -1007,6 +1039,11 @@ end:
attrs.derefBytes = attr.getDereferenceableBytes();
continue;
+ case llvm::Attribute::Alignment:
+ attrs.set(ParamAttrs::Align);
+ attrs.align = ilog2(attr.getAlignment()->value());
+ continue;
+
case llvm::Attribute::NoUndef:
attrs.set(ParamAttrs::NoUndef);
continue;
@@ -1066,7 +1103,14 @@ end:
if (uint64_t b = f.getDereferenceableBytes(ridx)) {
attrs.set(FnAttrs::Dereferenceable);
- attrs.setDerefBytes(b);
+ attrs.derefBytes = b;
+ }
+
+ if (f.hasAttribute(ridx, llvm::Attribute::Alignment)) {
+ unsigned a = f.getAttribute(ridx, llvm::Attribute::Alignment)
+ .getAlignment()->value();
+ attrs.set(FnAttrs::Align);
+ attrs.align = ilog2(a);
}
// create all BBs upfront in topological order

ADD
tests/alive-tv/attrs/align-callsite.srctgt.ll
+define void @src(i8* %p) {
+ call void @g(i8* %p)
+ load i8, i8* %p
+ ret void
+}
+
+define void @tgt(i8* %p) {
+ call void @g(i8* %p)
+ load i8, i8* %p, align 4
+ ret void
+}
+
+declare void @g(i8* align(4))

ADD
tests/alive-tv/attrs/align-callsite2.srctgt.ll
+define void @src(i8* %p) {
+ call void @g(i8* align(4) %p)
+ load i8, i8* %p
+ ret void
+}
+
+define void @tgt(i8* %p) {
+ call void @g(i8* align(4) %p)
+ load i8, i8* %p, align 4
+ ret void
+}
+
+declare void @g(i8*)

ADD
tests/alive-tv/attrs/align-callsite3.srctgt.ll
+define void @src(i8* %p) {
+ call void @g(i8* align(4) %p)
+ load i8, i8* %p
+ ret void
+}
+
+define void @tgt(i8* %p) {
+ call void @g(i8* align(4) %p)
+ load i8, i8* %p, align 8
+ ret void
+}
+
+declare void @g(i8* align(8))

ADD
tests/alive-tv/attrs/align-fndef.srctgt.ll
+define void @src(i8* align(4) %p) {
+ load i8, i8* %p
+ ret void
+}
+
+define void @tgt(i8* align(4) %p) {
+ load i8, i8* %p, align 4
+ ret void
+}

ADD
tests/alive-tv/attrs/align-fndef2.srctgt.ll
+define align 4 i8* @src(i8* %p) {
+ load i8, i8* %p
+ ret i8* %p
+}
+
+define align 4 i8* @tgt(i8* %p) {
+ load i8, i8* %p, align 4
+ ret i8* %p
+}

ADD
tests/alive-tv/attrs/align-ret-callsite.srctgt.ll
+define void @src() {
+ %p = call align 4 i8* @g()
+ load i8, i8* %p
+ ret void
+}
+
+define void @tgt() {
+ %p = call align 4 i8* @g()
+ load i8, i8* %p, align 4
+ ret void
+}
+
+declare i8* @g()

ADD
tests/alive-tv/attrs/align-ret-callsite2.srctgt.ll
+define void @src() {
+ %p = call i8* @g()
+ load i8, i8* %p
+ ret void
+}
+
+define void @tgt() {
+ %p = call i8* @g()
+ load i8, i8* %p, align 4
+ ret void
+}
+
+declare align 4 i8* @g()

Add support for align attribute (#557)