@@ -35,7 +35,7 @@ struct DeNaN : public WalkerPass<
3535 // Adds calls.
3636 bool addsEffects () override { return true ; }
3737
38- Name deNan32, deNan64;
38+ Name deNan32, deNan64, deNan128 ;
3939
4040 void visitExpression (Expression* expr) {
4141 // If the expression returns a floating-point value, ensure it is not a
@@ -67,6 +67,13 @@ struct DeNaN : public WalkerPass<
6767 } else {
6868 replacement = builder.makeCall (deNan64, {expr}, Type::f64 );
6969 }
70+ } else if (expr->type == Type::v128) {
71+ if (c && hasNaNLane (c)) {
72+ uint8_t zero[16 ] = {};
73+ replacement = builder.makeConst (Literal (zero));
74+ } else {
75+ replacement = builder.makeCall (deNan128, {expr}, Type::v128);
76+ }
7077 }
7178 if (replacement) {
7279 // We can't do this outside of a function, like in a global initializer,
@@ -98,6 +105,11 @@ struct DeNaN : public WalkerPass<
98105 i,
99106 builder.makeCall (
100107 deNan64, {builder.makeLocalGet (i, Type::f64 )}, Type::f64 )));
108+ } else if (func->getLocalType (i) == Type::v128) {
109+ fixes.push_back (builder.makeLocalSet (
110+ i,
111+ builder.makeCall (
112+ deNan128, {builder.makeLocalGet (i, Type::v128)}, Type::v128)));
101113 }
102114 }
103115 if (!fixes.empty ()) {
@@ -115,34 +127,90 @@ struct DeNaN : public WalkerPass<
115127 // Pick names for the helper functions.
116128 deNan32 = Names::getValidFunctionName (*module , " deNan32" );
117129 deNan64 = Names::getValidFunctionName (*module , " deNan64" );
130+ deNan128 = Names::getValidFunctionName (*module , " deNan128" );
118131
119132 ControlFlowWalker<DeNaN, UnifiedExpressionVisitor<DeNaN>>::doWalkModule (
120133 module );
121134
122135 // Add helper functions after the walk, so they are not instrumented.
136+ addFunc (module , deNan32, Type::f32 , Literal (float (0 )), EqFloat32);
137+ addFunc (module , deNan64, Type::f64 , Literal (double (0 )), EqFloat64);
138+
139+ if (module ->features .hasSIMD ()) {
140+ uint8_t zero128[16 ] = {};
141+ addFunc (module , deNan128, Type::v128, Literal (zero128));
142+ }
143+ }
144+
145+ // Add a de-NaN-ing helper function.
146+ void addFunc (Module* module ,
147+ Name name,
148+ Type type,
149+ Literal literal,
150+ std::optional<BinaryOp> op = {}) {
123151 Builder builder (*module );
124- auto add = [&](Name name, Type type, Literal literal, BinaryOp op) {
125- auto func = Builder::makeFunction (name, Signature (type, type), {});
126- // Compare the value to itself to check if it is a NaN, and return 0 if
127- // so:
152+ auto func = Builder::makeFunction (name, Signature (type, type), {});
153+ // Compare the value to itself to check if it is a NaN, and return 0 if
154+ // so:
155+ //
156+ // (if (result f*)
157+ // (f*.eq
158+ // (local.get $0)
159+ // (local.get $0)
160+ // )
161+ // (local.get $0)
162+ // (f*.const 0)
163+ // )
164+ Expression* condition;
165+ if (type != Type::v128) {
166+ // Generate a simple condition.
167+ assert (op);
168+ condition = builder.makeBinary (
169+ *op, builder.makeLocalGet (0 , type), builder.makeLocalGet (0 , type));
170+ } else {
171+ assert (!op);
172+ // v128 is trickier as the 128 bits may contain f32s or f64s, and we
173+ // need to check for nans both ways in principle. However, the f32 NaN
174+ // pattern is a superset of f64, since it checks less bits (8 bit
175+ // exponent vs 11), and it is checked in more places (4 32-bit values vs
176+ // 2 64-bit ones), so we can just check that. That is, this reduces to 4
177+ // checks of f32s, but is otherwise the same as a check of a single f32.
128178 //
129- // (if (result f*)
130- // (f*.eq
131- // (local.get $0)
132- // (local.get $0)
133- // )
134- // (local.get $0)
135- // (f*.const 0)
136- // )
137- func->body = builder.makeIf (
138- builder.makeBinary (
139- op, builder.makeLocalGet (0 , type), builder.makeLocalGet (0 , type)),
140- builder.makeLocalGet (0 , type),
141- builder.makeConst (literal));
142- module ->addFunction (std::move (func));
143- };
144- add (deNan32, Type::f32 , Literal (float (0 )), EqFloat32);
145- add (deNan64, Type::f64 , Literal (double (0 )), EqFloat64);
179+ // However there is additional complexity, which is that if we do
180+ // EqVecF32x4 then we get all-1s for each case where we compare equal.
181+ // That itself is a NaN pattern, which means that running this pass
182+ // twice would interfere with itself. To avoid that we'd need a way to
183+ // detect our previous instrumentation and not instrument it, but that
184+ // is tricky (we can't depend on function names etc. while fuzzing).
185+ // Instead, extract the lanes and use f32 checks.
186+ auto getLane = [&](Index index) {
187+ return builder.makeSIMDExtract (
188+ ExtractLaneVecF32x4, builder.makeLocalGet (0 , type), index);
189+ };
190+ auto getLaneCheck = [&](Index index) {
191+ return builder.makeBinary (EqFloat32, getLane (index), getLane (index));
192+ };
193+ auto * firstTwo =
194+ builder.makeBinary (AndInt32, getLaneCheck (0 ), getLaneCheck (1 ));
195+ auto * lastTwo =
196+ builder.makeBinary (AndInt32, getLaneCheck (2 ), getLaneCheck (3 ));
197+ condition = builder.makeBinary (AndInt32, firstTwo, lastTwo);
198+ }
199+ func->body = builder.makeIf (
200+ condition, builder.makeLocalGet (0 , type), builder.makeConst (literal));
201+ module ->addFunction (std::move (func));
202+ };
203+
204+ // Check if a contant v128 may contain f32 or f64 NaNs.
205+ bool hasNaNLane (Const* c) {
206+ assert (c->type == Type::v128);
207+ auto value = c->value ;
208+
209+ // Compute if all f32s are equal to themselves.
210+ auto test32 = value.eqF32x4 (value);
211+ test32 = test32.allTrueI32x4 ();
212+
213+ return !test32.getInteger ();
146214 }
147215};
148216
0 commit comments