uniformize fast-math handling code
MODIFY
ir/instr.cpp
ir/instr.cpp
@@ -45,28 +45,53 @@ expr Instr::getTypeConstraints() const {
}
+ostream& operator<<(ostream &os, const FastMathFlags &fm) {
+ if (fm.flags == FastMathFlags::FastMath)
+ return os << "fast ";
+
+ if (fm.flags & FastMathFlags::NNaN)
+ os << "nnan ";
+ if (fm.flags & FastMathFlags::NInf)
+ os << "ninf ";
+ if (fm.flags & FastMathFlags::NSZ)
+ os << "nsz ";
+ if (fm.flags & FastMathFlags::ARCP)
+ os << "arcp ";
+ if (fm.flags & FastMathFlags::Contract)
+ os << "contract ";
+ if (fm.flags & FastMathFlags::Reassoc)
+ os << "reassoc ";
+ if (fm.flags & FastMathFlags::AFN)
+ os << "afn ";
+ return os;
+}
+
+
BinOp::BinOp(Type &type, string &&name, Value &lhs, Value &rhs, Op op,
- unsigned flags)
- : Instr(type, move(name)), lhs(&lhs), rhs(&rhs), op(op), flags(flags) {
+ unsigned flags, FastMathFlags fmath)
+ : Instr(type, move(name)), lhs(&lhs), rhs(&rhs), op(op), flags(flags),
+ fmath(fmath) {
switch (op) {
case Add:
case Sub:
case Mul:
case Shl:
assert((flags & (NSW | NUW)) == flags);
+ assert(fmath.isNone());
break;
case SDiv:
case UDiv:
case AShr:
case LShr:
assert((flags & Exact) == flags);
+ assert(fmath.isNone());
break;
case FAdd:
case FSub:
case FMul:
case FDiv:
case FRem:
- assert((flags & FastMath) == flags);
+ assert(flags == None);
break;
case SRem:
case URem:
@@ -85,7 +110,8 @@ BinOp::BinOp(Type &type, string &&name, Value &lhs, Value &rhs, Op op,
case USub_Overflow:
case SMul_Overflow:
case UMul_Overflow:
- assert(flags == 0);
+ assert(flags == None);
+ assert(fmath.isNone());
break;
}
}
@@ -142,23 +168,8 @@ void BinOp::print(ostream &os) const {
os << "nuw ";
if (flags & Exact)
os << "exact ";
- if ((flags & FastMath) == FastMath) {
- os << "fast ";
- } else {
- if (flags & NNaN)
- os << "nnan ";
- if (flags & NInf)
- os << "ninf ";
- if (flags & NSZ)
- os << "nsz ";
- if (flags & ARCP)
- os << "arcp ";
- if (flags & Contract)
- os << "contract ";
- if (flags & Reassoc)
- os << "reassoc ";
- }
- os << print_type(getType()) << lhs->getName() << ", " << rhs->getName();
+ os << fmath
+ << print_type(getType()) << lhs->getName() << ", " << rhs->getName();
}
static void div_ub(State &s, const expr &a, const expr &b, const expr &ap,
@@ -184,8 +195,9 @@ static expr any_fp_zero(State &s, expr v) {
}
static StateValue fm_poison(State &s, expr a, expr b,
- function<expr(expr&,expr&)> fn, unsigned flags) {
- if (flags & BinOp::NSZ) {
+ function<expr(expr&,expr&)> fn,
+ FastMathFlags fmath, bool only_input) {
+ if (fmath.flags & FastMathFlags::NSZ) {
a = any_fp_zero(s, move(a));
b = any_fp_zero(s, move(b));
}
@@ -193,17 +205,25 @@ static StateValue fm_poison(State &s, expr a, expr b,
expr val = fn(a, b);
expr non_poison(true);
- if (flags & BinOp::NNaN)
- non_poison &= !a.isNaN() && !b.isNaN() && !val.isNaN();
- if (flags & BinOp::NInf)
- non_poison &= !a.isInf() && !b.isInf() && !val.isInf();
- if (flags & BinOp::ARCP)
+ if (fmath.flags & FastMathFlags::NNaN) {
+ non_poison &= !a.isNaN() && !b.isNaN();
+ if (!only_input)
+ non_poison &= !val.isNaN();
+ }
+ if (fmath.flags & FastMathFlags::NInf) {
+ non_poison &= !a.isInf() && !b.isInf();
+ if (!only_input)
+ non_poison &= !val.isInf();
+ }
+ if (fmath.flags & FastMathFlags::ARCP)
+ non_poison &= expr(); // TODO
+ if (fmath.flags & FastMathFlags::Contract)
non_poison &= expr(); // TODO
- if (flags & BinOp::Contract)
+ if (fmath.flags & FastMathFlags::Reassoc)
non_poison &= expr(); // TODO
- if (flags & BinOp::Reassoc)
+ if (fmath.flags & FastMathFlags::AFN)
non_poison &= expr(); // TODO
- if (flags & BinOp::NSZ)
+ if (fmath.flags & FastMathFlags::NSZ)
val = any_fp_zero(s, move(val));
return { move(val), move(non_poison) };
@@ -413,35 +433,36 @@ StateValue BinOp::toSMT(State &s) const {
case FAdd:
fn = [&](auto a, auto ap, auto b, auto bp) -> StateValue {
return fm_poison(s, a, b, [](expr &a, expr &b) { return a.fadd(b); },
- flags);
+ fmath, false);
};
break;
case FSub:
fn = [&](auto a, auto ap, auto b, auto bp) -> StateValue {
return fm_poison(s, a, b, [](expr &a, expr &b) { return a.fsub(b); },
- flags);
+ fmath, false);
};
break;
case FMul:
fn = [&](auto a, auto ap, auto b, auto bp) -> StateValue {
return fm_poison(s, a, b, [](expr &a, expr &b) { return a.fmul(b); },
- flags);
+ fmath, false);
};
break;
case FDiv:
fn = [&](auto a, auto ap, auto b, auto bp) -> StateValue {
return fm_poison(s, a, b, [](expr &a, expr &b) { return a.fdiv(b); },
- flags);
+ fmath, false);
};
break;
case FRem:
fn = [&](auto a, auto ap, auto b, auto bp) -> StateValue {
// TODO; Z3 has no support for LLVM's frem which is actually an fmod
- return fm_poison(s, a, b, [](expr &a, expr &b) { return expr(); }, flags);
+ return fm_poison(s, a, b, [](expr &a, expr &b) { return expr(); }, fmath,
+ false);
};
break;
}
@@ -560,7 +581,8 @@ expr BinOp::getTypeConstraints(const Function &f) const {
}
unique_ptr<Instr> BinOp::dup(const string &suffix) const {
- return make_unique<BinOp>(getType(), getName()+suffix, *lhs, *rhs, op, flags);
+ return make_unique<BinOp>(getType(), getName()+suffix, *lhs, *rhs, op, flags,
+ fmath);
}
@@ -582,7 +604,8 @@ void UnaryOp::print(ostream &os) const {
case FNeg: str = "fneg "; break;
}
- os << getName() << " = " << str << print_type(getType()) << val->getName();
+ os << getName() << " = " << str << fmath << print_type(getType())
+ << val->getName();
}
StateValue UnaryOp::toSMT(State &s) const {
@@ -604,6 +627,9 @@ StateValue UnaryOp::toSMT(State &s) const {
fn = [](auto v) { return v.ctpop(); };
break;
case FNeg:
+ // TODO
+ if (!fmath.isNone())
+ return {};
fn = [](auto v) { return v.fneg(); };
break;
}
@@ -649,7 +675,7 @@ expr UnaryOp::getTypeConstraints(const Function &f) const {
}
unique_ptr<Instr> UnaryOp::dup(const string &suffix) const {
- return make_unique<UnaryOp>(getType(), getName() + suffix, *val, op);
+ return make_unique<UnaryOp>(getType(), getName() + suffix, *val, op, fmath);
}
@@ -1294,15 +1320,8 @@ void FCmp::print(ostream &os) const {
case UNE: condtxt = "une "; break;
case UNO: condtxt = "uno "; break;
}
- os << getName() << " = fcmp ";
- if (flags & NNaN)
- os << "nnan ";
- if (flags & NInf)
- os << "ninf ";
- if (flags & Reassoc)
- os << "reassoc ";
- os << condtxt << print_type(getType()) << a->getName() << ", "
- << b->getName();
+ os << getName() << " = fcmp " << fmath << condtxt << print_type(getType())
+ << a->getName() << ", " << b->getName();
}
StateValue FCmp::toSMT(State &s) const {
@@ -1310,33 +1329,27 @@ StateValue FCmp::toSMT(State &s) const {
auto &b_eval = s[*b];
auto fn = [&](const auto &a, const auto &b) -> StateValue {
- const expr &av = a.value, &bv = b.value;
- expr val;
+ auto cmp = [&](const expr &a, const expr &b) {
switch (cond) {
- case OEQ: val = av.foeq(bv); break;
- case OGT: val = av.fogt(bv); break;
- case OGE: val = av.foge(bv); break;
- case OLT: val = av.folt(bv); break;
- case OLE: val = av.fole(bv); break;
- case ONE: val = av.fone(bv); break;
- case ORD: val = av.ford(bv); break;
- case UEQ: val = av.fueq(bv); break;
- case UGT: val = av.fugt(bv); break;
- case UGE: val = av.fuge(bv); break;
- case ULT: val = av.fult(bv); break;
- case ULE: val = av.fule(bv); break;
- case UNE: val = av.fune(bv); break;
- case UNO: val = av.funo(bv); break;
- }
- expr np = a.non_poison && b.non_poison;
- if (flags & NNaN)
- np &= !av.isNaN() && !bv.isNaN();
- if (flags & NInf)
- np &= !av.isInf() && !bv.isInf();
- if (flags & Reassoc)
- np &= expr(); // TODO
-
- return { val.toBVBool(), move(np) };
+ case OEQ: return a.foeq(b);
+ case OGT: return a.fogt(b);
+ case OGE: return a.foge(b);
+ case OLT: return a.folt(b);
+ case OLE: return a.fole(b);
+ case ONE: return a.fone(b);
+ case ORD: return a.ford(b);
+ case UEQ: return a.fueq(b);
+ case UGT: return a.fugt(b);
+ case UGE: return a.fuge(b);
+ case ULT: return a.fult(b);
+ case ULE: return a.fule(b);
+ case UNE: return a.fune(b);
+ case UNO: return a.funo(b);
+ }
+ UNREACHABLE();
+ };
+ auto [val, np] = fm_poison(s, a.value, b.value, cmp, fmath, true);
+ return { val.toBVBool(), a.non_poison && b.non_poison && np };
};
if (auto agg = a->getType().getAsAggregateType()) {
@@ -1358,7 +1371,7 @@ expr FCmp::getTypeConstraints(const Function &f) const {
}
unique_ptr<Instr> FCmp::dup(const string &suffix) const {
- return make_unique<FCmp>(getType(), getName() + suffix, cond, *a, *b, flags);
+ return make_unique<FCmp>(getType(), getName() + suffix, cond, *a, *b, fmath);
}

MODIFY
ir/instr.h
ir/instr.h
@@ -27,6 +27,19 @@ public:
};
+struct FastMathFlags final {
+ enum Flags {
+ None = 0, NNaN = 1 << 0, NInf = 1 << 1, NSZ = 1 << 2, ARCP = 1 << 3,
+ Contract = 1 << 4, Reassoc = 1 << 5, AFN = 1 << 6,
+ FastMath = NNaN | NInf | NSZ | ARCP | Contract | Reassoc | AFN
+ };
+ unsigned flags = None;
+
+ bool isNone() const { return flags == None; }
+ friend std::ostream& operator<<(std::ostream &os, const FastMathFlags &fm);
+};
+
+
class BinOp final : public Instr {
public:
enum Op { Add, Sub, Mul, SDiv, UDiv, SRem, URem, Shl, AShr, LShr,
@@ -35,19 +48,17 @@ public:
SMul_Overflow, UMul_Overflow,
FAdd, FSub, FMul, FDiv, FRem,
And, Or, Xor, Cttz, Ctlz };
- enum Flags { None = 0, NSW = 1 << 0, NUW = 1 << 1, Exact = 1 << 2,
- NNaN = 1 << 3, NInf = 1 << 4, NSZ = 1 << 5, ARCP = 1 << 6,
- Contract = 1 << 7, Reassoc = 1 << 8,
- FastMath = NNaN | NInf | NSZ | ARCP | Contract | Reassoc };
+ enum Flags { None = 0, NSW = 1 << 0, NUW = 1 << 1, Exact = 1 << 2 };
private:
Value *lhs, *rhs;
Op op;
unsigned flags;
+ FastMathFlags fmath;
public:
BinOp(Type &type, std::string &&name, Value &lhs, Value &rhs, Op op,
- unsigned flags = 0);
+ unsigned flags = 0, FastMathFlags fmath = {});
std::vector<Value*> operands() const override;
void rauw(const Value &what, Value &with) override;
@@ -65,10 +76,12 @@ public:
private:
Value *val;
Op op;
+ FastMathFlags fmath;
public:
- UnaryOp(Type &type, std::string &&name, Value &val, Op op)
- : Instr(type, std::move(name)), val(&val), op(op) {}
+ UnaryOp(Type &type, std::string &&name, Value &val, Op op,
+ FastMathFlags fmath = {})
+ : Instr(type, std::move(name)), val(&val), op(op), fmath(fmath) {}
std::vector<Value*> operands() const override;
void rauw(const Value &what, Value &with) override;
@@ -201,17 +214,16 @@ class FCmp final : public Instr {
public:
enum Cond { OEQ, OGT, OGE, OLT, OLE, ONE, ORD,
UEQ, UGT, UGE, ULT, ULE, UNE, UNO };
- enum Flags { None = 0, NNaN = 1, NInf = 2, Reassoc = 4 };
private:
Value *a, *b;
Cond cond;
- unsigned flags = 0;
+ FastMathFlags fmath;
public:
FCmp(Type &type, std::string &&name, Cond cond, Value &a, Value &b,
- unsigned flags)
- : Instr(type, move(name)), a(&a), b(&b), cond(cond), flags(flags) {}
+ FastMathFlags fmath)
+ : Instr(type, move(name)), a(&a), b(&b), cond(cond), fmath(fmath) {}
std::vector<Value*> operands() const override;
void rauw(const Value &what, Value &with) override;

MODIFY
llvm_util/llvm2alive.cpp
llvm_util/llvm2alive.cpp
@@ -57,6 +57,29 @@ string_view s(llvm::StringRef str) {
return ret; \
} while (0)
+
+FastMathFlags parse_fmath(llvm::Instruction &i) {
+ FastMathFlags fmath;
+ if (auto op = dyn_cast<llvm::FPMathOperator>(&i)) {
+ if (op->hasNoNaNs())
+ fmath.flags |= FastMathFlags::NNaN;
+ if (op->hasNoInfs())
+ fmath.flags |= FastMathFlags::NInf;
+ if (op->hasNoSignedZeros())
+ fmath.flags |= FastMathFlags::NSZ;
+ if (op->hasAllowReciprocal())
+ fmath.flags |= FastMathFlags::ARCP;
+ if (op->hasAllowContract())
+ fmath.flags |= FastMathFlags::Contract;
+ if (op->hasAllowReassoc())
+ fmath.flags |= FastMathFlags::Reassoc;
+ if (op->hasApproxFunc())
+ fmath.flags |= FastMathFlags::AFN;
+ }
+ return fmath;
+}
+
+
class llvm2alive_ : public llvm::InstVisitor<llvm2alive_, unique_ptr<Instr>> {
BasicBlock *BB;
llvm::Function &f;
@@ -94,7 +117,6 @@ class llvm2alive_ : public llvm::InstVisitor<llvm2alive_, unique_ptr<Instr>> {
return i;
}
-
Value* copy_inserter(AggregateValue *ag) {
auto v = make_unique<UnaryOp>(*const_cast<Type *>(&ag->getType()),
"%__copy_" + to_string(copy_idx++), *ag,
@@ -130,12 +152,8 @@ public:
default:
return error(i);
}
-
- // TODO: support FP fast-math stuff
- if (isa<llvm::FPMathOperator>(i) && i.getFastMathFlags().any())
- return error(i);
-
- RETURN_IDENTIFIER(make_unique<UnaryOp>(*ty, value_name(i), *val, op));
+ RETURN_IDENTIFIER(make_unique<UnaryOp>(*ty, value_name(i), *val, op,
+ parse_fmath(i)));
}
RetTy visitBinaryOperator(llvm::BinaryOperator &i) {
@@ -171,24 +189,8 @@ public:
flags |= BinOp::NUW;
if (isa<llvm::PossiblyExactOperator>(i) && i.isExact())
flags = BinOp::Exact;
-
- if (isa<llvm::FPMathOperator>(i)) {
- auto FM = i.getFastMathFlags();
- if (FM.noNaNs())
- flags |= BinOp::NNaN;
- if (FM.noInfs())
- flags |= BinOp::NInf;
- if (FM.noSignedZeros())
- flags |= BinOp::NSZ;
- if (FM.allowReciprocal())
- flags |= BinOp::ARCP;
- if (FM.allowContract())
- flags |= BinOp::Contract;
- if (FM.allowReassoc())
- flags |= BinOp::Reassoc;
- }
RETURN_IDENTIFIER(make_unique<BinOp>(*ty, value_name(i), *a, *b, alive_op,
- flags));
+ flags, parse_fmath(i)));
}
RetTy visitCastInst(llvm::CastInst &i) {
@@ -320,17 +322,8 @@ public:
default:
UNREACHABLE();
}
-
- unsigned flags = 0;
- if (i.getFastMathFlags().noNaNs())
- flags |= FCmp::NNaN;
- if (i.getFastMathFlags().noInfs())
- flags |= FCmp::NInf;
- if (i.getFastMathFlags().allowReassoc())
- flags |= FCmp::Reassoc;
-
RETURN_IDENTIFIER(make_unique<FCmp>(*ty, value_name(i), cond, *a, *b,
- flags));
+ parse_fmath(i)));
}
RetTy visitSelectInst(llvm::SelectInst &i) {
@@ -392,16 +385,15 @@ public:
auto opty = I.getOperand()->getType();
auto ofs_ty = llvm::IntegerType::get(i.getContext(), 64);
- if (auto opvty = llvm::dyn_cast<llvm::VectorType>(opty)) {
+ if (auto opvty = dyn_cast<llvm::VectorType>(opty)) {
assert(!opvty->isScalable());
vector<llvm::Constant *> offsets;
for (unsigned i = 0; i < opvty->getElementCount().Min; ++i) {
llvm::Constant *constofs = nullptr;
- if (auto cdv = llvm::dyn_cast<llvm::ConstantDataVector>(
- I.getOperand())) {
+ if (auto cdv = dyn_cast<llvm::ConstantDataVector>(I.getOperand())) {
constofs = cdv->getElementAsConstant(i);
- } else if (auto cv = llvm::dyn_cast<llvm::ConstantAggregateZero>(
+ } else if (auto cv = dyn_cast<llvm::ConstantAggregateZero>(
I.getOperand())) {
constofs = cv->getSequentialElement();
} else {

MODIFY
tools/alive_parser.cpp
tools/alive_parser.cpp
@@ -594,18 +594,31 @@ static unsigned parse_exact() {
return BinOp::None;
}
-static unsigned parse_fast_math() {
- unsigned flags = BinOp::None;
+static FastMathFlags parse_fast_math(token op_token) {
+ FastMathFlags fmath;
while (true) {
if (tokenizer.consumeIf(NNAN)) {
- flags |= BinOp::NNaN;
+ fmath.flags |= FastMathFlags::NNaN;
} else if (tokenizer.consumeIf(NINF)) {
- flags |= BinOp::NInf;
+ fmath.flags |= FastMathFlags::NInf;
} else {
break;
}
}
- return flags;
+
+ switch (op_token) {
+ case FADD:
+ case FSUB:
+ case FMUL:
+ case FDIV:
+ case FREM:
+ case FCMP:
+ break;
+ default:
+ if (!fmath.isNone())
+ error("Unexpected fast-math tokens");
+ }
+ return fmath;
}
@@ -626,7 +639,6 @@ static unsigned parse_binop_flags(token op_token) {
case FMUL:
case FDIV:
case FREM:
- return parse_fast_math();
case SREM:
case UREM:
case UADD_SAT:
@@ -652,6 +664,7 @@ static unsigned parse_binop_flags(token op_token) {
static unique_ptr<Instr> parse_binop(string_view name, token op_token) {
auto flags = parse_binop_flags(op_token);
+ auto fmath = parse_fast_math(op_token);
auto &type = parse_type();
auto &a = parse_operand(type);
parse_comma();
@@ -712,7 +725,7 @@ static unique_ptr<Instr> parse_binop(string_view name, token op_token) {
default:
UNREACHABLE();
}
- return make_unique<BinOp>(*rettype, string(name), a, b, op, flags);
+ return make_unique<BinOp>(*rettype, string(name), a, b, op, flags, fmath);
}
static unique_ptr<Instr> parse_unaryop(string_view name, token op_token) {
@@ -871,7 +884,8 @@ static unique_ptr<Instr> parse_fcmp(string_view name) {
get_constant(cond_t == TRUE, bool_ty),
UnaryOp::Copy);
default:
- return make_unique<FCmp>(bool_ty, string(name), cond, a, b, 0);
+ return make_unique<FCmp>(bool_ty, string(name), cond, a, b,
+ parse_fast_math(FCMP));
}
UNREACHABLE();
}

uniformize fast-math handling code