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
4 changes: 0 additions & 4 deletions tslang/include/TypeScript/Defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,6 @@
#define DLL_IMPORT "dllimport"
#define DLL_NAME "dllname"

// module attribute: the class types the module throws, for binding an untyped catch on the
// Itanium path (linux::SaveCatchVarOpLowering)
#define THROWN_CLASSES_ATTR_NAME "ts.thrown_classes"

#if __LP64__
#define TRAMPOLINE_SIZE 48
#else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ constexpr const auto *typeName = "";
constexpr const auto *classTypeInfoName = "_ZTVN10__cxxabiv117__class_type_infoE";
constexpr const auto *singleInheritanceClassTypeInfoName = "_ZTVN10__cxxabiv120__si_class_type_infoE";
constexpr const auto *pointerTypeInfoName = "_ZTVN10__cxxabiv119__pointer_type_infoE";
// A class is thrown as a pointer, and its `_ZTIP<n><name>` - a __pointer_type_info - has one
// more field after the four libstdc++ reads: the address of a thunk that boxes the thrown
// instance into an `any` (the class's own descriptor included). An untyped catch calls it for
// a class it knows nothing about, e.g. one only another module throws - the Itanium
// counterpart of the Windows `.PEAX` copy thunk. Every module emitting the type_info (a throw
// 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;
} // namespace ClassType

} // namespace linux
Expand Down
4 changes: 3 additions & 1 deletion tslang/include/TypeScript/MLIRLogic/MLIRRTTIHelperVC.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ class MLIRRTTIHelperVC
{
}

// Windows only: the Itanium path has no copy functions to point at a thunk
// Windows: the copy thunks of the CatchableType records; Itanium: the box thunk of a class's
// pointer type_info - see linux::ClassType::boxThunkPrefix
void setCopyThunkBuilder(MLIRRTTIHelperVCWin32::CopyThunkBuilder builder)
{
rttiWin.setCopyThunkBuilder(builder);
rttiLinux.setCopyThunkBuilder(builder);
}

bool setRTTIForType(mlir::Location loc, mlir::Type type, std::function<ClassInfo::TypePtr(StringRef fullClassName)> resolveClassInfo)
Expand Down
58 changes: 50 additions & 8 deletions tslang/include/TypeScript/MLIRLogic/MLIRRTTIHelperVCLinux.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,29 @@ class MLIRRTTIHelperVCLinux

SmallVector<TypeNames> types;

public:
// Builds the thunk `name`: void name(ref<target> dest, ref<source> src) - the same builder as
// MLIRRTTIHelperVCWin32's. Here it makes a class's box thunk, see linux::ClassType::boxThunkPrefix.
using CopyThunkBuilder = std::function<mlir::LogicalResult(mlir::Location loc, StringRef name, mlir::Type source,
mlir::Type target)>;

private:
// the class a class type_info is emitted for: the box thunk's source
mlir::Type classType;
CopyThunkBuilder copyThunkBuilder;

public:
MLIRRTTIHelperVCLinux(mlir::OpBuilder &rewriter, mlir::ModuleOp &parentModule, CompileOptions& compileOptions)
: rewriter(rewriter), parentModule(parentModule), mth(rewriter.getContext(), compileOptions), mlh(), mcl(rewriter, compileOptions)
{
// setI32AsCatchType();
}

void setCopyThunkBuilder(CopyThunkBuilder builder)
{
copyThunkBuilder = builder;
}

