Skip to content

Commit 6cdffee

Browse files
committed
fix optimizing two shifts into one; if the number of effective shifts overflows, it is not vali to just add them
1 parent 56e4975 commit 6cdffee

5 files changed

Lines changed: 112 additions & 18 deletions

File tree

src/ast/bits.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,20 @@ struct Bits {
4242

4343
// gets the number of effective shifts a shift operation does. In
4444
// wasm, only 5 bits matter for 32-bit shifts, and 6 for 64.
45-
static uint32_t getEffectiveShifts(Const* amount) {
45+
static Index getEffectiveShifts(Index amount, WasmType type) {
46+
if (type == i32) {
47+
return amount & 31;
48+
} else if (type == i64) {
49+
return amount & 63;
50+
}
51+
WASM_UNREACHABLE();
52+
}
53+
54+
static Index getEffectiveShifts(Const* amount) {
4655
if (amount->type == i32) {
47-
return amount->value.geti32() & 31;
56+
return getEffectiveShifts(amount->value.geti32(), i32);
4857
} else if (amount->type == i64) {
49-
return amount->value.geti64() & 63;
58+
return getEffectiveShifts(amount->value.geti64(), i64);
5059
}
5160
WASM_UNREACHABLE();
5261
}

src/ast/literal-utils.h

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,31 @@ namespace wasm {
2323

2424
namespace LiteralUtils {
2525

26-
inline Expression* makeZero(WasmType type, Module& wasm) {
27-
Literal value;
26+
inline Literal makeLiteralFromInt32(int32_t x, WasmType type) {
2827
switch (type) {
29-
case i32: value = Literal(int32_t(0)); break;
30-
case i64: value = Literal(int64_t(0)); break;
31-
case f32: value = Literal(float(0)); break;
32-
case f64: value = Literal(double(0)); break;
28+
case i32: return Literal(int32_t(x)); break;
29+
case i64: return Literal(int64_t(x)); break;
30+
case f32: return Literal(float(x)); break;
31+
case f64: return Literal(double(x)); break;
3332
default: WASM_UNREACHABLE();
3433
}
34+
}
35+
36+
inline Literal makeLiteralZero(WasmType type) {
37+
return makeLiteralFromInt32(0, type);
38+
}
39+
40+
inline Expression* makeFromInt32(int32_t x, WasmType type, Module& wasm) {
3541
auto* ret = wasm.allocator.alloc<Const>();
36-
ret->value = value;
37-
ret->type = value.type;
42+
ret->value = makeLiteralFromInt32(x, type);
43+
ret->type = type;
3844
return ret;
3945
}
4046

47+
inline Expression* makeZero(WasmType type, Module& wasm) {
48+
return makeFromInt32(0, type, wasm);
49+
}
50+
4151
} // namespace LiteralUtils
4252

4353
} // namespace wasm

src/passes/OptimizeInstructions.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <ast/effects.h>
3030
#include <ast/manipulation.h>
3131
#include <ast/properties.h>
32+
#include <ast/literal-utils.h>
3233

3334
namespace wasm {
3435

@@ -533,9 +534,16 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions,
533534
} else if (left->op == OrInt32) {
534535
leftRight->value = leftRight->value.or_(right->value);
535536
return left;
536-
} else if (left->op == ShlInt32 || left->op == ShrUInt32 || left->op == ShrSInt32) {
537-
leftRight->value = leftRight->value.add(right->value);
538-
return left;
537+
} else if (left->op == ShlInt32 || left->op == ShrUInt32 || left->op == ShrSInt32 ||
538+
left->op == ShlInt64 || left->op == ShrUInt64 || left->op == ShrSInt64) {
539+
// shifts only use an effective amount from the constant, so adding must
540+
// be done carefully
541+
auto total = Bits::getEffectiveShifts(leftRight) + Bits::getEffectiveShifts(right);
542+
if (total == Bits::getEffectiveShifts(total, left->type)) {
543+
// no overflow, we can do this
544+
leftRight->value = LiteralUtils::makeLiteralFromInt32(total, left->type);
545+
return left;
546+
} // TODO: handle overflows
539547
}
540548
}
541549
}

test/passes/optimize-instructions.txt

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
(type $4 (func (param i32 i32)))
77
(type $5 (func (param i32)))
88
(type $6 (func (param i32 i32) (result i32)))
9+
(type $7 (func (param i64) (result i64)))
910
(memory $0 0)
1011
(export "load-off-2" (func $load-off-2))
1112
(func $f (type $0) (param $i1 i32) (param $i2 i64)
@@ -734,7 +735,7 @@
734735
(i32.shr_s
735736
(i32.shl
736737
(i32.const 32)
737-
(i32.const 59)
738+
(i32.const 27)
738739
)
739740
(i32.const 24)
740741
)
@@ -1083,19 +1084,19 @@
10831084
(drop
10841085
(i32.shl
10851086
(get_local $0)
1086-
(i32.const 211)
1087+
(i32.const 19)
10871088
)
10881089
)
10891090
(drop
10901091
(i32.shr_s
10911092
(get_local $0)
1092-
(i32.const 211)
1093+
(i32.const 19)
10931094
)
10941095
)
10951096
(drop
10961097
(i32.shr_u
10971098
(get_local $0)
1098-
(i32.const 211)
1099+
(i32.const 19)
10991100
)
11001101
)
11011102
(drop
@@ -2024,4 +2025,34 @@
20242025
(i32.const 255)
20252026
)
20262027
)
2028+
(func $shifts-square-overflow (type $3) (param $x i32) (result i32)
2029+
(i32.shr_u
2030+
(i32.shr_u
2031+
(get_local $x)
2032+
(i32.const 65535)
2033+
)
2034+
(i32.const 32767)
2035+
)
2036+
)
2037+
(func $shifts-square-no-overflow-small (type $3) (param $x i32) (result i32)
2038+
(i32.shr_u
2039+
(get_local $x)
2040+
(i32.const 9)
2041+
)
2042+
)
2043+
(func $shifts-square-overflow-64 (type $7) (param $x i64) (result i64)
2044+
(i64.shr_u
2045+
(i64.shr_u
2046+
(get_local $x)
2047+
(i64.const 65535)
2048+
)
2049+
(i64.const 64767)
2050+
)
2051+
)
2052+
(func $shifts-square-no-overflow-small-64 (type $7) (param $x i64) (result i64)
2053+
(i64.shr_u
2054+
(get_local $x)
2055+
(i64.const 9)
2056+
)
2057+
)
20272058
)

