Skip to content

Commit 4b7c610

Browse files
authored
Allow DWARF and multivalue together (#6570)
This allows writing of binaries with DWARF info when multivalue is enabled. Currently we just crash when both are enabled together. This just assumes, unless we have run DWARF-invalidating passes, all locals added for tuples or scratch locals would have been added at the end of the local list, so just printing all locals in order would preserve the DWARF info. Tuple locals are expanded in place and scratch locals are added at the end.
1 parent d58c546 commit 4b7c610

File tree

3 files changed

+135
-9
lines changed

3 files changed

+135
-9
lines changed

src/wasm/wasm-stack.cpp

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2568,25 +2568,45 @@ void BinaryInstWriter::mapLocalsAndEmitHeader() {
25682568
for (Index i = 0; i < func->getNumParams(); i++) {
25692569
mappedLocals[std::make_pair(i, 0)] = i;
25702570
}
2571+
25712572
// Normally we map all locals of the same type into a range of adjacent
25722573
// addresses, which is more compact. However, if we need to keep DWARF valid,
25732574
// do not do any reordering at all - instead, do a trivial mapping that
25742575
// keeps everything unmoved.
2576+
//
2577+
// Unless we have run DWARF-invalidating passes, all locals added during the
2578+
// process that are not in DWARF info (tuple locals, tuple scratch locals,
2579+
// locals to resolve stacky format, ..) have been all tacked on to the
2580+
// existing locals and happen at the end, so as long as we print the local
2581+
// types in order, we don't invalidate original local DWARF info here.
25752582
if (DWARF) {
2576-
FindAll<TupleExtract> extracts(func->body);
2577-
if (!extracts.list.empty()) {
2578-
Fatal() << "DWARF + multivalue is not yet complete";
2583+
Index mappedIndex = func->getVarIndexBase();
2584+
for (Index i = func->getVarIndexBase(); i < func->getNumLocals(); i++) {
2585+
size_t size = func->getLocalType(i).size();
2586+
for (Index j = 0; j < size; j++) {
2587+
mappedLocals[std::make_pair(i, j)] = mappedIndex + j;
2588+
}
2589+
mappedIndex += size;
25792590
}
2580-
Index varStart = func->getVarIndexBase();
2581-
Index varEnd = varStart + func->getNumVars();
2582-
o << U32LEB(func->getNumVars());
2583-
for (Index i = varStart; i < varEnd; i++) {
2584-
mappedLocals[std::make_pair(i, 0)] = i;
2591+
countScratchLocals();
2592+
2593+
size_t numBinaryLocals =
2594+
mappedIndex - func->getVarIndexBase() + scratchLocals.size();
2595+
o << U32LEB(numBinaryLocals);
2596+
for (Index i = func->getVarIndexBase(); i < func->getNumLocals(); i++) {
2597+
for (const auto& type : func->getLocalType(i)) {
2598+
o << U32LEB(1);
2599+
parent.writeType(type);
2600+
}
2601+
}
2602+
for (auto& [type, _] : scratchLocals) {
25852603
o << U32LEB(1);
2586-
parent.writeType(func->getLocalType(i));
2604+
parent.writeType(type);
2605+
scratchLocals[type] = mappedIndex++;
25872606
}
25882607
return;
25892608
}
2609+
25902610
for (auto type : func->vars) {
25912611
for (const auto& t : type) {
25922612
noteLocalType(t);
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
;; Test that we handle multivalue + DWARF correctly. When we need to preserve
2+
;; DWARF info, we don't do any local reordering and all newly added locals
3+
;; during parsing/writing are added at the end of the local list.
4+
5+
;; Generated from this c file with the following command:
6+
;; $ emcc -g -Xclang -target-abi -Xclang experimental-mv dwarf-multivalue.c -o dwarf-multivalue.wasm
7+
;;
8+
;; struct MyStruct {
9+
;; int a;
10+
;; float b;
11+
;; };
12+
;;
13+
;; struct MyStruct foo() {
14+
;; struct MyStruct ret = {.a = 3, .b = 3.5};
15+
;; return ret;
16+
;; }
17+
;;
18+
;; void test() {
19+
;; struct MyStruct s = foo();
20+
;; }
21+
;;
22+
;; int main() {
23+
;; test();
24+
;; return 0;
25+
;; }
26+
27+
;; The original wasm file's $test function's locals are as follows:
28+
;; (func $test
29+
;; (local $0 i32)
30+
;; (local $1 i32)
31+
;; (local $2 i32)
32+
;; (local $3 i32)
33+
;; (local $4 f32)
34+
;; (local $5 i32)
35+
;; (local $6 i32)
36+
;; (local $7 i32)
37+
;; (local $8 f32)
38+
;; (local $9 i32)
39+
;; (local $10 f32)
40+
41+
;; If we parse this wasm file into Binaryen IR, two locals are added in the
42+
;; process. Here $11 is added for tuple parsing and $12 is added for stacky IR
43+
;; resolving during binary reading process.
44+
;; RUN: wasm-dis %s.wasm -o - | filecheck %s --check-prefix=ORIG
45+
;; ORIG: (func $test
46+
;; ORIG-NEXT: (local $0 i32)
47+
;; ORIG-NEXT: (local $1 i32)
48+
;; ORIG-NEXT: (local $2 i32)
49+
;; ORIG-NEXT: (local $3 i32)
50+
;; ORIG-NEXT: (local $4 f32)
51+
;; ORIG-NEXT: (local $5 i32)
52+
;; ORIG-NEXT: (local $6 i32)
53+
;; ORIG-NEXT: (local $7 i32)
54+
;; ORIG-NEXT: (local $8 f32)
55+
;; ORIG-NEXT: (local $9 i32)
56+
;; ORIG-NEXT: (local $10 f32)
57+
;; ORIG-NEXT: (local $11 (tuple i32 f32))
58+
;; ORIG-NEXT: (local $12 i32)
59+
60+
;; If we write this IR into binary, even if this cannot be displayed in the wast
61+
;; format, the local order of $test will look like this, because we don't
62+
;; reorder locals:
63+
;; (func $test
64+
;; (local $0 i32)
65+
;; (local $1 i32)
66+
;; (local $2 i32)
67+
;; (local $3 i32)
68+
;; (local $4 f32)
69+
;; (local $5 i32)
70+
;; (local $6 i32)
71+
;; (local $7 i32)
72+
;; (local $8 f32)
73+
;; (local $9 i32)
74+
;; (local $10 f32)
75+
;; (local $11 i32) ;; Previous (local $11 (tuple i32 f32))'s first element
76+
;; (local $12 f32) ;; Previous (local $11 (tuple i32 f32))'s second element
77+
;; (local $13 i32) ;; Previous (local $12 i32)
78+
;; (local $14 f32) ;; scratch local for f32
79+
80+
;; We parse this binary again into Binaryen IR, roundtripping the original
81+
;; binary. Here until (local $14) is the same with the previous list, and $15 is
82+
;; is added for tuple parsing and $16 is added for stacky IR resolving during
83+
;; binary reading process.
84+
;; RUN: wasm-opt -all -g --roundtrip %s.wasm -S -o - | filecheck %s --check-prefix=ROUNDTRIP
85+
;; ROUNDTRIP: (func $test
86+
;; ROUNDTRIP-NEXT: (local $0 i32)
87+
;; ROUNDTRIP-NEXT: (local $1 i32)
88+
;; ROUNDTRIP-NEXT: (local $2 i32)
89+
;; ROUNDTRIP-NEXT: (local $3 i32)
90+
;; ROUNDTRIP-NEXT: (local $4 f32)
91+
;; ROUNDTRIP-NEXT: (local $5 i32)
92+
;; ROUNDTRIP-NEXT: (local $6 i32)
93+
;; ROUNDTRIP-NEXT: (local $7 i32)
94+
;; ROUNDTRIP-NEXT: (local $8 f32)
95+
;; ROUNDTRIP-NEXT: (local $9 i32)
96+
;; ROUNDTRIP-NEXT: (local $10 f32)
97+
;; ROUNDTRIP-NEXT: (local $11 i32)
98+
;; ROUNDTRIP-NEXT: (local $12 f32)
99+
;; ROUNDTRIP-NEXT: (local $13 i32)
100+
;; ROUNDTRIP-NEXT: (local $14 f32)
101+
;; ROUNDTRIP-NEXT: (local $15 (tuple i32 f32))
102+
;; ROUNDTRIP-NEXT: (local $16 i32)
103+
104+
;; We can see that we don't reorder the locals during the process and the
105+
;; original list of locals, local $0~$10, is untouched, to NOT invalidate DWARF
106+
;; info.
5.96 KB
Binary file not shown.

0 commit comments

Comments
 (0)