void setF32AsCatchType()
{
types.push_back({linux::F32Type::typeName, TypeInfo::Value, -1});
Expand Down Expand Up @@ -191,6 +207,7 @@ class MLIRRTTIHelperVCLinux
classInfo->getBasesWithRoot(classAndBases);

setClassTypeAsCatchType(classAndBases);
this->classType = classInfo->classType;
})
.Case<mlir_ts::AnyType>([&](auto anyType) {
// This overload only declares the RTTI globals a catch may reference. An
Expand All @@ -205,6 +222,9 @@ class MLIRRTTIHelperVCLinux
setI64AsCatchType();
setStringTypeAsCatchType();
setI8PtrAsCatchType();
// what tells a thrown class apart from the rest: its type_info is a
// __pointer_type_info, which carries the box thunk
types.push_back({linux::ClassType::pointerTypeInfoName, TypeInfo::Value, -1});
})
.Default([&](auto type) {
LLVM_DEBUG(llvm::dbgs() << "...unsupported throw/catch type: " << type << "\n";);
Expand Down Expand Up @@ -428,7 +448,8 @@ class MLIRRTTIHelperVCLinux
case TypeInfo::SingleInheritance_ClassTypeInfo:
return mth.getTupleType({mth.getOpaqueType(), mth.getOpaqueType(), mth.getOpaqueType()});
case TypeInfo::Pointer_TypeInfo:
return mth.getTupleType({mth.getOpaqueType(), mth.getOpaqueType(), mth.getI32Type(), mth.getOpaqueType()});
// the last field is ours, not libstdc++'s - see linux::ClassType::boxThunkPrefix
return mth.getTupleType({mth.getOpaqueType(), mth.getOpaqueType(), mth.getI32Type(), mth.getOpaqueType(), mth.getOpaqueType()});
default:
return mth.getTupleType({mth.getOpaqueType(), mth.getOpaqueType()});
}
Expand Down Expand Up @@ -490,13 +511,7 @@ class MLIRRTTIHelperVCLinux
mlir::Type tiType;
if (classType)
{
SmallVector<mlir::Type> tiTypes;
tiTypes.push_back(mth.getOpaqueType());
tiTypes.push_back(mth.getOpaqueType());
tiTypes.push_back(mth.getI32Type());
tiTypes.push_back(mth.getOpaqueType());

tiType = mth.getTupleType(tiTypes);
tiType = getTIType(TypeInfo::Pointer_TypeInfo);
}
else
{
Expand All @@ -508,6 +523,31 @@ class MLIRRTTIHelperVCLinux
return throwInfoPtr;
}

// The box thunk field of a class's pointer type_info: the thunk's address, or null when
// nothing here can build it - an untyped catch then boxes the instance as a plain object.
mlir::Value boxThunkValue(mlir::Location loc, StringRef label)
{
auto thunkName = std::string(linux::ClassType::boxThunkPrefix) + label.str();
if (!parentModule.lookupSymbol(thunkName))
{
auto built = false;
if (copyThunkBuilder && classType)
{
mlir::OpBuilder::InsertionGuard guard(rewriter);
built = mlir::succeeded(copyThunkBuilder(loc, thunkName, classType, mlir_ts::AnyType::get(rewriter.getContext())));
}

if (!built)
{
auto nullValue = rewriter.create<mlir_ts::NullOp>(loc, mth.getNullType());
return rewriter.create<mlir_ts::CastOp>(loc, mth.getOpaqueType(), nullValue);
}
}

return rewriter.create<mlir_ts::SymbolRefOp>(loc, mth.getOpaqueType(),
mlir::FlatSymbolRefAttr::get(rewriter.getContext(), thunkName));
}

mlir::LogicalResult typeInfoRef(mlir::Location loc, StringRef className, TypeInfo ti, StringRef baseName = "",
TypeInfo baseti = TypeInfo::Value)
{
Expand Down Expand Up @@ -551,6 +591,8 @@ class MLIRRTTIHelperVCLinux

auto castValue4 = rewriter.create<mlir_ts::CastOp>(loc, mth.getOpaqueType(), itemValue4);
setStructValue(loc, structVal, castValue4, 3);

setStructValue(loc, structVal, boxThunkValue(loc, labelValue(className, ti)), linux::ClassType::boxThunkField);
}
else if (ti == TypeInfo::SingleInheritance_ClassTypeInfo)
{
Expand Down
65 changes: 49 additions & 16 deletions tslang/lib/TypeScript/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1579,6 +1579,12 @@ struct FuncOpLowering : public TsLlvmPattern<mlir_ts::FuncOp>
continue;
}

// module-local: a private symbol alone still comes out external
if (name == "internal_linkage") {
newFuncOp->setAttr("llvm.linkage", LLVM::LinkageAttr::get(getContext(), LLVM::linkage::Linkage::Internal));
continue;
}

auto addAttr =
std::find(skipAttrs.begin(), skipAttrs.end(), name) == skipAttrs.end();
if (addAttr && name == DLL_NAME)
Expand Down Expand Up @@ -5138,26 +5144,53 @@ struct SaveCatchVarOpLowering : public TsLlvmPattern<mlir_ts::SaveCatchVarOp>
{::typescript::linux::I8PtrType::typeName, mlir_ts::AnyType::get(ctx)}};

