Skip to content
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
7 changes: 7 additions & 0 deletions tslang/lib/TypeScript/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4005,6 +4005,13 @@ struct ArithmeticBinaryOpLowering : public TsLlvmPattern<mlir_ts::ArithmeticBina
transformed.getOperand2(), rewriter);

case SyntaxKind::GreaterThanGreaterThanToken:
// the lowered operand is signless: an unsigned one (u64 keeps its type) shifts zeros in
if (arithmeticBinaryOp.getOperand1().getType().isUnsignedInteger())
{
return BinOp<mlir_ts::ArithmeticBinaryOp, arith::ShRUIOp, arith::ShRUIOp>(
arithmeticBinaryOp, transformed.getOperand1(), maskShiftAmount(arithmeticBinaryOp->getLoc(), transformed.getOperand2(), rewriter), rewriter);
}

return BinOp<mlir_ts::ArithmeticBinaryOp, arith::ShRSIOp, arith::ShRSIOp, arith::ShRUIOp>(
arithmeticBinaryOp, transformed.getOperand1(), maskShiftAmount(arithmeticBinaryOp->getLoc(), transformed.getOperand2(), rewriter), rewriter);

Expand Down
27 changes: 21 additions & 6 deletions tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5744,7 +5744,9 @@ class MLIRGenImpl
result = leftInt << rightInt.urem(leftInt.getBitWidth());
break;
case SyntaxKind::GreaterThanGreaterThanToken:
result = leftInt.ashr(rightInt.urem(leftInt.getBitWidth()));
result = resultType.isUnsignedInteger()
? leftInt.lshr(rightInt.urem(leftInt.getBitWidth()))
: leftInt.ashr(rightInt.urem(leftInt.getBitWidth()));
break;
case SyntaxKind::GreaterThanGreaterThanGreaterThanToken:
result = leftInt.lshr(rightInt.urem(leftInt.getBitWidth()));
Expand Down Expand Up @@ -6733,18 +6735,31 @@ class MLIRGenImpl
case SyntaxKind::AmpersandToken:
case SyntaxKind::BarToken:
case SyntaxKind::CaretToken:
// cast to int
if (leftExpressionValue.getType() != builder.getI32Type())
{
// cast to int: JavaScript's Int32 coercion, but a 64-bit integer left operand keeps its
// type (and signedness), the right one is cast to it. Cast to i32, `u64 << 40` shifted
// by 40 mod 32 and dropped the upper half.
mlir::Type intType = builder.getI32Type();
if (auto leftIntType = dyn_cast<mlir::IntegerType>(leftExpressionValue.getType()))
{
CAST(leftExpressionValue, location, builder.getI32Type(), leftExpressionValue, genContext);
if (leftIntType.getWidth() > 32)
{
intType = leftIntType;
}
}

if (rightExpressionValue.getType() != builder.getI32Type())
if (leftExpressionValue.getType() != intType)
{
CAST(rightExpressionValue, location, builder.getI32Type(), rightExpressionValue, genContext);
CAST(leftExpressionValue, location, intType, leftExpressionValue, genContext);
}

if (rightExpressionValue.getType() != intType)
{
CAST(rightExpressionValue, location, intType, rightExpressionValue, genContext);
}

break;
}
case SyntaxKind::SlashToken:
case SyntaxKind::PercentToken:
case SyntaxKind::AsteriskAsteriskToken:
Expand Down
3 changes: 3 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ tslang_add_test(NAME test-compile-01-enums COMMAND test-runner "${PROJECT_SOURCE
tslang_add_test(NAME test-compile-00-numbers COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00numbers.ts")
tslang_add_test(NAME test-compile-00-equals COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00equals.ts")
tslang_add_test(NAME test-compile-00-bool-arith-ops COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00bool_arith_ops.ts")
tslang_add_test(NAME test-compile-00-int64-shift-bitwise COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00int64_shift_bitwise.ts")
tslang_add_test(NAME test-compile-00-bool-const-array COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00bool_const_array.ts")
tslang_add_test(NAME test-compile-00-mixed-type-ops COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00mixed_type_ops.ts")
tslang_add_test(NAME test-compile-00-generator-manual-next COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00generator_manual_next.ts")
Expand Down Expand Up @@ -802,6 +803,7 @@ tslang_add_test(NAME test-jit-01-enums COMMAND test-runner -jit "${PROJECT_SOURC
tslang_add_test(NAME test-jit-00-numbers COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00numbers.ts")
tslang_add_test(NAME test-jit-00-equals COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00equals.ts")
tslang_add_test(NAME test-jit-00-bool-arith-ops COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00bool_arith_ops.ts")
tslang_add_test(NAME test-jit-00-int64-shift-bitwise COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00int64_shift_bitwise.ts")
tslang_add_test(NAME test-jit-00-bool-const-array COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00bool_const_array.ts")
tslang_add_test(NAME test-jit-00-mixed-type-ops COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00mixed_type_ops.ts")
tslang_add_test(NAME test-jit-00-generator-manual-next COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00generator_manual_next.ts")
Expand Down Expand Up @@ -1789,6 +1791,7 @@ set(TSLANG_CORPUS
00async_gc_threading.ts
00async_result_types.ts
00bool_arith_ops.ts
00int64_shift_bitwise.ts
00bool_const_array.ts
00break_continue_scope_exit.ts
00break_continue.ts
Expand Down
70 changes: 70 additions & 0 deletions tslang/test/tester/tests/00int64_shift_bitwise.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// regression test: shifts and bitwise operators on 64-bit integers keep the 64-bit width.
//
// bug found and fixed: `<<`, `>>`, `>>>`, `&`, `|` and `^` cast both operands to i32 (the
// JavaScript Int32 coercion of `number`) whatever their type, so `u64 << 40` shifted by
// 40 mod 32 = 8 and gave 256, and the upper half of a u64 was lost in `&`/`|`/`^`. Also `>>`
// of an unsigned operand shifted the sign bit in.

function main() {
let a: u64 = 1;
const twoTo40: u64 = 1099511627776;
const twoTo33: u64 = 8589934592;

const b = a << 40;
print(b);
assert(b == twoTo40);

let c: u64 = 1;
c = c << 33;
assert(c == twoTo33);

let g: u64 = 1;
g <<= 40;
assert(g == twoTo40);

let i: s64 = 1;
const twoTo40s: s64 = 1099511627776;
assert(i << 40 == twoTo40s);

// right shifts of a 64-bit value
const one: u64 = 1;
let f: u64 = 1099511627776;
const r1: u64 = 128;
assert(f >> 33 == r1);
assert(f >>> 33 == r1);

// an unsigned `>>` shifts zeros in
let h: u64 = 1;
h = h << 63;
assert(h >> 63 == one);
assert(h >>> 63 == one);

const three: u64 = 3;
let k: u64 = 3;
assert(k << 62 >> 62 == three);

// a signed `>>` keeps the sign, `>>>` doesn't
let s: s64 = -8;
const minusFour: s64 = -4;
const fifteen: s64 = 15;
assert(s >> 1 == minusFour);
assert(s >>> 60 == fifteen);

// bitwise operators keep the upper half
let m: u64 = 0xFFFFFFFFFF;
const upper: u64 = 0xFF00000000;
const zero: u64 = 0;
assert((m & 0xFF00000000) == upper);
assert((m | 1) == m);
assert((m ^ m) == zero);

// a `number` operand still shifts by its count mod 32, as in JavaScript
let n = 1;
assert(n << 33 == 2);
assert(1 << 33 == 2);
let x = -8;
assert(x >> 1 == -4);
assert(x >>> 28 == 15);

print("done.");
}
Loading