Skip to content

Commit 654cfca

Browse files
authored
Generalize transforms for #3153 (#3193)
Implement a more general (additional) version of #3153 which also handles negative constant divisors: `(int32)x % -4 == 0` --> `(x & 3) == 0` `x % -C_pot == 0` --> `(x & (abs(C_pot) - 1)) == 0` and special two-complement values as well: `(int32)x % 0x80000000 == 0` --> `(x & 0x7fffffff) == 0` `(int64)x % 0x8000000000000000 == 0` --> `(x & 0x7fffffffffffffff) == 0` as separete rules: `(int32)x % 0x80000000` --> `x & 0x7fffffff` `(int64)x % 0x8000000000000000` --> `x & 0x7fffffffffffffff` The [previous pr](#3153) didn't use these possibilities.
1 parent 3da6648 commit 654cfca

File tree

4 files changed

+252
-20
lines changed

4 files changed

+252
-20
lines changed

src/literal.h

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,57 @@ class Literal {
9696
}
9797
return false;
9898
}
99+
bool isZero() const {
100+
switch (type.getBasic()) {
101+
case Type::i32:
102+
return i32 == 0;
103+
case Type::i64:
104+
return i64 == 0LL;
105+
case Type::f32:
106+
return bit_cast<float>(i32) == 0.0f;
107+
case Type::f64:
108+
return bit_cast<double>(i64) == 0.0;
109+
case Type::v128: {
110+
uint8_t zeros[16] = {0};
111+
return memcmp(&v128, zeros, 16) == 0;
112+
}
113+
default:
114+
WASM_UNREACHABLE("unexpected type");
115+
}
116+
}
117+
bool isSignedMin() const {
118+
switch (type.getBasic()) {
119+
case Type::i32:
120+
return i32 == std::numeric_limits<int32_t>::min();
121+
case Type::i64:
122+
return i64 == std::numeric_limits<int64_t>::min();
123+
default:
124+
WASM_UNREACHABLE("unexpected type");
125+
}
126+
}
127+
bool isSignedMax() const {
128+
switch (type.getBasic()) {
129+
case Type::i32:
130+
return i32 == std::numeric_limits<int32_t>::max();
131+
case Type::i64:
132+
return i64 == std::numeric_limits<int64_t>::max();
133+
default:
134+
WASM_UNREACHABLE("unexpected type");
135+
}
136+
}
137+
bool isUnsignedMax() const {
138+
switch (type.getBasic()) {
139+
case Type::i32:
140+
return uint32_t(i32) == std::numeric_limits<uint32_t>::max();
141+
case Type::i64:
142+
return uint64_t(i64) == std::numeric_limits<uint64_t>::max();
143+
default:
144+
WASM_UNREACHABLE("unexpected type");
145+
}
146+
}
99147

148+
static Literals makeZero(Type type);
149+
static Literal makeSingleZero(Type type);
100150
static Literal makeFromInt32(int32_t x, Type type) {
101151
switch (type.getBasic()) {
102152
case Type::i32:
@@ -116,7 +166,6 @@ class Literal {
116166
WASM_UNREACHABLE("unexpected type");
117167
}
118168
}
119-
120169
static Literal makeFromUInt64(uint64_t x, Type type) {
121170
switch (type.getBasic()) {
122171
case Type::i32:
@@ -131,10 +180,36 @@ class Literal {
131180
WASM_UNREACHABLE("unexpected type");
132181
}
133182
}
134-
135-
static Literals makeZero(Type type);
136-
static Literal makeSingleZero(Type type);
137-
183+
static Literal makeSignedMin(Type type) {
184+
switch (type.getBasic()) {
185+
case Type::i32:
186+
return Literal(std::numeric_limits<int32_t>::min());
187+
case Type::i64:
188+
return Literal(std::numeric_limits<int64_t>::min());
189+
default:
190+
WASM_UNREACHABLE("unexpected type");
191+
}
192+
}
193+
static Literal makeSignedMax(Type type) {
194+
switch (type.getBasic()) {
195+
case Type::i32:
196+
return Literal(std::numeric_limits<int32_t>::max());
197+
case Type::i64:
198+
return Literal(std::numeric_limits<int64_t>::max());
199+
default:
200+
WASM_UNREACHABLE("unexpected type");
201+
}
202+
}
203+
static Literal makeUnsignedMax(Type type) {
204+
switch (type.getBasic()) {
205+
case Type::i32:
206+
return Literal(std::numeric_limits<uint32_t>::max());
207+
case Type::i64:
208+
return Literal(std::numeric_limits<uint64_t>::max());
209+
default:
210+
WASM_UNREACHABLE("unexpected type");
211+
}
212+
}
138213
static Literal makeNull(Type type) {
139214
assert(type.isNullable());
140215
return Literal(type);

src/passes/OptimizeInstructions.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -298,15 +298,16 @@ struct OptimizeInstructions
298298
}
299299
}
300300
{
301-
// eqz((signed)x % C_pot) => eqz(x & (C_pot - 1))
301+
// eqz((signed)x % C_pot) => eqz(x & (abs(C_pot) - 1))
302302
Const* c;
303303
Binary* inner;
304304
if (matches(curr,
305305
unary(Abstract::EqZ,
306306
binary(&inner, Abstract::RemS, any(), ival(&c)))) &&
307-
Bits::isPowerOf2((uint64_t)c->value.getInteger())) {
307+
!c->value.isSignedMin() &&
308+
Bits::isPowerOf2(c->value.abs().getInteger())) {
308309
inner->op = Abstract::getBinary(c->type, Abstract::And);
309-
c->value = c->value.sub(Literal::makeFromInt32(1, c->type));
310+
c->value = c->value.abs().sub(Literal::makeFromInt32(1, c->type));
310311
return curr;
311312
}
312313
}
@@ -1307,17 +1308,27 @@ struct OptimizeInstructions
13071308
right->value = Literal::makeSingleZero(type);
13081309
return right;
13091310
}
1310-
// (signed)x % C_pot != 0 ==> x & (C_pot - 1) != 0
1311+
{
1312+
// (signed)x % (i32|i64).min_s ==> (x & (i32|i64).max_s)
1313+
if (matches(curr, binary(Abstract::RemS, any(&left), ival())) &&
1314+
right->value.isSignedMin()) {
1315+
curr->op = Abstract::getBinary(type, Abstract::And);
1316+
right->value = Literal::makeSignedMax(type);
1317+
return curr;
1318+
}
1319+
}
1320+
// (signed)x % C_pot != 0 ==> (x & (abs(C_pot) - 1)) != 0
13111321
{
13121322
Const* c;
13131323
Binary* inner;
13141324
if (matches(curr,
13151325
binary(Abstract::Ne,
13161326
binary(&inner, Abstract::RemS, any(), ival(&c)),
13171327
ival(0))) &&
1318-
Bits::isPowerOf2((uint64_t)c->value.getInteger())) {
1328+
!c->value.isSignedMin() &&
1329+
Bits::isPowerOf2(c->value.abs().getInteger())) {
13191330
inner->op = Abstract::getBinary(c->type, Abstract::And);
1320-
c->value = c->value.sub(Literal::makeFromInt32(1, c->type));
1331+
c->value = c->value.abs().sub(Literal::makeFromInt32(1, c->type));
13211332
return curr;
13221333
}
13231334
}

