From c93fc30d548aa9c9c62ebf07e47fc176c67e522b Mon Sep 17 00:00:00 2001 From: nishad shabbir Date: Wed, 19 Aug 2026 18:52:30 +0530 Subject: [PATCH] Write init expressions folded so they can be parsed back WriteInitExpr wrapped the whole expression list in a single pair of parentheses and then wrote the instructions unfolded. That is fine for the usual single-instruction init expression, but an extended constant expression has several instructions, and the result cannot be parsed: (global (;1;) (mut i32) (i32.const 44 i32.const 3 i32.sub)) wasm2wat produced this for a module using the extended-const proposal, so its output no longer assembled. Globals, and data and elem offsets, were all affected, and --fold-exprs made no difference because init expressions never went through the folded writer. Write them with WriteFoldedExprList instead, which emits a single folded expression that is valid in all of those positions: (global (;1;) (mut i32) (i32.sub (i32.const 44) (i32.const 3))) Single-instruction init expressions are unchanged. run-roundtrip.py did not accept --enable-extended-const, which is why this was never covered; add the flag along with a roundtrip test. --- src/wat-writer.cc | 10 ++++++---- test/roundtrip/extended-const.txt | 30 ++++++++++++++++++++++++++++++ test/run-roundtrip.py | 3 +++ 3 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 test/roundtrip/extended-const.txt diff --git a/src/wat-writer.cc b/src/wat-writer.cc index 77ac71aaef..edf49bf2aa 100644 --- a/src/wat-writer.cc +++ b/src/wat-writer.cc @@ -1439,11 +1439,13 @@ void WatWriter::FlushExprTreeStack() { void WatWriter::WriteInitExpr(const ExprList& expr) { if (!expr.empty()) { - WritePuts("(", NextChar::None); - WriteExprList(expr); + /* Init expressions are always written folded. These positions accept a + * single folded expression, so wrapping a sequence of instructions in one + * pair of parentheses would produce output that cannot be parsed back. */ + WriteFoldedExprList(expr); + FlushExprTreeStack(); /* clear the next char, so we don't write a newline after the expr */ - next_char_ = NextChar::None; - WritePuts(")", NextChar::Space); + next_char_ = NextChar::Space; } } diff --git a/test/roundtrip/extended-const.txt b/test/roundtrip/extended-const.txt new file mode 100644 index 0000000000..1baaa5fedf --- /dev/null +++ b/test/roundtrip/extended-const.txt @@ -0,0 +1,30 @@ +;;; TOOL: run-roundtrip +;;; ARGS: --stdout --enable-extended-const +(module + (global $g_import (import "foo" "bar") i32) + (memory 1) + (table 1 funcref) + (func) + (global (mut i32) (i32.sub (i32.const 44) (i32.const 3))) + (global i32 (i32.const 45)) + (data (i32.add (global.get $g_import) (i32.const 42)) "hello") + (elem (i32.mul (i32.const 4) (global.get $g_import)) func 0) +) +(;; STDOUT ;;; +(module + (type (;0;) (func)) + (import "foo" "bar" (global (;0;) i32)) + (func (;0;) (type 0)) + (table (;0;) 1 funcref) + (memory (;0;) 1) + (global (;1;) (mut i32) (i32.sub + (i32.const 44) + (i32.const 3))) + (global (;2;) i32 (i32.const 45)) + (elem (;0;) (i32.mul + (i32.const 4) + (global.get 0)) func 0) + (data (;0;) (i32.add + (global.get 0) + (i32.const 42)) "hello")) +;;; STDOUT ;;) diff --git a/test/run-roundtrip.py b/test/run-roundtrip.py index 95e1e0a7b3..75ce053877 100755 --- a/test/run-roundtrip.py +++ b/test/run-roundtrip.py @@ -117,6 +117,7 @@ def main(args): parser.add_argument('--enable-annotations', action='store_true') parser.add_argument('--enable-code-metadata', action='store_true') parser.add_argument('--enable-custom-page-sizes', action='store_true') + parser.add_argument('--enable-extended-const', action='store_true') # --inline-exports can reorder exports, so skip roundtrip check parser.add_argument('--inline-exports', action='store_true', help="write exports inline and skip end-to-end roundtrip check") @@ -144,6 +145,7 @@ def main(args): '--enable-annotations': options.enable_annotations, '--enable-code-metadata': options.enable_code_metadata, '--enable-custom-page-sizes': options.enable_custom_page_sizes, + '--enable-extended-const': options.enable_extended_const, '--reloc': options.reloc, '--no-check': options.no_check, }) @@ -167,6 +169,7 @@ def main(args): '--enable-annotations': options.enable_annotations, '--enable-code-metadata': options.enable_code_metadata, '--enable-custom-page-sizes': options.enable_custom_page_sizes, + '--enable-extended-const': options.enable_extended_const, '--inline-exports': options.inline_exports, '--inline-imports': options.inline_imports, '--no-debug-names': not options.debug_names,