Skip to content

Commit

Permalink
Allow DWARF and multivalue together (WebAssembly#6570)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
aheejin authored May 6, 2024
1 parent d58c546 commit 4b7c610
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 9 deletions.
38 changes: 29 additions & 9 deletions src/wasm/wasm-stack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2568,25 +2568,45 @@ void BinaryInstWriter::mapLocalsAndEmitHeader() {
for (Index i = 0; i < func->getNumParams(); i++) {
mappedLocals[std::make_pair(i, 0)] = i;
}

// Normally we map all locals of the same type into a range of adjacent
// addresses, which is more compact. However, if we need to keep DWARF valid,
// do not do any reordering at all - instead, do a trivial mapping that
// keeps everything unmoved.
//
// Unless we have run DWARF-invalidating passes, all locals added during the
// process that are not in DWARF info (tuple locals, tuple scratch locals,
// locals to resolve stacky format, ..) have been all tacked on to the
// existing locals and happen at the end, so as long as we print the local
// types in order, we don't invalidate original local DWARF info here.
if (DWARF) {
FindAll<TupleExtract> extracts(func->body);
if (!extracts.list.empty()) {
Fatal() << "DWARF + multivalue is not yet complete";
Index mappedIndex = func->getVarIndexBase();
for (Index i = func->getVarIndexBase(); i < func->getNumLocals(); i++) {
size_t size = func->getLocalType(i).size();
for (Index j = 0; j < size; j++) {
mappedLocals[std::make_pair(i, j)] = mappedIndex + j;
}
mappedIndex += size;
}
Index varStart = func->getVarIndexBase();
Index varEnd = varStart + func->getNumVars();
o << U32LEB(func->getNumVars());
for (Index i = varStart; i < varEnd; i++) {
mappedLocals[std::make_pair(i, 0)] = i;
countScratchLocals();

size_t numBinaryLocals =
mappedIndex - func->getVarIndexBase() + scratchLocals.size();
o << U32LEB(numBinaryLocals);
for (Index i = func->getVarIndexBase(); i < func->getNumLocals(); i++) {
for (const auto& type : func->getLocalType(i)) {
o << U32LEB(1);
parent.writeType(type);
}
}
for (auto& [type, _] : scratchLocals) {
o << U32LEB(1);
parent.writeType(func->getLocalType(i));
parent.writeType(type);
scratchLocals[type] = mappedIndex++;
}
return;
}

for (auto type : func->vars) {
for (const auto& t : type) {
noteLocalType(t);
Expand Down
106 changes: 106 additions & 0 deletions test/lit/binary/dwarf-multivalue.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
;; Test that we handle multivalue + DWARF correctly. When we need to preserve
;; DWARF info, we don't do any local reordering and all newly added locals
;; during parsing/writing are added at the end of the local list.

;; Generated from this c file with the following command:
;; $ emcc -g -Xclang -target-abi -Xclang experimental-mv dwarf-multivalue.c -o dwarf-multivalue.wasm
;;
;; struct MyStruct {
;; int a;
;; float b;
;; };
;;
;; struct MyStruct foo() {
;; struct MyStruct ret = {.a = 3, .b = 3.5};
;; return ret;
;; }
;;
;; void test() {
;; struct MyStruct s = foo();
;; }
;;
;; int main() {
;; test();
;; return 0;
;; }

;; The original wasm file's $test function's locals are as follows:
;; (func $test
;; (local $0 i32)
;; (local $1 i32)
;; (local $2 i32)
;; (local $3 i32)
;; (local $4 f32)
;; (local $5 i32)
;; (local $6 i32)
;; (local $7 i32)
;; (local $8 f32)
;; (local $9 i32)
;; (local $10 f32)

;; If we parse this wasm file into Binaryen IR, two locals are added in the
;; process. Here $11 is added for tuple parsing and $12 is added for stacky IR
;; resolving during binary reading process.
;; RUN: wasm-dis %s.wasm -o - | filecheck %s --check-prefix=ORIG
;; ORIG: (func $test
;; ORIG-NEXT: (local $0 i32)
;; ORIG-NEXT: (local $1 i32)
;; ORIG-NEXT: (local $2 i32)
;; ORIG-NEXT: (local $3 i32)
;; ORIG-NEXT: (local $4 f32)
;; ORIG-NEXT: (local $5 i32)
;; ORIG-NEXT: (local $6 i32)
;; ORIG-NEXT: (local $7 i32)
;; ORIG-NEXT: (local $8 f32)
;; ORIG-NEXT: (local $9 i32)
;; ORIG-NEXT: (local $10 f32)
;; ORIG-NEXT: (local $11 (tuple i32 f32))
;; ORIG-NEXT: (local $12 i32)

;; If we write this IR into binary, even if this cannot be displayed in the wast
;; format, the local order of $test will look like this, because we don't
;; reorder locals:
;; (func $test
;; (local $0 i32)
;; (local $1 i32)
;; (local $2 i32)
;; (local $3 i32)
;; (local $4 f32)
;; (local $5 i32)
;; (local $6 i32)
;; (local $7 i32)
;; (local $8 f32)
;; (local $9 i32)
;; (local $10 f32)
;; (local $11 i32) ;; Previous (local $11 (tuple i32 f32))'s first element
;; (local $12 f32) ;; Previous (local $11 (tuple i32 f32))'s second element
;; (local $13 i32) ;; Previous (local $12 i32)
;; (local $14 f32) ;; scratch local for f32

;; We parse this binary again into Binaryen IR, roundtripping the original
;; binary. Here until (local $14) is the same with the previous list, and $15 is
;; is added for tuple parsing and $16 is added for stacky IR resolving during
;; binary reading process.
;; RUN: wasm-opt -all -g --roundtrip %s.wasm -S -o - | filecheck %s --check-prefix=ROUNDTRIP
;; ROUNDTRIP: (func $test
;; ROUNDTRIP-NEXT: (local $0 i32)
;; ROUNDTRIP-NEXT: (local $1 i32)
;; ROUNDTRIP-NEXT: (local $2 i32)
;; ROUNDTRIP-NEXT: (local $3 i32)
;; ROUNDTRIP-NEXT: (local $4 f32)
;; ROUNDTRIP-NEXT: (local $5 i32)
;; ROUNDTRIP-NEXT: (local $6 i32)
;; ROUNDTRIP-NEXT: (local $7 i32)
;; ROUNDTRIP-NEXT: (local $8 f32)
;; ROUNDTRIP-NEXT: (local $9 i32)
;; ROUNDTRIP-NEXT: (local $10 f32)
;; ROUNDTRIP-NEXT: (local $11 i32)
;; ROUNDTRIP-NEXT: (local $12 f32)
;; ROUNDTRIP-NEXT: (local $13 i32)
;; ROUNDTRIP-NEXT: (local $14 f32)
;; ROUNDTRIP-NEXT: (local $15 (tuple i32 f32))
;; ROUNDTRIP-NEXT: (local $16 i32)

;; We can see that we don't reorder the locals during the process and the
;; original list of locals, local $0~$10, is untouched, to NOT invalidate DWARF
;; info.
Binary file added test/lit/binary/dwarf-multivalue.test.wasm
Binary file not shown.

0 comments on commit 4b7c610

Please sign in to comment.