summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md29
-rw-r--r--src/ir/module-utils.cpp4
-rw-r--r--src/parser/contexts.h8
-rw-r--r--src/passes/Print.cpp32
-rw-r--r--src/wasm-ir-builder.h14
-rw-r--r--src/wasm.h4
-rw-r--r--src/wasm/wasm-binary.cpp4
-rw-r--r--src/wasm/wasm-ir-builder.cpp61
-rw-r--r--src/wasm/wasm-s-parser.cpp4
-rw-r--r--test/example/debug-location-propagation.cpp8
-rw-r--r--test/lit/debug/source-map-stop.wast37
11 files changed, 165 insertions, 40 deletions
diff --git a/README.md b/README.md
index d4271742b..236174ceb 100644
--- a/README.md
+++ b/README.md
@@ -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)
+ )
+ )
+ )
)