Skip to content

Commit a852156

Browse files
authored
Change the Literal class's operator== to be bitwise (#1661)
The change means that nan values will be compared bitwise when writing A == B, and so the float rule of a nan is different from itself would not apply. I think this is a safer default. In particular this PR fixes a fuzz bug in the rse pass, which placed Literals in a hash table, and due to nan != nan, an infinite loop... Also, looks like we really want a bitwise comparison pretty much everywhere anyhow, as can be seen in the diff here. Really the single place we need a floaty comparison is in the intepreter where we implement f32.eq etc., and there the code was already using the proper code path anyhow.
1 parent 9750c18 commit a852156

File tree

8 files changed

+71
-20
lines changed

8 files changed

+71
-20
lines changed

src/ir/ExpressionAnalyzer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ bool ExpressionAnalyzer::flexibleEqual(Expression* left, Expression* right, Expr
257257
break;
258258
}
259259
case Expression::Id::ConstId: {
260-
if (!left->cast<Const>()->value.bitwiseEqual(right->cast<Const>()->value)) {
260+
if (left->cast<Const>()->value != right->cast<Const>()->value) {
261261
return false;
262262
}
263263
break;

src/literal.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,11 @@ class Literal {
7171
int64_t getInteger() const;
7272
double getFloat() const;
7373
int64_t getBits() const;
74+
// Equality checks for the type and the bits, so a nan float would
75+
// be compared bitwise (which means that a Literal containing a nan
76+
// would be equal to itself, if the bits are equal).
7477
bool operator==(const Literal& other) const;
7578
bool operator!=(const Literal& other) const;
76-
bool bitwiseEqual(const Literal& other) const;
7779

7880
static uint32_t NaNPayload(float f);
7981
static uint64_t NaNPayload(double f);
@@ -130,6 +132,9 @@ class Literal {
130132
Literal rotL(const Literal& other) const;
131133
Literal rotR(const Literal& other) const;
132134

135+
// Note that these functions perform equality checks based
136+
// on the type of the literal, so that (unlike the == operator)
137+
// a float nan would not be identical to itself.
133138
Literal eq(const Literal& other) const;
134139
Literal ne(const Literal& other) const;
135140
Literal ltS(const Literal& other) const;

src/passes/Precompute.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ struct Precompute : public WalkerPass<PostWalker<Precompute, UnifiedExpressionVi
316316
value = curr; // this is the first
317317
first = false;
318318
} else {
319-
if (!value.bitwiseEqual(curr)) {
319+
if (value != curr) {
320320
// not the same, give up
321321
value = Literal();
322322
break;

src/tools/execution-results.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ struct ExecutionResults {
7878
abort();
7979
}
8080
std::cout << "[fuzz-exec] comparing " << name << '\n';
81-
if (!results[name].bitwiseEqual(other.results[name])) {
81+
if (results[name] != other.results[name]) {
8282
std::cout << "not identical!\n";
8383
abort();
8484
}

src/tools/wasm-shell.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,14 +201,14 @@ static void run_asserts(Name moduleName, size_t* i, bool* checked, Module* wasm,
201201
->dynCast<Const>()
202202
->value;
203203
std::cerr << "seen " << result << ", expected " << expected << '\n';
204-
if (!expected.bitwiseEqual(result)) {
204+
if (expected != result) {
205205
std::cout << "unexpected, should be identical\n";
206206
abort();
207207
}
208208
} else {
209209
Literal expected;
210210
std::cerr << "seen " << result << ", expected " << expected << '\n';
211-
if (!expected.bitwiseEqual(result)) {
211+
if (expected != result) {
212212
std::cout << "unexpected, should be identical\n";
213213
abort();
214214
}

src/wasm/literal.cpp

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,26 +81,14 @@ int64_t Literal::getBits() const {
8181

8282
bool Literal::operator==(const Literal& other) const {
8383
if (type != other.type) return false;
84-
switch (type) {
85-
case Type::none: return true;
86-
case Type::i32: return i32 == other.i32;
87-
case Type::f32: return getf32() == other.getf32();
88-
case Type::i64: return i64 == other.i64;
89-
case Type::f64: return getf64() == other.getf64();
90-
default: abort();
91-
}
84+
if (type == none) return true;
85+
return getBits() == other.getBits();
9286
}
9387

9488
bool Literal::operator!=(const Literal& other) const {
9589
return !(*this == other);
9690
}
9791

98-
bool Literal::bitwiseEqual(const Literal& other) const {
99-
if (type != other.type) return false;
100-
if (type == none) return true;
101-
return getBits() == other.getBits();
102-
}
103-
10492
uint32_t Literal::NaNPayload(float f) {
10593
assert(std::isnan(f) && "expected a NaN");
10694
// SEEEEEEE EFFFFFFF FFFFFFFF FFFFFFFF

test/passes/rse.txt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,4 +430,33 @@
430430
)
431431
)
432432
)
433+
(func $fuzz-nan (; 18 ;) (type $2)
434+
(local $0 f64)
435+
(local $1 f64)
436+
(block $block
437+
(br_if $block
438+
(i32.const 0)
439+
)
440+
(loop $loop
441+
(set_local $1
442+
(get_local $0)
443+
)
444+
(set_local $0
445+
(f64.const -nan:0xfffffffffff87)
446+
)
447+
(br_if $loop
448+
(i32.const 1)
449+
)
450+
)
451+
)
452+
(set_local $0
453+
(get_local $1)
454+
)
455+
(if
456+
(i32.const 0)
457+
(drop
458+
(get_local $0)
459+
)
460+
)
461+
)
433462
)

test/passes/rse.wast

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,5 +248,34 @@
248248
)
249249
)
250250
)
251+
(func $fuzz-nan
252+
(local $0 f64)
253+
(local $1 f64)
254+
(block $block
255+
(br_if $block
256+
(i32.const 0)
257+
)
258+
(loop $loop
259+
(set_local $1
260+
(get_local $0)
261+
)
262+
(set_local $0
263+
(f64.const -nan:0xfffffffffff87)
264+
)
265+
(br_if $loop
266+
(i32.const 1)
267+
)
268+
)
269+
)
270+
(set_local $0 ;; make them equal
271+
(get_local $1)
272+
)
273+
(if
274+
(i32.const 0)
275+
(set_local $1 ;; we can drop this
276+
(get_local $0)
277+
)
278+
)
279+
)
251280
)
252281

0 commit comments

Comments
 (0)