Skip to content

Fix invalid codegen on small to long int cast #1921

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3521,10 +3521,10 @@ export class Compiler extends DiagnosticEmitter {
if (currentType != contextualType.nonNullableType) { // allow assigning non-nullable to nullable
if (constraints & Constraints.CONV_EXPLICIT) {
expr = this.convertExpression(expr, currentType, contextualType, true, expression);
this.currentType = contextualType;
this.currentType = currentType = contextualType;
} else if (constraints & Constraints.CONV_IMPLICIT) {
expr = this.convertExpression(expr, currentType, contextualType, false, expression);
this.currentType = contextualType;
this.currentType = currentType = contextualType;
}
}
if (wrap) expr = this.ensureSmallIntegerWrap(expr, currentType);
Expand Down
7 changes: 7 additions & 0 deletions src/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1351,6 +1351,13 @@ export class Flow {
case UnaryOp.ClzI32:
case UnaryOp.CtzI32:
case UnaryOp.PopcntI32: return type.size < 7;

// sign extensions overflow if result can have high garbage bits in the target type
case UnaryOp.Extend8I32: return type.size < (type.isUnsignedIntegerValue ? 32 : 8);
case UnaryOp.Extend8I64: return type.size < (type.isUnsignedIntegerValue ? 64 : 8);
case UnaryOp.Extend16I32: return type.size < (type.isUnsignedIntegerValue ? 32 : 16);
case UnaryOp.Extend16I64: return type.size < (type.isUnsignedIntegerValue ? 64 : 16);
case UnaryOp.Extend32I64: return type.size < (type.isUnsignedIntegerValue ? 64 : 32);
Comment on lines +1355 to +1360
Copy link
Member Author

@dcodeIO dcodeIO Jun 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a comment here for review, by example of i32.extend16_s:

If the target type is unsigned, an i32.extend16_s can yield high garbage bits if the target type is smaller than u32. u32 naturally does not care about garbage bits, but any smaller unsigned integer could have garbage high 1s after the instruction.

If the target type is signed, an i32.extend16_s can yield high garbage 0s after leading 1s if the target type is smaller than i16. If the target type is i16 or larger, the instruction would set all high bits to either 0 or 1, which makes the value valid regardless.

}
break;
}
Expand Down
2 changes: 0 additions & 2 deletions tests/compiler/abi.untouched.wat
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@
local.set $0
end
local.get $0
i32.extend8_s
i32.eqz
i32.eqz
if
Expand Down Expand Up @@ -176,7 +175,6 @@
)
(func $abi/exportedExported (result i32)
call $abi/exported
i32.extend8_s
)
(func $abi/exportedInternal (result i32)
call $abi/internal
Expand Down
4 changes: 4 additions & 0 deletions tests/compiler/cast.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"asc_flags": [
]
}
4 changes: 4 additions & 0 deletions tests/compiler/cast.optimized.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(module
(memory $0 0)
(export "memory" (memory $0))
)
96 changes: 96 additions & 0 deletions tests/compiler/cast.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
function test<T,U>(x: T): U {
// @ts-ignore
var y = x as U;
// @ts-ignore
return y;
}

test<i8,i8>(0);
test<i8,u8>(0);
test<i8,i16>(0);
test<i8,u16>(0);
test<i8,i32>(0);
test<i8,u32>(0);
test<i8,i64>(0);
test<i8,u64>(0);
test<i8,bool>(0);

test<u8,i8>(0);
test<u8,u8>(0);
test<u8,i16>(0);
test<u8,u16>(0);
test<u8,i32>(0);
test<u8,u32>(0);
test<u8,i64>(0);
test<u8,u64>(0);
test<u8,bool>(0);

test<i16,i8>(0);
test<i16,u8>(0);
test<i16,i16>(0);
test<i16,u16>(0);
test<i16,i32>(0);
test<i16,u32>(0);
test<i16,i64>(0);
test<i16,u64>(0);
test<i16,bool>(0);

test<u16,i8>(0);
test<u16,u8>(0);
test<u16,i16>(0);
test<u16,u16>(0);
test<u16,i32>(0);
test<u16,u32>(0);
test<u16,i64>(0);
test<u16,u64>(0);
test<u16,bool>(0);

test<i32,i8>(0);
test<i32,u8>(0);
test<i32,i16>(0);
test<i32,u16>(0);
test<i32,i32>(0);
test<i32,u32>(0);
test<i32,i64>(0);
test<i32,u64>(0);
test<i32,bool>(0);

test<u32,i8>(0);
test<u32,u8>(0);
test<u32,i16>(0);
test<u32,u16>(0);
test<u32,i32>(0);
test<u32,u32>(0);
test<u32,i64>(0);
test<u32,u64>(0);
test<u32,bool>(0);

test<i64,i8>(0);
test<i64,u8>(0);
test<i64,i16>(0);
test<i64,u16>(0);
test<i64,i32>(0);
test<i64,u32>(0);
test<i64,i64>(0);
test<i64,u64>(0);
test<i64,bool>(0);

test<u64,i8>(0);
test<u64,u8>(0);
test<u64,i16>(0);
test<u64,u16>(0);
test<u64,i32>(0);
test<u64,u32>(0);
test<u64,i64>(0);
test<u64,u64>(0);
test<u64,bool>(0);

test<bool,i8>(0);
test<bool,u8>(0);
test<bool,i16>(0);
test<bool,u16>(0);
test<bool,i32>(0);
test<bool,u32>(0);
test<bool,i64>(0);
test<bool,u64>(0);
test<bool,bool>(0);
Loading