// A class needs its own type descriptor in the box - unboxing checks for a class tag,
// and the descriptor carries the class's release routine - so only the classes this
// module throws can be recognised (MLIRGen lists them, see recordThrownClass). A class
// thrown from another module falls through to the plain-object box below.
if (auto thrownClasses = saveCatchVarOp->getParentOfType<mlir::ModuleOp>()->getAttrOfType<mlir::ArrayAttr>(
THROWN_CLASSES_ATTR_NAME))
{
for (auto thrownClass : thrownClasses.getAsValueRange<mlir::TypeAttr>())
{
auto classType = cast<mlir_ts::ClassType>(thrownClass);
auto name = classType.getName().getValue();
std::string typeInfoName = "_ZTIP" + std::to_string(name.size()) + name.str();
candidates.push_back({typeInfoName, classType});
}
}
// and the descriptor carries the class's release routine - and the class may be one this
// module has never heard of, thrown from another. So the thrower supplies the box: a
// class is thrown as a pointer, and its __pointer_type_info carries a thunk that boxes
// the instance (see linux::ClassType::boxThunkPrefix). Anything else - a foreign C++
// exception - is boxed as a plain object.
auto boxClass = [&]() -> mlir::Value {
auto pointerTypeInfoVtable = rewriter.create<LLVM::GEPOp>(
loc, ptrTy, ptrTy,
rewriter.create<LLVM::AddressOfOp>(loc, ptrTy, ::typescript::linux::ClassType::pointerTypeInfoName),
ArrayRef<LLVM::GEPArg>{2});
auto typeInfoVtable = rewriter.create<LLVM::LoadOp>(loc, ptrTy, typeInfo);
auto isPointer = rewriter.create<LLVM::ICmpOp>(loc, LLVM::ICmpPredicate::eq, typeInfoVtable, pointerTypeInfoVtable);
return clh.conditionalExpressionLowering(
loc, ptrTy, isPointer,
[&](OpBuilder &, Location) -> mlir::Value {
auto pointerTypeInfoType = LLVM::LLVMStructType::getLiteral(
ctx, {ptrTy, ptrTy, th.getI32Type(), ptrTy, ptrTy}, false);
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)); });
};

std::function<mlir::Value(size_t)> dispatch = [&](size_t index) -> mlir::Value {
if (index == candidates.size())
{
// not a type tslang throws from this module; box the pointer as a plain object
return boxThrown(mlir_ts::OpaqueType::get(ctx));
return boxClass();
}

auto [typeInfoName, type] = candidates[index];
Expand Down
54 changes: 41 additions & 13 deletions tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2999,7 +2999,6 @@ class MLIRGenImpl

void setCatchCopyThunkBuilder(MLIRRTTIHelperVC &rtti);

void recordThrownClass(mlir::Type thrownType);

ValueOrLogicalResult mlirGen(TypeAssertion typeAssertionAST, const GenContext &genContext);

Expand Down Expand Up @@ -3896,8 +3895,13 @@ class MLIRGenImpl
if (instanceOf->left == SyntaxKind::Identifier)
{
NodeFactory nf(NodeFactoryFlags::None);
return addSafeCastStatement(instanceOf->left, nf.createTypeReferenceNode(instanceOf->right),
false, elseSafeCase, genContext);
// a type reference names its type with an entity name: `N.C` has to become a
// qualified name - handed over as the property access it is, it crashed
if (auto typeName = instanceOfEntityName(nf, instanceOf->right))
{
return addSafeCastStatement(instanceOf->left, nf.createTypeReferenceNode(typeName),
false, elseSafeCase, genContext);
}
}
}
}
Expand All @@ -3920,6 +3924,30 @@ class MLIRGenImpl
return mlir::success();
}

// the right side of `x instanceof C` as the entity name of a type reference: `C`, or `N.C` as
// a qualified name; anything else (a call, an element access) names no type to narrow to
EntityName instanceOfEntityName(NodeFactory &nf, Expression expr)
{
if (expr == SyntaxKind::Identifier)
{
return expr;
}

if (expr == SyntaxKind::PropertyAccessExpression)
{
auto propertyAccess = expr.as<PropertyAccessExpression>();
if (propertyAccess->name == SyntaxKind::Identifier)
{
if (auto left = instanceOfEntityName(nf, propertyAccess->expression))
{
return nf.createQualifiedName(left, propertyAccess->name.as<Identifier>());
}
}
}

return undefined;
}

