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
132 changes: 123 additions & 9 deletions tslang/include/TypeScript/TypeScriptPassContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,146 @@

#include "mlir/IR/PatternMatch.h"

#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"

#include <cstdlib>

using namespace mlir;
using namespace ::typescript;
namespace mlir_ts = mlir::typescript;

namespace
{

// Set TSLANG_REPORT_STALE_LOWERING_CONTEXT to have each run report how many entries it drops from
// the run before (a stale entry that is looked up is always reported, and stops compilation).
inline bool reportStaleLoweringContext()
{
static const bool report = std::getenv("TSLANG_REPORT_STALE_LOWERING_CONTEXT") != nullptr;
return report;
}

// An entry written while lowering an earlier function was about to steer this one: the op it
// was written for is gone, and a new op took its address. Carrying on would branch into another
// function's blocks, so stop - in every build, since release is where it went unnoticed.
inline void reportStaleLoweringContextEntry(const char *table, Operation *op)
{
llvm::errs() << "stale lowering-context entry: " << table << " for '" << op->getName() << "' at "
<< op->getLoc() << "\n";
llvm::report_fatal_error("lowering-context entry left by a previous function");
}

// A side table keyed by Operation*. Its entries belong to one run of a lowering pass (one
// function, or one module) and are dropped when the next run begins: the ops they were written
// for are gone by then, and ops created later reuse their addresses. When that happened, an op
// in the next function inherited an `unwind`/`cleanup` entry pointing into the previous
// function's blocks, and the verifier failed with "reference to block defined in another
// region" - rarely, and only where the allocator happened to hand the address back
// (00try_finally_return.ts on Windows CI). Each entry also remembers its run, so a lookup that
// still finds one from an earlier run reports it.
template <typename V> class OpSideTable
{
public:
explicit OpSideTable(const char *name) : name(name)
{
}

void beginRun()
{
if (reportStaleLoweringContext() && !entries.empty())
{
llvm::errs() << "lowering-context: dropping " << entries.size() << " " << name
<< " entries of the previous run\n";
}

entries.clear();
run++;
}

// for writing: `table[op] = value`
V &operator[](Operation *op)
{
auto &entry = entries[op];
entry.run = run;
return entry.value;
}

V lookup(Operation *op) const
{
auto it = entries.find(op);
if (it == entries.end())
{
return V();
}

if (it->second.run != run && it->second.value)
{
reportStaleLoweringContextEntry(name, op);
}

return it->second.value;
}

bool contains(Operation *op) const
{
auto it = entries.find(op);
if (it == entries.end())
{
return false;
}

if (it->second.run != run)
{
reportStaleLoweringContextEntry(name, op);
}

return true;
}

private:
struct Entry
{
V value = V();
unsigned run = 0;
};

const char *name;
unsigned run = 0;
mlir::DenseMap<Operation *, Entry> entries;
};

struct TSContext
{
TSContext(CompileOptions &compileOptions) : compileOptions(compileOptions), jumps(), catchOpData(), unwind(), parentTryOp(), landingBlockOf(), returnBlock(nullptr) {};
TSContext(CompileOptions &compileOptions) : compileOptions(compileOptions), returnBlock(nullptr) {};

// call once before lowering each function (or module): entries written from here on belong to it
void beginRun()
{
jumps.beginRun();
catchOpData.beginRun();
unwind.beginRun();
cleanup.beginRun();
parentTryOp.beginRun();
landingBlockOf.beginRun();
leavesCatch.beginRun();
}

// options
CompileOptions &compileOptions;

// name, break, continue
mlir::DenseMap<Operation *, mlir::Block *> jumps;
mlir::DenseMap<Operation *, mlir::Value> catchOpData;
mlir::DenseMap<Operation *, mlir::Block *> unwind;
mlir::DenseMap<Operation *, mlir::Block *> cleanup;
mlir::DenseMap<Operation *, Operation *> parentTryOp;
mlir::DenseMap<Operation *, mlir::Block *> landingBlockOf;
OpSideTable<mlir::Block *> jumps{"jumps"};
OpSideTable<mlir::Value> catchOpData{"catchOpData"};
OpSideTable<mlir::Block *> unwind{"unwind"};
OpSideTable<mlir::Block *> cleanup{"cleanup"};
OpSideTable<Operation *> parentTryOp{"parentTryOp"};
OpSideTable<mlir::Block *> landingBlockOf{"landingBlockOf"};
// Throws that sit inside a catch clause and therefore have to end the active catch before
// they leave it. `return`, `break` and `continue` carry the same meaning in `unwind`, but
// a throw cannot: `unwind` already means its invoke destination, which is a different
// question with a different answer.
mlir::DenseSet<Operation *> leavesCatch;
OpSideTable<bool> leavesCatch{"leavesCatch"};
mlir::Block *returnBlock;
};

Expand All @@ -53,4 +167,4 @@ template <typename OpTy> class TsPattern : public OpRewritePattern<OpTy>
TSFunctionContext *tsFuncContext;
};

} // namespace
} // namespace
49 changes: 25 additions & 24 deletions tslang/lib/TypeScript/LowerToAffineLoops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ struct ReturnOpLowering : public TsPattern<mlir_ts::ReturnOp>
assert(tsContext->returnBlock);

