Skip to content

Commit

Permalink
Merge branch 'AliveToolkit:master' into mutate
Browse files Browse the repository at this point in the history
  • Loading branch information
Hatsunespica authored Jul 15, 2024
2 parents a847bfc + fa0504c commit 148f211
Show file tree
Hide file tree
Showing 45 changed files with 1,019 additions and 399 deletions.
1 change: 1 addition & 0 deletions BugList.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ Please contact us or submit a PR if something is missing or inaccurate.
93. InstCombine: align attribute doesn't dereferenceability (https://llvm.org/PR90446)
94. Reassociate: invalid propagation of overflow attributes at low bit-width (https://llvm.org/PR91417)
95. InstCombine: removes a select, making the code more poisonous (https://llvm.org/PR91691)
96. DSE removes store before free() incorrectly (https://llvm.org/PR97956)

### Bugs found in Z3
1. Incorrect bitblast for fprem (https://github.com/Z3Prover/z3/issues/2369)
Expand Down
80 changes: 51 additions & 29 deletions ir/attrs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
#include "ir/state.h"
#include "ir/state_value.h"
#include "ir/type.h"
#include "util/compiler.h"
#include <cassert>

using namespace std;
using namespace smt;
using namespace util;

namespace IR {
ostream& operator<<(ostream &os, const ParamAttrs &attr) {
Expand Down Expand Up @@ -339,6 +341,13 @@ uint64_t ParamAttrs::getDerefBytes() const {
return bytes;
}

uint64_t ParamAttrs::maxAccessSize() const {
uint64_t bytes = getDerefBytes();
if (has(ParamAttrs::DereferenceableOrNull))
bytes = max(bytes, derefOrNullBytes);
return round_up(bytes, align);
}

void ParamAttrs::merge(const ParamAttrs &other) {
bits |= other.bits;
derefBytes = max(derefBytes, other.derefBytes);
Expand All @@ -351,7 +360,7 @@ static expr
encodePtrAttrs(State &s, const expr &ptrvalue, uint64_t derefBytes,
uint64_t derefOrNullBytes, uint64_t align, bool nonnull,
bool nocapture, bool writable, const expr &allocsize,
Value *allocalign) {
Value *allocalign, bool isdecl) {
auto &m = s.getMemory();
Pointer p(m, ptrvalue);
expr non_poison(true);
Expand All @@ -364,19 +373,20 @@ encodePtrAttrs(State &s, const expr &ptrvalue, uint64_t derefBytes,
if (derefBytes || derefOrNullBytes || allocsize.isValid()) {
// dereferenceable, byval (ParamAttrs), dereferenceable_or_null
if (derefBytes)
s.addUB(merge(Pointer(m, ptrvalue)
.isDereferenceable(derefBytes, align, writable, true)));
s.addUB(merge(p.isDereferenceable(derefBytes, align, writable, true)));
if (derefOrNullBytes)
s.addUB(p.isNull() ||
merge(Pointer(m, ptrvalue)
.isDereferenceable(derefOrNullBytes, align, writable,
true)));
merge(p.isDereferenceable(derefOrNullBytes, align, writable,
true)));
if (allocsize.isValid())
s.addUB(p.isNull() ||
merge(Pointer(m, ptrvalue)
.isDereferenceable(allocsize, align, writable, true)));
} else if (align != 1)
non_poison &= Pointer(m, ptrvalue).isAligned(align);
merge(p.isDereferenceable(allocsize, align, writable, true)));
} else if (align != 1) {
non_poison &= p.isAligned(align);
if (isdecl)
s.addUB(merge(p.isDereferenceable(1, 1, false, true))
.implies(merge(p.isDereferenceable(1, align, false, true))));
}

if (allocalign) {
Pointer p(m, ptrvalue);
Expand All @@ -392,7 +402,8 @@ encodePtrAttrs(State &s, const expr &ptrvalue, uint64_t derefBytes,
return non_poison;
}

StateValue ParamAttrs::encode(State &s, StateValue &&val, const Type &ty) const{
StateValue ParamAttrs::encode(State &s, StateValue &&val, const Type &ty,
bool isdecl) const{
if (has(NoFPClass)) {
assert(ty.isFloatType());
val.non_poison &= !isfpclass(val.value, ty, nofpclass);
Expand All @@ -401,7 +412,8 @@ StateValue ParamAttrs::encode(State &s, StateValue &&val, const Type &ty) const{
if (ty.isPtrType())
val.non_poison &=
encodePtrAttrs(s, val.value, getDerefBytes(), derefOrNullBytes, align,
has(NonNull), has(NoCapture), has(Writable), {}, nullptr);
has(NonNull), has(NoCapture), has(Writable), {}, nullptr,
isdecl);

if (poisonImpliesUB()) {
s.addUB(std::move(val.non_poison));
Expand Down Expand Up @@ -504,7 +516,7 @@ StateValue FnAttrs::encode(State &s, StateValue &&val, const Type &ty,
if (ty.isPtrType())
val.non_poison &=
encodePtrAttrs(s, val.value, derefBytes, derefOrNullBytes, align,
has(NonNull), false, false, allocsize, allocalign);
has(NonNull), false, false, allocsize, allocalign, false);

if (poisonImpliesUB()) {
s.addUB(std::move(val.non_poison));
Expand All @@ -516,29 +528,39 @@ StateValue FnAttrs::encode(State &s, StateValue &&val, const Type &ty,


expr isfpclass(const expr &v, const Type &ty, uint16_t mask) {
if (mask == 1023)
return true;

auto *fpty = ty.getAsFloatType();
auto a = fpty->getFloat(v);
OrExpr result;
if (mask & (1 << 0))
result.add(fpty->isNaN(v, true));
if (mask & (1 << 1))
result.add(fpty->isNaN(v, false));
if (mask & (1 << 2))
result.add(a.isFPNegative() && a.isInf());
if (mask & (1 << 3))
result.add(a.isFPNegative() && a.isFPNormal());
if (mask & (1 << 4))
result.add(a.isFPNegative() && a.isFPSubNormal());
if (mask & (1 << 5))
result.add(a.isFPNegZero());
if (mask & (1 << 6))
result.add(a.isFPZero() && !a.isFPNegative());
if (mask & (1 << 7))
result.add(!a.isFPNegative() && a.isFPSubNormal());
if (mask & (1 << 8))
result.add(!a.isFPNegative() && a.isFPNormal());
if (mask & (1 << 9))
result.add(!a.isFPNegative() && a.isInf());

auto check = [&](unsigned idx_neg, unsigned idx_pos, auto test) {
unsigned mask_neg = 1u << idx_neg;
unsigned mask_pos = 1u << idx_pos;
unsigned mask_both = mask_neg | mask_pos;

if ((mask & mask_both) == mask_both) {
result.add(test());
} else if (mask & mask_neg) {
result.add(a.isFPNegative() && test());
} else if (mask & mask_pos) {
result.add(!a.isFPNegative() && test());
}
};
#define CHECK(neg, pos, fn) check(neg, pos, [&a]() { return a.fn(); })

CHECK(5, 6, isFPZero);
CHECK(4, 7, isFPSubNormal);
CHECK(3, 8, isFPNormal);
CHECK(2, 9, isInf);

#undef CHECK

return std::move(result)();
}

Expand Down
4 changes: 3 additions & 1 deletion ir/attrs.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,15 @@ class ParamAttrs final {
bool poisonImpliesUB() const;

uint64_t getDerefBytes() const;
uint64_t maxAccessSize() const;

void merge(const ParamAttrs &other);

friend std::ostream& operator<<(std::ostream &os, const ParamAttrs &attr);

// Encodes the semantics of attributes using UB and poison.
StateValue encode(State &s, StateValue &&val, const Type &ty) const;
StateValue encode(State &s, StateValue &&val, const Type &ty,
bool isdecl = false) const;
};

struct FPDenormalAttrs {
Expand Down
4 changes: 4 additions & 0 deletions ir/function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ unsigned Function::FnDecl::hash() const {
hash_ty(*ty);
}
hash_ty(*output);
hash.add(is_varargs * 32);
return hash();
}

Expand Down Expand Up @@ -849,6 +850,9 @@ void Function::print(ostream &os, bool print_header) const {
os << input.second << *input.first;
first = false;
}
if (decl.is_varargs) {
os << (first ? "..." : ", ...");
}
os << ')' << decl.attrs << '\n';
}
os << '\n';
Expand Down
1 change: 1 addition & 0 deletions ir/function.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class Function final {
struct FnDecl {
std::string name;
std::vector<std::pair<Type*, ParamAttrs>> inputs;
bool is_varargs = false;
Type *output;
FnAttrs attrs;
unsigned hash() const;
Expand Down
40 changes: 31 additions & 9 deletions ir/instr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2141,9 +2141,9 @@ unique_ptr<Instr> InsertValue::dup(Function &f, const string &suffix) const {
DEFINE_AS_RETZERO(FnCall, getMaxGEPOffset);

FnCall::FnCall(Type &type, string &&name, string &&fnName, FnAttrs &&attrs,
Value *fnptr)
Value *fnptr, unsigned var_arg_idx)
: MemInstr(type, std::move(name)), fnName(std::move(fnName)), fnptr(fnptr),
attrs(std::move(attrs)) {
attrs(std::move(attrs)), var_arg_idx(var_arg_idx) {
if (config::disallow_ub_exploitation)
this->attrs.set(FnAttrs::NoUndef);
assert(!fnptr || this->fnName.empty());
Expand Down Expand Up @@ -2287,7 +2287,11 @@ void FnCall::print(ostream &os) const {
<< (fnptr ? fnptr->getName() : fnName) << '(';

bool first = true;
unsigned idx = 0;
for (auto &[arg, attrs] : args) {
if (idx++ == var_arg_idx)
os << "...";

if (!first)
os << ", ";

Expand Down Expand Up @@ -2411,23 +2415,30 @@ StateValue FnCall::toSMT(State &s) const {
auto ptr = fnptr;
// This is a direct call, but check if there are indirect calls elsewhere
// to this function. If so, call it indirectly to match the other calls.
if (!ptr)
ptr = s.getFn().getGlobalVar(string_view(fnName).substr(1));
if (!ptr && has_indirect_fncalls)
ptr = s.getFn().getGlobalVar(fnName);

ostringstream fnName_mangled;
if (ptr) {
fnName_mangled << "#indirect_call";

Pointer p(s.getMemory(), s.getAndAddPoisonUB(*ptr, true).value);
inputs.emplace_back(p.reprWithoutAttrs(), true);
auto bid = p.getShortBid();
inputs.emplace_back(expr(bid), true);
s.addUB(p.isDereferenceable(1, 1, false));

Function::FnDecl decl;
decl.output = &getType();
unsigned idx = 0;
for (auto &[arg, params] : args) {
if (idx++ == var_arg_idx)
break;
decl.inputs.emplace_back(&arg->getType(), params);
}
s.addUB(expr::mkUF("#fndeclty", { inputs[0].value }, expr::mkUInt(0, 32)) ==
decl.is_varargs = var_arg_idx != -1u;
s.addUB(!p.isLocal());
s.addUB(p.getOffset() == 0);
s.addUB(expr::mkUF("#fndeclty", { std::move(bid) }, expr::mkUInt(0, 32)) ==
(indirect_hash = decl.hash()));
} else {
fnName_mangled << fnName;
Expand Down Expand Up @@ -2568,7 +2579,7 @@ expr FnCall::getTypeConstraints(const Function &f) const {

unique_ptr<Instr> FnCall::dup(Function &f, const string &suffix) const {
auto r = make_unique<FnCall>(getType(), getName() + suffix, string(fnName),
FnAttrs(attrs), fnptr);
FnAttrs(attrs), fnptr, var_arg_idx);
r->args = args;
r->approx = approx;
return r;
Expand Down Expand Up @@ -3255,6 +3266,17 @@ bool Assume::propagatesPoison() const {
}

bool Assume::hasSideEffects() const {
switch (kind) {
// assume(true) is NOP
case AndNonPoison:
case WellDefined:
if (auto *c = dynamic_cast<IntConst*>(args[0]))
if (auto n = c->getInt())
return *n != 1;
break;
default:
break;
}
return true;
}

Expand Down Expand Up @@ -3699,7 +3721,7 @@ uint64_t GEP::getMaxGEPOffset() const {
return UINT64_MAX;

if (auto n = getInt(*v)) {
off = add_saturate(off, abs((int64_t)mul * *n));
off = add_saturate(off, (int64_t)mul * *n);
continue;
}

Expand Down Expand Up @@ -3811,7 +3833,7 @@ StateValue GEP::toSMT(State &s) const {

// try to simplify the pointer
if (all_zeros.isFalse())
ptr.inbounds(true);
ptr.inbounds();
}

return { std::move(ptr).release(), non_poison() };
Expand Down
5 changes: 4 additions & 1 deletion ir/instr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1053,15 +1053,18 @@ class FnCall : public MemInstr {
Value *fnptr;
std::vector<std::pair<Value*, ParamAttrs>> args;
FnAttrs attrs;
unsigned var_arg_idx;
bool approx = false;

Value* getAlignArg() const;

public:
FnCall(Type &type, std::string &&name, std::string &&fnName,
FnAttrs &&attrs = FnAttrs::None, Value *fnptr = nullptr);
FnAttrs &&attrs = FnAttrs::None, Value *fnptr = nullptr,
unsigned var_arg_idx = -1u);
void addArg(Value &arg, ParamAttrs &&attrs);
const auto& getFnName() const { return fnName; }
Value* getFnPtr() const { return fnptr; }
const auto& getArgs() const { return args; }
const auto& getAttributes() const { return attrs; }
bool hasAttribute(const FnAttrs::Attribute &i) const { return attrs.has(i); }
Expand Down
Loading

0 comments on commit 148f211

Please sign in to comment.