Skip to content

Commit 9b57abd

Browse files
authored
fix: Wrap RHS of SMI shifts to maximum size of type in bits (#1511)
BREAKING CHANGE: When shifting a small integer value of type `i8`/`u8` or `i16`/`u16`, only the 3 respectively 4 least significant bits of the RHS value affect the result, analogous to the result of an `i32.shl` only being affected by the 5 least significant bits of the RHS value. Example: `someI8 << 8` previously produced the value `0`, but now produces `someI8` due to masking the RHS as `8 & 7 = 0` (3 bits).
1 parent a0a9da7 commit 9b57abd

File tree

6 files changed

+69
-25
lines changed

6 files changed

+69
-25
lines changed

src/compiler.ts

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5128,7 +5128,7 @@ export class Compiler extends DiagnosticEmitter {
51285128
module.binary(BinaryOp.EqI8x16, leftExpr, rightExpr)
51295129
);
51305130
}
5131-
case TypeKind.FUNCREF:
5131+
case TypeKind.FUNCREF:
51325132
case TypeKind.EXTERNREF:
51335133
case TypeKind.EXNREF:
51345134
case TypeKind.ANYREF: {
@@ -5641,14 +5641,18 @@ export class Compiler extends DiagnosticEmitter {
56415641
// Cares about garbage bits on the RHS, but only for types smaller than 5 bits
56425642
var module = this.module;
56435643
switch (type.kind) {
5644-
case TypeKind.BOOL: {
5645-
rightExpr = this.ensureSmallIntegerWrap(rightExpr, type);
5646-
// falls through
5647-
}
5644+
case TypeKind.BOOL: return leftExpr;
56485645
case TypeKind.I8:
56495646
case TypeKind.I16:
56505647
case TypeKind.U8:
5651-
case TypeKind.U16:
5648+
case TypeKind.U16: {
5649+
// leftExpr << (rightExpr & (7|15))
5650+
return module.binary(
5651+
BinaryOp.ShlI32,
5652+
leftExpr,
5653+
module.binary(BinaryOp.AndI32, rightExpr, module.i32(type.size - 1))
5654+
);
5655+
}
56525656
case TypeKind.I32:
56535657
case TypeKind.U32: {
56545658
return module.binary(BinaryOp.ShlI32, leftExpr, rightExpr);
@@ -5677,10 +5681,24 @@ export class Compiler extends DiagnosticEmitter {
56775681
// and signedness
56785682
var module = this.module;
56795683
switch (type.kind) {
5684+
case TypeKind.BOOL: return leftExpr;
56805685
case TypeKind.I8:
56815686
case TypeKind.I16: {
5682-
leftExpr = this.ensureSmallIntegerWrap(leftExpr, type);
5683-
// falls through
5687+
// leftExpr >> (rightExpr & (7|15))
5688+
return module.binary(
5689+
BinaryOp.ShrI32,
5690+
this.ensureSmallIntegerWrap(leftExpr, type),
5691+
module.binary(BinaryOp.AndI32, rightExpr, module.i32(type.size - 1))
5692+
);
5693+
}
5694+
case TypeKind.U8:
5695+
case TypeKind.U16: {
5696+
// leftExpr >>> (rightExpr & (7|15))
5697+
return module.binary(
5698+
BinaryOp.ShrU32,
5699+
this.ensureSmallIntegerWrap(leftExpr, type),
5700+
module.binary(BinaryOp.AndI32, rightExpr, module.i32(type.size - 1))
5701+
);
56845702
}
56855703
case TypeKind.I32: {
56865704
return module.binary(BinaryOp.ShrI32, leftExpr, rightExpr);
@@ -5697,15 +5715,6 @@ export class Compiler extends DiagnosticEmitter {
56975715
rightExpr
56985716
);
56995717
}
5700-
case TypeKind.BOOL: {
5701-
rightExpr = this.ensureSmallIntegerWrap(rightExpr, type);
5702-
// falls through
5703-
}
5704-
case TypeKind.U8:
5705-
case TypeKind.U16: {
5706-
leftExpr = this.ensureSmallIntegerWrap(leftExpr, type);
5707-
// falls through
5708-
}
57095718
case TypeKind.U32: {
57105719
return module.binary(BinaryOp.ShrU32, leftExpr, rightExpr);
57115720
}
@@ -5730,16 +5739,17 @@ export class Compiler extends DiagnosticEmitter {
57305739
// Cares about garbage bits on the LHS, but on the RHS only for types smaller than 5 bits
57315740
var module = this.module;
57325741
switch (type.kind) {
5733-
case TypeKind.BOOL: {
5734-
rightExpr = this.ensureSmallIntegerWrap(rightExpr, type);
5735-
// falls through
5736-
}
5742+
case TypeKind.BOOL: return leftExpr;
57375743
case TypeKind.I8:
57385744
case TypeKind.I16:
57395745
case TypeKind.U8:
57405746
case TypeKind.U16: {
5741-
leftExpr = this.ensureSmallIntegerWrap(leftExpr, type);
5742-
// falls through
5747+
// leftExpr >>> (rightExpr & (7|15))
5748+
return module.binary(
5749+
BinaryOp.ShrU32,
5750+
this.ensureSmallIntegerWrap(leftExpr, type),
5751+
module.binary(BinaryOp.AndI32, rightExpr, module.i32(type.size - 1))
5752+
);
57435753
}
57445754
case TypeKind.I32:
57455755
case TypeKind.U32: {

tests/compiler/abi.untouched.wat

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@
8888
i32.const 24
8989
i32.shr_s
9090
i32.const 24
91+
i32.const 7
92+
i32.and
9193
i32.shr_s
9294
local.set $0
9395
else

tests/compiler/retain-i32.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ function test(a: u32, b: u32): void {
77
assert(<i8>(a & b) == <i8>(<i8>a & <i8>b));
88
assert(<i8>(a | b) == <i8>(<i8>a | <i8>b));
99
assert(<i8>(a ^ b) == <i8>(<i8>a ^ <i8>b));
10-
assert(<i8>(a << b) == <i8>(<i8>a << <i8>b));
10+
assert(<i8>(a << (b & 7)) == <i8>(<i8>a << <i8>b));
1111

1212
// unsigned
1313
assert(<u8>(a + b) == <u8>(<u8>a + <u8>b));
@@ -16,7 +16,7 @@ function test(a: u32, b: u32): void {
1616
assert(<u8>(a & b) == <u8>(<u8>a & <u8>b));
1717
assert(<u8>(a | b) == <u8>(<u8>a | <u8>b));
1818
assert(<u8>(a ^ b) == <u8>(<u8>a ^ <u8>b));
19-
assert(<u8>(a << b) == <u8>(<u8>a << <u8>b));
19+
assert(<u8>(a << (b & 7)) == <u8>(<u8>a << <u8>b));
2020
}
2121

2222
// signed

tests/compiler/retain-i32.untouched.wat

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,17 @@
167167
end
168168
local.get $0
169169
local.get $1
170+
i32.const 7
171+
i32.and
170172
i32.shl
171173
i32.const 24
172174
i32.shl
173175
i32.const 24
174176
i32.shr_s
175177
local.get $0
176178
local.get $1
179+
i32.const 7
180+
i32.and
177181
i32.shl
178182
i32.const 24
179183
i32.shl
@@ -311,11 +315,15 @@
311315
end
312316
local.get $0
313317
local.get $1
318+
i32.const 7
319+
i32.and
314320
i32.shl
315321
i32.const 255
316322
i32.and
317323
local.get $0
318324
local.get $1
325+
i32.const 7
326+
i32.and
319327
i32.shl
320328
i32.const 255
321329
i32.and

tests/compiler/std/dataview.untouched.wat

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2124,13 +2124,17 @@
21242124
drop
21252125
local.get $0
21262126
i32.const 8
2127+
i32.const 15
2128+
i32.and
21272129
i32.shl
21282130
local.get $0
21292131
i32.const 16
21302132
i32.shl
21312133
i32.const 16
21322134
i32.shr_s
21332135
i32.const 8
2136+
i32.const 15
2137+
i32.and
21342138
i32.shr_s
21352139
i32.const 255
21362140
i32.and
@@ -2344,11 +2348,15 @@
23442348
drop
23452349
local.get $0
23462350
i32.const 8
2351+
i32.const 15
2352+
i32.and
23472353
i32.shl
23482354
local.get $0
23492355
i32.const 65535
23502356
i32.and
23512357
i32.const 8
2358+
i32.const 15
2359+
i32.and
23522360
i32.shr_u
23532361
i32.const 255
23542362
i32.and

tests/compiler/std/polyfills.untouched.wat

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,15 @@
5454
drop
5555
local.get $0
5656
i32.const 8
57+
i32.const 15
58+
i32.and
5759
i32.shl
5860
local.get $0
5961
i32.const 65535
6062
i32.and
6163
i32.const 8
64+
i32.const 15
65+
i32.and
6266
i32.shr_u
6367
i32.const 255
6468
i32.and
@@ -74,13 +78,17 @@
7478
drop
7579
local.get $0
7680
i32.const 8
81+
i32.const 15
82+
i32.and
7783
i32.shl
7884
local.get $0
7985
i32.const 16
8086
i32.shl
8187
i32.const 16
8288
i32.shr_s
8389
i32.const 8
90+
i32.const 15
91+
i32.and
8492
i32.shr_s
8593
i32.const 255
8694
i32.and
@@ -347,11 +355,15 @@
347355
drop
348356
local.get $0
349357
i32.const 8
358+
i32.const 15
359+
i32.and
350360
i32.shl
351361
local.get $0
352362
i32.const 65535
353363
i32.and
354364
i32.const 8
365+
i32.const 15
366+
i32.and
355367
i32.shr_u
356368
i32.const 255
357369
i32.and
@@ -374,13 +386,17 @@
374386
drop
375387
local.get $0
376388
i32.const 8
389+
i32.const 15
390+
i32.and
377391
i32.shl
378392
local.get $0
379393
i32.const 16
380394
i32.shl
381395
i32.const 16
382396
i32.shr_s
383397
i32.const 8
398+
i32.const 15
399+
i32.and
384400
i32.shr_s
385401
i32.const 255
386402
i32.and

0 commit comments

Comments
 (0)