Skip to content

Commit 8f12144

Browse files
committed
fix #922: implement support for nofpclass in fn args and ret value
1 parent d0c6800 commit 8f12144

File tree

5 files changed

+88
-76
lines changed

5 files changed

+88
-76
lines changed

ir/attrs.cpp

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ ostream& operator<<(ostream &os, const ParamAttrs &attr) {
2828
os << "dereferenceable(" << attr.derefBytes << ") ";
2929
if (attr.has(ParamAttrs::NoUndef))
3030
os << "noundef ";
31+
if (attr.has(ParamAttrs::NoFPClass))
32+
os << " nofpclass(" << attr.nofpclass << ')';
3133
if (attr.has(ParamAttrs::Align))
3234
os << "align(" << attr.align << ") ";
3335
if (attr.has(ParamAttrs::Returned))
@@ -80,6 +82,8 @@ ostream& operator<<(ostream &os, const FnAttrs &attr) {
8082
os << " nofree";
8183
if (attr.has(FnAttrs::NoUndef))
8284
os << " noundef";
85+
if (attr.has(FnAttrs::NoFPClass))
86+
os << " nofpclass(" << attr.nofpclass << ')';
8387
if (attr.has(FnAttrs::Align))
8488
os << " align(" << attr.align << ')';
8589
if (attr.has(FnAttrs::NoThrow))
@@ -373,6 +377,11 @@ encodePtrAttrs(State &s, const expr &ptrvalue, uint64_t derefBytes,
373377
}
374378

375379
StateValue ParamAttrs::encode(State &s, StateValue &&val, const Type &ty) const{
380+
if (has(NoFPClass)) {
381+
assert(ty.isFloatType());
382+
val.non_poison &= !isfpclass(val.value, ty, nofpclass);
383+
}
384+
376385
if (ty.isPtrType())
377386
val.non_poison &=
378387
encodePtrAttrs(s, val.value, getDerefBytes(), derefOrNullBytes, align,
@@ -473,11 +482,16 @@ bool FnAttrs::refinedBy(const FnAttrs &other) const {
473482
StateValue FnAttrs::encode(State &s, StateValue &&val, const Type &ty,
474483
const expr &allocsize,
475484
Value *allocalign) const {
476-
if (has(FnAttrs::NNaN)) {
485+
if (has(NNaN)) {
477486
assert(ty.isFloatType());
478487
val.non_poison &= !ty.getAsFloatType()->getFloat(val.value).isNaN();
479488
}
480489

490+
if (has(NoFPClass)) {
491+
assert(ty.isFloatType());
492+
val.non_poison &= !isfpclass(val.value, ty, nofpclass);
493+
}
494+
481495
if (ty.isPtrType())
482496
val.non_poison &=
483497
encodePtrAttrs(s, val.value, derefBytes, derefOrNullBytes, align,
@@ -492,6 +506,34 @@ StateValue FnAttrs::encode(State &s, StateValue &&val, const Type &ty,
492506
}
493507

494508

509+
expr isfpclass(const expr &v, const Type &ty, uint16_t mask) {
510+
auto *fpty = ty.getAsFloatType();
511+
auto a = fpty->getFloat(v);
512+
OrExpr result;
513+
if (mask & (1 << 0))
514+
result.add(fpty->isNaN(v, true));
515+
if (mask & (1 << 1))
516+
result.add(fpty->isNaN(v, false));
517+
if (mask & (1 << 2))
518+
result.add(a.isFPNegative() && a.isInf());
519+
if (mask & (1 << 3))
520+
result.add(a.isFPNegative() && a.isFPNormal());
521+
if (mask & (1 << 4))
522+
result.add(a.isFPNegative() && a.isFPSubNormal());
523+
if (mask & (1 << 5))
524+
result.add(a.isFPNegZero());
525+
if (mask & (1 << 6))
526+
result.add(a.isFPZero() && !a.isFPNegative());
527+
if (mask & (1 << 7))
528+
result.add(!a.isFPNegative() && a.isFPSubNormal());
529+
if (mask & (1 << 8))
530+
result.add(!a.isFPNegative() && a.isFPNormal());
531+
if (mask & (1 << 9))
532+
result.add(!a.isFPNegative() && a.isInf());
533+
return std::move(result)();
534+
}
535+
536+
495537
ostream& operator<<(ostream &os, const FastMathFlags &fm) {
496538
if (fm.flags == FastMathFlags::FastMath)
497539
return os << "fast ";

ir/attrs.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,15 @@ class ParamAttrs final {
6464
NoUndef = 1<<6, Align = 1<<7, Returned = 1<<8,
6565
NoAlias = 1<<9, DereferenceableOrNull = 1<<10,
6666
AllocPtr = 1<<11, AllocAlign = 1<<12,
67-
ZeroExt = 1<<13, SignExt = 1<<14};
67+
ZeroExt = 1<<13, SignExt = 1<<14, NoFPClass = 1<<15 };
6868

6969
ParamAttrs(unsigned bits = None) : bits(bits) {}
7070

7171
uint64_t derefBytes = 0; // Dereferenceable
7272
uint64_t derefOrNullBytes = 0; // DereferenceableOrNull
7373
uint64_t blockSize = 0; // exact block size for e.g. byval args
7474
uint64_t align = 1;
75+
uint16_t nofpclass = 0;
7576

7677
bool has(Attribute a) const { return (bits & a) != 0; }
7778
void set(Attribute a) { bits |= (unsigned)a; }
@@ -125,7 +126,7 @@ class FnAttrs final {
125126
DereferenceableOrNull = 1 << 10,
126127
NullPointerIsValid = 1 << 11,
127128
AllocSize = 1 << 12, ZeroExt = 1<<13,
128-
SignExt = 1<<14 };
129+
SignExt = 1<<14, NoFPClass = 1<<15, };
129130

130131
FnAttrs(unsigned bits = None) : bits(bits) {}
131132

@@ -143,6 +144,8 @@ class FnAttrs final {
143144

144145
std::string allocfamily;
145146

147+
uint16_t nofpclass = 0;
148+
146149
void add(AllocKind k) { allockind |= (uint8_t)k; }
147150
bool has(AllocKind k) const { return allockind & (uint8_t)k; }
148151
bool isAlloc() const { return allockind != 0; }
@@ -209,4 +212,6 @@ struct FpExceptionMode final {
209212
friend std::ostream& operator<<(std::ostream &os, FpExceptionMode ex);
210213
};
211214

215+
smt::expr isfpclass(const smt::expr &v, const Type &ty, uint16_t mask);
216+
212217
}

ir/instr.cpp

Lines changed: 11 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ BinOp::BinOp(Type &type, string &&name, Value &lhs, Value &rhs, Op op,
123123
assert((flags & Exact) == flags);
124124
break;
125125
default:
126-
assert((flags & NoUndef) == flags);
126+
assert(flags == 0);
127127
break;
128128
}
129129
}
@@ -137,7 +137,7 @@ bool BinOp::propagatesPoison() const {
137137
}
138138

139139
bool BinOp::hasSideEffects() const {
140-
return isDivOrRem() || (flags & NoUndef);
140+
return isDivOrRem();
141141
}
142142

143143
void BinOp::rauw(const Value &what, Value &with) {
@@ -191,8 +191,6 @@ void BinOp::print(ostream &os) const {
191191
if (flags & Exact)
192192
os << "exact ";
193193
os << *lhs << ", " << rhs->getName();
194-
if (flags & NoUndef)
195-
os << ", !noundef";
196194
}
197195

198196
static void div_ub(State &s, const expr &a, const expr &b, const expr &ap,
@@ -451,33 +449,25 @@ StateValue BinOp::toSMT(State &s) const {
451449
break;
452450
}
453451

454-
bool noundef = flags & NoUndef;
455452
function<pair<StateValue,StateValue>(const expr&, const expr&, const expr&,
456453
const expr&)> zip_op;
457454
if (vertical_zip) {
458455
zip_op = [&](auto &a, auto &ap, auto &b, auto &bp) {
459456
auto [v1, v2] = fn(a, ap, b, bp);
460457
expr non_poison = ap && bp;
461-
if (noundef) {
462-
s.addUB(std::move(non_poison));
463-
non_poison = true;
464-
}
465458
StateValue sv1(std::move(v1), expr(non_poison));
466-
return make_pair(std::move(sv1), StateValue(std::move(v2), std::move(non_poison)));
459+
return make_pair(std::move(sv1),
460+
StateValue(std::move(v2), std::move(non_poison)));
467461
};
468462
} else {
469463
scalar_op = [&](auto &a, auto &ap, auto &b, auto &bp) -> StateValue {
470464
auto [v, np] = fn(a, ap, b, bp);
471-
if (noundef) {
472-
s.addUB(std::move(np));
473-
np = true;
474-
}
475465
return { std::move(v), ap && bp && np };
476466
};
477467
}
478468

479-
auto &a = s.getVal(*lhs, noundef);
480-
auto &b = s.getVal(*rhs, isDivOrRem() || noundef);
469+
auto &a = s[*lhs];
470+
auto &b = s.getVal(*rhs, isDivOrRem());
481471

482472
if (lhs->getType().isVectorType()) {
483473
auto retty = getType().getAsAggregateType();
@@ -1480,32 +1470,9 @@ StateValue TestOp::toSMT(State &s) const {
14801470
switch (op) {
14811471
case Is_FPClass:
14821472
fn = [&](const expr &v, const Type &ty) -> expr {
1483-
uint64_t n;
1484-
ENSURE(b.value.isUInt(n) && b.non_poison.isTrue());
1485-
auto *fpty = ty.getAsFloatType();
1486-
auto a = fpty->getFloat(v);
1487-
OrExpr result;
1488-
if (n & (1 << 0))
1489-
result.add(fpty->isNaN(v, true));
1490-
if (n & (1 << 1))
1491-
result.add(fpty->isNaN(v, false));
1492-
if (n & (1 << 2))
1493-
result.add(a.isFPNegative() && a.isInf());
1494-
if (n & (1 << 3))
1495-
result.add(a.isFPNegative() && a.isFPNormal());
1496-
if (n & (1 << 4))
1497-
result.add(a.isFPNegative() && a.isFPSubNormal());
1498-
if (n & (1 << 5))
1499-
result.add(a.isFPNegZero());
1500-
if (n & (1 << 6))
1501-
result.add(a.isFPZero() && !a.isFPNegative());
1502-
if (n & (1 << 7))
1503-
result.add(!a.isFPNegative() && a.isFPSubNormal());
1504-
if (n & (1 << 8))
1505-
result.add(!a.isFPNegative() && a.isFPNormal());
1506-
if (n & (1 << 9))
1507-
result.add(!a.isFPNegative() && a.isInf());
1508-
return result().toBVBool();
1473+
uint64_t mask;
1474+
ENSURE(b.value.isUInt(mask) && b.non_poison.isTrue());
1475+
return isfpclass(v, ty, mask).toBVBool();
15091476
};
15101477
break;
15111478
}
@@ -1693,7 +1660,7 @@ bool FpConversionOp::propagatesPoison() const {
16931660
}
16941661

16951662
bool FpConversionOp::hasSideEffects() const {
1696-
return flags & NoUndef;
1663+
return false;
16971664
}
16981665

16991666
void FpConversionOp::rauw(const Value &what, Value &with) {
@@ -1718,13 +1685,10 @@ void FpConversionOp::print(ostream &os) const {
17181685
os << ", rounding=" << rm;
17191686
if (!ex.ignore())
17201687
os << ", exceptions=" << ex;
1721-
if (flags & NoUndef)
1722-
os << ", !noundef";
17231688
}
17241689

17251690
StateValue FpConversionOp::toSMT(State &s) const {
1726-
bool noundef = flags & NoUndef;
1727-
auto &v = s.getVal(*val, noundef);
1691+
auto &v = s[*val];
17281692
function<StateValue(const expr &, const Type &, FpRoundingMode)> fn;
17291693

17301694
switch (op) {
@@ -1810,11 +1774,6 @@ StateValue FpConversionOp::toSMT(State &s) const {
18101774
: fn(val, to_type, rm);
18111775
np.add(std::move(ret.non_poison));
18121776

1813-
if (noundef) {
1814-
s.addUB(np());
1815-
np.reset();
1816-
}
1817-
18181777
return { to_type.isFloatType()
18191778
? to_type.getAsFloatType()->fromFloat(s, ret.value)
18201779
: std::move(ret.value), np()};

ir/instr.h

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ class BinOp final : public Instr {
3535
SAdd_Overflow, UAdd_Overflow, SSub_Overflow, USub_Overflow,
3636
SMul_Overflow, UMul_Overflow,
3737
And, Or, Xor, Cttz, Ctlz, UMin, UMax, SMin, SMax, Abs };
38-
enum Flags { None = 0, NSW = 1 << 0, NUW = 1 << 1, Exact = 1 << 2,
39-
NoUndef = 1 << 3 };
38+
enum Flags { None = 0, NSW = 1 << 0, NUW = 1 << 1, Exact = 1 << 2 };
4039

4140
private:
4241
Value *lhs, *rhs;
@@ -288,21 +287,16 @@ class FpConversionOp final : public Instr {
288287
enum Op { SIntToFP, UIntToFP, FPToSInt, FPToUInt, FPExt, FPTrunc, LRInt,
289288
LRound };
290289

291-
enum Flags { None = 0, NoUndef = 1<<0 };
292-
293290
private:
294291
Value *val;
295292
Op op;
296293
FpRoundingMode rm;
297294
FpExceptionMode ex;
298-
unsigned flags;
299295

300296
public:
301297
FpConversionOp(Type &type, std::string &&name, Value &val, Op op,
302-
FpRoundingMode rm = {}, FpExceptionMode ex = {},
303-
unsigned flags = None)
304-
: Instr(type, std::move(name)), val(&val), op(op), rm(rm), ex(ex),
305-
flags(flags) {}
298+
FpRoundingMode rm = {}, FpExceptionMode ex = {})
299+
: Instr(type, std::move(name)), val(&val), op(op), rm(rm), ex(ex) {}
306300

307301
std::vector<Value*> operands() const override;
308302
bool propagatesPoison() const override;

llvm_util/llvm2alive.cpp

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,14 @@ string_view s(llvm::StringRef str) {
9696
return ret; \
9797
} while (0)
9898

99+
#define RETURN_IDENTIFIER_FNATTRS(op, attrs) \
100+
do { \
101+
auto ret = op; \
102+
add_identifier(i, *ret.get()); \
103+
if (attrs.has(FnAttrs::NoUndef)) \
104+
BB->addInstr(make_unique<Assume>(*ret, Assume::WellDefined)); \
105+
return ret; \
106+
} while (0)
99107

100108

101109
class llvm2alive_ : public llvm::InstVisitor<llvm2alive_, unique_ptr<Instr>> {
@@ -853,11 +861,8 @@ class llvm2alive_ : public llvm::InstVisitor<llvm2alive_, unique_ptr<Instr>> {
853861
}
854862
FnAttrs attrs;
855863
parse_fn_attrs(i, attrs);
856-
unsigned flags = BinOp::None;
857-
if (attrs.has(FnAttrs::NoUndef))
858-
flags |= BinOp::NoUndef;
859-
RETURN_IDENTIFIER(make_unique<BinOp>(*ty, value_name(i), *a, *b, op,
860-
flags));
864+
RETURN_IDENTIFIER_FNATTRS(
865+
make_unique<BinOp>(*ty, value_name(i), *a, *b, op), attrs);
861866
}
862867
case llvm::Intrinsic::bitreverse:
863868
case llvm::Intrinsic::bswap:
@@ -1067,13 +1072,10 @@ class llvm2alive_ : public llvm::InstVisitor<llvm2alive_, unique_ptr<Instr>> {
10671072
}
10681073
FnAttrs attrs;
10691074
parse_fn_attrs(i, attrs);
1070-
unsigned flags = FpConversionOp::None;
1071-
if (attrs.has(FnAttrs::NoUndef))
1072-
flags |= FpConversionOp::NoUndef;
1073-
RETURN_IDENTIFIER(make_unique<FpConversionOp>(*ty, value_name(i), *val,
1074-
op, parse_rounding(i),
1075-
parse_exceptions(i),
1076-
flags));
1075+
RETURN_IDENTIFIER_FNATTRS(
1076+
make_unique<FpConversionOp>(*ty, value_name(i), *val, op,
1077+
parse_rounding(i), parse_exceptions(i)),
1078+
attrs);
10771079
}
10781080
case llvm::Intrinsic::is_fpclass:
10791081
{
@@ -1353,6 +1355,11 @@ class llvm2alive_ : public llvm::InstVisitor<llvm2alive_, unique_ptr<Instr>> {
13531355
attrs.set(ParamAttrs::NoUndef);
13541356
break;
13551357

1358+
case llvm::Attribute::NoFPClass:
1359+
attrs.set(ParamAttrs::NoFPClass);
1360+
attrs.nofpclass = (uint16_t)llvmattr.getNoFPClass();
1361+
break;
1362+
13561363
case llvm::Attribute::Returned:
13571364
attrs.set(ParamAttrs::Returned);
13581365
break;
@@ -1404,6 +1411,11 @@ class llvm2alive_ : public llvm::InstVisitor<llvm2alive_, unique_ptr<Instr>> {
14041411
attrs.align = max(attrs.align, llvmattr.getAlignment()->value());
14051412
break;
14061413

1414+
case llvm::Attribute::NoFPClass:
1415+
attrs.set(FnAttrs::NoFPClass);
1416+
attrs.nofpclass = (uint16_t)llvmattr.getNoFPClass();
1417+
break;
1418+
14071419
default: break;
14081420
}
14091421
}

0 commit comments

Comments
 (0)