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
6 changes: 6 additions & 0 deletions tslang/include/TypeScript/Defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,12 @@
// INTERFACE_VTABLE_HEADER_SLOTS.
#define INTERFACE_VTABLE_INSTANCEOF_SLOT 0
#define INTERFACE_VTABLE_HEADER_SLOTS 1
// on the global of an interface's null vtable (mlirGenNullInterfaceVTable): a null interface is seen
// through it, and nothing implements the interface by it
#define NULL_INTERFACE_VTABLE_ATTR_NAME "__null_interface_vtable"
// on the C++ runtime's type_info vtable a `throw`'s exception tables name (`??_7type_info@@6B@`,
// `_ZTVN10__cxxabiv1...`): a foreign global, but no program value is ever read out of it or stored into it
#define EH_TYPE_INFO_ATTR_NAME "__eh_type_info"
// on a ts.Cast from one interface to another whose members are not where the source has them (an
// interface that does not extend the source): for each slot of the target's vtable, the slot of the
// source's that holds it, or -1 for an optional member the source lacks. The cast then gives the
Expand Down
42 changes: 39 additions & 3 deletions tslang/include/TypeScript/LowerToLLVM/CastLogicHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,32 @@ class CastLogicHelper
external = true;
}

// The address of the vtable a null interface is seen through: the one MLIRGen named on the cast
// (mlirGenNullInterfaceVTableCast), whose methods throw; none when it named none, as for a cast
// the lowering makes itself.
mlir::Value nullInterfaceVTableAddress()
{
auto vtableRef = op ? op->getAttrOfType<mlir::FlatSymbolRefAttr>(NULL_INTERFACE_VTABLE_ATTR_NAME) : mlir::FlatSymbolRefAttr();
if (!vtableRef)
{
return mlir::Value();
}

return rewriter.create<LLVM::AddressOfOp>(loc, th.getPtrType(), vtableRef.getValue());
}

// the same, as an operand of an interface made here, or `noVTable`
mlir::Value nullInterfaceVTable(mlir::Value noVTable)
{
auto vtableAddress = nullInterfaceVTableAddress();
if (!vtableAddress)
{
return noVTable;
}

return rewriter.create<mlir_ts::DialectCastOp>(loc, mlir_ts::OpaqueType::get(rewriter.getContext()), vtableAddress);
}

mlir::Value cast(mlir::Value in, mlir::Type inType, mlir::Type resType)
{
auto inLLVMType = tch.convertType(inType);
Expand Down Expand Up @@ -332,7 +358,16 @@ class CastLogicHelper
if (auto ifaceType = dyn_cast<mlir_ts::InterfaceType>(resType))
{
// create null interface
return rewriter.create<mlir_ts::NewInterfaceOp>(loc, ifaceType, in, in);
return rewriter.create<mlir_ts::NewInterfaceOp>(loc, ifaceType, in, nullInterfaceVTable(in));
}

// the vtable a null class object is seen through as an interface (castToInterfaceSpecialCases)
if (isa<mlir_ts::OpaqueType>(resType))
{
if (auto vtableAddress = nullInterfaceVTableAddress())
{
return vtableAddress;
}
}

if (auto resHybridFunc = dyn_cast<mlir_ts::HybridFunctionType>(resType))
Expand Down Expand Up @@ -424,10 +459,11 @@ class CastLogicHelper
return rewriter.create<mlir_ts::ConstantOp>(loc, llvmBoolType, rewriter.getBoolAttr(true));
}

