Skip to content

Commit 140386e

Browse files
authored
Source maps: Allow specifying that an expression has no debug info in text (#6520)
;;@ with nothing else (no source:line) can be used to specify that the following expression does not have any debug info associated to it. This can be used to stop the automatic propagation of debug info in the text parsers. The text printer has also been updated to output this comment when needed.
1 parent 55f33b5 commit 140386e

File tree

11 files changed

+165
-40
lines changed

11 files changed

+165
-40
lines changed

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,35 @@ environment. That will print this for the above `add`:
924924
(full print mode also adds a `[type]` for each expression, right before the
925925
debug location).
926926

927+
The debug information is also propagated from an expression to its
928+
next sibling:
929+
```wat
930+
;;@ src.cpp:100:33
931+
(local.set $x
932+
(i32.const 0)
933+
)
934+
(local.set $y ;; This receives an annotation of src.cpp:100:33
935+
(i32.const 0)
936+
)
937+
```
938+
939+
You can prevent the propagation of debug info by explicitly mentioning
940+
that an expression has not debug info using the annotation `;;@` with
941+
nothing else:
942+
```wat
943+
;;@ src.cpp:100:33
944+
(local.set $x
945+
;;@
946+
(i32.const 0) ;; This does not receive any annotation
947+
)
948+
;;@
949+
(local.set $y ;; This does not receive any annotation
950+
(i32.const 7)
951+
)
952+
```
953+
This stops the propagatation to children and siblings as well. So,
954+
expression `(i32.const 7)` does not have any debug info either.
955+
927956
There is no shorthand in the binary format. That is, roundtripping (writing and
928957
reading) through a binary + source map should not change which expressions have
929958
debug info on them or the contents of that info.

src/ir/module-utils.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ Function* copyFunction(Function* func,
6060
// Update file indices if needed
6161
if (fileIndexMap) {
6262
for (auto& iter : ret->debugLocations) {
63-
iter.second.fileIndex = (*fileIndexMap)[iter.second.fileIndex];
63+
if (iter.second) {
64+
iter.second->fileIndex = (*fileIndexMap)[iter.second->fileIndex];
65+
}
6466
}
6567
updateLocationSet(ret->prologLocation, *fileIndexMap);
6668
updateLocationSet(ret->epilogLocation, *fileIndexMap);

src/parser/contexts.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1731,6 +1731,11 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
17311731
return;
17321732
}
17331733
Lexer lexer(annotation->contents);
1734+
if (lexer.empty()) {
1735+
irBuilder.setDebugLocation(std::nullopt);
1736+
return;
1737+
}
1738+
17341739
auto contents = lexer.takeKeyword();
17351740
if (!contents || !lexer.empty()) {
17361741
return;
@@ -1766,7 +1771,8 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
17661771
assert(wasm.debugInfoFileNames.size() == it->second);
17671772
wasm.debugInfoFileNames.push_back(std::string(file));
17681773
}
1769-
irBuilder.setDebugLocation({it->second, *line, *col});
1774+
irBuilder.setDebugLocation(
1775+
Function::DebugLocation({it->second, *line, *col}));
17701776
}
17711777

17721778
Result<> makeBlock(Index pos,

src/passes/Print.cpp

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,12 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> {
117117

118118
Module* currModule = nullptr;
119119
Function* currFunction = nullptr;
120-
Function::DebugLocation lastPrintedLocation;
120+
// Keep track of the last printed debug location to avoid printing
121+
// repeated debug locations for children. nullopt means that we have
122+
// not yet printed any debug location, or that we last printed an
123+
// annotation indicating that the expression had no associated
124+
// debug location.
125+
std::optional<Function::DebugLocation> lastPrintedLocation;
121126
bool debugInfo;
122127

123128
// Used to print delegate's depth argument when it throws to the caller
@@ -247,7 +252,8 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> {
247252
return o;
248253
}
249254

250-
void printDebugLocation(const Function::DebugLocation& location);
255+
void
256+
printDebugLocation(const std::optional<Function::DebugLocation>& location);
251257
void printDebugLocation(Expression* curr);
252258

253259
// Prints debug info for a delimiter in an expression.
@@ -2475,17 +2481,24 @@ std::ostream& PrintSExpression::printPrefixedTypes(const char* prefix,
24752481
}
24762482

24772483
void PrintSExpression::printDebugLocation(
2478-
const Function::DebugLocation& location) {
2484+
const std::optional<Function::DebugLocation>& location) {
2485+
if (minify) {
2486+
return;
2487+
}
24792488
// Do not skip repeated debug info in full mode, for less-confusing debugging:
24802489
// full mode prints out everything in the most verbose manner.
24812490
if (lastPrintedLocation == location && indent > lastPrintIndent && !full) {
24822491
return;
24832492
}
24842493
lastPrintedLocation = location;
24852494
lastPrintIndent = indent;
2486-
auto fileName = currModule->debugInfoFileNames[location.fileIndex];
2487-
o << ";;@ " << fileName << ":" << location.lineNumber << ":"
2488-
<< location.columnNumber << '\n';
2495+
if (!location) {
2496+
o << ";;@\n";
2497+
} else {
2498+
auto fileName = currModule->debugInfoFileNames[location->fileIndex];
2499+
o << ";;@ " << fileName << ":" << location->lineNumber << ":"
2500+
<< location->columnNumber << '\n';
2501+
}
24892502
doIndent(o, indent);
24902503
}
24912504

@@ -2496,6 +2509,8 @@ void PrintSExpression::printDebugLocation(Expression* curr) {
24962509
auto iter = debugLocations.find(curr);
24972510
if (iter != debugLocations.end()) {
24982511
printDebugLocation(iter->second);
2512+
} else {
2513+
printDebugLocation(std::nullopt);
24992514
}
25002515
// show a binary position, if there is one
25012516
if (debugInfo) {
@@ -2958,7 +2973,7 @@ void PrintSExpression::visitFunction(Function* curr) {
29582973
void PrintSExpression::visitImportedFunction(Function* curr) {
29592974
doIndent(o, indent);
29602975
currFunction = curr;
2961-
lastPrintedLocation = {0, 0, 0};
2976+
lastPrintedLocation = std::nullopt;
29622977
o << '(';
29632978
emitImportHeader(curr);
29642979
handleSignature(curr->type, curr->name);
@@ -2969,7 +2984,8 @@ void PrintSExpression::visitImportedFunction(Function* curr) {
29692984
void PrintSExpression::visitDefinedFunction(Function* curr) {
29702985
doIndent(o, indent);
29712986
currFunction = curr;
2972-
lastPrintedLocation = {0, 0, 0};
2987+
lastPrintedLocation = std::nullopt;
2988+
lastPrintIndent = 0;
29732989
if (currFunction->prologLocation.size()) {
29742990
printDebugLocation(*currFunction->prologLocation.begin());
29752991
}

src/wasm-ir-builder.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
5858

5959
// Set the debug location to be attached to the next visited, created, or
6060
// pushed instruction.
61-
void setDebugLocation(const Function::DebugLocation&);
61+
void setDebugLocation(const std::optional<Function::DebugLocation>&);
6262

6363
// Handle the boundaries of control flow structures. Users may choose to use
6464
// the corresponding `makeXYZ` function below instead of `visitXYZStart`, but
@@ -238,7 +238,17 @@ class IRBuilder : public UnifiedExpressionVisitor<IRBuilder, Result<>> {
238238
Module& wasm;
239239
Function* func;
240240
Builder builder;
241-
std::optional<Function::DebugLocation> debugLoc;
241+
242+
// The location lacks debug info as it was marked as not having it.
243+
struct NoDebug : public std::monostate {};
244+
// The location lacks debug info, but was not marked as not having
245+
// it, and it can receive it from the parent or its previous sibling
246+
// (if it has one).
247+
struct CanReceiveDebug : public std::monostate {};
248+
using DebugVariant =
249+
std::variant<NoDebug, CanReceiveDebug, Function::DebugLocation>;
250+
251+
DebugVariant debugLoc;
242252

243253
struct ChildPopper;
244254

src/wasm.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2167,7 +2167,9 @@ class Function : public Importable {
21672167
: columnNumber < other.columnNumber;
21682168
}
21692169
};
2170-
std::unordered_map<Expression*, DebugLocation> debugLocations;
2170+
// One can explicitly set the debug location of an expression to
2171+
// nullopt to stop the propagation of debug locations.
2172+
std::unordered_map<Expression*, std::optional<DebugLocation>> debugLocations;
21712173
std::set<DebugLocation> prologLocation;
21722174
std::set<DebugLocation> epilogLocation;
21732175

src/wasm/wasm-binary.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,9 +1411,9 @@ void WasmBinaryWriter::writeDebugLocation(Expression* curr, Function* func) {
14111411
if (sourceMap) {
14121412
auto& debugLocations = func->debugLocations;
14131413
auto iter = debugLocations.find(curr);
1414-
if (iter != debugLocations.end()) {
1414+
if (iter != debugLocations.end() && iter->second) {
14151415
// There is debug information here, write it out.
1416-
writeDebugLocation(iter->second);
1416+
writeDebugLocation(*(iter->second));
14171417
} else {
14181418
// This expression has no debug location.
14191419
writeNoDebugLocation();

src/wasm/wasm-ir-builder.cpp

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -168,21 +168,37 @@ Result<Expression*> IRBuilder::build() {
168168
return expr;
169169
}
170170

171-
void IRBuilder::setDebugLocation(const Function::DebugLocation& loc) {
172-
DBG(std::cerr << "setting debugloc " << loc.fileIndex << ":" << loc.lineNumber
173-
<< ":" << loc.columnNumber << "\n";);
174-
debugLoc = loc;
171+
void IRBuilder::setDebugLocation(
172+
const std::optional<Function::DebugLocation>& loc) {
173+
if (loc) {
174+
DBG(std::cerr << "setting debugloc " << loc->fileIndex << ":"
175+
<< loc->lineNumber << ":" << loc->columnNumber << "\n";);
176+
} else {
177+
DBG(std::cerr << "setting debugloc to none\n";);
178+
}
179+
if (loc) {
180+
debugLoc = *loc;
181+
} else {
182+
debugLoc = NoDebug();
183+
}
175184
}
176185

177186
void IRBuilder::applyDebugLoc(Expression* expr) {
178-
if (debugLoc) {
187+
if (!std::get_if<CanReceiveDebug>(&debugLoc)) {
179188
if (func) {
180-
DBG(std::cerr << "applying debugloc " << debugLoc->fileIndex << ":"
181-
<< debugLoc->lineNumber << ":" << debugLoc->columnNumber
182-
<< " to expression " << ShallowExpression{expr} << "\n");
183-
func->debugLocations[expr] = *debugLoc;
189+
if (auto* loc = std::get_if<Function::DebugLocation>(&debugLoc)) {
190+
DBG(std::cerr << "applying debugloc " << loc->fileIndex << ":"
191+
<< loc->lineNumber << ":" << loc->columnNumber
192+
<< " to expression " << ShallowExpression{expr} << "\n");
193+
func->debugLocations[expr] = *loc;
194+
} else {
195+
assert(std::get_if<NoDebug>(&debugLoc));
196+
DBG(std::cerr << "applying debugloc to expression "
197+
<< ShallowExpression{expr} << "\n");
198+
func->debugLocations[expr] = std::nullopt;
199+
}
184200
}
185-
debugLoc.reset();
201+
debugLoc = CanReceiveDebug();
186202
}
187203
}
188204

@@ -677,10 +693,10 @@ Result<> IRBuilder::visitFunctionStart(Function* func) {
677693
if (!scopeStack.empty()) {
678694
return Err{"unexpected start of function"};
679695
}
680-
if (debugLoc) {
681-
func->prologLocation.insert(*debugLoc);
682-
debugLoc.reset();
696+
if (auto* loc = std::get_if<Function::DebugLocation>(&debugLoc)) {
697+
func->prologLocation.insert(*loc);
683698
}
699+
debugLoc = CanReceiveDebug();
684700
scopeStack.push_back(ScopeCtx::makeFunc(func));
685701
this->func = func;
686702
return Ok{};
@@ -718,12 +734,13 @@ Result<> IRBuilder::visitTryTableStart(TryTable* trytable, Name label) {
718734
}
719735

720736
Result<Expression*> IRBuilder::finishScope(Block* block) {
721-
if (debugLoc) {
722-
DBG(std::cerr << "discarding debugloc " << debugLoc->fileIndex << ":"
723-
<< debugLoc->lineNumber << ":" << debugLoc->columnNumber
724-
<< "\n");
737+
#if IR_BUILDER_DEBUG
738+
if (auto* loc = std::get_if<Function::DebugLocation>(&debugLoc)) {
739+
std::cerr << "discarding debugloc " << loc->fileIndex << ":"
740+
<< loc->lineNumber << ":" << loc->columnNumber << "\n";
725741
}
726-
debugLoc.reset();
742+
#endif
743+
debugLoc = CanReceiveDebug();
727744

728745
if (scopeStack.empty() || scopeStack.back().isNone()) {
729746
return Err{"unexpected end of scope"};
@@ -913,10 +930,12 @@ Result<> IRBuilder::visitEnd() {
913930
if (scope.isNone()) {
914931
return Err{"unexpected end"};
915932
}
916-
if (auto* func = scope.getFunction(); func && debugLoc) {
917-
func->epilogLocation.insert(*debugLoc);
918-
debugLoc.reset();
933+
if (auto* func = scope.getFunction(); func) {
934+
if (auto* loc = std::get_if<Function::DebugLocation>(&debugLoc)) {
935+
func->epilogLocation.insert(*loc);
936+
}
919937
}
938+
debugLoc = CanReceiveDebug();
920939
auto expr = finishScope(scope.getBlock());
921940
CHECK_ERR(expr);
922941

src/wasm/wasm-s-parser.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ void SExpressionParser::parseDebugLocation() {
219219
while (debugLocEnd[0] && debugLocEnd[0] != '\n') {
220220
debugLocEnd++;
221221
}
222+
if (debugLocEnd == debugLoc) {
223+
loc = nullptr;
224+
return;
225+
}
222226
char const* pos = debugLoc;
223227
while (pos < debugLocEnd && pos[0] != ':') {
224228
pos++;

test/example/debug-location-propagation.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ int main() {
3333

3434
auto& debugLocations = module->getFunction("adder")->debugLocations;
3535
assert(debugLocations.size() == 4);
36-
assert(debugLocations[x].columnNumber == 13);
37-
assert(debugLocations[y].columnNumber == 13);
38-
assert(debugLocations[add].columnNumber == 2);
39-
assert(debugLocations[drop].columnNumber == 2);
36+
assert(debugLocations[x]->columnNumber == 13);
37+
assert(debugLocations[y]->columnNumber == 13);
38+
assert(debugLocations[add]->columnNumber == 2);
39+
assert(debugLocations[drop]->columnNumber == 2);
4040

4141
BinaryenSetDebugInfo(false);
4242
BinaryenModuleDispose(module);

0 commit comments

Comments
 (0)