auto retBlock = tsContext->returnBlock;
if (auto unwind = tsContext->unwind[op])
if (auto unwind = tsContext->unwind.lookup(op))
{
rewriter.create<mlir_ts::EndCatchOp>(loc);
}
Expand All @@ -122,7 +122,7 @@ struct ReturnOpLowering : public TsPattern<mlir_ts::ReturnOp>

rewriter.setInsertionPointToEnd(opBlock);

if (auto cleanup = tsContext->cleanup[op])
if (auto cleanup = tsContext->cleanup.lookup(op))
{
rewriter.create<mlir::cf::BranchOp>(loc, cleanup);
}
Expand Down Expand Up @@ -152,7 +152,7 @@ struct ReturnValOpLowering : public TsPattern<mlir_ts::ReturnValOp>

// save value into return
rewriter.create<mlir_ts::StoreOp>(op.getLoc(), op.getOperand(), op.getReference());
if (auto unwind = tsContext->unwind[op])
if (auto unwind = tsContext->unwind.lookup(op))
{
rewriter.create<mlir_ts::EndCatchOp>(loc);
}
Expand All @@ -164,7 +164,7 @@ struct ReturnValOpLowering : public TsPattern<mlir_ts::ReturnValOp>

rewriter.setInsertionPointToEnd(opBlock);

if (auto cleanup = tsContext->cleanup[op])
if (auto cleanup = tsContext->cleanup.lookup(op))
{
rewriter.create<mlir::cf::BranchOp>(loc, cleanup);
}
Expand Down Expand Up @@ -791,10 +791,10 @@ struct BreakOpLowering : public TsPattern<mlir_ts::BreakOp>
OpBuilder::InsertionGuard guard(rewriter);
Location loc = breakOp.getLoc();

auto jump = tsContext->jumps[breakOp];
auto jump = tsContext->jumps.lookup(breakOp);
assert(jump);

if (auto unwind = tsContext->unwind[breakOp])
if (auto unwind = tsContext->unwind.lookup(breakOp))
{
rewriter.create<mlir_ts::EndCatchOp>(loc);
}
Expand All @@ -817,10 +817,10 @@ struct ContinueOpLowering : public TsPattern<mlir_ts::ContinueOp>
OpBuilder::InsertionGuard guard(rewriter);
Location loc = continueOp.getLoc();

auto jump = tsContext->jumps[continueOp];
auto jump = tsContext->jumps.lookup(continueOp);
assert(jump);

if (auto unwind = tsContext->unwind[continueOp])
if (auto unwind = tsContext->unwind.lookup(continueOp))
{
rewriter.create<mlir_ts::EndCatchOp>(loc);
}
Expand Down Expand Up @@ -1294,8 +1294,8 @@ struct TryOpLowering : public TsPattern<mlir_ts::TryOp>
CodeLogicHelper clh(tryOp, rewriter);

auto module = tryOp->getParentOfType<mlir::ModuleOp>();
auto parentTryOp = tsContext->parentTryOp[tryOp.getOperation()];
mlir::Block *parentTryOpLandingPad = parentTryOp ? tsContext->landingBlockOf[parentTryOp] : nullptr;
auto parentTryOp = tsContext->parentTryOp.lookup(tryOp.getOperation());
mlir::Block *parentTryOpLandingPad = parentTryOp ? tsContext->landingBlockOf.lookup(parentTryOp) : nullptr;

