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: 4 additions & 0 deletions tslang/include/TypeScript/Defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@
#define RETURN_VARIABLE_NAME ".return"
#define CAPTURED_NAME ".captured"
#define LABEL_ATTR_NAME "label"
// Windows: a landing pad's catch-pad id, and the id of the catch it is nested in - see tagPad in
// TryOpLowering and ts.internal.eh_pad in Win32ExceptionPass
#define EH_PAD_ID_ATTR_NAME "__eh_pad_id"
#define EH_PAD_PARENT_ATTR_NAME "__eh_pad_parent"
#define UNDEFINED_NAME "undefined"
#define INFINITY_NAME "Infinity"
#define NAN_NAME "NaN"
Expand Down
6 changes: 6 additions & 0 deletions tslang/include/TypeScript/TypeScriptPassContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ struct TSContext
finallyBlockOf.beginRun();
landingBlockOf.beginRun();
leavesCatch.beginRun();
catchPadIdOf.beginRun();
}

// options
Expand All @@ -156,6 +157,11 @@ struct TSContext
// a throw cannot: `unwind` already means its invoke destination, which is a different
// question with a different answer.
OpSideTable<bool> leavesCatch{"leavesCatch"};
// Windows: the id a try's catch landing pad goes by, so that the pads of a try inside that
// catch clause can name it as the funclet they are nested in - see tagPad in TryOpLowering
// and ts.internal.eh_pad in Win32ExceptionPass. Ids are unique within the module.
OpSideTable<int> catchPadIdOf{"catchPadIdOf"};
int nextCatchPadId = 0;
mlir::Block *returnBlock;
};

Expand Down
68 changes: 62 additions & 6 deletions tslang/lib/TypeScript/LowerToAffineLoops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1329,6 +1329,48 @@ struct TryOpLowering : public TsPattern<mlir_ts::TryOp>
MLIRRTTIHelperVC rttih(rewriter, module, tsContext->compileOptions);
auto i8PtrTy = mth.getOpaqueType();

// Windows: which catch funclet this try's pads are nested in - the catch clause of the
// nearest try around it that holds it in its catch, through any try bodies in between.
// Win32ExceptionPass cannot tell that from the IR: a pad reached from inside a catch is
// either a try nested in it or where exceptions leave it, and the two look alike. A try
// in a finally is left at the top, as every pad was before.
auto funcletParentId = 0;
if (tsContext->compileOptions.isWindows)
{
for (Operation *child = tryOp.getOperation(), *parent = parentTryOp; parent;
child = parent, parent = tsContext->parentTryOp.lookup(parent))
{
if (tsContext->inParentTryCatch.lookup(child))
{
funcletParentId = tsContext->catchPadIdOf.lookup(parent);
break;
}

if (!tsContext->inParentTryBody.lookup(child))
{
break;
}
}
}

auto catchPadId = 0;
auto tagPad = [&](mlir_ts::LandingPadOp landingPadOp, bool isCatchPad) {
if (!tsContext->compileOptions.isWindows)
{
return;
}

if (isCatchPad && catchPadId)
{
landingPadOp->setAttr(EH_PAD_ID_ATTR_NAME, rewriter.getI32IntegerAttr(catchPadId));
}

if (funcletParentId)
{
landingPadOp->setAttr(EH_PAD_PARENT_ATTR_NAME, rewriter.getI32IntegerAttr(funcletParentId));
}
};

// find catch var
//
// This region walk must stop at a nested `try`: a `try/catch` written inside a catch
Expand Down Expand Up @@ -1487,13 +1529,18 @@ struct TryOpLowering : public TsPattern<mlir_ts::TryOp>
// is what ends the catch - ending it here as well runs it twice and
// breaks the unwind (51exceptions.ts is the case that proves it).
//
// Except for a throw inside a try nested in this catch: that one unwinds to
// the nested try's own pad, not to the finally. Left inside this catch's
// funclet, the nested pad would be a catchswitch `within none` reached from
// inside a catchpad - invalid funclet nesting, and a crash at run time. So
// it ends the catch first, exactly as it does when there is no finally.
// A throw inside a try nested in this catch does not leave the catch: the
// nested try's pad takes it, and on Windows Win32ExceptionPass nests that
// pad in this catch's funclet. It used to end the catch first, when the pass
// could not nest pads - which, with a `using` in the nested try, ended the
// catch between the using's first call and its throw, both unwinding to the
// same cleanup pad: one edge inside the funclet and one outside it, which
// the verifier rejects. The Itanium path has no funclets and keeps ending
// the catch there, as it always did.
auto inNestedTry = op->getParentOfType<mlir_ts::TryOp>() != nullptr;
if (!finallyHasOps || inNestedTry)
auto leaves = tsContext->compileOptions.isWindows ? !finallyHasOps && !inNestedTry
: !finallyHasOps || inNestedTry;
if (leaves)
{
tsContext->leavesCatch[op] = true;
}
Expand Down Expand Up @@ -1740,6 +1787,7 @@ struct TryOpLowering : public TsPattern<mlir_ts::TryOp>

