diff options
-rw-r--r-- | README.md | 29 | ||||
-rw-r--r-- | src/ir/module-utils.cpp | 4 | ||||
-rw-r--r-- | src/parser/contexts.h | 8 | ||||
-rw-r--r-- | src/passes/Print.cpp | 32 | ||||
-rw-r--r-- | src/wasm-ir-builder.h | 14 | ||||
-rw-r--r-- | src/wasm.h | 4 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 4 | ||||
-rw-r--r-- | src/wasm/wasm-ir-builder.cpp | 61 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 4 | ||||
-rw-r--r-- | test/example/debug-location-propagation.cpp | 8 | ||||
-rw-r--r-- | test/lit/debug/source-map-stop.wast | 37 |
11 files changed, 165 insertions, 40 deletions
@@ -924,6 +924,35 @@ environment. That will print this for the above `add`: (full print mode also adds a `[type]` for each expression, right before the debug location). +The debug information is also propagated from an expression to its +next sibling: +```wat +;;@ src.cpp:100:33 +(local.set $x + (i32.const 0) +) +(local.set $y ;; This receives an annotation of src.cpp:100:33 + (i32.const 0) +) +``` + +You can prevent the propagation of debug info by explicitly mentioning +that an expression has not debug info using the annotation `;;@` with +nothing else: +```wat +;;@ src.cpp:100:33 +(local.set $x + ;;@ + (i32.const 0) ;; This does not receive any annotation +) +;;@ +(local.set $y ;; This does not receive any annotation + (i32.const 7) +) +``` +This stops the propagatation to children and siblings as well. So, +expression `(i32.const 7)` does not have any debug info either. + There is no shorthand in the binary format. That is, roundtripping (writing and reading) through a binary + source map should not change which expressions have debug info on them or the contents of that info. diff --git a/src/ir/module-utils.cpp b/src/ir/module-utils.cpp index e169f0ff1..99652ceff 100644 --- a/src/ir/module-utils.cpp +++ b/src/ir/module-utils.cpp @@ -60,7 +60,9 @@ Function* copyFunction(Function* func, // Update file indices if needed if (fileIndexMap) { for (auto& iter : ret->debugLocations) { - iter.second.fileIndex = (*fileIndexMap)[iter.second.fileIndex]; + if (iter.second) { + iter.second->fileIndex = (*fileIndexMap)[iter.second->fileIndex]; + } } updateLocationSet(ret->prologLocation, *fileIndexMap); updateLocationSet(ret->epilogLocation, *fileIndexMap); diff --git a/src/parser/contexts.h b/src/parser/contexts.h index 3e42d0d6b..f66e3d051 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -1731,6 +1731,11 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> { return; } Lexer lexer(annotation->contents); + if (lexer.empty()) { + irBuilder.setDebugLocation(std::nullopt); + return; + } + auto contents = lexer.takeKeyword(); if (!contents || !lexer.empty()) { return; @@ -1766,7 +1771,8 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> { assert(wasm.debugInfoFileNames.size() == it->second); wasm.debugInfoFileNames.push_back(std::string(file)); } - irBuilder.setDebugLocation({it->second, *line, *col}); + irBuilder.setDebugLocation( + Function::DebugLocation({it->second, *line, *col})); } Result<> makeBlock(Index pos, diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 02eaea21c..21b5e6b20 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -117,7 +117,12 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> { Module* currModule = nullptr; Function* currFunction = nullptr; - Function::DebugLocation lastPrintedLocation; + // Keep track of the last printed debug location to avoid printing + // repeated debug locations for children. nullopt means that we have + // not yet printed any debug location, or that we last printed an + // annotation indicating that the expression had no associated + // debug location. + std::optional<Function::DebugLocation> lastPrintedLocation; bool debugInfo; // Used to print delegate's depth argument when it throws to the caller @@ -247,7 +252,8 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> { return o; } - void printDebugLocation(const Function::DebugLocation& location); + void + printDebugLocation(const std::optional<Function::DebugLocation>& location); void printDebugLocation(Expression* curr); // Prints debug info for a delimiter in an expression. @@ -2475,7 +2481,10 @@ std::ostream& PrintSExpression::printPrefixedTypes(const char* prefix, } void PrintSExpression::printDebugLocation( - const Function::DebugLocation& location) { + const std::optional<Function::DebugLocation>& location) { + if (minify) { + return; + } // Do not skip repeated debug info in full mode, for less-confusing debugging: // full mode prints out everything in the most verbose manner. if (lastPrintedLocation == location && indent > lastPrintIndent && !full) { @@ -2483,9 +2492,13 @@ void PrintSExpression::printDebugLocation( } lastPrintedLocation = location; lastPrintIndent = indent; - auto fileName = currModule->debugInfoFileNames[location.fileIndex]; - o << ";;@ " << fileName << ":" << location.lineNumber << ":" - << location.columnNumber << '\n'; + if (!location) { + o << ";;@\n"; + } else { + auto fileName = currModule->debugInfoFileNames[location->fileIndex]; + o << ";;@ " << fileName << ":" << location->lineNumber << ":" + << location->columnNumber << '\n'; + } doIndent(o, indent); } @@ -2496,6 +2509,8 @@ void PrintSExpression::printDebugLocation(Expression* curr) { auto iter = debugLocations.find(curr); if (iter != debugLocations.end()) { printDebugLocation(iter->second); + } else { + printDebugLocation(std::nullopt); } // show a binary position, if there is one if (debugInfo) { @@ -2958,7 +2973,7 @@ void PrintSExpression::visitFunction(Function* curr) { void PrintSExpression::visitImportedFunction(Function* curr) { doIndent(o, indent); currFunction = curr; - lastPrintedLocation = {0, 0, 0}; + lastPrintedLocation = std::nullopt; o << '('; emitImportHeader(curr); handleSignature(curr->type, curr->name); @@ -2969,7 +2984,8 @@ void PrintSExpression::visitImportedFunction(Function* curr) { void PrintSExpression::visitDefinedFunction(Function* curr) { doIndent(o, indent); currFunction = curr; - lastPrintedLocation = {0, 0, 0}; + lastPrintedLocation = std::nullopt; + lastPrintIndent = 0; if (currFunction->prologLocation.size()) { printDebugLocation(*currFunction->prologLocation.begin()); } diff --git a/src/wasm-ir-builder.h b/src/wasm-ir-builder.h index 3c4a52bea..650462e12 100644 --- a/src/wasm-ir-builder.h +++ b/src/wasm-ir-builder.h @@ -58,7 +58,7 @@ public: // Set the debug location to be attached to the next visited, created, or // pushed instruction. - void setDebugLocation(const Function::DebugLocation&); + void setDebugLocation(const std::optional<Function::DebugLocation>&); // Handle the boundaries of control flow structures. Users may choose to use // the corresponding `makeXYZ` function below instead of `visitXYZStart`, but @@ -238,7 +238,17 @@ private: Module& wasm; Function* func; Builder builder; - std::optional<Function::DebugLocation> debugLoc; + + // The location lacks debug info as it was marked as not having it. + struct NoDebug : public std::monostate {}; + // The location lacks debug info, but was not marked as not having + // it, and it can receive it from the parent or its previous sibling + // (if it has one). + struct CanReceiveDebug : public std::monostate {}; + using DebugVariant = + std::variant<NoDebug, CanReceiveDebug, Function::DebugLocation>; + + DebugVariant debugLoc; struct ChildPopper; diff --git a/src/wasm.h b/src/wasm.h index 2f3f09ad5..dd7164499 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -2167,7 +2167,9 @@ public: : columnNumber < other.columnNumber; } }; - std::unordered_map<Expression*, DebugLocation> debugLocations; + // One can explicitly set the debug location of an expression to + // nullopt to stop the propagation of debug locations. + std::unordered_map<Expression*, std::optional<DebugLocation>> debugLocations; std::set<DebugLocation> prologLocation; std::set<DebugLocation> epilogLocation; diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index c1e506c8e..d72626b90 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -1411,9 +1411,9 @@ void WasmBinaryWriter::writeDebugLocation(Expression* curr, Function* func) { if (sourceMap) { auto& debugLocations = func->debugLocations; auto iter = debugLocations.find(curr); - if (iter != debugLocations.end()) { + if (iter != debugLocations.end() && iter->second) { // There is debug information here, write it out. - writeDebugLocation(iter->second); + writeDebugLocation(*(iter->second)); } else { // This expression has no debug location. writeNoDebugLocation(); diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index 91e990180..f816d4d1e 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -168,21 +168,37 @@ Result<Expression*> IRBuilder::build() { return expr; } -void IRBuilder::setDebugLocation(const Function::DebugLocation& loc) { - DBG(std::cerr << "setting debugloc " << loc.fileIndex << ":" << loc.lineNumber - << ":" << loc.columnNumber << "\n";); - debugLoc = loc; +void IRBuilder::setDebugLocation( + const std::optional<Function::DebugLocation>& loc) { + if (loc) { + DBG(std::cerr << "setting debugloc " << loc->fileIndex << ":" + << loc->lineNumber << ":" << loc->columnNumber << "\n";); + } else { + DBG(std::cerr << "setting debugloc to none\n";); + } + if (loc) { + debugLoc = *loc; + } else { + debugLoc = NoDebug(); + } } void IRBuilder::applyDebugLoc(Expression* expr) { - if (debugLoc) { + if (!std::get_if<CanReceiveDebug>(&debugLoc)) { if (func) { - DBG(std::cerr << "applying debugloc " << debugLoc->fileIndex << ":" - << debugLoc->lineNumber << ":" << debugLoc->columnNumber - << " to expression " << ShallowExpression{expr} << "\n"); - func->debugLocations[expr] = *debugLoc; + if (auto* loc = std::get_if<Function::DebugLocation>(&debugLoc)) { + DBG(std::cerr << "applying debugloc " << loc->fileIndex << ":" + << loc->lineNumber << ":" << loc->columnNumber + << " to expression " << ShallowExpression{expr} << "\n"); + func->debugLocations[expr] = *loc; + } else { + assert(std::get_if<NoDebug>(&debugLoc)); + DBG(std::cerr << "applying debugloc to expression " + << ShallowExpression{expr} << "\n"); + func->debugLocations[expr] = std::nullopt; + } } - debugLoc.reset(); + debugLoc = CanReceiveDebug(); } } @@ -677,10 +693,10 @@ Result<> IRBuilder::visitFunctionStart(Function* func) { if (!scopeStack.empty()) { return Err{"unexpected start of function"}; } - if (debugLoc) { - func->prologLocation.insert(*debugLoc); - debugLoc.reset(); + if (auto* loc = std::get_if<Function::DebugLocation>(&debugLoc)) { + func->prologLocation.insert(*loc); } + debugLoc = CanReceiveDebug(); scopeStack.push_back(ScopeCtx::makeFunc(func)); this->func = func; return Ok{}; @@ -718,12 +734,13 @@ Result<> IRBuilder::visitTryTableStart(TryTable* trytable, Name label) { } Result<Expression*> IRBuilder::finishScope(Block* block) { - if (debugLoc) { - DBG(std::cerr << "discarding debugloc " << debugLoc->fileIndex << ":" - << debugLoc->lineNumber << ":" << debugLoc->columnNumber - << "\n"); +#if IR_BUILDER_DEBUG + if (auto* loc = std::get_if<Function::DebugLocation>(&debugLoc)) { + std::cerr << "discarding debugloc " << loc->fileIndex << ":" + << loc->lineNumber << ":" << loc->columnNumber << "\n"; } - debugLoc.reset(); +#endif + debugLoc = CanReceiveDebug(); if (scopeStack.empty() || scopeStack.back().isNone()) { return Err{"unexpected end of scope"}; @@ -913,10 +930,12 @@ Result<> IRBuilder::visitEnd() { if (scope.isNone()) { return Err{"unexpected end"}; } - if (auto* func = scope.getFunction(); func && debugLoc) { - func->epilogLocation.insert(*debugLoc); - debugLoc.reset(); + if (auto* func = scope.getFunction(); func) { + if (auto* loc = std::get_if<Function::DebugLocation>(&debugLoc)) { + func->epilogLocation.insert(*loc); + } } + debugLoc = CanReceiveDebug(); auto expr = finishScope(scope.getBlock()); CHECK_ERR(expr); diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index bca94a768..be6fc5100 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -219,6 +219,10 @@ void SExpressionParser::parseDebugLocation() { while (debugLocEnd[0] && debugLocEnd[0] != '\n') { debugLocEnd++; } + if (debugLocEnd == debugLoc) { + loc = nullptr; + return; + } char const* pos = debugLoc; while (pos < debugLocEnd && pos[0] != ':') { pos++; diff --git a/test/example/debug-location-propagation.cpp b/test/example/debug-location-propagation.cpp index b4685e391..06bf8ab48 100644 --- a/test/example/debug-location-propagation.cpp +++ b/test/example/debug-location-propagation.cpp @@ -33,10 +33,10 @@ int main() { auto& debugLocations = module->getFunction("adder")->debugLocations; assert(debugLocations.size() == 4); - assert(debugLocations[x].columnNumber == 13); - assert(debugLocations[y].columnNumber == 13); - assert(debugLocations[add].columnNumber == 2); - assert(debugLocations[drop].columnNumber == 2); + assert(debugLocations[x]->columnNumber == 13); + assert(debugLocations[y]->columnNumber == 13); + assert(debugLocations[add]->columnNumber == 2); + assert(debugLocations[drop]->columnNumber == 2); BinaryenSetDebugInfo(false); BinaryenModuleDispose(module); diff --git a/test/lit/debug/source-map-stop.wast b/test/lit/debug/source-map-stop.wast index cdc06505e..95545e65a 100644 --- a/test/lit/debug/source-map-stop.wast +++ b/test/lit/debug/source-map-stop.wast @@ -124,4 +124,41 @@ ;;@ waka:200:2 (i32.const 2) ) + + ;; CHECK: (func $foo (param $x i32) (param $y i32) + ;; CHECK-NEXT: ;;@ src.cpp:90:1 + ;; CHECK-NEXT: (if + ;; CHECK-NEXT: ;;@ + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: ;;@ src.cpp:100:1 + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: ;;@ + ;; CHECK-NEXT: (return) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $foo (param $x i32) (param $y i32) + ;;@ src.cpp:90:1 + (if + ;;@ + (i32.add + (local.get $x) + (local.get $y) + ) + (then + ;;@ src.cpp:100:1 + (return) + ) + (else + ;;@ + (return) + ) + ) + ) ) |