Skip to content
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,11 @@ not handle things like re-indexing of locals, and so passes that might break
DWARF are disabled by default. As a result, this mode is not suitable for a
fully optimized release build, but it can be useful for local debugging.

When rewriting line tables, surviving instructions retain their original source
intervals. Inserted instructions without an original location are marked with
line zero, rather than inheriting a neighboring source line. This does not
synthesize source-level DWARF for newly generated helper functions.

## FAQ

* Why the weird name for the project?
Expand Down
3 changes: 3 additions & 0 deletions src/passes/Asyncify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1553,6 +1553,9 @@ struct AsyncifyLocals : public WalkerPass<PostWalker<AsyncifyLocals>> {
newBody->finalize(func->getResults());
}
func->body = newBody;
// The terminal end now returns from unwinding, not from the original source
// body. In particular, its dummy return value is not a source-level return.
func->expressionLocations.erase(nullptr);
// Making things like returns conditional may alter types.
ReFinalize().walkFunctionInModule(func, getModule());
}
Expand Down
10 changes: 10 additions & 0 deletions src/wasm-binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "parsing.h"
#include "source-map.h"
#include "wasm-builder.h"
#include "wasm-debug.h"
#include "wasm-features.h"
#include "wasm-ir-builder.h"
#include "wasm-traversal.h"
Expand Down Expand Up @@ -1479,6 +1480,13 @@ class WasmBinaryWriter {
void trackExpressionEnd(Expression* curr, Function* func);
void trackExpressionDelimiter(Expression* curr, Function* func, size_t id);

// Track a written instruction (not an empty expression). A null expression
// denotes generated code, or the function's final end when isEnd is true.
void trackInstruction(Expression* curr,
Function* func,
BinaryLocation start,
bool isEnd = false);

// Writes code annotations into a buffer and returns it. We cannot write them
// directly into the output since we write function code first (to get the
// offsets for the annotations), and only then can write annotations, which we
Expand Down Expand Up @@ -1558,6 +1566,8 @@ class WasmBinaryWriter {

// General debugging info: track locations as we write.
BinaryLocations binaryLocations;
// Output-only DWARF origins belong to the writer, not the main IR.
std::vector<Debug::InstructionLocation> instructionLocations;
size_t binaryLocationsSizeAtSectionStart;
// Track the expressions that we added for the current function being
// written, so that we can update those specific binary locations when
Expand Down
12 changes: 11 additions & 1 deletion src/wasm-debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#define wasm_wasm_debug_h

#include <string>
#include <vector>

#include "pass.h"
#include "wasm.h"
Expand All @@ -39,8 +40,17 @@ void dumpDWARF(const Module& wasm);
// will disable optimizations that currently cause issues with debug info.)
bool shouldPreserveDWARF(PassOptions& options, Module& wasm);

// Instruction origins collected while emitting the binary, including opcodes
// without a corresponding IR node. Zero denotes an absent source origin.
struct InstructionLocation {
BinaryLocation oldAddr, newAddr;
};

// Update the DWARF sections.
void writeDWARFSections(Module& wasm, const BinaryLocations& newLocations);
void writeDWARFSections(
Module& wasm,
const BinaryLocations& newLocations,
const std::vector<InstructionLocation>& instructionLocations);

} // namespace wasm::Debug

Expand Down
10 changes: 8 additions & 2 deletions src/wasm-stack.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,16 @@ class BinaryInstWriter : public OverriddenVisitor<BinaryInstWriter> {
scratchLocals(parent.getModule()->features) {}

void visit(Expression* curr) {
auto start = o.size();
if (func) {
parent.trackExpressionStart(curr, func);
}
OverriddenVisitor<BinaryInstWriter>::visit(curr);
if (func) {
parent.trackExpressionEnd(curr, func);
if (DWARF) {
parent.trackInstruction(curr, func, start);
}
}
}

Expand All @@ -123,7 +127,9 @@ class BinaryInstWriter : public OverriddenVisitor<BinaryInstWriter> {
void emitScopeEnd(Expression* curr);
// emit an end at the end of a function
void emitFunctionEnd();
void emitUnreachable();
// Standalone writer-inserted traps have no origin. Other traps get their
// origin from the surrounding visit(), including explicit unreachables.
void emitUnreachable(bool generated = false);
void emitUnreachableLocalSet(Index index);
void mapLocalsAndEmitHeader();

Expand Down Expand Up @@ -554,7 +560,7 @@ class BinaryenIRToBinaryWriter
}
writer.emitFunctionEnd();
}
void emitUnreachable() { writer.emitUnreachable(); }
void emitUnreachable() { writer.emitUnreachable(true); }
void emitUnreachableLocalSet(Index index) {
writer.emitUnreachableLocalSet(index);
}
Expand Down
48 changes: 46 additions & 2 deletions src/wasm/wasm-binary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ void WasmBinaryWriter::write() {
// Update DWARF user sections after writing the data they refer to
// (function bodies), and before writing the user sections themselves.
if (Debug::hasDWARFSections(*wasm)) {
Debug::writeDWARFSections(*wasm, binaryLocations);
Debug::writeDWARFSections(*wasm, binaryLocations, instructionLocations);
}
#endif

Expand Down Expand Up @@ -194,6 +194,9 @@ void WasmBinaryWriter::finishSection(int32_t start) {
item -= totalAdjustment;
}
}
for (auto& location : instructionLocations) {
location.newAddr -= totalAdjustment;
}
}
}

