diff --git a/tslang/lib/TypeScript/MLIRGenImpl.h b/tslang/lib/TypeScript/MLIRGenImpl.h index c4b942158..18fbb081c 100644 --- a/tslang/lib/TypeScript/MLIRGenImpl.h +++ b/tslang/lib/TypeScript/MLIRGenImpl.h @@ -6630,10 +6630,12 @@ class MLIRGenImpl // 64-bit integer types - a rung of the promotion ladder - not a size or a pointer. The // getIndexType() beside them is a different thing entirely and IS target-dependent; it // sits at this rung because that is where it promotes, not because it is 64 bits wide. - getNumberType(), builder.getF64Type(), builder.getI64Type(), SInt(64), builder.getIndexType(), - builder.getF32Type(), SInt(32), builder.getI32Type(), - builder.getF16Type(), SInt(16), builder.getI16Type(), - SInt(8), builder.getI8Type() + // An unsigned type follows the signed and signless ones of its width. Missing from the + // ladder, `u64 * 2` found only the literal's s32 and multiplied in 32 bits. + getNumberType(), builder.getF64Type(), builder.getI64Type(), SInt(64), UInt(64), builder.getIndexType(), + builder.getF32Type(), SInt(32), builder.getI32Type(), UInt(32), + builder.getF16Type(), SInt(16), builder.getI16Type(), UInt(16), + SInt(8), builder.getI8Type(), UInt(8) }; } @@ -6689,6 +6691,11 @@ class MLIRGenImpl return mlir::IntegerType::get(builder.getContext(), width, mlir::IntegerType::Signed); } + mlir::Type UInt(int width) + { + return mlir::IntegerType::get(builder.getContext(), width, mlir::IntegerType::Unsigned); + } + // JS/TS `===`/`!==` never coerce: operands of clearly different primitive kinds // (boolean vs number, boolean vs string, string vs number) are simply unequal, // full stop. Everything downstream of this function (adjustTypesForBinaryOp's diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index 891d8b56e..29e28da30 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -272,6 +272,7 @@ tslang_add_test(NAME test-compile-00-numbers COMMAND test-runner "${PROJECT_SOUR 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-int64-mul-literal COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00int64_mul_literal.ts") tslang_add_test(NAME test-compile-00-literal-union-arith COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00literal_union_arith.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") @@ -807,6 +808,7 @@ tslang_add_test(NAME test-jit-00-numbers COMMAND test-runner -jit "${PROJECT_SOU 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-int64-mul-literal COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00int64_mul_literal.ts") tslang_add_test(NAME test-jit-00-literal-union-arith COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00literal_union_arith.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") @@ -1798,6 +1800,7 @@ set(TSLANG_CORPUS 00async_result_types.ts 00bool_arith_ops.ts 00int64_shift_bitwise.ts + 00int64_mul_literal.ts 00literal_union_arith.ts 00bool_const_array.ts 00break_continue_scope_exit.ts diff --git a/tslang/test/tester/tests/00int64_mul_literal.ts b/tslang/test/tester/tests/00int64_mul_literal.ts new file mode 100644 index 000000000..7a18f2ae5 --- /dev/null +++ b/tslang/test/tester/tests/00int64_mul_literal.ts @@ -0,0 +1,41 @@ +// regression test: arithmetic of a 64-bit unsigned integer and an integer literal is done in 64 bits. +// +// bug found and fixed: the numeric promotion ladder had no unsigned types, so `u64 * 2` found only +// the literal's s32 and multiplied in 32 bits: `2^32 * 2` was 0, and `d *= 256` wrapped to 0 after +// four bytes. + +function main() { + const twoTo32: u64 = 4294967296; + const twoTo33: u64 = 8589934592; + const twoTo32Plus2: u64 = 4294967298; + const twoTo32Minus2: u64 = 4294967294; + + let a: u64 = 4294967296; + assert(a * 2 == twoTo33); + assert(2 * a == twoTo33); + assert(a + 2 == twoTo32Plus2); + assert(a - 2 == twoTo32Minus2); + + let d: u64 = 16777216; + d *= 256; + assert(d == twoTo32); + + // the shape the bug was found in: a std::string's word built a byte at a time + let word: u64 = 0; + let shift: u64 = 1; + const bytes = [109, 97, 105, 110, 95, 118, 115]; + for (const b of bytes) { + const byte: u64 = b; + word += byte * shift; + shift *= 256; + } + + const mainVs: u64 = 32499774568096109; + assert(word == mainVs); + + const k: u64 = 256; + let e: u64 = 16777216; + assert(e * k == twoTo32); + + print("done."); +}