Skip to content

Commit 058ab50

Browse files
kripkencadcode
authored andcommitted
DWARF: high_pc computation (WebAssembly#2595)
Update high_pc values. These are interesting as they may be a relative offset compared to the low_pc. For functions we already had both a start and an end. Add such tracking for instructions as well.
1 parent 61dbfb7 commit 058ab50

File tree

11 files changed

+151
-66
lines changed

11 files changed

+151
-66
lines changed

src/passes/Print.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1443,8 +1443,8 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
14431443
auto iter = currFunction->expressionLocations.find(curr);
14441444
if (iter != currFunction->expressionLocations.end()) {
14451445
Colors::grey(o);
1446-
o << ";; code offset: 0x" << std::hex << iter->second << std::dec
1447-
<< '\n';
1446+
o << ";; code offset: 0x" << std::hex << iter->second.start
1447+
<< std::dec << '\n';
14481448
restoreNormalColor(o);
14491449
doIndent(o, indent);
14501450
}

src/wasm-binary.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,7 @@ class WasmBinaryWriter {
10141014
void writeSourceMapEpilog();
10151015
void writeDebugLocation(const Function::DebugLocation& loc);
10161016
void writeDebugLocation(Expression* curr, Function* func);
1017+
void writeDebugLocationEnd(Expression* curr, Function* func);
10171018

10181019
// helpers
10191020
void writeInlineString(const char* name);

src/wasm-stack.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ class BinaryInstWriter : public OverriddenVisitor<BinaryInstWriter> {
9292
parent.writeDebugLocation(curr, func);
9393
}
9494
OverriddenVisitor<BinaryInstWriter>::visit(curr);
95+
if (func && !sourceMap) {
96+
parent.writeDebugLocationEnd(curr, func);
97+
}
9598
}
9699

97100
void visitBlock(Block* curr);

src/wasm.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,7 +1167,7 @@ struct BinaryLocations {
11671167
struct Span {
11681168
uint32_t start, end;
11691169
};
1170-
std::unordered_map<Expression*, uint32_t> expressions;
1170+
std::unordered_map<Expression*, Span> expressions;
11711171
std::unordered_map<Function*, Span> functions;
11721172
};
11731173

@@ -1225,7 +1225,7 @@ class Function : public Importable {
12251225
std::set<DebugLocation> epilogLocation;
12261226

12271227
// General debugging info support: track instructions and the function itself.
1228-
std::unordered_map<Expression*, uint32_t> expressionLocations;
1228+
std::unordered_map<Expression*, BinaryLocations::Span> expressionLocations;
12291229
BinaryLocations::Span funcLocation;
12301230

12311231
size_t getNumParams();

src/wasm/wasm-binary.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,8 @@ void WasmBinaryWriter::finishSection(int32_t start) {
160160
// We are relative to the section start.
161161
auto totalAdjustment = adjustmentForLEBShrinking + body;
162162
for (auto& pair : binaryLocations.expressions) {
163-
pair.second -= totalAdjustment;
163+
pair.second.start -= totalAdjustment;
164+
pair.second.end -= totalAdjustment;
164165
}
165166
for (auto& pair : binaryLocations.functions) {
166167
pair.second.start -= totalAdjustment;
@@ -339,7 +340,9 @@ void WasmBinaryWriter::writeFunctions() {
339340
for (auto* curr : binaryLocationTrackedExpressionsForFunc) {
340341
// We added the binary locations, adjust them: they must be relative
341342
// to the code section.
342-
binaryLocations.expressions[curr] -= adjustmentForLEBShrinking;
343+
auto& span = binaryLocations.expressions[curr];
344+
span.start -= adjustmentForLEBShrinking;
345+
span.end -= adjustmentForLEBShrinking;
343346
}
344347
}
345348
if (!binaryLocationTrackedExpressionsForFunc.empty()) {
@@ -708,11 +711,20 @@ void WasmBinaryWriter::writeDebugLocation(Expression* curr, Function* func) {
708711
// If this is an instruction in a function, and if the original wasm had
709712
// binary locations tracked, then track it in the output as well.
710713
if (func && !func->expressionLocations.empty()) {
711-
binaryLocations.expressions[curr] = o.size();
714+
binaryLocations.expressions[curr] =
715+
BinaryLocations::Span{uint32_t(o.size()), 0};
712716
binaryLocationTrackedExpressionsForFunc.push_back(curr);
713717
}
714718
}
715719

720+
void WasmBinaryWriter::writeDebugLocationEnd(Expression* curr, Function* func) {
721+
if (func && !func->expressionLocations.empty()) {
722+
auto& span = binaryLocations.expressions.at(curr);
723+
assert(span.end == 0);
724+
span.end = o.size();
725+
}
726+
}
727+
716728
void WasmBinaryWriter::writeInlineString(const char* name) {
717729
int32_t size = strlen(name);
718730
o << U32LEB(size);
@@ -2293,7 +2305,9 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
22932305
currFunction->debugLocations[curr] = *currDebugLocation.begin();
22942306
}
22952307
if (DWARF && currFunction) {
2296-
currFunction->expressionLocations[curr] = startPos - codeSectionLocation;
2308+
currFunction->expressionLocations[curr] =
2309+
BinaryLocations::Span{uint32_t(startPos - codeSectionLocation),
2310+
uint32_t(pos - codeSectionLocation)};
22972311
}
22982312
}
22992313
BYN_TRACE("zz recurse from " << depth-- << " at " << pos << std::endl);

src/wasm/wasm-debug.cpp

Lines changed: 113 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -328,45 +328,59 @@ struct LineState {
328328
}
329329
};
330330

331-
// Represents a mapping of addresses to expressions.
331+
// Represents a mapping of addresses to expressions. We track beginnings and
332+
// endings of expressions separately, since the end of one (which is one past
333+
// the end in DWARF notation) overlaps with the beginning of the next, and also
334+
// to let us use contextual information (we may know we are looking up the end
335+
// of an instruction).
332336
struct AddrExprMap {
333-
std::unordered_map<uint32_t, Expression*> map;
337+
std::unordered_map<uint32_t, Expression*> startMap;
338+
std::unordered_map<uint32_t, Expression*> endMap;
334339

335340
// Construct the map from the binaryLocations loaded from the wasm.
336341
AddrExprMap(const Module& wasm) {
337342
for (auto& func : wasm.functions) {
338343
for (auto pair : func->expressionLocations) {
339-
assert(map.count(pair.second) == 0);
340-
map[pair.second] = pair.first;
344+
add(pair.first, pair.second);
341345
}
342346
}
343347
}
344348

345349
// Construct the map from new binaryLocations just written
346350
AddrExprMap(const BinaryLocations& newLocations) {
347351
for (auto pair : newLocations.expressions) {
348-
assert(map.count(pair.second) == 0);
349-
map[pair.second] = pair.first;
352+
add(pair.first, pair.second);
350353
}
351354
}
352355

353-
Expression* get(uint32_t addr) const {
354-
auto iter = map.find(addr);
355-
if (iter != map.end()) {
356+
Expression* getStart(uint32_t addr) const {
357+
auto iter = startMap.find(addr);
358+
if (iter != startMap.end()) {
356359
return iter->second;
357360
}
358361
return nullptr;
359362
}
360363

361-
void dump() const {
362-
std::cout << " (size: " << map.size() << ")\n";
363-
for (auto pair : map) {
364-
std::cout << " " << pair.first << " => " << pair.second << '\n';
364+
Expression* getEnd(uint32_t addr) const {
365+
auto iter = endMap.find(addr);
366+
if (iter != endMap.end()) {
367+
return iter->second;
365368
}
369+
return nullptr;
370+
}
371+
372+
private:
373+
void add(Expression* expr, BinaryLocations::Span span) {
374+
assert(startMap.count(span.start) == 0);
375+
startMap[span.start] = expr;
376+
assert(endMap.count(span.end) == 0);
377+
endMap[span.end] = expr;
366378
}
367379
};
368380

369-
// Represents a mapping of addresses to expressions.
381+
// Represents a mapping of addresses to expressions. Note that we use a single
382+
// map for the start and end addresses, since there is no chance of a function's
383+
// start overlapping with another's end (there is the size LEB in the middle).
370384
struct FuncAddrMap {
371385
std::unordered_map<uint32_t, Function*> map;
372386

@@ -415,10 +429,6 @@ struct LocationUpdater {
415429
// TODO: for memory efficiency, we may want to do this in a streaming manner,
416430
// binary to binary, without YAML IR.
417431

418-
// TODO: apparently DWARF offsets may be into the middle of instructions...
419-
// we may need to track their spans too
420-
// https://github.com/WebAssembly/debugging/issues/9#issuecomment-567720872
421-
422432
LocationUpdater(Module& wasm, const BinaryLocations& newLocations)
423433
: wasm(wasm), newLocations(newLocations), oldExprAddrMap(wasm),
424434
newExprAddrMap(newLocations), oldFuncAddrMap(wasm) {}
@@ -427,10 +437,21 @@ struct LocationUpdater {
427437
// address, or if there was but if that instruction no longer exists, return
428438
// 0. Otherwise, return the new updated location.
429439
uint32_t getNewExprAddr(uint32_t oldAddr) const {
430-
if (auto* expr = oldExprAddrMap.get(oldAddr)) {
440+
if (auto* expr = oldExprAddrMap.getStart(oldAddr)) {
431441
auto iter = newLocations.expressions.find(expr);
432442
if (iter != newLocations.expressions.end()) {
433-
uint32_t newAddr = iter->second;
443+
uint32_t newAddr = iter->second.start;
444+
return newAddr;
445+
}
446+
}
447+
return 0;
448+
}
449+
450+
uint32_t getNewExprEndAddr(uint32_t oldAddr) const {
451+
if (auto* expr = oldExprAddrMap.getEnd(oldAddr)) {
452+
auto iter = newLocations.expressions.find(expr);
453+
if (iter != newLocations.expressions.end()) {
454+
uint32_t newAddr = iter->second.end;
434455
return newAddr;
435456
}
436457
}
@@ -529,6 +550,76 @@ static void iterContextAndYAML(const T& contextList, U& yamlList, W func) {
529550
assert(yamlValue == yamlList.end());
530551
}
531552

553+
static void updateDIE(const llvm::DWARFDebugInfoEntry& DIE,
554+
llvm::DWARFYAML::Entry& yamlEntry,
555+
const llvm::DWARFAbbreviationDeclaration* abbrevDecl,
556+
const LocationUpdater& locationUpdater) {
557+
auto tag = DIE.getTag();
558+
// Pairs of low/high_pc require some special handling, as the high
559+
// may be an offset relative to the low. First, process the low_pcs.
560+
uint32_t oldLowPC = 0, newLowPC = 0;
561+
iterContextAndYAML(
562+
abbrevDecl->attributes(),
563+
yamlEntry.Values,
564+
[&](const llvm::DWARFAbbreviationDeclaration::AttributeSpec& attrSpec,
565+
llvm::DWARFYAML::FormValue& yamlValue) {
566+
auto attr = attrSpec.Attr;
567+
if (attr != llvm::dwarf::DW_AT_low_pc) {
568+
return;
569+
}
570+
uint32_t oldValue = yamlValue.Value, newValue = 0;
571+
if (tag == llvm::dwarf::DW_TAG_GNU_call_site ||
572+
tag == llvm::dwarf::DW_TAG_inlined_subroutine ||
573+
tag == llvm::dwarf::DW_TAG_lexical_block ||
574+
tag == llvm::dwarf::DW_TAG_label) {
575+
newValue = locationUpdater.getNewExprAddr(oldValue);
576+
} else if (tag == llvm::dwarf::DW_TAG_compile_unit ||
577+
tag == llvm::dwarf::DW_TAG_subprogram) {
578+
newValue = locationUpdater.getNewFuncAddr(oldValue);
579+
} else {
580+
Fatal() << "unknown tag with low_pc "
581+
<< llvm::dwarf::TagString(tag).str();
582+
}
583+
oldLowPC = oldValue;
584+
newLowPC = newValue;
585+
yamlValue.Value = newValue;
586+
});
587+
// Next, process the high_pcs.
588+
// TODO: do this more efficiently, without a second traversal (but that's a
589+
// little tricky given the special double-traversal we have).
590+
iterContextAndYAML(
591+
abbrevDecl->attributes(),
592+
yamlEntry.Values,
593+
[&](const llvm::DWARFAbbreviationDeclaration::AttributeSpec& attrSpec,
594+
llvm::DWARFYAML::FormValue& yamlValue) {
595+
auto attr = attrSpec.Attr;
596+
if (attr != llvm::dwarf::DW_AT_high_pc) {
597+
return;
598+
}
599+
uint32_t oldValue = yamlValue.Value, newValue = 0;
600+
bool isRelative = attrSpec.Form == llvm::dwarf::DW_FORM_data4;
601+
if (isRelative) {
602+
oldValue += oldLowPC;
603+
}
604+
if (tag == llvm::dwarf::DW_TAG_GNU_call_site ||
605+
tag == llvm::dwarf::DW_TAG_inlined_subroutine ||
606+
tag == llvm::dwarf::DW_TAG_lexical_block ||
607+
tag == llvm::dwarf::DW_TAG_label) {
608+
newValue = locationUpdater.getNewExprEndAddr(oldValue);
609+
} else if (tag == llvm::dwarf::DW_TAG_compile_unit ||
610+
tag == llvm::dwarf::DW_TAG_subprogram) {
611+
newValue = locationUpdater.getNewFuncAddr(oldValue);
612+
} else {
613+
Fatal() << "unknown tag with low_pc "
614+
<< llvm::dwarf::TagString(tag).str();
615+
}
616+
if (isRelative) {
617+
newValue -= newLowPC;
618+
}
619+
yamlValue.Value = newValue;
620+
});
621+
}
622+
532623
static void updateCompileUnits(const BinaryenDWARFInfo& info,
533624
llvm::DWARFYAML::Data& yaml,
534625
const LocationUpdater& locationUpdater) {
@@ -545,36 +636,12 @@ static void updateCompileUnits(const BinaryenDWARFInfo& info,
545636
yamlUnit.Entries,
546637
[&](const llvm::DWARFDebugInfoEntry& DIE,
547638
llvm::DWARFYAML::Entry& yamlEntry) {
548-
auto tag = DIE.getTag();
549639
// Process the entries in each relevant DIE, looking for attributes to
550640
// change.
551641
auto abbrevDecl = DIE.getAbbreviationDeclarationPtr();
552642
if (abbrevDecl) {
553-
iterContextAndYAML(
554-
abbrevDecl->attributes(),
555-
yamlEntry.Values,
556-
[&](const llvm::DWARFAbbreviationDeclaration::AttributeSpec&
557-
attrSpec,
558-
llvm::DWARFYAML::FormValue& yamlValue) {
559-
if (attrSpec.Attr == llvm::dwarf::DW_AT_low_pc) {
560-
if (tag == llvm::dwarf::DW_TAG_GNU_call_site ||
561-
tag == llvm::dwarf::DW_TAG_inlined_subroutine ||
562-
tag == llvm::dwarf::DW_TAG_lexical_block ||
563-
tag == llvm::dwarf::DW_TAG_label) {
564-
// low_pc in certain tags represent expressions.
565-
yamlValue.Value =
566-
locationUpdater.getNewExprAddr(yamlValue.Value);
567-
} else if (tag == llvm::dwarf::DW_TAG_compile_unit ||
568-
tag == llvm::dwarf::DW_TAG_subprogram) {
569-
// low_pc in certain tags represent function.
570-
yamlValue.Value =
571-
locationUpdater.getNewFuncAddr(yamlValue.Value);
572-
} else {
573-
Fatal() << "unknown tag with low_pc "
574-
<< llvm::dwarf::TagString(tag).str();
575-
}
576-
}
577-
});
643+
// This is relevant; look for things to update.
644+
updateDIE(DIE, yamlEntry, abbrevDecl, locationUpdater);
578645
}
579646
});
580647
});

test/passes/dwarfdump_roundtrip_dwarfdump.bin.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,11 @@ Abbrev table for offset: 0x00000000
138138
DW_AT_stmt_list [DW_FORM_sec_offset] (0x00000000)
139139
DW_AT_comp_dir [DW_FORM_strp] ( .debug_str[0x0000009b] = "/usr/local/google/home/azakai/Dev/emscripten")
140140
DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000)
141-
DW_AT_high_pc [DW_FORM_data4] (0x00000002)
141+
DW_AT_high_pc [DW_FORM_data4] (0x00000000)
142142

143143
0x00000026: DW_TAG_subprogram [2]
144144
DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000)
145-
DW_AT_high_pc [DW_FORM_data4] (0x00000002)
145+
DW_AT_high_pc [DW_FORM_data4] (0x00000000)
146146
DW_AT_linkage_name [DW_FORM_strp] ( .debug_str[0x000000c8] = "_Z3foov")
147147
DW_AT_name [DW_FORM_strp] ( .debug_str[0x000000d0] = "foo")
148148
DW_AT_decl_file [DW_FORM_data1] ("/usr/local/google/home/azakai/Dev/emscripten/a.cpp")

test/passes/fannkuch3.bin.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2534,7 +2534,7 @@ Abbrev table for offset: 0x00000000
25342534

25352535
0x00000082: DW_TAG_subprogram [10] *
25362536
DW_AT_low_pc [DW_FORM_addr] (0x0000000000000006)
2537-
DW_AT_high_pc [DW_FORM_data4] (0x00000397)
2537+
DW_AT_high_pc [DW_FORM_data4] (0x00000383)
25382538
DW_AT_frame_base [DW_FORM_exprloc] (DW_OP_WASM_location 0x1 +0, DW_OP_stack_value)
25392539
DW_AT_GNU_all_call_sites [DW_FORM_flag_present] (true)
25402540
DW_AT_linkage_name [DW_FORM_strp] ( .debug_str[0x00000166] = "_Z15fannkuch_workerPv")
@@ -2818,7 +2818,7 @@ Abbrev table for offset: 0x00000000
28182818

28192819
0x0000023b: DW_TAG_subprogram [23] *
28202820
DW_AT_low_pc [DW_FORM_addr] (0x000000000000038b)
2821-
DW_AT_high_pc [DW_FORM_data4] (0x00000342)
2821+
DW_AT_high_pc [DW_FORM_data4] (0x000002fb)
28222822
DW_AT_frame_base [DW_FORM_exprloc] (DW_OP_WASM_location 0x0 +2, DW_OP_stack_value)
28232823
DW_AT_GNU_all_call_sites [DW_FORM_flag_present] (true)
28242824
DW_AT_name [DW_FORM_strp] ( .debug_str[0x0000018c] = "main")
@@ -2851,7 +2851,7 @@ Abbrev table for offset: 0x00000000
28512851
0x00000278: DW_TAG_inlined_subroutine [24] *
28522852
DW_AT_abstract_origin [DW_FORM_ref4] (cu + 0x01a8 => {0x000001a8} "_ZL8fannkuchi")
28532853
DW_AT_low_pc [DW_FORM_addr] (0x00000000000003c6)
2854-
DW_AT_high_pc [DW_FORM_data4] (0x000002c8)
2854+
DW_AT_high_pc [DW_FORM_data4] (0x0000026d)
28552855
DW_AT_call_file [DW_FORM_data1] ("/usr/local/google/home/azakai/Dev/emscripten/tests/fannkuch.cpp")
28562856
DW_AT_call_line [DW_FORM_data1] (159)
28572857
DW_AT_call_column [DW_FORM_data1] (0x29)

test/passes/fannkuch3_manyopts.bin.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2534,7 +2534,7 @@ Abbrev table for offset: 0x00000000
25342534

25352535
0x00000082: DW_TAG_subprogram [10] *
25362536
DW_AT_low_pc [DW_FORM_addr] (0x0000000000000007)
2537-
DW_AT_high_pc [DW_FORM_data4] (0x00000397)
2537+
DW_AT_high_pc [DW_FORM_data4] (0x00000353)
25382538
DW_AT_frame_base [DW_FORM_exprloc] (DW_OP_WASM_location 0x1 +0, DW_OP_stack_value)
25392539
DW_AT_GNU_all_call_sites [DW_FORM_flag_present] (true)
25402540
DW_AT_linkage_name [DW_FORM_strp] ( .debug_str[0x00000166] = "_Z15fannkuch_workerPv")
@@ -2818,7 +2818,7 @@ Abbrev table for offset: 0x00000000
28182818

28192819
0x0000023b: DW_TAG_subprogram [23] *
28202820
DW_AT_low_pc [DW_FORM_addr] (0x000000000000035c)
2821-
DW_AT_high_pc [DW_FORM_data4] (0x00000342)
2821+
DW_AT_high_pc [DW_FORM_data4] (0x000002cf)
28222822
DW_AT_frame_base [DW_FORM_exprloc] (DW_OP_WASM_location 0x0 +2, DW_OP_stack_value)
28232823
DW_AT_GNU_all_call_sites [DW_FORM_flag_present] (true)
28242824
DW_AT_name [DW_FORM_strp] ( .debug_str[0x0000018c] = "main")
@@ -2851,7 +2851,7 @@ Abbrev table for offset: 0x00000000
28512851
0x00000278: DW_TAG_inlined_subroutine [24] *
28522852
DW_AT_abstract_origin [DW_FORM_ref4] (cu + 0x01a8 => {0x000001a8} "_ZL8fannkuchi")
28532853
DW_AT_low_pc [DW_FORM_addr] (0x0000000000000000)
2854-
DW_AT_high_pc [DW_FORM_data4] (0x000002c8)
2854+
DW_AT_high_pc [DW_FORM_data4] (0x00000000)
28552855
DW_AT_call_file [DW_FORM_data1] ("/usr/local/google/home/azakai/Dev/emscripten/tests/fannkuch.cpp")
28562856
DW_AT_call_line [DW_FORM_data1] (159)
28572857
DW_AT_call_column [DW_FORM_data1] (0x29)

0 commit comments

Comments
 (0)