mlir::LogicalResult mlirGen(IfStatement ifStatementAST, const GenContext &genContext);

mlir::LogicalResult mlirGen(DoStatement doStatementAST, const GenContext &genContext);
Expand Down Expand Up @@ -4774,25 +4802,25 @@ class MLIRGenImpl
return mlir::Value();
}

// A class imported from a shared library (-shared) keeps its rtti behind a pointer
// resolved at load time, and the property access already reads through it - so the
// value is a string here too. This used to insist on a ref for such a class and fail,
// which dropped the call: `x instanceof C` did not compile and `<C>anyValue` always
// threw "Can't cast from any type".
auto rttiOfClassValue = V(resultRtti);
if (classInfo->isDynamicImport)
{
if (auto valueRefType = dyn_cast<mlir_ts::RefType>(rttiOfClassValue.getType()))
{
rttiOfClassValue = builder.create<mlir_ts::LoadOp>(location, valueRefType.getElementType(), rttiOfClassValue);
}
else
{
// isDynamicImport (@dllimport(path) with an argument) is itself an untested
// feature path with zero coverage anywhere in the test suite (see §4.10 of
// docs/not-implemented-audit.md) - this crash is nested one level deeper
// inside that already-untested path. Fail cleanly instead of crashing.
emitError(location, "unsupported RTTI value type for dynamic import");
return mlir::Value();
}
}

assert(rttiOfClassValue);
if (!isa<mlir_ts::StringType>(rttiOfClassValue.getType()))
{
emitError(location, "unsupported RTTI value type ") << to_print(rttiOfClassValue.getType());
return mlir::Value();
}

auto instanceOfFuncType = mlir_ts::FunctionType::get(
builder.getContext(), SmallVector<mlir::Type>{getOpaqueType(), getStringType()},
Expand Down
37 changes: 5 additions & 32 deletions tslang/lib/TypeScript/MLIRGenStatements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,11 @@ namespace mlirgen
// symbol table and wire into this thunk's exit.
auto funcOp = mlir_ts::FuncOp::create(location, name, funcType);
funcOp.setPrivate();
// internal: every module that throws or catches the type builds the same thunk, and a
// catch of a class another module throws failed to link (duplicate symbol). Not
// linkonce_odr in a comdat like the records that point at it: under the Win64 JIT a
// catch through such a thunk crashed (access violation).
funcOp->setAttr("internal_linkage", builder.getUnitAttr());

// its own context: the thunk is a function of its own, and has to be emitted even when
// the throw or catch that needs it is only being generated speculatively
Expand Down Expand Up @@ -1101,33 +1106,6 @@ namespace mlirgen
});
}

// An untyped catch on the Itanium path is a catch-all that finds out what it caught from the
// exception's type_info, and a class needs its own descriptor in the `any` box it binds - so
// the lowering needs to know which classes can arrive. See linux::SaveCatchVarOpLowering.
void MLIRGenImpl::recordThrownClass(mlir::Type thrownType)
{
auto classType = dyn_cast<mlir_ts::ClassType>(mth.stripLiteralType(thrownType));
if (!classType)
{
return;
}

SmallVector<mlir::Attribute> thrownClasses;
if (auto existing = theModule->getAttrOfType<mlir::ArrayAttr>(THROWN_CLASSES_ATTR_NAME))
{
thrownClasses.append(existing.begin(), existing.end());
}

auto classTypeAttr = mlir::TypeAttr::get(classType);
if (llvm::is_contained(thrownClasses, classTypeAttr))
{
return;
}

thrownClasses.push_back(classTypeAttr);
theModule->setAttr(THROWN_CLASSES_ATTR_NAME, builder.getArrayAttr(thrownClasses));
}

mlir::LogicalResult MLIRGenImpl::mlirGen(ThrowStatement throwStatementAST, const GenContext &genContext)
{
auto location = loc(throwStatementAST);
Expand All @@ -1149,11 +1127,6 @@ namespace mlirgen
emitError(location, "Not supported type in throw");
return mlir::failure();
}

if (!compileOptions.isWindows)
{
recordThrownClass(exception.getType());
}
}

return mlir::success();
Expand Down
Loading
Loading