test/passes/optimize-instructions.wast

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2451,4 +2451,40 @@
24512451
(i32.const 255)
24522452
)
24532453
)
2454+
(func $shifts-square-overflow (param $x i32) (result i32)
2455+
(i32.shr_u
2456+
(i32.shr_u
2457+
(get_local $x)
2458+
(i32.const 65535) ;; 31 bits effectively
2459+
)
2460+
(i32.const 32767) ;; also 31 bits, so two shifts that force the value into nothing for sure
2461+
)
2462+
)
2463+
(func $shifts-square-no-overflow-small (param $x i32) (result i32)
2464+
(i32.shr_u
2465+
(i32.shr_u
2466+
(get_local $x)
2467+
(i32.const 1031) ;; 7 bits effectively
2468+
)
2469+
(i32.const 4098) ;; 2 bits effectively
2470+
)
2471+
)
2472+
(func $shifts-square-overflow-64 (param $x i64) (result i64)
2473+
(i64.shr_u
2474+
(i64.shr_u
2475+
(get_local $x)
2476+
(i64.const 65535) ;; 63 bits effectively
2477+
)
2478+
(i64.const 64767) ;; also 63 bits, so two shifts that force the value into nothing for sure
2479+
)
2480+
)
2481+
(func $shifts-square-no-overflow-small-64 (param $x i64) (result i64)
2482+
(i64.shr_u
2483+
(i64.shr_u
2484+
(get_local $x)
2485+
(i64.const 1031) ;; 7 bits effectively
2486+
)
2487+
(i64.const 4098) ;; 2 bits effectively
2488+
)
2489+
)
24542490
)

0 commit comments

Comments
 (0)