test/passes/optimize-instructions_all-features.txt

Lines changed: 79 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2737,13 +2737,25 @@
27372737
)
27382738
(unreachable)
27392739
)
2740-
(func $srem-by-1 (param $x i32) (param $y i64)
2740+
(func $srem-by-const (param $x i32) (param $y i64)
27412741
(drop
27422742
(i32.const 0)
27432743
)
27442744
(drop
27452745
(i64.const 0)
27462746
)
2747+
(drop
2748+
(i32.and
2749+
(local.get $x)
2750+
(i32.const 2147483647)
2751+
)
2752+
)
2753+
(drop
2754+
(i64.and
2755+
(local.get $y)
2756+
(i64.const 9223372036854775807)
2757+
)
2758+
)
27472759
)
27482760
(func $srem-by-pot-eq-ne-zero (param $x i32) (param $y i64)
27492761
(drop
@@ -2770,6 +2782,22 @@
27702782
)
27712783
)
27722784
)
2785+
(drop
2786+
(i64.eqz
2787+
(i64.and
2788+
(local.get $y)
2789+
(i64.const 3)
2790+
)
2791+
)
2792+
)
2793+
(drop
2794+
(i32.eqz
2795+
(i32.and
2796+
(local.get $x)
2797+
(i32.const 3)
2798+
)
2799+
)
2800+
)
27732801
(drop
27742802
(i64.eqz
27752803
(i64.and
@@ -2778,6 +2806,22 @@
27782806
)
27792807
)
27802808
)
2809+
(drop
2810+
(i32.eqz
2811+
(i32.and
2812+
(local.get $x)
2813+
(i32.const 3)
2814+
)
2815+
)
2816+
)
2817+
(drop
2818+
(i64.eqz
2819+
(i64.and
2820+
(local.get $y)
2821+
(i64.const 3)
2822+
)
2823+
)
2824+
)
27812825
(drop
27822826
(i32.ne
27832827
(i32.and
@@ -2797,17 +2841,48 @@
27972841
)
27982842
(drop
27992843
(i32.eqz
2800-
(i32.rem_s
2844+
(i32.const 0)
2845+
)
2846+
)
2847+
(drop
2848+
(i32.eqz
2849+
(i32.and
28012850
(local.get $x)
2802-
(i32.const 3)
2851+
(i32.const 2147483647)
2852+
)
2853+
)
2854+
)
2855+
(drop
2856+
(i32.ne
2857+
(i32.and
2858+
(local.get $x)
2859+
(i32.const 2147483647)
2860+
)
2861+
(i32.const 0)
2862+
)
2863+
)
2864+
(drop
2865+
(i64.eqz
2866+
(i64.and
2867+
(local.get $y)
2868+
(i64.const 9223372036854775807)
28032869
)
28042870
)
28052871
)
2872+
(drop
2873+
(i64.ne
2874+
(i64.and
2875+
(local.get $y)
2876+
(i64.const 9223372036854775807)
2877+
)
2878+
(i64.const 0)
2879+
)
2880+
)
28062881
(drop
28072882
(i32.eqz
28082883
(i32.rem_s
28092884
(local.get $x)
2810-
(i32.const -4)
2885+
(i32.const 3)
28112886
)
28122887
)
28132888
)

0 commit comments

Comments
 (0)