auto landingPadCleanupOp = rewriter.create<mlir_ts::LandingPadOp>(
loc, rttih.getLandingPadType(), rewriter.getBoolAttr(true), ValueRange{undefArrayValue});
tagPad(landingPadCleanupOp, false);
auto beginCleanupCallInfo = rewriter.create<mlir_ts::BeginCleanupOp>(loc);

rewriter.setInsertionPoint(cleanupBlockLast->getTerminator());
Expand Down Expand Up @@ -1767,6 +1815,12 @@ struct TryOpLowering : public TsPattern<mlir_ts::TryOp>
rewriter.replaceOpWithNewOp<mlir_ts::EndCleanupOp>(resultOpCleanup, landingPadCleanupOp, unwindDests);
}

if (catchHasOps && tsContext->compileOptions.isWindows)
{
catchPadId = ++tsContext->nextCatchPadId;
tsContext->catchPadIdOf[tryOp.getOperation()] = catchPadId;
}

mlir::Value cmpValue;
SmallVector<mlir::Value> cmpWidenedValues;
if (catchHasOps)
Expand All @@ -1785,6 +1839,7 @@ struct TryOpLowering : public TsPattern<mlir_ts::TryOp>

auto landingPadOp = rewriter.create<mlir_ts::LandingPadOp>(loc, rttih.getLandingPadType(),
rewriter.getBoolAttr(false), catchTypes);
tagPad(landingPadOp, true);

// On Windows a typed catch that does not match is left by the runtime, through the
// catchswitch's unwind edge - but Win32ExceptionPass can only point that edge at the
Expand Down Expand Up @@ -1872,6 +1927,7 @@ struct TryOpLowering : public TsPattern<mlir_ts::TryOp>

auto landingPadCleanupOp = rewriter.create<mlir_ts::LandingPadOp>(
loc, rttih.getLandingPadType(), rewriter.getBoolAttr(true), ValueRange{undefArrayValue});
tagPad(landingPadCleanupOp, false);
auto beginCleanupCallInfo = rewriter.create<mlir_ts::BeginCleanupOp>(loc);

rewriter.setInsertionPoint(finallyBlockLast->getTerminator());
Expand Down
32 changes: 29 additions & 3 deletions tslang/lib/TypeScript/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4736,11 +4736,37 @@ struct LandingPadOpLowering : public TsLlvmPattern<mlir_ts::LandingPadOp>
ConversionPatternRewriter &rewriter) const final
{
Location loc = landingPadOp.getLoc();

// the funclet nesting TryOpLowering worked out, for Win32ExceptionPass: a call right
// after the pad, ts.internal.eh_pad(this catch pad's id, id of the catch it is nested
// in), 0 for none. The pass reads it and removes it.
auto padId = landingPadOp->getAttrOfType<mlir::IntegerAttr>(EH_PAD_ID_ATTR_NAME);
auto padParent = landingPadOp->getAttrOfType<mlir::IntegerAttr>(EH_PAD_PARENT_ATTR_NAME);
auto tagPad = [&](LLVM::LandingpadOp newLandingPadOp) {
if (!padId && !padParent)
{
return;
}

TypeHelper th(rewriter);
LLVMCodeHelper ch(landingPadOp, rewriter, getTypeConverter(), tsLlvmContext->compileOptions);
CodeLogicHelper clh(landingPadOp, rewriter);

auto ehPadFunc = ch.getOrInsertFunction(
"ts.internal.eh_pad", th.getFunctionType(th.getVoidType(), {th.getI32Type(), th.getI32Type()}));

mlir::OpBuilder::InsertionGuard guard(rewriter);
rewriter.setInsertionPointAfter(newLandingPadOp);
rewriter.create<LLVM::CallOp>(loc, ehPadFunc,
ValueRange{clh.createI32ConstantOf(padId ? padId.getInt() : 0),
clh.createI32ConstantOf(padParent ? padParent.getInt() : 0)});
};

if (!landingPadOp.getCleanup())
{
auto catch1 = transformed.getCatches().front();
mlir::Type llvmLandingPadTy = getTypeConverter()->convertType(landingPadOp.getType());
rewriter.replaceOpWithNewOp<LLVM::LandingpadOp>(landingPadOp, llvmLandingPadTy, false, ValueRange{catch1});
tagPad(rewriter.replaceOpWithNewOp<LLVM::LandingpadOp>(landingPadOp, llvmLandingPadTy, false, ValueRange{catch1}));
}
else
{
Expand All @@ -4754,8 +4780,8 @@ struct LandingPadOpLowering : public TsLlvmPattern<mlir_ts::LandingPadOp>
auto cleanupMarker = transformed.getCatches().front();

mlir::Type llvmLandingPadTy = getTypeConverter()->convertType(landingPadOp.getType());
rewriter.replaceOpWithNewOp<LLVM::LandingpadOp>(landingPadOp, llvmLandingPadTy, true,
ValueRange{cleanupMarker});
tagPad(rewriter.replaceOpWithNewOp<LLVM::LandingpadOp>(landingPadOp, llvmLandingPadTy, true,
ValueRange{cleanupMarker}));
}

return success();
Expand Down
Loading
Loading