// set when its `this` is: a null interface may be seen through a vtable (UnaryBinLogicalOrHelper)
if (auto ifaceType = dyn_cast<mlir_ts::InterfaceType>(inType))
{
auto ptrValue =
rewriter.create<mlir_ts::ExtractInterfaceVTableOp>(loc, mlir_ts::OpaqueType::get(rewriter.getContext()), in);
rewriter.create<mlir_ts::ExtractInterfaceThisOp>(loc, mlir_ts::OpaqueType::get(rewriter.getContext()), in);
auto inLLVMType = tch.convertType(ptrValue.getType());
auto llvmBoolType = tch.convertType(boolType);
return castLLVMTypes(ptrValue, inLLVMType, boolType, llvmBoolType);
Expand Down Expand Up @@ -556,7 +592,7 @@ class CastLogicHelper
{
// create null interface
auto nullVal = rewriter.create<mlir_ts::NullOp>(loc, mlir_ts::NullType::get(ifaceType.getContext()));
return rewriter.create<mlir_ts::NewInterfaceOp>(loc, ifaceType, nullVal, nullVal);
return rewriter.create<mlir_ts::NewInterfaceOp>(loc, ifaceType, nullVal, nullInterfaceVTable(nullVal));
}

if (auto classType = dyn_cast<mlir_ts::ClassType>(resType))
Expand Down
20 changes: 11 additions & 9 deletions tslang/include/TypeScript/LowerToLLVM/UnaryBinLogicalOrHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,24 +270,26 @@ mlir::Value LogicOp(Operation *binOp, SyntaxKind op, mlir::Value left, mlir::Typ
}
else if (isa<mlir_ts::InterfaceType>(leftType))
{
// TODO, extract interface VTable to compare
auto leftVtableValue =
// an interface is the object it is made over: compared by its `this`. A null interface's
// `this` is null whatever vtable it is seen through (the interface's null vtable, whose
// methods throw - mlirGenNullInterfaceVTable - or none at all, zeroed storage)
auto leftThisValue =
left.getDefiningOp<mlir_ts::NullOp>() || left.getDefiningOp<LLVM::ZeroOp>() || matchPattern(left, m_Zero())
? left
: builder.create<mlir_ts::ExtractInterfaceVTableOp>(loc, mlir_ts::OpaqueType::get(leftType.getContext()), left);
auto rightVtableValue =
: builder.create<mlir_ts::ExtractInterfaceThisOp>(loc, mlir_ts::OpaqueType::get(leftType.getContext()), left);
auto rightThisValue =
right.getDefiningOp<mlir_ts::NullOp>() || right.getDefiningOp<LLVM::ZeroOp>() || matchPattern(right, m_Zero())
? right
: builder.create<mlir_ts::ExtractInterfaceVTableOp>(loc, mlir_ts::OpaqueType::get(rightType.getContext()), right);
: builder.create<mlir_ts::ExtractInterfaceThisOp>(loc, mlir_ts::OpaqueType::get(rightType.getContext()), right);

// excluded string
auto intPtrType = llvmtch.getIntPtrType(0);

mlir::Value leftVtableValueAsLLVMType = builder.create<mlir_ts::DialectCastOp>(loc, typeConverter.convertType(leftVtableValue.getType()), leftVtableValue);
mlir::Value rightVtableValueAsLLVMType = builder.create<mlir_ts::DialectCastOp>(loc, typeConverter.convertType(rightVtableValue.getType()), rightVtableValue);
mlir::Value leftThisValueAsLLVMType = builder.create<mlir_ts::DialectCastOp>(loc, typeConverter.convertType(leftThisValue.getType()), leftThisValue);
mlir::Value rightThisValueAsLLVMType = builder.create<mlir_ts::DialectCastOp>(loc, typeConverter.convertType(rightThisValue.getType()), rightThisValue);

mlir::Value leftPtrValue = builder.create<LLVM::PtrToIntOp>(loc, intPtrType, leftVtableValueAsLLVMType);
mlir::Value rightPtrValue = builder.create<LLVM::PtrToIntOp>(loc, intPtrType, rightVtableValueAsLLVMType);
mlir::Value leftPtrValue = builder.create<LLVM::PtrToIntOp>(loc, intPtrType, leftThisValueAsLLVMType);
mlir::Value rightPtrValue = builder.create<LLVM::PtrToIntOp>(loc, intPtrType, rightThisValueAsLLVMType);

auto value = builder.create<StdIOpTy>(loc, v1, leftPtrValue, rightPtrValue);
return value;
Expand Down
3 changes: 2 additions & 1 deletion tslang/include/TypeScript/MLIRLogic/MLIRRTTIHelperVCLinux.h
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,8 @@ class MLIRRTTIHelperVCLinux
return mlir::failure();
}

rewriter.create<mlir_ts::GlobalOp>(loc, mth.getOpaqueType(), true, name, LLVM::Linkage::External);
auto globalOp = rewriter.create<mlir_ts::GlobalOp>(loc, mth.getOpaqueType(), true, name, LLVM::Linkage::External);
globalOp->setAttr(EH_TYPE_INFO_ATTR_NAME, rewriter.getUnitAttr());
return mlir::success();
}

Expand Down
3 changes: 2 additions & 1 deletion tslang/include/TypeScript/MLIRLogic/MLIRRTTIHelperVCWin32.h
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,8 @@ class MLIRRTTIHelperVCWin32
return mlir::failure();
}

rewriter.create<mlir_ts::GlobalOp>(loc, mth.getOpaqueType(), true, name, LLVM::Linkage::External);
auto globalOp = rewriter.create<mlir_ts::GlobalOp>(loc, mth.getOpaqueType(), true, name, LLVM::Linkage::External);
globalOp->setAttr(EH_TYPE_INFO_ATTR_NAME, rewriter.getUnitAttr());
return mlir::success();
}