Expand Down Expand Up @@ -530,6 +533,7 @@ void WasmBinaryWriter::writeFunctions() {
// Do not smear any debug location from the previous function.
writeNoDebugLocation();
size_t sourceMapLocationsSizeAtFunctionStart = sourceMapLocations.size();
auto instructionLocationsStart = instructionLocations.size();
size_t sizePos = writeU32LEBPlaceholder();
size_t start = o.size();
// Emit Stack IR if present.
Expand Down Expand Up @@ -582,6 +586,10 @@ void WasmBinaryWriter::writeFunctions() {
}
}
}
for (auto i = instructionLocationsStart; i < instructionLocations.size();
++i) {
instructionLocations[i].newAddr -= adjustmentForLEBShrinking;
}
}
// We need to track the function location if we are tracking the locations
// of expressions inside it, or, if it has code annotations (the function
Expand Down Expand Up @@ -1754,7 +1762,35 @@ void WasmBinaryWriter::trackExpressionDelimiter(Expression* curr,
// need to enable that here.
if (func && !func->expressionLocations.empty()) {
binaryLocations.delimiters[curr][id] = o.size();
auto old = func->delimiterLocations.find(curr);
auto oldAddr =
old != func->delimiterLocations.end() && id < old->second.size()
? old->second[id]
: 0;
instructionLocations.push_back({oldAddr, BinaryLocation(o.size())});
}
}

void WasmBinaryWriter::trackInstruction(Expression* curr,
Function* func,
BinaryLocation start,
bool isEnd) {
if (!func || func->expressionLocations.empty() || start == o.size()) {
return;
}
BinaryLocation oldAddr = 0;
if (curr) {
auto old = func->expressionLocations.find(curr);
if (old != func->expressionLocations.end()) {
oldAddr = isEnd ? old->second.end - 1 : old->second.start;
}
} else if (isEnd) {
auto old = func->expressionLocations.find(nullptr);
if (old != func->expressionLocations.end()) {
oldAddr = old->second.start;
}
}
instructionLocations.push_back({oldAddr, start});
}

std::optional<BufferWithRandomAccess> WasmBinaryWriter::writeCodeAnnotations() {
Expand Down Expand Up @@ -3488,6 +3524,12 @@ void WasmBinaryReader::readFunctions() {
}
}

if (needCodeLocations) {
// The terminal end has no expression. Keep its origin in the existing
// position map, separately from the function's range for DWARF DIEs.
auto end = func->funcLocation.end;
func->expressionLocations[nullptr] = {end - 1, end};
}
sourceMapReader.finishFunction();
TypeUpdating::handleNonDefaultableLocals(func.get(), wasm);
currFunction = nullptr;
Expand Down Expand Up @@ -5757,7 +5799,9 @@ void WasmBinaryReader::readExpressionHints(Name sectionName,
std::unordered_map<BinaryLocation, Expression*> locationsMap;

for (auto& [expr, span] : func->expressionLocations) {
locationsMap[span.start] = expr;
if (expr) {
locationsMap[span.start] = expr;
}
}

auto numHints = getU32LEB();
Expand Down
Loading
Loading