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
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ constexpr const auto *pointerTypeInfoName = "_ZTVN10__cxxabiv119__pointer_type_i
// or a typed catch of the class) emits the same thunk, so the linkonce_odr copies agree.
constexpr const auto *boxThunkPrefix = ".eh.copy.box.";
constexpr int boxThunkField = 4;
// Only tslang's own __pointer_type_info has that field, so it says so in __flags - a bit
// libstdc++ leaves alone (it defines the low qualifier bits only, and compares a throw's flags
// with a catch's, which are emitted here alike). Any other pointer exception - a C++
// `throw "text"` (`_ZTIPKc`), a pointer to a C++ class, a type_info from before the field -
// ends after the pointee, and reading the field there called whatever came next.
constexpr int flagsField = 2;
constexpr int boxThunkFlag = 0x40000000;
} // namespace ClassType

} // namespace linux
Expand Down
6 changes: 4 additions & 2 deletions tslang/include/TypeScript/MLIRLogic/MLIRRTTIHelperVCLinux.h
Original file line number Diff line number Diff line change
Expand Up @@ -602,8 +602,10 @@ class MLIRRTTIHelperVCLinux

if (ti == TypeInfo::Pointer_TypeInfo)
{
auto itemValueI32 = rewriter.create<mlir_ts::ConstantOp>(loc, mth.getI32Type(), mth.getI32AttrValue(0));
setStructValue(loc, structVal, itemValueI32, 2);
// no qualifiers; the flag says the box thunk field below is there
auto itemValueI32 = rewriter.create<mlir_ts::ConstantOp>(loc, mth.getI32Type(),
mth.getI32AttrValue(linux::ClassType::boxThunkFlag));
setStructValue(loc, structVal, itemValueI32, linux::ClassType::flagsField);

// add base class name
auto itemValue4 = rewriter.create<mlir_ts::AddressOfOp>(
Expand Down
50 changes: 33 additions & 17 deletions tslang/lib/TypeScript/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5174,26 +5174,42 @@ struct SaveCatchVarOpLowering : public TsLlvmPattern<mlir_ts::SaveCatchVarOp>
[&](OpBuilder &, Location) -> mlir::Value {
auto pointerTypeInfoType = LLVM::LLVMStructType::getLiteral(
ctx, {ptrTy, ptrTy, th.getI32Type(), ptrTy, ptrTy}, false);
auto thunkAddress = rewriter.create<LLVM::GEPOp>(
// only a type_info whose flags say so has the thunk field - see boxThunkFlag;
// the field is not read for any other, since it may lie past the object's end
auto flagsAddress = rewriter.create<LLVM::GEPOp>(
loc, ptrTy, pointerTypeInfoType, typeInfo,
ArrayRef<LLVM::GEPArg>{0, ::typescript::linux::ClassType::boxThunkField});
auto thunk = rewriter.create<LLVM::LoadOp>(loc, ptrTy, thunkAddress);
auto hasThunk = rewriter.create<LLVM::ICmpOp>(loc, LLVM::ICmpPredicate::ne, thunk,
rewriter.create<LLVM::ZeroOp>(loc, ptrTy));
ArrayRef<LLVM::GEPArg>{0, ::typescript::linux::ClassType::flagsField});
auto flags = rewriter.create<LLVM::LoadOp>(loc, th.getI32Type(), flagsAddress);
auto flagBit = rewriter.create<LLVM::ConstantOp>(
loc, th.getI32Type(), rewriter.getI32IntegerAttr(::typescript::linux::ClassType::boxThunkFlag));
auto flagged = rewriter.create<LLVM::ICmpOp>(
loc, LLVM::ICmpPredicate::ne, rewriter.create<LLVM::AndOp>(loc, flags, flagBit),
rewriter.create<LLVM::ConstantOp>(loc, th.getI32Type(), rewriter.getI32IntegerAttr(0)));
return clh.conditionalExpressionLowering(
loc, ptrTy, hasThunk,
loc, ptrTy, flagged,
[&](OpBuilder &, Location) -> mlir::Value {
auto anyType = mlir_ts::AnyType::get(ctx);
auto boxSlot = rewriter.create<mlir_ts::VariableOp>(
loc, mlir_ts::RefType::get(anyType), mlir::Value(), rewriter.getBoolAttr(false),
rewriter.getIndexAttr(0));
auto boxSlotPtr = rewriter.create<mlir_ts::DialectCastOp>(loc, ptrTy, boxSlot);
// void thunk(ref<any> dest, ref<class> src); exceptionInfo is the
// address of the thrown pointer
auto thunkType = LLVM::LLVMFunctionType::get(th.getVoidType(), {ptrTy, ptrTy});
rewriter.create<LLVM::CallOp>(loc, thunkType, ValueRange{thunk, boxSlotPtr, exceptionInfo});
auto boxed = rewriter.create<mlir_ts::LoadOp>(loc, anyType, boxSlot);
return rewriter.create<mlir_ts::DialectCastOp>(loc, ptrTy, boxed);
auto thunkAddress = rewriter.create<LLVM::GEPOp>(
loc, ptrTy, pointerTypeInfoType, typeInfo,
ArrayRef<LLVM::GEPArg>{0, ::typescript::linux::ClassType::boxThunkField});
auto thunk = rewriter.create<LLVM::LoadOp>(loc, ptrTy, thunkAddress);
auto hasThunk = rewriter.create<LLVM::ICmpOp>(loc, LLVM::ICmpPredicate::ne, thunk,
rewriter.create<LLVM::ZeroOp>(loc, ptrTy));
return clh.conditionalExpressionLowering(
loc, ptrTy, hasThunk,
[&](OpBuilder &, Location) -> mlir::Value {
auto anyType = mlir_ts::AnyType::get(ctx);
auto boxSlot = rewriter.create<mlir_ts::VariableOp>(
loc, mlir_ts::RefType::get(anyType), mlir::Value(), rewriter.getBoolAttr(false),
rewriter.getIndexAttr(0));
auto boxSlotPtr = rewriter.create<mlir_ts::DialectCastOp>(loc, ptrTy, boxSlot);
// void thunk(ref<any> dest, ref<class> src); exceptionInfo is the
// address of the thrown pointer
auto thunkType = LLVM::LLVMFunctionType::get(th.getVoidType(), {ptrTy, ptrTy});
rewriter.create<LLVM::CallOp>(loc, thunkType, ValueRange{thunk, boxSlotPtr, exceptionInfo});
auto boxed = rewriter.create<mlir_ts::LoadOp>(loc, anyType, boxSlot);
return rewriter.create<mlir_ts::DialectCastOp>(loc, ptrTy, boxed);
},
[&](OpBuilder &, Location) { return boxThrown(mlir_ts::OpaqueType::get(ctx)); });
},
[&](OpBuilder &, Location) { return boxThrown(mlir_ts::OpaqueType::get(ctx)); });
},
Expand Down
16 changes: 16 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2459,3 +2459,19 @@ if (TSLANG_TEST_X86)
message(STATUS "x86 twins: ${x86_twin_count} registered (${x86_excluded_count} excluded), "
"${x86_jit_skipped_count} -jit registrations without a twin")
endif()

# An untyped catch of a C++ exception tslang did not throw (Itanium only: the box thunk field of
# a __pointer_type_info is a Linux mechanism). foreign_throw.cpp throws a `const char*`, a
# pointer to a C++ struct and an int; the catch must box each without calling into the type_info.
if (NOT WIN32)
add_library(tslang_foreign_throw SHARED foreign-throw/foreign_throw.cpp)
add_dependencies(tslang_foreign_throw TypeScriptRuntime)
add_test(NAME test-jit-foreign-throw-catch
COMMAND $<TARGET_FILE:tslang> --emit=jit --no-default-lib
--shared-libs=$<TARGET_FILE:TypeScriptRuntime>
--shared-libs=$<TARGET_FILE:tslang_foreign_throw>
"${PROJECT_SOURCE_DIR}/test/tester/foreign-throw/catch.ts")
set_tests_properties(test-jit-foreign-throw-catch
PROPERTIES PASS_REGULAR_EXPRESSION "done\\."
FAIL_REGULAR_EXPRESSION "Stack dump|Segmentation|assertion failed")
endif()
44 changes: 44 additions & 0 deletions tslang/test/tester/foreign-throw/catch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// An untyped catch of a C++ exception tslang did not throw. A pointer exception's type_info is a
// __pointer_type_info, as a thrown tslang class's is, and the catch called the box thunk field
// that only tslang's has - past the end of libstdc++'s, so a `throw "boom"` from C++ made a
// wild call. Only a type_info flagged as tslang's is asked for its thunk now.
declare function foreign_throw_cstr(): void;
declare function foreign_throw_ptr(): void;
declare function foreign_throw_int(): void;

class Own {
constructor(public n: number) {}
}

function main() {
let caught = 0;

try {
foreign_throw_cstr();
} catch (e) {
caught++;
}

try {
foreign_throw_ptr();
} catch (e) {
caught++;
}

try {
foreign_throw_int();
} catch (e) {
caught++;
}

// a tslang class still goes through its box thunk
try {
throw new Own(5);
} catch (e) {
caught++;
assert(e instanceof Own, "own class boxed as itself");
}

assert(caught == 4, "every exception caught");
print("done.");
}
22 changes: 22 additions & 0 deletions tslang/test/tester/foreign-throw/foreign_throw.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// C++ exceptions that tslang never threw, for foreign-throw/catch.ts
struct Foreign
{
int x;
};

static Foreign foreign{42};

extern "C" void foreign_throw_cstr()
{
throw "boom";
}

extern "C" void foreign_throw_ptr()
{
throw &foreign;
}

extern "C" void foreign_throw_int()
{
throw 7;
}
Loading