Expand Down
132 changes: 101 additions & 31 deletions tslang/lib/TypeScript/MLIRGenCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1011,46 +1011,57 @@ namespace mlirgen
{
if (auto interfaceType = dyn_cast<mlir_ts::InterfaceType>(type))
{
if (auto classType = dyn_cast<mlir_ts::ClassType>(valueType))
// null (or undefined) is an interface over a null `this`, seen through the interface's null
// vtable, whose methods throw (mlirGenNullInterfaceVTable). It stays the cast the ownership
// passes know holds nothing; the cast names the vtable, and the lowering puts it in.
if (isa<mlir_ts::NullType, mlir_ts::UndefinedType>(valueType))
{
auto result = mlirGenPropertyAccessExpression(location, value, VTABLE_NAME, genContext);
auto vtableAccess = V(result);

auto classInfo = getClassInfoByFullName(classType.getName().getValue());
assert(classInfo);

auto implementIndex = classInfo->getImplementIndex(interfaceType.getName().getValue());
if (implementIndex >= 0)
auto interfaceInfo = getInterfaceInfoByFullName(interfaceType.getName().getValue());
if (!interfaceInfo)
{
auto interfaceVirtTableIndex = classInfo->implements[implementIndex].virtualIndex;

assert(genContext.allowPartialResolve || interfaceVirtTableIndex >= 0);

auto interfaceVTablePtr = builder.create<mlir_ts::VTableOffsetRefOp>(
location, mth.getInterfaceVTableType(interfaceType), vtableAccess, interfaceVirtTableIndex);

auto newInterface = builder.create<mlir_ts::NewInterfaceOp>(
location, mlir::TypeRange{interfaceType}, value, interfaceVTablePtr);
return V(newInterface);
return std::nullopt;
}

// create interface vtable from current class
return mlirGenNullInterfaceVTableCast(location, interfaceType, interfaceInfo, genContext, value);
}

// A class object is a pointer that may be null (`C | null` is stored as `C`), and null
// stays null: an interface over a null `this`. The vtable may be read through the object,
// so it is chosen by a test for null: a null object is seen through the interface's null
// vtable, whose methods throw (mlirGenNullInterfaceVTable). The interface is built over
// the object outside the test, so that the ownership passes see it is the object (a view),
// not a value merged from branches.
if (auto classType = dyn_cast<mlir_ts::ClassType>(valueType))
{
auto interfaceInfo = getInterfaceInfoByFullName(interfaceType.getName().getValue());
assert(interfaceInfo);

if (auto createdInterfaceVTableForClass =
mlirGenCreateInterfaceVTableForClass(location, classInfo, interfaceInfo, genContext))
{
LLVM_DEBUG(llvm::dbgs() << "\n!!"
<< "@ created interface:" << V(createdInterfaceVTableForClass) << "\n";);
auto newInterface = builder.create<mlir_ts::NewInterfaceOp>(
location, mlir::TypeRange{interfaceType}, value, createdInterfaceVTableForClass);
auto nullValue = builder.create<mlir_ts::NullOp>(location, getNullType());
auto isNotNull = builder.create<mlir_ts::LogicalBinaryOp>(
location, getBooleanType(), builder.getI32IntegerAttr((int)SyntaxKind::ExclamationEqualsEqualsToken), value, nullValue);

auto vtableType = mth.getInterfaceVTableType(interfaceType);
MLIRCodeLogicHelper mclh(builder, location, compileOptions);
auto interfaceVTable = mclh.conditionalValue(isNotNull,
[&]() -> ValueOrLogicalResult {
auto classVTable = mlirGenClassInterfaceVTable(location, value, classType, interfaceType, genContext);
EXIT_IF_FAILED_OR_NO_VALUE(classVTable)
if (V(classVTable).getType() == vtableType)
{
return classVTable;
}

return V(newInterface);
}
return V(builder.create<mlir_ts::CastOp>(location, vtableType, V(classVTable)));
},
[&](mlir::Type) -> ValueOrLogicalResult {
// null, as the ownership passes know it, naming the null vtable the lowering puts in
return mlirGenNullInterfaceVTableCast(location, vtableType, interfaceInfo, genContext);
});
EXIT_IF_FAILED_OR_NO_VALUE(interfaceVTable)

emitError(location) << "type: " << classType.getName() << " missing interface: " << interfaceType.getName();
return mlir::failure();
auto newInterface = builder.create<mlir_ts::NewInterfaceOp>(
location, mlir::TypeRange{interfaceType}, value, V(interfaceVTable));
return V(newInterface);
}

// tuple to interface
Expand Down Expand Up @@ -1097,6 +1108,41 @@ namespace mlirgen
return std::nullopt;
}

ValueOrLogicalResult MLIRGenImpl::mlirGenClassInterfaceVTable(mlir::Location location, mlir::Value value, mlir_ts::ClassType classType, mlir_ts::InterfaceType interfaceType, const GenContext &genContext)
{
auto classInfo = getClassInfoByFullName(classType.getName().getValue());
assert(classInfo);

auto implementIndex = classInfo->getImplementIndex(interfaceType.getName().getValue());
if (implementIndex >= 0)
{
auto result = mlirGenPropertyAccessExpression(location, value, VTABLE_NAME, genContext);
auto vtableAccess = V(result);

auto interfaceVirtTableIndex = classInfo->implements[implementIndex].virtualIndex;

assert(genContext.allowPartialResolve || interfaceVirtTableIndex >= 0);

return V(builder.create<mlir_ts::VTableOffsetRefOp>(
location, mth.getInterfaceVTableType(interfaceType), vtableAccess, interfaceVirtTableIndex));
}

// create interface vtable from current class
auto interfaceInfo = getInterfaceInfoByFullName(interfaceType.getName().getValue());
assert(interfaceInfo);

if (auto createdInterfaceVTableForClass =
mlirGenCreateInterfaceVTableForClass(location, classInfo, interfaceInfo, genContext))
{
LLVM_DEBUG(llvm::dbgs() << "\n!!"
<< "@ created interface:" << V(createdInterfaceVTableForClass) << "\n";);
return createdInterfaceVTableForClass;
}

emitError(location) << "type: " << classType.getName() << " missing interface: " << interfaceType.getName();
return mlir::failure();
}

// An interface value keeps the vtable it was made with, laid out for its own interface. Another
// interface it is cast to reads that vtable by its own slot numbers, which match only when it is the
// value's interface or one it extends (whose members come first): `interface HasDist { dist }` read
Expand Down Expand Up @@ -1644,6 +1690,30 @@ namespace mlirgen
return std::nullopt;
}

