diff --git a/tslang/include/TypeScript/TypeScriptPassContext.h b/tslang/include/TypeScript/TypeScriptPassContext.h index 0539f9ac4..68bfc03a8 100644 --- a/tslang/include/TypeScript/TypeScriptPassContext.h +++ b/tslang/include/TypeScript/TypeScriptPassContext.h @@ -4,6 +4,11 @@ #include "mlir/IR/PatternMatch.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + +#include + using namespace mlir; using namespace ::typescript; namespace mlir_ts = mlir::typescript; @@ -11,25 +16,134 @@ 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 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 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 jumps; - mlir::DenseMap catchOpData; - mlir::DenseMap unwind; - mlir::DenseMap cleanup; - mlir::DenseMap parentTryOp; - mlir::DenseMap landingBlockOf; + OpSideTable jumps{"jumps"}; + OpSideTable catchOpData{"catchOpData"}; + OpSideTable unwind{"unwind"}; + OpSideTable cleanup{"cleanup"}; + OpSideTable parentTryOp{"parentTryOp"}; + OpSideTable 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 leavesCatch; + OpSideTable leavesCatch{"leavesCatch"}; mlir::Block *returnBlock; }; @@ -53,4 +167,4 @@ template class TsPattern : public OpRewritePattern TSFunctionContext *tsFuncContext; }; -} // namespace \ No newline at end of file +} // namespace diff --git a/tslang/lib/TypeScript/LowerToAffineLoops.cpp b/tslang/lib/TypeScript/LowerToAffineLoops.cpp index 244f3dd18..bcd381a6d 100644 --- a/tslang/lib/TypeScript/LowerToAffineLoops.cpp +++ b/tslang/lib/TypeScript/LowerToAffineLoops.cpp @@ -111,7 +111,7 @@ struct ReturnOpLowering : public TsPattern assert(tsContext->returnBlock); auto retBlock = tsContext->returnBlock; - if (auto unwind = tsContext->unwind[op]) + if (auto unwind = tsContext->unwind.lookup(op)) { rewriter.create(loc); } @@ -122,7 +122,7 @@ struct ReturnOpLowering : public TsPattern rewriter.setInsertionPointToEnd(opBlock); - if (auto cleanup = tsContext->cleanup[op]) + if (auto cleanup = tsContext->cleanup.lookup(op)) { rewriter.create(loc, cleanup); } @@ -152,7 +152,7 @@ struct ReturnValOpLowering : public TsPattern // save value into return rewriter.create(op.getLoc(), op.getOperand(), op.getReference()); - if (auto unwind = tsContext->unwind[op]) + if (auto unwind = tsContext->unwind.lookup(op)) { rewriter.create(loc); } @@ -164,7 +164,7 @@ struct ReturnValOpLowering : public TsPattern rewriter.setInsertionPointToEnd(opBlock); - if (auto cleanup = tsContext->cleanup[op]) + if (auto cleanup = tsContext->cleanup.lookup(op)) { rewriter.create(loc, cleanup); } @@ -791,10 +791,10 @@ struct BreakOpLowering : public TsPattern 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(loc); } @@ -817,10 +817,10 @@ struct ContinueOpLowering : public TsPattern 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(loc); } @@ -1294,8 +1294,8 @@ struct TryOpLowering : public TsPattern CodeLogicHelper clh(tryOp, rewriter); auto module = tryOp->getParentOfType(); - 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(); @@ -1368,7 +1368,7 @@ struct TryOpLowering : public TsPattern mlir::SmallVector escapingJumpsCatches; auto collectEscapingJumps = [&](mlir::Region ®ion, mlir::SmallVector &to) { region.walk([&](Operation *op) { - if (isa(op) && tsContext->jumps.count(op)) + if (isa(op) && tsContext->jumps.contains(op)) { to.push_back(op); } @@ -1449,7 +1449,7 @@ struct TryOpLowering : public TsPattern // breaks the unwind (51exceptions.ts is the case that proves it). if (!finallyHasOps) { - tsContext->leavesCatch.insert(op); + tsContext->leavesCatch[op] = true; } } }; @@ -1487,16 +1487,14 @@ struct TryOpLowering : public TsPattern 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; } } }; @@ -1538,7 +1536,7 @@ struct TryOpLowering : public TsPattern { mlir::DenseMap finallyCopyPerTarget; auto routeThroughFinally = [&](Operation *op) { - auto target = tsContext->jumps[op]; + auto target = tsContext->jumps.lookup(op); auto &finallyCopy = finallyCopyPerTarget[target]; if (!finallyCopy) { @@ -1977,7 +1975,7 @@ struct CatchOpLowering : public TsPattern Location loc = catchOp.getLoc(); - auto catchDataValue = tsContext->catchOpData[catchOp]; + auto catchDataValue = tsContext->catchOpData.lookup(catchOp); if (catchDataValue) { rewriter.create(loc, catchDataValue, catchOp.getCatchArg()); @@ -2000,7 +1998,7 @@ struct CallOpLowering : public TsPattern 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); @@ -2035,7 +2033,7 @@ struct CallIndirectOpLowering : public TsPattern 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); @@ -2079,12 +2077,12 @@ struct ThrowOpLowering : public TsPattern // 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(loc); } - if (auto unwind = tsContext->unwind[throwOp]) + if (auto unwind = tsContext->unwind.lookup(throwOp)) { rewriter.replaceOpWithNewOp(throwOp, throwOp.getException(), unwind); } @@ -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 @@ -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 @@ -2610,6 +2610,7 @@ void TypeScriptToAffineLoweringModulePass::runOnOperation() TSFunctionContext tsFuncContext{}; AddTsAffineLegalOps(target); + tsContext.beginRun(); AddTsAffinePatterns(getContext(), target, patterns, tsContext, tsFuncContext); // + Global ops