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 @@ -4036,6 +4036,13 @@ struct ArithmeticBinaryOpLowering : public TsLlvmPattern<mlir_ts::ArithmeticBina
transformed.getOperand2(), rewriter);

case SyntaxKind::PercentToken:
// the lowered operand is signless: an unsigned one (`u64 % u64`) takes the unsigned remainder
if (arithmeticBinaryOp.getOperand1().getType().isUnsignedInteger())
{
return BinOp<mlir_ts::ArithmeticBinaryOp, arith::RemUIOp, arith::RemFOp>(arithmeticBinaryOp, transformed.getOperand1(),
transformed.getOperand2(), rewriter);
}

return BinOp<mlir_ts::ArithmeticBinaryOp, arith::RemSIOp, arith::RemFOp, arith::RemUIOp>(arithmeticBinaryOp, transformed.getOperand1(),
transformed.getOperand2(), rewriter);

Expand Down
26 changes: 25 additions & 1 deletion tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -6798,8 +6798,32 @@ class MLIRGenImpl

break;
}
case SyntaxKind::SlashToken:
case SyntaxKind::PercentToken:
{
// `%` of two integers, one of them 64-bit, is done in that 64-bit type: through a
// number (f64) it was wrong once the value passed 2^53. Narrower integers keep the
// number path, which is exact for them.
auto leftIntType = dyn_cast<mlir::IntegerType>(leftExpressionValue.getType());
auto rightIntType = dyn_cast<mlir::IntegerType>(rightExpressionValue.getType());
if (leftIntType && rightIntType && (leftIntType.getWidth() > 32 || rightIntType.getWidth() > 32))
{
mlir::Type intType = leftIntType.getWidth() >= rightIntType.getWidth() ? leftIntType : rightIntType;
if (leftExpressionValue.getType() != intType)
{
CAST(leftExpressionValue, location, intType, leftExpressionValue, genContext);
}

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

break;
}

[[fallthrough]];
}
case SyntaxKind::SlashToken:
case SyntaxKind::AsteriskAsteriskToken:

// TODO: should it be int type especially PercentToken?
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 @@ -461,6 +461,7 @@ tslang_add_test(NAME test-compile-00-throw-number-untyped-catch COMMAND test-run
tslang_add_test(NAME test-compile-00-throw-in-block-debug-info COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00throw_in_block_debug_info.ts")
tslang_add_test(NAME test-compile-00-catch-untyped-value COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00catch_untyped_value.ts")
tslang_add_test(NAME test-compile-00-throw-bool-bigint COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00throw_bool_bigint.ts")
tslang_add_test(NAME test-compile-00-int64-remainder COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00int64_remainder.ts")
tslang_add_test(NAME test-compile-00-throw-f32 COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00throw_f32.ts")
tslang_add_test(NAME test-compile-00-throw-u32 COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00throw_u32.ts")
tslang_add_test(NAME test-compile-00-cast-to-f32 COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00cast_to_f32.ts")
Expand Down Expand Up @@ -992,6 +993,7 @@ tslang_add_test(NAME test-jit-00-throw-number-untyped-catch COMMAND test-runner
tslang_add_test(NAME test-jit-00-throw-in-block-debug-info COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00throw_in_block_debug_info.ts")
tslang_add_test(NAME test-jit-00-catch-untyped-value COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00catch_untyped_value.ts")
tslang_add_test(NAME test-jit-00-throw-bool-bigint COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00throw_bool_bigint.ts")
tslang_add_test(NAME test-jit-00-int64-remainder COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00int64_remainder.ts")
tslang_add_test(NAME test-jit-00-throw-f32 COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00throw_f32.ts")
tslang_add_test(NAME test-jit-00-throw-u32 COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00throw_u32.ts")
tslang_add_test(NAME test-jit-00-cast-to-f32 COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00cast_to_f32.ts")
Expand Down Expand Up @@ -2048,6 +2050,7 @@ set(TSLANG_CORPUS
00symbol.ts
00throw_in_catch.ts
00throw_bool_bigint.ts
00int64_remainder.ts
00throw_f32.ts
00throw_u32.ts
00throw_in_block_debug_info.ts
Expand Down
48 changes: 48 additions & 0 deletions tslang/test/tester/tests/00int64_remainder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// regression test: `%` of 64-bit integers is an integer remainder.
//
// bug found and fixed: `%` cast both operands to number (f64), so a u64 remainder was wrong once the
// value passed 2^53 (the low byte of "main_vs\0" read 108 instead of 109). An unsigned operand
// also has to take the unsigned remainder.

function main() {
const BYTE: u64 = 256;
let w: u64 = 32499774568096109; // the bytes of "main_vs\0", little-endian
const m: u64 = 109;
assert(w % BYTE == m);
assert(w % 256 == m);

let r: u64 = 32499774568096109;
r %= 256;
assert(r == m);

// read every byte back
const expected = [109, 97, 105, 110, 95, 118, 115, 0];
let word: u64 = 32499774568096109;
for (const e of expected) {
const byte: u64 = e;
assert(word % BYTE == byte);
word = word >> 8;
}

// all 64 bits set: unsigned, not -1
let max: u64 = 18446744073709551615;
const five: u64 = 5;
const one: u64 = 1;
assert(max % 10 == five);
assert(max % 7 == one);

// signed: the remainder takes the dividend's sign
let s: s64 = -32499774568096109;
const minus109: s64 = -109;
const minus340597691: s64 = -340597691;
assert(s % 256 == minus109);
assert(s % 1000000007 == minus340597691);

// narrower integers keep the number path
let i: s32 = 1000;
assert(i % 7 == 6);
let n: number = 7.5;
assert(n % 2 == 1.5);

print("done.");
}
Loading