// `C | null` given to `I | null`: both are stored as their one member (null is a null pointer
// in it), so this is the class-to-interface cast of `C` to `I`. A plain cast read the class
// pointer as an interface.
if (auto srcUnionType = dyn_cast<mlir_ts::UnionType>(valueType); srcUnionType && isa<mlir_ts::InterfaceType>(baseType))
{
mlir::Type srcBaseType;
if (mth.isUnionTypeNeedsTag(location, srcUnionType, srcBaseType) || !isa_and_nonnull<mlir_ts::ClassType>(srcBaseType))
{
return std::nullopt;
}

auto member = builder.create<mlir_ts::GetValueFromUnionOp>(location, srcBaseType, value);
CAST_A(casted, location, baseType, member, genContext);
return V(builder.create<mlir_ts::CastOp>(location, type, casted));
}

// null given to `I | null` is the null interface, as null given to `I` is (castToInterfaceSpecialCases)
if (isa<mlir_ts::NullType, mlir_ts::UndefinedType>(valueType) && isa<mlir_ts::InterfaceType>(baseType))
{
auto casted = cast(location, baseType, value, genContext, /*disableStrictNullCheck=*/true);
EXIT_IF_FAILED_OR_NO_VALUE(casted)
return V(builder.create<mlir_ts::CastOp>(location, type, V(casted)));
}

if (isa<mlir_ts::NullType>(valueType) || isa<mlir_ts::UndefinedType>(valueType) || isa<mlir_ts::AnyType>(valueType)
|| isa<mlir_ts::OptionalType>(valueType) || isa<mlir_ts::UnionType>(valueType))
{
Expand Down
7 changes: 5 additions & 2 deletions tslang/lib/TypeScript/MLIRGenFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1948,9 +1948,12 @@ namespace mlirgen
return mlir::failure();
}

// add exit code
// add exit code: a function returning nothing declares no return variable of its own, and one
// made while another function is being generated (a null interface's method stub for a cast
// in it) finds that function's instead
auto retVarInfo = symbolTable.lookup(RETURN_VARIABLE_NAME);
if (retVarInfo.first)
auto *retVarDef = retVarInfo.first ? retVarInfo.first.getDefiningOp() : nullptr;
if (retVarDef && funcOp->isProperAncestor(retVarDef))
{
builder.create<mlir_ts::ExitOp>(location, retVarInfo.first);
}
Expand Down
Loading
Loading