diff --git a/tslang/include/TypeScript/LowerToLLVM/LLVMCodeHelper.h b/tslang/include/TypeScript/LowerToLLVM/LLVMCodeHelper.h index 520bc611a..04b6a0a4a 100644 --- a/tslang/include/TypeScript/LowerToLLVM/LLVMCodeHelper.h +++ b/tslang/include/TypeScript/LowerToLLVM/LLVMCodeHelper.h @@ -575,8 +575,15 @@ class LLVMCodeHelper : public LLVMCodeHelperBase DenseElementsAttr denseAttr; if (isDense) { - auto vectorType = mlir::VectorType::get({static_cast(value.size())}, llvmElementType); - dataType = vectorType; + // A vector of elements narrower than a byte (a boolean's i1) is packed into bits, but + // array elements are read one byte apart: `[false, true]` read back as garbage. Such + // data is an array, laid out a byte per element, like the elements it is copied to. + auto shapeSize = static_cast(value.size()); + auto packedIntoBits = llvmElementType.isInteger() && llvmElementType.getIntOrFloatBitWidth() % 8 != 0; + mlir::ShapedType shapedType = packedIntoBits + ? (mlir::ShapedType)mlir::RankedTensorType::get({shapeSize}, llvmElementType) + : (mlir::ShapedType)mlir::VectorType::get({shapeSize}, llvmElementType); + dataType = packedIntoBits ? arrayType : (mlir::Type)shapedType; if (llvmElementType.isIntOrIndex()) { @@ -585,7 +592,7 @@ class LLVMCodeHelper : public LLVMCodeHelperBase values.push_back(cast(value_).getValue()); }); - denseAttr = DenseElementsAttr::get(vectorType, values); + denseAttr = DenseElementsAttr::get(shapedType, values); } else { @@ -594,7 +601,7 @@ class LLVMCodeHelper : public LLVMCodeHelperBase values.push_back(cast(value_).getValue()); }); - denseAttr = DenseElementsAttr::get(vectorType, values); + denseAttr = DenseElementsAttr::get(shapedType, values); } } diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index 7a9a4d06e..9e3c66841 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -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-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") tslang_add_test(NAME test-compile-00-generator-manual-next-2 COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00generator_manual_next2.ts") @@ -791,6 +792,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-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") tslang_add_test(NAME test-jit-00-generator-manual-next-2 COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00generator_manual_next2.ts") @@ -1745,6 +1747,7 @@ set(TSLANG_CORPUS 00async_gc_threading.ts 00async_result_types.ts 00bool_arith_ops.ts + 00bool_const_array.ts 00break_continue_scope_exit.ts 00break_continue.ts 00capture_in_new_arguments.ts diff --git a/tslang/test/tester/tests/00bool_const_array.ts b/tslang/test/tester/tests/00bool_const_array.ts new file mode 100644 index 000000000..c8981470f --- /dev/null +++ b/tslang/test/tester/tests/00bool_const_array.ts @@ -0,0 +1,53 @@ +// The elements of a boolean array literal read back wrong: the constant data was an LLVM vector of +// i1, which packs its elements into bits, while elements are read one byte apart. So `if (F[i])` +// was never taken, a count of the true elements came out as 10 for four elements, and F[1] was +// false. + +const FLAGS = [false, true, false, true]; + +function countTrue(flags: boolean[]) { + let n = 0; + for (let i = 0; i < flags.length; i++) { + if (flags[i]) { + n++; + } + } + + return n; +} + +function locals() { + let a = [true, false, true]; + assert(a[0], "a[0]"); + assert(!a[1], "a[1]"); + assert(a[2], "a[2]"); + + let n = 0; + for (const x of a) { + if (x) n++; + } + + assert(n == 2, "for-of count"); +} + +function many() { + // more elements than fit in a byte of bits + const m = [true, false, false, true, true, false, true, false, true, true, false, true]; + assert(countTrue(m) == 7, "twelve elements"); + assert(m[11], "m[11]"); + assert(!m[10], "m[10]"); +} + +function main() { + assert(FLAGS.length == 4, "length"); + assert(countTrue(FLAGS) == 2, "count"); + const b: boolean = FLAGS[1]; + assert(b, "FLAGS[1]"); + assert(!FLAGS[0], "FLAGS[0]"); + assert(FLAGS[3], "FLAGS[3]"); + + locals(); + many(); + + print("done."); +}