MLIRRTTIHelperVC rttih(rewriter, module, tsContext->compileOptions);
auto i8PtrTy = mth.getOpaqueType();
Expand Down Expand Up @@ -1368,7 +1368,7 @@ struct TryOpLowering : public TsPattern<mlir_ts::TryOp>
mlir::SmallVector<Operation *> escapingJumpsCatches;
auto collectEscapingJumps = [&](mlir::Region &region, mlir::SmallVector<Operation *> &to) {
region.walk([&](Operation *op) {
if (isa<mlir_ts::BreakOp, mlir_ts::ContinueOp>(op) && tsContext->jumps.count(op))
if (isa<mlir_ts::BreakOp, mlir_ts::ContinueOp>(op) && tsContext->jumps.contains(op))
{
to.push_back(op);
}
Expand Down Expand Up @@ -1449,7 +1449,7 @@ struct TryOpLowering : public TsPattern<mlir_ts::TryOp>
// breaks the unwind (51exceptions.ts is the case that proves it).
if (!finallyHasOps)
{
tsContext->leavesCatch.insert(op);
tsContext->leavesCatch[op] = true;
}
}
};
Expand Down Expand Up @@ -1487,16 +1487,14 @@ struct TryOpLowering : public TsPattern<mlir_ts::TryOp>
auto propagateTsContextEntries = [&](const mlir::IRMapping &mapping) {
for (auto &[oldOp, newOp] : mapping.getOperationMap())
{
auto jumpIt = tsContext->jumps.find(oldOp);
if (jumpIt != tsContext->jumps.end())
if (auto jump = tsContext->jumps.lookup(oldOp))
{
tsContext->jumps[newOp] = jumpIt->second;
tsContext->jumps[newOp] = jump;
}

auto parentIt = tsContext->parentTryOp.find(oldOp);
if (parentIt != tsContext->parentTryOp.end())
if (auto parent = tsContext->parentTryOp.lookup(oldOp))
{
tsContext->parentTryOp[newOp] = parentIt->second;
tsContext->parentTryOp[newOp] = parent;
}
}
};
Expand Down Expand Up @@ -1538,7 +1536,7 @@ struct TryOpLowering : public TsPattern<mlir_ts::TryOp>
{
mlir::DenseMap<mlir::Block *, mlir::Block *> finallyCopyPerTarget;
auto routeThroughFinally = [&](Operation *op) {
auto target = tsContext->jumps[op];
auto target = tsContext->jumps.lookup(op);
auto &finallyCopy = finallyCopyPerTarget[target];
if (!finallyCopy)
{
Expand Down Expand Up @@ -1977,7 +1975,7 @@ struct CatchOpLowering : public TsPattern<mlir_ts::CatchOp>

Location loc = catchOp.getLoc();

auto catchDataValue = tsContext->catchOpData[catchOp];
auto catchDataValue = tsContext->catchOpData.lookup(catchOp);
if (catchDataValue)
{
rewriter.create<mlir_ts::SaveCatchVarOp>(loc, catchDataValue, catchOp.getCatchArg());
Expand All @@ -2000,7 +1998,7 @@ struct CallOpLowering : public TsPattern<mlir_ts::CallOp>

LogicalResult matchAndRewrite(mlir_ts::CallOp op, PatternRewriter &rewriter) const final
{
if (auto unwind = tsContext->unwind[op])
if (auto unwind = tsContext->unwind.lookup(op))
{
{
OpBuilder::InsertionGuard guard(rewriter);
Expand Down Expand Up @@ -2035,7 +2033,7 @@ struct CallIndirectOpLowering : public TsPattern<mlir_ts::CallIndirectOp>

LogicalResult matchAndRewrite(mlir_ts::CallIndirectOp op, PatternRewriter &rewriter) const final
{
if (auto unwind = tsContext->unwind[op])
if (auto unwind = tsContext->unwind.lookup(op))
{
{
OpBuilder::InsertionGuard guard(rewriter);
Expand Down Expand Up @@ -2079,12 +2077,12 @@ struct ThrowOpLowering : public TsPattern<mlir_ts::ThrowOp>
// terminator - and Win32ExceptionPass then picks an end for itself, splitting the
// block ahead of the throw and emitting the catchret before a call that still carries
// the funclet token. That IR reaches the backend and crashes it.
if (tsContext->leavesCatch.contains(throwOp.getOperation()))
if (tsContext->leavesCatch.lookup(throwOp.getOperation()))
{
rewriter.create<mlir_ts::EndCatchOp>(loc);
}

if (auto unwind = tsContext->unwind[throwOp])
if (auto unwind = tsContext->unwind.lookup(throwOp))
{
rewriter.replaceOpWithNewOp<mlir_ts::ThrowUnwindOp>(throwOp, throwOp.getException(), unwind);
}
Expand Down Expand Up @@ -2553,6 +2551,7 @@ void TypeScriptToAffineLoweringTSFuncPass::runOnFunction()

TSFunctionContext tsFuncContext{};
AddTsAffineLegalOps(target);
tsContext.beginRun();
AddTsAffinePatterns(getContext(), target, patterns, tsContext, tsFuncContext);

// With the target and rewrite patterns defined, we can now attempt the
Expand Down Expand Up @@ -2583,6 +2582,7 @@ void TypeScriptToAffineLoweringFuncPass::runOnOperation()

TSFunctionContext tsFuncContext{};
AddTsAffineLegalOps(target);
tsContext.beginRun();
AddTsAffinePatterns(getContext(), target, patterns, tsContext, tsFuncContext);

// TODO: Hack to fix issue with Async
Expand Down Expand Up @@ -2610,6 +2610,7 @@ void TypeScriptToAffineLoweringModulePass::runOnOperation()

TSFunctionContext tsFuncContext{};
AddTsAffineLegalOps(target);
tsContext.beginRun();
AddTsAffinePatterns(getContext(), target, patterns, tsContext, tsFuncContext);

// + Global ops
Expand Down
Loading