From ada32e4825cfd10382ce2767377f162b6bb193ec Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 11:48:06 -0700 Subject: [PATCH 01/21] go --- src/ir/intrinsics.h | 7 ++- src/parser/contexts.h | 2 + src/passes/Print.cpp | 11 ++++ src/passes/StripToolchainAnnotations.cpp | 1 + src/wasm-binary.h | 2 + src/wasm.h | 11 ++++ src/wasm/wasm-binary.cpp | 78 ++++++++++++++---------- src/wasm/wasm-ir-builder.cpp | 5 ++ src/wasm/wasm.cpp | 1 + 9 files changed, 85 insertions(+), 33 deletions(-) diff --git a/src/ir/intrinsics.h b/src/ir/intrinsics.h index 2f6809c826b..d5654862e73 100644 --- a/src/ir/intrinsics.h +++ b/src/ir/intrinsics.h @@ -141,8 +141,8 @@ class Intrinsics { } // Given a call in a function, return all the annotations for it. The call may - // be annotated itself (which takes precedence), or the function it calls be - // annotated. + // be annotated itself (which takes precedence), or the function it calls may + // be annotated. CodeAnnotation getCallAnnotations(Call* call, Function* func) { // Combine annotations from the call itself and from the called function. auto ret = getAnnotations(call, func); @@ -168,6 +168,9 @@ class Intrinsics { if (!ret.idempotent) { ret.idempotent = funcAnnotations.idempotent; } + if (!ret.toolchainInline) { + ret.toolchainInline = funcAnnotations.toolchainInline; + } } return ret; diff --git a/src/parser/contexts.h b/src/parser/contexts.h index 06a515e32d1..3ea68de0b76 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -1372,6 +1372,8 @@ struct AnnotationParserCtx { ret.jsCalled = true; } else if (a.kind == Annotations::IdempotentHint) { ret.idempotent = true; + } else if (a.kind == Annotations::ToolchainInlineHint) { + ret.toolchainInline = true; } } diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 86ce8595683..b899e979453 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2838,6 +2838,17 @@ void PrintSExpression::printCodeAnnotations(const CodeAnnotation& annotation) { restoreNormalColor(o); doIndent(o, indent); } + if (annotation.toolchainInline) { + Colors::grey(o); + std::ofstream saved; + saved.copyfmt(o); + o << "(@" << Annotations::ToolchainInlineHint << " \"\\" << std::hex + << std::setfill('0') << std::setw(2) << int(*annotation.inline_) + << "\")\n"; + o.copyfmt(saved); + restoreNormalColor(o); + doIndent(o, indent); + } } void PrintSExpression::printExpressionContents(Expression* curr) { diff --git a/src/passes/StripToolchainAnnotations.cpp b/src/passes/StripToolchainAnnotations.cpp index c2f61a74460..d1e17995add 100644 --- a/src/passes/StripToolchainAnnotations.cpp +++ b/src/passes/StripToolchainAnnotations.cpp @@ -59,6 +59,7 @@ struct StripToolchainAnnotations annotation.removableIfUnused = false; annotation.jsCalled = false; annotation.idempotent = false; + annotation.toolchainInline = std::nullopt; } }; diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 92bb588d53c..225bb490752 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1480,6 +1480,7 @@ class WasmBinaryWriter { std::optional getRemovableIfUnusedHintsBuffer(); std::optional getJSCalledHintsBuffer(); std::optional getIdempotentHintsBuffer(); + std::optional getToolchainInlineHintsBuffer(); // helpers void writeInlineString(std::string_view name); @@ -1780,6 +1781,7 @@ class WasmBinaryReader { void readRemovableIfUnusedHints(size_t payloadLen); void readJSCalledHints(size_t payloadLen); void readIdempotentHints(size_t payloadLen); + void readToolchainInlineHints(size_t payloadLen); std::tuple readMemoryAccess(bool isAtomic, bool isRMW); diff --git a/src/wasm.h b/src/wasm.h index 9f81336144d..12cc3df7e98 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -2378,6 +2378,11 @@ struct CodeAnnotation { // optimize things like Java class constructors. bool idempotent = false; + // An inlining hint at the toolchain level, in contrast to inline_, above, + // which is for VMs. (E.g., one may want to not inline at the toolchain level + // to keep size small, and tell VMs to inline at runtime.) + std::optional toolchainInline = false; + bool operator==(const CodeAnnotation& other) const { return equalOnSemanticsPreserving(other) && equalOnSemanticsAltering(other); } @@ -2477,6 +2482,12 @@ class Function : public Importable { bool noFullInline = false; bool noPartialInline = false; + // Whether to always inline a function, despite other flags like optimizing + // for size (except for things that prevent inlining for correctness reasons, + // like infinite recursion). + bool alwaysFullInline = false; + // TODO: alwaysPartialInline, if that is useful? + // Methods Signature getSig() { return type.getHeapType().getSignature(); } Type getParams() { return getSig().params; } diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 49497af127a..ab1a6c337ef 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -1662,6 +1662,7 @@ std::optional WasmBinaryWriter::writeCodeAnnotations() { append(getRemovableIfUnusedHintsBuffer()); append(getJSCalledHintsBuffer()); append(getIdempotentHintsBuffer()); + append(getToolchainInlineHintsBuffer()); return ret; } @@ -1783,23 +1784,24 @@ std::optional WasmBinaryWriter::getBranchHintsBuffer() { }); } -std::optional WasmBinaryWriter::getInlineHintsBuffer() { - return writeExpressionHints( - Annotations::InlineHint, - [](const CodeAnnotation& annotation) { return annotation.inline_; }, - [](const CodeAnnotation& annotation, BufferWithRandomAccess& buffer) { - // Hint size, always 1 for now. - buffer << U32LEB(1); - - // We must only emit hints that are present. - assert(annotation.inline_); - - // Hint must fit in one byte. - assert(*annotation.inline_ <= 127); - - // Hint contents: inline frequency count - buffer << U32LEB(*annotation.inline_); +// Writes a simple i7 hint, in the range [0..127]. +#define WRITE_I7_HINT(code, field) \ + return writeExpressionHints( \ + code, \ + [](const CodeAnnotation& annotation) { return annotation.field; }, \ + [](const CodeAnnotation& annotation, BufferWithRandomAccess& buffer) { \ + /* Hint size, always 1 for now. */ \ + buffer << U32LEB(1); \ + /* We must only emit hints that are present. */ \ + assert(annotation.field); \ + /* Hint must fit in one byte. */ \ + assert(*annotation.field <= 127); \ + /* Hint contents: inline frequency count. */ \ + buffer << U32LEB(*annotation.field); \ }); + +std::optional WasmBinaryWriter::getInlineHintsBuffer() { + WRITE_I7_HINT(Annotations::InlineHint, inline_); } // Writes a simple boolean hint of size 0. Receives the code and the field name @@ -1827,6 +1829,10 @@ WasmBinaryWriter::getIdempotentHintsBuffer() { WRITE_BOOLEAN_HINT(Annotations::IdempotentHint, idempotent); } +std::optional WasmBinaryWriter::getToolchainInlineHintsBuffer() { + WRITE_I7_HINT(Annotations::ToolchainInlineHint, toolchainInline); +} + void WasmBinaryWriter::writeData(const char* data, size_t size) { for (size_t i = 0; i < size; i++) { o << int8_t(data[i]); @@ -2103,7 +2109,8 @@ void WasmBinaryReader::preScan() { sectionName == Annotations::InlineHint || sectionName == Annotations::RemovableIfUnusedHint || sectionName == Annotations::JSCalledHint || - sectionName == Annotations::IdempotentHint) { + sectionName == Annotations::IdempotentHint || + sectionName == Annotations::ToolchainInlineHint) { // Code annotations require code locations. // TODO: We could note which functions require code locations, as an // optimization. @@ -2271,6 +2278,9 @@ void WasmBinaryReader::readCustomSection(size_t payloadLen) { } else if (sectionName == Annotations::IdempotentHint) { deferredAnnotationSections.push_back(AnnotationSectionInfo{ pos, [this, payloadLen]() { this->readIdempotentHints(payloadLen); }}); + } else if (sectionName == Annotations::ToolchainInlineHint) { + deferredAnnotationSections.push_back(AnnotationSectionInfo{ + pos, [this, payloadLen]() { this->readToolchainInlineHints(payloadLen); }}); } else { // an unfamiliar custom section if (sectionName.equals(BinaryConsts::CustomSections::Linking)) { @@ -5617,21 +5627,23 @@ void WasmBinaryReader::readBranchHints(size_t payloadLen) { }); } -void WasmBinaryReader::readInlineHints(size_t payloadLen) { - readExpressionHints( - Annotations::InlineHint, payloadLen, [&](CodeAnnotation& annotation) { - auto size = getU32LEB(); - if (size != 1) { - throwError("bad InlineHint size"); - } - - uint8_t inline_ = getInt8(); - if (inline_ > 127) { - throwError("bad InlineHint value"); - } - - annotation.inline_ = inline_; +// Reads a simple i7 hint, in the range [0..127]. +#define READ_I7_HINT(code, field) \ + readExpressionHints( \ + , payloadLen, [&](CodeAnnotation& annotation) { \ + auto size = getU32LEB(); \ + if (size != 1) { \ + throwError("bad InlineHint size"); \ + } \ + uint8_t field = getInt8(); \ + if (field > 127) { \ + throwError("bad InlineHint value"); \ + } \ + annotation.field = field; \ }); + +void WasmBinaryReader::readInlineHints(size_t payloadLen) { + READ_I7_HINT(Annotations::InlineHint, inline_); } // Reads a simple boolean hint of size 0. Receives the code and the field name @@ -5657,6 +5669,10 @@ void WasmBinaryReader::readIdempotentHints(size_t payloadLen) { READ_BOOLEAN_HINT(Annotations::IdempotentHint, idempotent); } +void WasmBinaryReader::readToolchainInlineHints(size_t payloadLen) { + READ_I7_HINT(Annotations::ToolchainInlineHint, toolchainInline); +} + std::tuple WasmBinaryReader::readMemoryAccess(bool isAtomic, bool isRMW) { auto rawAlignment = getU32LEB(); diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index 8e924414587..c729796d0a9 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -2863,6 +2863,11 @@ void IRBuilder::applyAnnotations(Expression* expr, assert(func); func->codeAnnotations[expr].idempotent = true; } + + if (annotation.toolchainInline) { + assert(func); + func->codeAnnotations[expr].toolchainInline = true; + } } } // namespace wasm diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 85c308ea645..dc8b8f7ae36 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -90,6 +90,7 @@ const Name InlineHint = "metadata.code.inline"; const Name RemovableIfUnusedHint = "binaryen.removable.if.unused"; const Name JSCalledHint = "binaryen.js.called"; const Name IdempotentHint = "binaryen.idempotent"; +const Name ToolchainInlinetHint = "binaryen.inline"; } // namespace Annotations From b6cfacda0df1fe722eb86f137c2550122613220f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 11:48:11 -0700 Subject: [PATCH 02/21] form --- src/wasm/wasm-binary.cpp | 60 +++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index ab1a6c337ef..69b38c3caf8 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -1785,19 +1785,19 @@ std::optional WasmBinaryWriter::getBranchHintsBuffer() { } // Writes a simple i7 hint, in the range [0..127]. -#define WRITE_I7_HINT(code, field) \ - return writeExpressionHints( \ - code, \ - [](const CodeAnnotation& annotation) { return annotation.field; }, \ - [](const CodeAnnotation& annotation, BufferWithRandomAccess& buffer) { \ - /* Hint size, always 1 for now. */ \ - buffer << U32LEB(1); \ - /* We must only emit hints that are present. */ \ - assert(annotation.field); \ - /* Hint must fit in one byte. */ \ - assert(*annotation.field <= 127); \ - /* Hint contents: inline frequency count. */ \ - buffer << U32LEB(*annotation.field); \ +#define WRITE_I7_HINT(code, field) \ + return writeExpressionHints( \ + code, \ + [](const CodeAnnotation& annotation) { return annotation.field; }, \ + [](const CodeAnnotation& annotation, BufferWithRandomAccess& buffer) { \ + /* Hint size, always 1 for now. */ \ + buffer << U32LEB(1); \ + /* We must only emit hints that are present. */ \ + assert(annotation.field); \ + /* Hint must fit in one byte. */ \ + assert(*annotation.field <= 127); \ + /* Hint contents: inline frequency count. */ \ + buffer << U32LEB(*annotation.field); \ }); std::optional WasmBinaryWriter::getInlineHintsBuffer() { @@ -1829,7 +1829,8 @@ WasmBinaryWriter::getIdempotentHintsBuffer() { WRITE_BOOLEAN_HINT(Annotations::IdempotentHint, idempotent); } -std::optional WasmBinaryWriter::getToolchainInlineHintsBuffer() { +std::optional +WasmBinaryWriter::getToolchainInlineHintsBuffer() { WRITE_I7_HINT(Annotations::ToolchainInlineHint, toolchainInline); } @@ -2279,8 +2280,10 @@ void WasmBinaryReader::readCustomSection(size_t payloadLen) { deferredAnnotationSections.push_back(AnnotationSectionInfo{ pos, [this, payloadLen]() { this->readIdempotentHints(payloadLen); }}); } else if (sectionName == Annotations::ToolchainInlineHint) { - deferredAnnotationSections.push_back(AnnotationSectionInfo{ - pos, [this, payloadLen]() { this->readToolchainInlineHints(payloadLen); }}); + deferredAnnotationSections.push_back( + AnnotationSectionInfo{pos, [this, payloadLen]() { + this->readToolchainInlineHints(payloadLen); + }}); } else { // an unfamiliar custom section if (sectionName.equals(BinaryConsts::CustomSections::Linking)) { @@ -5628,19 +5631,18 @@ void WasmBinaryReader::readBranchHints(size_t payloadLen) { } // Reads a simple i7 hint, in the range [0..127]. -#define READ_I7_HINT(code, field) \ - readExpressionHints( \ - , payloadLen, [&](CodeAnnotation& annotation) { \ - auto size = getU32LEB(); \ - if (size != 1) { \ - throwError("bad InlineHint size"); \ - } \ - uint8_t field = getInt8(); \ - if (field > 127) { \ - throwError("bad InlineHint value"); \ - } \ - annotation.field = field; \ - }); +#define READ_I7_HINT(code, field) \ + readExpressionHints(, payloadLen, [&](CodeAnnotation& annotation) { \ + auto size = getU32LEB(); \ + if (size != 1) { \ + throwError("bad InlineHint size"); \ + } \ + uint8_t field = getInt8(); \ + if (field > 127) { \ + throwError("bad InlineHint value"); \ + } \ + annotation.field = field; \ + }); void WasmBinaryReader::readInlineHints(size_t payloadLen) { READ_I7_HINT(Annotations::InlineHint, inline_); From 592539bf97a2b215c47027b2b49480c30a96c7d5 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 11:52:16 -0700 Subject: [PATCH 03/21] tests --- .../strip-toolchain-annotations-func.wast | 7 +- .../passes/strip-toolchain-annotations.wast | 4 + test/lit/passes/toolchain-inlining.wast | 1011 +++++++++++++++++ test/lit/toolchain-inline-hints-func.wast | 21 + test/lit/toolchain-inline-hints.wast | 85 ++ 5 files changed, 1126 insertions(+), 2 deletions(-) create mode 100644 test/lit/passes/toolchain-inlining.wast create mode 100644 test/lit/toolchain-inline-hints-func.wast create mode 100644 test/lit/toolchain-inline-hints.wast diff --git a/test/lit/passes/strip-toolchain-annotations-func.wast b/test/lit/passes/strip-toolchain-annotations-func.wast index eb80273d70a..755166a4baa 100644 --- a/test/lit/passes/strip-toolchain-annotations-func.wast +++ b/test/lit/passes/strip-toolchain-annotations-func.wast @@ -42,6 +42,9 @@ (func $idempotent ;; This hint should be removed too. ) -) - + (@binaryen.inline "\00") + (func $test-func-a + ;; This hint should be removed too. + ) +) diff --git a/test/lit/passes/strip-toolchain-annotations.wast b/test/lit/passes/strip-toolchain-annotations.wast index 1c13cb9b3a9..ae40354a80f 100644 --- a/test/lit/passes/strip-toolchain-annotations.wast +++ b/test/lit/passes/strip-toolchain-annotations.wast @@ -44,5 +44,9 @@ ;; This should be removed too. (@binaryen.idempotent) (call $test (i32.const 4)) + + ;; And this. + (@binaryen.inline "\00") + (call $test (i32.const 5)) ) ) diff --git a/test/lit/passes/toolchain-inlining.wast b/test/lit/passes/toolchain-inlining.wast new file mode 100644 index 00000000000..d9a7a8c34cb --- /dev/null +++ b/test/lit/passes/toolchain-inlining.wast @@ -0,0 +1,1011 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +XXX + +TODO + +;; Enable both full and partial inlining, and show how we filter out functions +;; using --no-*-inline. The functions with "yes" in their names will always be +;; inlined, while the ones with "maybe" will be filtered out in some modes. + +;; RUN: foreach %s %t wasm-opt --inlining --optimize-level=3 --partial-inlining-ifs=1 -S -o - | filecheck %s --check-prefix YES_ALL +;; RUN: foreach %s %t wasm-opt --no-partial-inline=*maybe* --inlining --optimize-level=3 --partial-inlining-ifs=1 -S -o - | filecheck %s --check-prefix NO_PART +;; RUN: foreach %s %t wasm-opt --no-full-inline=*maybe* --inlining --optimize-level=3 --partial-inlining-ifs=1 -S -o - | filecheck %s --check-prefix NO_FULL +;; RUN: foreach %s %t wasm-opt --no-inline=*maybe* --inlining --optimize-level=3 --partial-inlining-ifs=1 -S -o - | filecheck %s --check-prefix NO_BOTH +;; RUN: foreach %s %t wasm-opt --no-inline=*****maybe***** --inlining --optimize-level=3 --partial-inlining-ifs=1 -S -o - | filecheck %s --check-prefix NO_BOTH + +(module + ;; YES_ALL: (type $0 (func)) + + ;; YES_ALL: (type $1 (func (param i32))) + + ;; YES_ALL: (import "a" "b" (func $import)) + ;; NO_PART: (type $0 (func)) + + ;; NO_PART: (type $1 (func (param i32))) + + ;; NO_PART: (import "a" "b" (func $import)) + ;; NO_FULL: (type $0 (func)) + + ;; NO_FULL: (type $1 (func (param i32))) + + ;; NO_FULL: (import "a" "b" (func $import)) + ;; NO_BOTH: (type $0 (func)) + + ;; NO_BOTH: (type $1 (func (param i32))) + + ;; NO_BOTH: (import "a" "b" (func $import)) + (import "a" "b" (func $import)) + + (func $full-yes-inline (param $x i32) + (call $import) + ) + + ;; NO_FULL: (func $full-maybe-inline (param $x i32) + ;; NO_FULL-NEXT: (call $import) + ;; NO_FULL-NEXT: ) + ;; NO_BOTH: (func $full-maybe-inline (param $x i32) + ;; NO_BOTH-NEXT: (call $import) + ;; NO_BOTH-NEXT: ) + (func $full-maybe-inline (param $x i32) + (call $import) + ) + + (func $partial-yes-inline (param $x i32) + (if + (local.get $x) + (then + (return) + ) + ) + (loop $l + (call $import) + (br $l) + ) + ) + + ;; NO_PART: (func $partial-maybe-inline (param $x i32) + ;; NO_PART-NEXT: (if + ;; NO_PART-NEXT: (local.get $x) + ;; NO_PART-NEXT: (then + ;; NO_PART-NEXT: (return) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (loop $l + ;; NO_PART-NEXT: (call $import) + ;; NO_PART-NEXT: (br $l) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_BOTH: (func $partial-maybe-inline (param $x i32) + ;; NO_BOTH-NEXT: (if + ;; NO_BOTH-NEXT: (local.get $x) + ;; NO_BOTH-NEXT: (then + ;; NO_BOTH-NEXT: (return) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (loop $l + ;; NO_BOTH-NEXT: (call $import) + ;; NO_BOTH-NEXT: (br $l) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + (func $partial-maybe-inline (param $x i32) + (if + (local.get $x) + (then + (return) + ) + ) + (loop $l + (call $import) + (br $l) + ) + ) + + ;; YES_ALL: (func $caller + ;; YES_ALL-NEXT: (local $0 i32) + ;; YES_ALL-NEXT: (local $1 i32) + ;; YES_ALL-NEXT: (local $2 i32) + ;; YES_ALL-NEXT: (local $3 i32) + ;; YES_ALL-NEXT: (block $__inlined_func$full-yes-inline + ;; YES_ALL-NEXT: (local.set $0 + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (call $import) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block $__inlined_func$full-maybe-inline$1 + ;; YES_ALL-NEXT: (local.set $1 + ;; YES_ALL-NEXT: (i32.const 1) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (call $import) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-yes-inline$2 + ;; YES_ALL-NEXT: (local.set $2 + ;; YES_ALL-NEXT: (i32.const 2) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (if + ;; YES_ALL-NEXT: (i32.eqz + ;; YES_ALL-NEXT: (local.get $2) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (then + ;; YES_ALL-NEXT: (call $byn-split-outlined-A$partial-yes-inline + ;; YES_ALL-NEXT: (local.get $2) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-maybe-inline$3 + ;; YES_ALL-NEXT: (local.set $3 + ;; YES_ALL-NEXT: (i32.const 3) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (if + ;; YES_ALL-NEXT: (i32.eqz + ;; YES_ALL-NEXT: (local.get $3) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (then + ;; YES_ALL-NEXT: (call $byn-split-outlined-A$partial-maybe-inline + ;; YES_ALL-NEXT: (local.get $3) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; NO_PART: (func $caller + ;; NO_PART-NEXT: (local $0 i32) + ;; NO_PART-NEXT: (local $1 i32) + ;; NO_PART-NEXT: (local $2 i32) + ;; NO_PART-NEXT: (block $__inlined_func$full-yes-inline + ;; NO_PART-NEXT: (local.set $0 + ;; NO_PART-NEXT: (i32.const 0) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (call $import) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (block $__inlined_func$full-maybe-inline$1 + ;; NO_PART-NEXT: (local.set $1 + ;; NO_PART-NEXT: (i32.const 1) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (call $import) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-yes-inline$2 + ;; NO_PART-NEXT: (local.set $2 + ;; NO_PART-NEXT: (i32.const 2) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (if + ;; NO_PART-NEXT: (i32.eqz + ;; NO_PART-NEXT: (local.get $2) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (then + ;; NO_PART-NEXT: (call $byn-split-outlined-A$partial-yes-inline + ;; NO_PART-NEXT: (local.get $2) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (call $partial-maybe-inline + ;; NO_PART-NEXT: (i32.const 3) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_FULL: (func $caller + ;; NO_FULL-NEXT: (local $0 i32) + ;; NO_FULL-NEXT: (local $1 i32) + ;; NO_FULL-NEXT: (local $2 i32) + ;; NO_FULL-NEXT: (block $__inlined_func$full-yes-inline + ;; NO_FULL-NEXT: (local.set $0 + ;; NO_FULL-NEXT: (i32.const 0) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (call $import) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (call $full-maybe-inline + ;; NO_FULL-NEXT: (i32.const 1) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-yes-inline$1 + ;; NO_FULL-NEXT: (local.set $1 + ;; NO_FULL-NEXT: (i32.const 2) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (if + ;; NO_FULL-NEXT: (i32.eqz + ;; NO_FULL-NEXT: (local.get $1) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (then + ;; NO_FULL-NEXT: (call $byn-split-outlined-A$partial-yes-inline + ;; NO_FULL-NEXT: (local.get $1) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-maybe-inline$2 + ;; NO_FULL-NEXT: (local.set $2 + ;; NO_FULL-NEXT: (i32.const 3) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (if + ;; NO_FULL-NEXT: (i32.eqz + ;; NO_FULL-NEXT: (local.get $2) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (then + ;; NO_FULL-NEXT: (call $byn-split-outlined-A$partial-maybe-inline + ;; NO_FULL-NEXT: (local.get $2) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_BOTH: (func $caller + ;; NO_BOTH-NEXT: (local $0 i32) + ;; NO_BOTH-NEXT: (local $1 i32) + ;; NO_BOTH-NEXT: (block $__inlined_func$full-yes-inline + ;; NO_BOTH-NEXT: (local.set $0 + ;; NO_BOTH-NEXT: (i32.const 0) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (call $import) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (call $full-maybe-inline + ;; NO_BOTH-NEXT: (i32.const 1) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-yes-inline$1 + ;; NO_BOTH-NEXT: (local.set $1 + ;; NO_BOTH-NEXT: (i32.const 2) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (if + ;; NO_BOTH-NEXT: (i32.eqz + ;; NO_BOTH-NEXT: (local.get $1) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (then + ;; NO_BOTH-NEXT: (call $byn-split-outlined-A$partial-yes-inline + ;; NO_BOTH-NEXT: (local.get $1) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (call $partial-maybe-inline + ;; NO_BOTH-NEXT: (i32.const 3) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + (func $caller + (call $full-yes-inline + (i32.const 0) + ) + (call $full-maybe-inline + (i32.const 1) + ) + (call $partial-yes-inline + (i32.const 2) + ) + (call $partial-maybe-inline + (i32.const 3) + ) + ) + + ;; YES_ALL: (func $caller-2 + ;; YES_ALL-NEXT: (local $0 i32) + ;; YES_ALL-NEXT: (local $1 i32) + ;; YES_ALL-NEXT: (local $2 i32) + ;; YES_ALL-NEXT: (local $3 i32) + ;; YES_ALL-NEXT: (block $__inlined_func$full-yes-inline$4 + ;; YES_ALL-NEXT: (local.set $0 + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (call $import) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block $__inlined_func$full-maybe-inline$5 + ;; YES_ALL-NEXT: (local.set $1 + ;; YES_ALL-NEXT: (i32.const 1) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (call $import) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-yes-inline$6 + ;; YES_ALL-NEXT: (local.set $2 + ;; YES_ALL-NEXT: (i32.const 2) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (if + ;; YES_ALL-NEXT: (i32.eqz + ;; YES_ALL-NEXT: (local.get $2) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (then + ;; YES_ALL-NEXT: (call $byn-split-outlined-A$partial-yes-inline + ;; YES_ALL-NEXT: (local.get $2) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-maybe-inline$7 + ;; YES_ALL-NEXT: (local.set $3 + ;; YES_ALL-NEXT: (i32.const 3) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (if + ;; YES_ALL-NEXT: (i32.eqz + ;; YES_ALL-NEXT: (local.get $3) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (then + ;; YES_ALL-NEXT: (call $byn-split-outlined-A$partial-maybe-inline + ;; YES_ALL-NEXT: (local.get $3) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; NO_PART: (func $caller-2 + ;; NO_PART-NEXT: (local $0 i32) + ;; NO_PART-NEXT: (local $1 i32) + ;; NO_PART-NEXT: (local $2 i32) + ;; NO_PART-NEXT: (block $__inlined_func$full-yes-inline$3 + ;; NO_PART-NEXT: (local.set $0 + ;; NO_PART-NEXT: (i32.const 0) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (call $import) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (block $__inlined_func$full-maybe-inline$4 + ;; NO_PART-NEXT: (local.set $1 + ;; NO_PART-NEXT: (i32.const 1) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (call $import) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-yes-inline$5 + ;; NO_PART-NEXT: (local.set $2 + ;; NO_PART-NEXT: (i32.const 2) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (if + ;; NO_PART-NEXT: (i32.eqz + ;; NO_PART-NEXT: (local.get $2) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (then + ;; NO_PART-NEXT: (call $byn-split-outlined-A$partial-yes-inline + ;; NO_PART-NEXT: (local.get $2) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (call $partial-maybe-inline + ;; NO_PART-NEXT: (i32.const 3) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_FULL: (func $caller-2 + ;; NO_FULL-NEXT: (local $0 i32) + ;; NO_FULL-NEXT: (local $1 i32) + ;; NO_FULL-NEXT: (local $2 i32) + ;; NO_FULL-NEXT: (block $__inlined_func$full-yes-inline$3 + ;; NO_FULL-NEXT: (local.set $0 + ;; NO_FULL-NEXT: (i32.const 0) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (call $import) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (call $full-maybe-inline + ;; NO_FULL-NEXT: (i32.const 1) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-yes-inline$4 + ;; NO_FULL-NEXT: (local.set $1 + ;; NO_FULL-NEXT: (i32.const 2) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (if + ;; NO_FULL-NEXT: (i32.eqz + ;; NO_FULL-NEXT: (local.get $1) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (then + ;; NO_FULL-NEXT: (call $byn-split-outlined-A$partial-yes-inline + ;; NO_FULL-NEXT: (local.get $1) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-maybe-inline$5 + ;; NO_FULL-NEXT: (local.set $2 + ;; NO_FULL-NEXT: (i32.const 3) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (if + ;; NO_FULL-NEXT: (i32.eqz + ;; NO_FULL-NEXT: (local.get $2) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (then + ;; NO_FULL-NEXT: (call $byn-split-outlined-A$partial-maybe-inline + ;; NO_FULL-NEXT: (local.get $2) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_BOTH: (func $caller-2 + ;; NO_BOTH-NEXT: (local $0 i32) + ;; NO_BOTH-NEXT: (local $1 i32) + ;; NO_BOTH-NEXT: (block $__inlined_func$full-yes-inline$2 + ;; NO_BOTH-NEXT: (local.set $0 + ;; NO_BOTH-NEXT: (i32.const 0) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (call $import) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (call $full-maybe-inline + ;; NO_BOTH-NEXT: (i32.const 1) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-yes-inline$3 + ;; NO_BOTH-NEXT: (local.set $1 + ;; NO_BOTH-NEXT: (i32.const 2) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (if + ;; NO_BOTH-NEXT: (i32.eqz + ;; NO_BOTH-NEXT: (local.get $1) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (then + ;; NO_BOTH-NEXT: (call $byn-split-outlined-A$partial-yes-inline + ;; NO_BOTH-NEXT: (local.get $1) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (call $partial-maybe-inline + ;; NO_BOTH-NEXT: (i32.const 3) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + (func $caller-2 + ;; Same as $caller, to prevent the functions from having a single use (which + ;; is always inlined). + (call $full-yes-inline + (i32.const 0) + ) + (call $full-maybe-inline + (i32.const 1) + ) + (call $partial-yes-inline + (i32.const 2) + ) + (call $partial-maybe-inline + (i32.const 3) + ) + ) +) + +;; YES_ALL: (func $byn-split-outlined-A$partial-yes-inline (param $x i32) +;; YES_ALL-NEXT: (loop $l +;; YES_ALL-NEXT: (call $import) +;; YES_ALL-NEXT: (br $l) +;; YES_ALL-NEXT: ) +;; YES_ALL-NEXT: ) + +;; YES_ALL: (func $byn-split-outlined-A$partial-maybe-inline (param $x i32) +;; YES_ALL-NEXT: (loop $l +;; YES_ALL-NEXT: (call $import) +;; YES_ALL-NEXT: (br $l) +;; YES_ALL-NEXT: ) +;; YES_ALL-NEXT: ) + +;; NO_PART: (func $byn-split-outlined-A$partial-yes-inline (param $x i32) +;; NO_PART-NEXT: (loop $l +;; NO_PART-NEXT: (call $import) +;; NO_PART-NEXT: (br $l) +;; NO_PART-NEXT: ) +;; NO_PART-NEXT: ) + +;; NO_FULL: (func $byn-split-outlined-A$partial-yes-inline (param $x i32) +;; NO_FULL-NEXT: (loop $l +;; NO_FULL-NEXT: (call $import) +;; NO_FULL-NEXT: (br $l) +;; NO_FULL-NEXT: ) +;; NO_FULL-NEXT: ) + +;; NO_FULL: (func $byn-split-outlined-A$partial-maybe-inline (param $x i32) +;; NO_FULL-NEXT: (loop $l +;; NO_FULL-NEXT: (call $import) +;; NO_FULL-NEXT: (br $l) +;; NO_FULL-NEXT: ) +;; NO_FULL-NEXT: ) + +;; NO_BOTH: (func $byn-split-outlined-A$partial-yes-inline (param $x i32) +;; NO_BOTH-NEXT: (loop $l +;; NO_BOTH-NEXT: (call $import) +;; NO_BOTH-NEXT: (br $l) +;; NO_BOTH-NEXT: ) +;; NO_BOTH-NEXT: ) +(module + ;; YES_ALL: (type $0 (func)) + + ;; YES_ALL: (import "out" "func" (func $import)) + ;; NO_PART: (type $0 (func)) + + ;; NO_PART: (type $1 (func (param i32))) + + ;; NO_PART: (import "out" "func" (func $import)) + ;; NO_FULL: (type $0 (func)) + + ;; NO_FULL: (type $1 (func (param i32))) + + ;; NO_FULL: (import "out" "func" (func $import)) + ;; NO_BOTH: (type $0 (func)) + + ;; NO_BOTH: (type $1 (func (param i32))) + + ;; NO_BOTH: (import "out" "func" (func $import)) + (import "out" "func" (func $import)) + + ;; NO_PART: (func $maybe-partial-or-full-1 (param $x i32) + ;; NO_PART-NEXT: (if + ;; NO_PART-NEXT: (local.get $x) + ;; NO_PART-NEXT: (then + ;; NO_PART-NEXT: (call $import) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_BOTH: (func $maybe-partial-or-full-1 (param $x i32) + ;; NO_BOTH-NEXT: (if + ;; NO_BOTH-NEXT: (local.get $x) + ;; NO_BOTH-NEXT: (then + ;; NO_BOTH-NEXT: (call $import) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + (func $maybe-partial-or-full-1 (param $x i32) + ;; This function can be partially inlined, but after doing so we want to + ;; inline the remainder as well, so instead we fully inline it. When full + ;; inlining is disabled but partial inlining is enabled, we should only + ;; partially inline it. + (if + (local.get $x) + (then + (call $import) + ) + ) + ) + + ;; NO_PART: (func $maybe-partial-or-full-2 (param $x i32) + ;; NO_PART-NEXT: (if + ;; NO_PART-NEXT: (local.get $x) + ;; NO_PART-NEXT: (then + ;; NO_PART-NEXT: (return) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (nop) + ;; NO_PART-NEXT: (drop + ;; NO_PART-NEXT: (i32.const 0) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (drop + ;; NO_PART-NEXT: (i32.const 0) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (drop + ;; NO_PART-NEXT: (i32.const 0) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (drop + ;; NO_PART-NEXT: (i32.const 0) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (drop + ;; NO_PART-NEXT: (i32.const 0) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (drop + ;; NO_PART-NEXT: (i32.const 0) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (drop + ;; NO_PART-NEXT: (i32.const 0) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (drop + ;; NO_PART-NEXT: (i32.const 0) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_BOTH: (func $maybe-partial-or-full-2 (param $x i32) + ;; NO_BOTH-NEXT: (if + ;; NO_BOTH-NEXT: (local.get $x) + ;; NO_BOTH-NEXT: (then + ;; NO_BOTH-NEXT: (return) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (nop) + ;; NO_BOTH-NEXT: (drop + ;; NO_BOTH-NEXT: (i32.const 0) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (drop + ;; NO_BOTH-NEXT: (i32.const 0) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (drop + ;; NO_BOTH-NEXT: (i32.const 0) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (drop + ;; NO_BOTH-NEXT: (i32.const 0) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (drop + ;; NO_BOTH-NEXT: (i32.const 0) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (drop + ;; NO_BOTH-NEXT: (i32.const 0) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (drop + ;; NO_BOTH-NEXT: (i32.const 0) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (drop + ;; NO_BOTH-NEXT: (i32.const 0) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + (func $maybe-partial-or-full-2 (param $x i32) + ;; As above, but for another form of partial inlining. Here we need to add + ;; some extra things to the function size for partial inlining to kick in. + (if + (local.get $x) + (then + (return) + ) + ) + (nop) + (drop + (i32.const 0) + ) + (drop + (i32.const 0) + ) + (drop + (i32.const 0) + ) + (drop + (i32.const 0) + ) + (drop + (i32.const 0) + ) + (drop + (i32.const 0) + ) + (drop + (i32.const 0) + ) + (drop + (i32.const 0) + ) + ) + +(func $very-long-name-we-should-not-error-on-even-though-it-is-very-very-long + ;; Test a long name. + ) + + ;; NO_FULL: (func $very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long + ;; NO_FULL-NEXT: ) + ;; NO_BOTH: (func $very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long + ;; NO_BOTH-NEXT: ) + (func $very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long + ;; Test a long name with "maybe" in it. + ) + + ;; NO_FULL: (func $very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long + ;; NO_FULL-NEXT: ) + ;; NO_BOTH: (func $very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long + ;; NO_BOTH-NEXT: ) + (func $very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long + ;; Test a long name with "maybe" in it, and a partial match earlier ("may"). + ) + +(func $maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmayb + ;; Test a long name with many partial matches but no real match. +) + +;; NO_FULL: (func $maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybe +;; NO_FULL-NEXT: ) +;; NO_BOTH: (func $maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybe +;; NO_BOTH-NEXT: ) +(func $maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybe + ;; Test a long name with many partial matches and one real match right at the end. +) + +(func $mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm + ;; Test a long name with even more tiny partial matches but no real match. +) + + ;; YES_ALL: (func $caller + ;; YES_ALL-NEXT: (local $0 i32) + ;; YES_ALL-NEXT: (local $1 i32) + ;; YES_ALL-NEXT: (local $2 i32) + ;; YES_ALL-NEXT: (local $3 i32) + ;; YES_ALL-NEXT: (block $__inlined_func$maybe-partial-or-full-1 + ;; YES_ALL-NEXT: (local.set $0 + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (if + ;; YES_ALL-NEXT: (local.get $0) + ;; YES_ALL-NEXT: (then + ;; YES_ALL-NEXT: (call $import) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block $__inlined_func$maybe-partial-or-full-1$1 + ;; YES_ALL-NEXT: (local.set $1 + ;; YES_ALL-NEXT: (i32.const 1) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (if + ;; YES_ALL-NEXT: (local.get $1) + ;; YES_ALL-NEXT: (then + ;; YES_ALL-NEXT: (call $import) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block $__inlined_func$maybe-partial-or-full-2$2 + ;; YES_ALL-NEXT: (local.set $2 + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block + ;; YES_ALL-NEXT: (if + ;; YES_ALL-NEXT: (local.get $2) + ;; YES_ALL-NEXT: (then + ;; YES_ALL-NEXT: (br $__inlined_func$maybe-partial-or-full-2$2) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (nop) + ;; YES_ALL-NEXT: (drop + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (drop + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (drop + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (drop + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (drop + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (drop + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (drop + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (drop + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block $__inlined_func$maybe-partial-or-full-2$3 + ;; YES_ALL-NEXT: (local.set $3 + ;; YES_ALL-NEXT: (i32.const 1) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block + ;; YES_ALL-NEXT: (if + ;; YES_ALL-NEXT: (local.get $3) + ;; YES_ALL-NEXT: (then + ;; YES_ALL-NEXT: (br $__inlined_func$maybe-partial-or-full-2$3) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (nop) + ;; YES_ALL-NEXT: (drop + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (drop + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (drop + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (drop + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (drop + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (drop + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (drop + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (drop + ;; YES_ALL-NEXT: (i32.const 0) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block $__inlined_func$very-long-name-we-should-not-error-on-even-though-it-is-very-very-long$4 + ;; YES_ALL-NEXT: (block + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block $__inlined_func$very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long$5 + ;; YES_ALL-NEXT: (block + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block $__inlined_func$very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long$6 + ;; YES_ALL-NEXT: (block + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block $__inlined_func$maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmayb$7 + ;; YES_ALL-NEXT: (block + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block $__inlined_func$maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybe$8 + ;; YES_ALL-NEXT: (block + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block $__inlined_func$mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm$9 + ;; YES_ALL-NEXT: (block + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; NO_PART: (func $caller + ;; NO_PART-NEXT: (call $maybe-partial-or-full-1 + ;; NO_PART-NEXT: (i32.const 0) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (call $maybe-partial-or-full-1 + ;; NO_PART-NEXT: (i32.const 1) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (call $maybe-partial-or-full-2 + ;; NO_PART-NEXT: (i32.const 0) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (call $maybe-partial-or-full-2 + ;; NO_PART-NEXT: (i32.const 1) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (block $__inlined_func$very-long-name-we-should-not-error-on-even-though-it-is-very-very-long + ;; NO_PART-NEXT: (block + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (block $__inlined_func$very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long$1 + ;; NO_PART-NEXT: (block + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (block $__inlined_func$very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long$2 + ;; NO_PART-NEXT: (block + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (block $__inlined_func$maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmayb$3 + ;; NO_PART-NEXT: (block + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (block $__inlined_func$maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybe$4 + ;; NO_PART-NEXT: (block + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (block $__inlined_func$mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm$5 + ;; NO_PART-NEXT: (block + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_FULL: (func $caller + ;; NO_FULL-NEXT: (local $0 i32) + ;; NO_FULL-NEXT: (local $1 i32) + ;; NO_FULL-NEXT: (local $2 i32) + ;; NO_FULL-NEXT: (local $3 i32) + ;; NO_FULL-NEXT: (block $__inlined_func$byn-split-inlineable-B$maybe-partial-or-full-1 + ;; NO_FULL-NEXT: (local.set $0 + ;; NO_FULL-NEXT: (i32.const 0) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (if + ;; NO_FULL-NEXT: (local.get $0) + ;; NO_FULL-NEXT: (then + ;; NO_FULL-NEXT: (call $byn-split-outlined-B$maybe-partial-or-full-1 + ;; NO_FULL-NEXT: (local.get $0) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (block $__inlined_func$byn-split-inlineable-B$maybe-partial-or-full-1$1 + ;; NO_FULL-NEXT: (local.set $1 + ;; NO_FULL-NEXT: (i32.const 1) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (if + ;; NO_FULL-NEXT: (local.get $1) + ;; NO_FULL-NEXT: (then + ;; NO_FULL-NEXT: (call $byn-split-outlined-B$maybe-partial-or-full-1 + ;; NO_FULL-NEXT: (local.get $1) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (block $__inlined_func$byn-split-inlineable-A$maybe-partial-or-full-2$2 + ;; NO_FULL-NEXT: (local.set $2 + ;; NO_FULL-NEXT: (i32.const 0) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (if + ;; NO_FULL-NEXT: (i32.eqz + ;; NO_FULL-NEXT: (local.get $2) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (then + ;; NO_FULL-NEXT: (call $byn-split-outlined-A$maybe-partial-or-full-2 + ;; NO_FULL-NEXT: (local.get $2) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (block $__inlined_func$byn-split-inlineable-A$maybe-partial-or-full-2$3 + ;; NO_FULL-NEXT: (local.set $3 + ;; NO_FULL-NEXT: (i32.const 1) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (if + ;; NO_FULL-NEXT: (i32.eqz + ;; NO_FULL-NEXT: (local.get $3) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (then + ;; NO_FULL-NEXT: (call $byn-split-outlined-A$maybe-partial-or-full-2 + ;; NO_FULL-NEXT: (local.get $3) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (block $__inlined_func$very-long-name-we-should-not-error-on-even-though-it-is-very-very-long$4 + ;; NO_FULL-NEXT: (block + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (call $very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long) + ;; NO_FULL-NEXT: (call $very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long) + ;; NO_FULL-NEXT: (block $__inlined_func$maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmayb$5 + ;; NO_FULL-NEXT: (block + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (call $maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybe) + ;; NO_FULL-NEXT: (block $__inlined_func$mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm$6 + ;; NO_FULL-NEXT: (block + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_BOTH: (func $caller + ;; NO_BOTH-NEXT: (call $maybe-partial-or-full-1 + ;; NO_BOTH-NEXT: (i32.const 0) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (call $maybe-partial-or-full-1 + ;; NO_BOTH-NEXT: (i32.const 1) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (call $maybe-partial-or-full-2 + ;; NO_BOTH-NEXT: (i32.const 0) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (call $maybe-partial-or-full-2 + ;; NO_BOTH-NEXT: (i32.const 1) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (block $__inlined_func$very-long-name-we-should-not-error-on-even-though-it-is-very-very-long + ;; NO_BOTH-NEXT: (block + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (call $very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long) + ;; NO_BOTH-NEXT: (call $very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long) + ;; NO_BOTH-NEXT: (block $__inlined_func$maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmayb$1 + ;; NO_BOTH-NEXT: (block + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (call $maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybe) + ;; NO_BOTH-NEXT: (block $__inlined_func$mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm$2 + ;; NO_BOTH-NEXT: (block + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + (func $caller + ;; In YES_ALL we will fully inline all of these. In NO_FULL we will partially + ;; inline. In NO_PART and NO_BOTH we will not inline at all (in theory we + ;; could do full inlining when only partial inlining is disabled, but we only + ;; see that full inlining is possible as a result of partial inlining, so if + ;; partial is disabled we don't get there, and full inlining is not + ;; justified). + (call $maybe-partial-or-full-1 + (i32.const 0) + ) + (call $maybe-partial-or-full-1 + (i32.const 1) + ) + (call $maybe-partial-or-full-2 + (i32.const 0) + ) + (call $maybe-partial-or-full-2 + (i32.const 1) + ) + (call $very-long-name-we-should-not-error-on-even-though-it-is-very-very-long) + (call $very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long) + (call $very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long) + (call $maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmayb) + (call $maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybe) + (call $mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm) + ) +) +;; NO_FULL: (func $byn-split-outlined-B$maybe-partial-or-full-1 (param $x i32) +;; NO_FULL-NEXT: (call $import) +;; NO_FULL-NEXT: ) + +;; NO_FULL: (func $byn-split-outlined-A$maybe-partial-or-full-2 (param $x i32) +;; NO_FULL-NEXT: (nop) +;; NO_FULL-NEXT: (drop +;; NO_FULL-NEXT: (i32.const 0) +;; NO_FULL-NEXT: ) +;; NO_FULL-NEXT: (drop +;; NO_FULL-NEXT: (i32.const 0) +;; NO_FULL-NEXT: ) +;; NO_FULL-NEXT: (drop +;; NO_FULL-NEXT: (i32.const 0) +;; NO_FULL-NEXT: ) +;; NO_FULL-NEXT: (drop +;; NO_FULL-NEXT: (i32.const 0) +;; NO_FULL-NEXT: ) +;; NO_FULL-NEXT: (drop +;; NO_FULL-NEXT: (i32.const 0) +;; NO_FULL-NEXT: ) +;; NO_FULL-NEXT: (drop +;; NO_FULL-NEXT: (i32.const 0) +;; NO_FULL-NEXT: ) +;; NO_FULL-NEXT: (drop +;; NO_FULL-NEXT: (i32.const 0) +;; NO_FULL-NEXT: ) +;; NO_FULL-NEXT: (drop +;; NO_FULL-NEXT: (i32.const 0) +;; NO_FULL-NEXT: ) +;; NO_FULL-NEXT: ) diff --git a/test/lit/toolchain-inline-hints-func.wast b/test/lit/toolchain-inline-hints-func.wast new file mode 100644 index 00000000000..363ef512729 --- /dev/null +++ b/test/lit/toolchain-inline-hints-func.wast @@ -0,0 +1,21 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. +;; RUN: wasm-opt -all %s -S -o - | filecheck %s +;; RUN: wasm-opt -all --roundtrip %s -S -o - | filecheck %s + +(module + ;; CHECK: (@binaryen.inline "\12") + ;; CHECK-NEXT: (func $func-annotation (type $0) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (@binaryen.inline "\12") + (func $func-annotation + ;; The annotation here is on the function. + (drop + (i32.const 0) + ) + ) +) + + diff --git a/test/lit/toolchain-inline-hints.wast b/test/lit/toolchain-inline-hints.wast new file mode 100644 index 00000000000..a61d9ff5392 --- /dev/null +++ b/test/lit/toolchain-inline-hints.wast @@ -0,0 +1,85 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; RUN: wasm-opt -all %s -S -o - | filecheck %s + +;; RUN: wasm-opt -all --roundtrip %s -S -o - | filecheck %s --check-prefix=RTRIP + +(module + ;; CHECK: (type $func (func)) + ;; RTRIP: (type $func (func)) + (type $func (func)) + + ;; CHECK: (table $table 10 20 funcref) + ;; RTRIP: (table $table 10 20 funcref) + (table $table 10 20 funcref) + + ;; CHECK: (elem declare func $func) + + ;; CHECK: (func $func (type $func) + ;; CHECK-NEXT: (@binaryen.inline "\00") + ;; CHECK-NEXT: (call $func) + ;; CHECK-NEXT: (@binaryen.inline "\01") + ;; CHECK-NEXT: (call $func) + ;; CHECK-NEXT: (@binaryen.inline "\7e") + ;; CHECK-NEXT: (call $func) + ;; CHECK-NEXT: (@binaryen.inline "\7f") + ;; CHECK-NEXT: (call $func) + ;; CHECK-NEXT: (call $func) + ;; CHECK-NEXT: ) + ;; RTRIP: (elem declare func $func) + + ;; RTRIP: (func $func (type $func) + ;; RTRIP-NEXT: (@binaryen.inline "\00") + ;; RTRIP-NEXT: (call $func) + ;; RTRIP-NEXT: (@binaryen.inline "\01") + ;; RTRIP-NEXT: (call $func) + ;; RTRIP-NEXT: (@binaryen.inline "\7e") + ;; RTRIP-NEXT: (call $func) + ;; RTRIP-NEXT: (@binaryen.inline "\7f") + ;; RTRIP-NEXT: (call $func) + ;; RTRIP-NEXT: (call $func) + ;; RTRIP-NEXT: ) + (func $func + (@binaryen.inline "\00") + (call $func) + (@binaryen.inline "\01") + (call $func) + (@binaryen.inline "\7e") + (call $func) + (@binaryen.inline "\7f") + (call $func) + ;; Unannotated + (call $func) + ) + + ;; CHECK: (func $other-calls (type $func) + ;; CHECK-NEXT: (@binaryen.inline "\12") + ;; CHECK-NEXT: (call_indirect $table (type $func) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (@binaryen.inline "\34") + ;; CHECK-NEXT: (call_ref $func + ;; CHECK-NEXT: (ref.func $func) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; RTRIP: (func $other-calls (type $func) + ;; RTRIP-NEXT: (@binaryen.inline "\12") + ;; RTRIP-NEXT: (call_indirect $table (type $func) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: (@binaryen.inline "\34") + ;; RTRIP-NEXT: (call_ref $func + ;; RTRIP-NEXT: (ref.func $func) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: ) + (func $other-calls + (@binaryen.inline "\12") + (call_indirect (type $func) + (i32.const 0) + ) + (@binaryen.inline "\34") + (call_ref $func + (ref.func $func) + ) + ) +) From 2977221c972b2bcd976a38c1e511addbef6f7dc4 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 11:53:08 -0700 Subject: [PATCH 04/21] work --- src/wasm-annotations.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wasm-annotations.h b/src/wasm-annotations.h index b71424685c7..11c766ecf3a 100644 --- a/src/wasm-annotations.h +++ b/src/wasm-annotations.h @@ -30,6 +30,7 @@ extern const Name InlineHint; extern const Name RemovableIfUnusedHint; extern const Name JSCalledHint; extern const Name IdempotentHint; +extern const Name ToolchainInlineHint; } // namespace wasm::Annotations From 2e23c2f590099610670d951f710cd80faf77339a Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 11:55:11 -0700 Subject: [PATCH 05/21] work --- src/wasm/wasm-binary.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 69b38c3caf8..c25553ed5f3 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -5632,7 +5632,7 @@ void WasmBinaryReader::readBranchHints(size_t payloadLen) { // Reads a simple i7 hint, in the range [0..127]. #define READ_I7_HINT(code, field) \ - readExpressionHints(, payloadLen, [&](CodeAnnotation& annotation) { \ + readExpressionHints(code, payloadLen, [&](CodeAnnotation& annotation) { \ auto size = getU32LEB(); \ if (size != 1) { \ throwError("bad InlineHint size"); \ From 22b21f2369dea4d4df5b6043ace9224d2b5696d1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 11:57:27 -0700 Subject: [PATCH 06/21] work --- src/wasm/wasm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index dc8b8f7ae36..15a1e3e6d86 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -90,7 +90,7 @@ const Name InlineHint = "metadata.code.inline"; const Name RemovableIfUnusedHint = "binaryen.removable.if.unused"; const Name JSCalledHint = "binaryen.js.called"; const Name IdempotentHint = "binaryen.idempotent"; -const Name ToolchainInlinetHint = "binaryen.inline"; +const Name ToolchainInlineHint = "binaryen.inline"; } // namespace Annotations From cbb218bbdc84c33c1ae9c2980ba84b5333b6e369 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 12:01:06 -0700 Subject: [PATCH 07/21] work --- src/passes/Inlining.cpp | 10 ++++++++++ src/wasm.h | 2 +- test/lit/passes/toolchain-inlining.wast | 14 ++------------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp index cd328a7e1be..bae7601696e 100644 --- a/src/passes/Inlining.cpp +++ b/src/passes/Inlining.cpp @@ -114,6 +114,7 @@ struct FunctionInfo { bool usedGlobally; TrivialInstruction trivialInstruction; InliningMode inliningMode; + std::optional toolchainInlineHint; FunctionInfo() { clear(); } @@ -126,6 +127,7 @@ struct FunctionInfo { usedGlobally = false; trivialInstruction = TrivialInstruction::NotTrivial; inliningMode = InliningMode::Unknown; + toolchainInlineHint = std::nullopt; } // Provide an explicit = operator as the |refs| field lacks one by default. @@ -138,6 +140,7 @@ struct FunctionInfo { usedGlobally = other.usedGlobally; trivialInstruction = other.trivialInstruction; inliningMode = other.inliningMode; + toolchainInlineHint = other.toolchainInlineHint; return *this; } @@ -148,6 +151,13 @@ struct FunctionInfo { if (hasTryDelegate) { return false; } + // Follow the toolchain hint, if present. + if (toolchainInlineHint == CodeAnnotation::NeverInline) { + return false; + } + if (toolchainInlineHint == CodeAnnotation::AlwaysInline) { + return true; + } // If it's small enough that we always want to inline such things, do so. if (size <= options.inlining.alwaysInlineMaxSize) { return true; diff --git a/src/wasm.h b/src/wasm.h index 12cc3df7e98..b9357819981 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -2381,7 +2381,7 @@ struct CodeAnnotation { // An inlining hint at the toolchain level, in contrast to inline_, above, // which is for VMs. (E.g., one may want to not inline at the toolchain level // to keep size small, and tell VMs to inline at runtime.) - std::optional toolchainInline = false; + std::optional toolchainInline; bool operator==(const CodeAnnotation& other) const { return equalOnSemanticsPreserving(other) && equalOnSemanticsAltering(other); diff --git a/test/lit/passes/toolchain-inlining.wast b/test/lit/passes/toolchain-inlining.wast index d9a7a8c34cb..9a0f7d3c043 100644 --- a/test/lit/passes/toolchain-inlining.wast +++ b/test/lit/passes/toolchain-inlining.wast @@ -1,19 +1,9 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -XXX -TODO - -;; Enable both full and partial inlining, and show how we filter out functions -;; using --no-*-inline. The functions with "yes" in their names will always be -;; inlined, while the ones with "maybe" will be filtered out in some modes. - -;; RUN: foreach %s %t wasm-opt --inlining --optimize-level=3 --partial-inlining-ifs=1 -S -o - | filecheck %s --check-prefix YES_ALL -;; RUN: foreach %s %t wasm-opt --no-partial-inline=*maybe* --inlining --optimize-level=3 --partial-inlining-ifs=1 -S -o - | filecheck %s --check-prefix NO_PART -;; RUN: foreach %s %t wasm-opt --no-full-inline=*maybe* --inlining --optimize-level=3 --partial-inlining-ifs=1 -S -o - | filecheck %s --check-prefix NO_FULL -;; RUN: foreach %s %t wasm-opt --no-inline=*maybe* --inlining --optimize-level=3 --partial-inlining-ifs=1 -S -o - | filecheck %s --check-prefix NO_BOTH -;; RUN: foreach %s %t wasm-opt --no-inline=*****maybe***** --inlining --optimize-level=3 --partial-inlining-ifs=1 -S -o - | filecheck %s --check-prefix NO_BOTH +;; RUN: foreach %s %t wasm-opt --inlining --optimize-level=3 --partial-inlining-ifs=1 -S -o - | filecheck %s +TODO (module ;; YES_ALL: (type $0 (func)) From 6476d4df666fec804da71d2591f697a9ee259705 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 12:07:45 -0700 Subject: [PATCH 08/21] work --- src/passes/Inlining.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp index bae7601696e..eeee2d8b8b8 100644 --- a/src/passes/Inlining.cpp +++ b/src/passes/Inlining.cpp @@ -246,6 +246,7 @@ struct FunctionInfoScanner auto& info = infos[curr->name]; info.size = Measurer::measure(curr->body); + info.toolchainInlineHint = curr->funcAnnotations.toolchainInline; // If the body is a simple instruction with roughly the same encoded size as // a `call` instruction, and arguments are function locals read in order, From 5967d4969e69e4382c70d459f17de1b2cc798edf Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 12:13:07 -0700 Subject: [PATCH 09/21] work --- .../passes/strip-toolchain-annotations.wast | 3 + test/lit/passes/toolchain-inlining.wast | 1104 ++--------------- test/lit/toolchain-inline-hints-func.wast | 2 +- test/lit/toolchain-inline-hints.wast | 20 +- 4 files changed, 132 insertions(+), 997 deletions(-) diff --git a/test/lit/passes/strip-toolchain-annotations.wast b/test/lit/passes/strip-toolchain-annotations.wast index ae40354a80f..6c2af951adb 100644 --- a/test/lit/passes/strip-toolchain-annotations.wast +++ b/test/lit/passes/strip-toolchain-annotations.wast @@ -22,6 +22,9 @@ ;; CHECK-NEXT: (call $test ;; CHECK-NEXT: (i32.const 4) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $test + ;; CHECK-NEXT: (i32.const 5) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $test (param i32) ;; Inlining hints are not removed, as they are for the VM too. diff --git a/test/lit/passes/toolchain-inlining.wast b/test/lit/passes/toolchain-inlining.wast index 9a0f7d3c043..5afc0f9446f 100644 --- a/test/lit/passes/toolchain-inlining.wast +++ b/test/lit/passes/toolchain-inlining.wast @@ -1,1001 +1,133 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. +;; RUN: foreach %s %t wasm-opt --inlining --optimize-level=3 -S -o - | filecheck %s -;; RUN: foreach %s %t wasm-opt --inlining --optimize-level=3 --partial-inlining-ifs=1 -S -o - | filecheck %s - -TODO (module - ;; YES_ALL: (type $0 (func)) - - ;; YES_ALL: (type $1 (func (param i32))) - - ;; YES_ALL: (import "a" "b" (func $import)) - ;; NO_PART: (type $0 (func)) - - ;; NO_PART: (type $1 (func (param i32))) - - ;; NO_PART: (import "a" "b" (func $import)) - ;; NO_FULL: (type $0 (func)) - - ;; NO_FULL: (type $1 (func (param i32))) - - ;; NO_FULL: (import "a" "b" (func $import)) - ;; NO_BOTH: (type $0 (func)) - - ;; NO_BOTH: (type $1 (func (param i32))) - - ;; NO_BOTH: (import "a" "b" (func $import)) - (import "a" "b" (func $import)) - - (func $full-yes-inline (param $x i32) - (call $import) - ) - - ;; NO_FULL: (func $full-maybe-inline (param $x i32) - ;; NO_FULL-NEXT: (call $import) - ;; NO_FULL-NEXT: ) - ;; NO_BOTH: (func $full-maybe-inline (param $x i32) - ;; NO_BOTH-NEXT: (call $import) - ;; NO_BOTH-NEXT: ) - (func $full-maybe-inline (param $x i32) - (call $import) - ) - - (func $partial-yes-inline (param $x i32) - (if - (local.get $x) - (then - (return) + (@binaryen.inline "\00") + (func $never (result i32) + (i32.const 42) + ) + + (func $never-unannotated (result i32) + ;; Idential to above, but without the annotation. This will be inlined, + ;; showing the hint works. + (i32.const 42) + ) + + (@binaryen.inline "\7f") + (func $always (result i32) + (local $x i32) + ;; Add code that normally makes us not inline. + (loop $loop + (local.set $x + (i32.add + (local.get $x) + (i32.const 1) + ) ) - ) - (loop $l - (call $import) - (br $l) - ) - ) - - ;; NO_PART: (func $partial-maybe-inline (param $x i32) - ;; NO_PART-NEXT: (if - ;; NO_PART-NEXT: (local.get $x) - ;; NO_PART-NEXT: (then - ;; NO_PART-NEXT: (return) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (loop $l - ;; NO_PART-NEXT: (call $import) - ;; NO_PART-NEXT: (br $l) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_BOTH: (func $partial-maybe-inline (param $x i32) - ;; NO_BOTH-NEXT: (if - ;; NO_BOTH-NEXT: (local.get $x) - ;; NO_BOTH-NEXT: (then - ;; NO_BOTH-NEXT: (return) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (loop $l - ;; NO_BOTH-NEXT: (call $import) - ;; NO_BOTH-NEXT: (br $l) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: ) - (func $partial-maybe-inline (param $x i32) - (if - (local.get $x) - (then - (return) + (br_if $loop + (i32.eqz + (local.get $x) + ) ) ) - (loop $l - (call $import) - (br $l) + (local.get $x) + ) + + (func $always-unannotated (result i32) + (local $x i32) + ;; Idential to above, but without the annotation. This will not be inlined, + ;; showing the hint works. + (loop $loop + (local.set $x + (i32.add + (local.get $x) + (i32.const 1) + ) + ) + (br_if $loop + (i32.eqz + (local.get $x) + ) + ) ) - ) - - ;; YES_ALL: (func $caller - ;; YES_ALL-NEXT: (local $0 i32) - ;; YES_ALL-NEXT: (local $1 i32) - ;; YES_ALL-NEXT: (local $2 i32) - ;; YES_ALL-NEXT: (local $3 i32) - ;; YES_ALL-NEXT: (block $__inlined_func$full-yes-inline - ;; YES_ALL-NEXT: (local.set $0 - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (call $import) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (block $__inlined_func$full-maybe-inline$1 - ;; YES_ALL-NEXT: (local.set $1 - ;; YES_ALL-NEXT: (i32.const 1) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (call $import) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-yes-inline$2 - ;; YES_ALL-NEXT: (local.set $2 - ;; YES_ALL-NEXT: (i32.const 2) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (if - ;; YES_ALL-NEXT: (i32.eqz - ;; YES_ALL-NEXT: (local.get $2) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (then - ;; YES_ALL-NEXT: (call $byn-split-outlined-A$partial-yes-inline - ;; YES_ALL-NEXT: (local.get $2) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-maybe-inline$3 - ;; YES_ALL-NEXT: (local.set $3 - ;; YES_ALL-NEXT: (i32.const 3) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (if - ;; YES_ALL-NEXT: (i32.eqz - ;; YES_ALL-NEXT: (local.get $3) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (then - ;; YES_ALL-NEXT: (call $byn-split-outlined-A$partial-maybe-inline - ;; YES_ALL-NEXT: (local.get $3) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; NO_PART: (func $caller - ;; NO_PART-NEXT: (local $0 i32) - ;; NO_PART-NEXT: (local $1 i32) - ;; NO_PART-NEXT: (local $2 i32) - ;; NO_PART-NEXT: (block $__inlined_func$full-yes-inline - ;; NO_PART-NEXT: (local.set $0 - ;; NO_PART-NEXT: (i32.const 0) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (call $import) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (block $__inlined_func$full-maybe-inline$1 - ;; NO_PART-NEXT: (local.set $1 - ;; NO_PART-NEXT: (i32.const 1) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (call $import) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-yes-inline$2 - ;; NO_PART-NEXT: (local.set $2 - ;; NO_PART-NEXT: (i32.const 2) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (if - ;; NO_PART-NEXT: (i32.eqz - ;; NO_PART-NEXT: (local.get $2) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (then - ;; NO_PART-NEXT: (call $byn-split-outlined-A$partial-yes-inline - ;; NO_PART-NEXT: (local.get $2) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (call $partial-maybe-inline - ;; NO_PART-NEXT: (i32.const 3) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_FULL: (func $caller - ;; NO_FULL-NEXT: (local $0 i32) - ;; NO_FULL-NEXT: (local $1 i32) - ;; NO_FULL-NEXT: (local $2 i32) - ;; NO_FULL-NEXT: (block $__inlined_func$full-yes-inline - ;; NO_FULL-NEXT: (local.set $0 - ;; NO_FULL-NEXT: (i32.const 0) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (call $import) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (call $full-maybe-inline - ;; NO_FULL-NEXT: (i32.const 1) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-yes-inline$1 - ;; NO_FULL-NEXT: (local.set $1 - ;; NO_FULL-NEXT: (i32.const 2) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (if - ;; NO_FULL-NEXT: (i32.eqz - ;; NO_FULL-NEXT: (local.get $1) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (then - ;; NO_FULL-NEXT: (call $byn-split-outlined-A$partial-yes-inline - ;; NO_FULL-NEXT: (local.get $1) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-maybe-inline$2 - ;; NO_FULL-NEXT: (local.set $2 - ;; NO_FULL-NEXT: (i32.const 3) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (if - ;; NO_FULL-NEXT: (i32.eqz - ;; NO_FULL-NEXT: (local.get $2) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (then - ;; NO_FULL-NEXT: (call $byn-split-outlined-A$partial-maybe-inline - ;; NO_FULL-NEXT: (local.get $2) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_BOTH: (func $caller - ;; NO_BOTH-NEXT: (local $0 i32) - ;; NO_BOTH-NEXT: (local $1 i32) - ;; NO_BOTH-NEXT: (block $__inlined_func$full-yes-inline - ;; NO_BOTH-NEXT: (local.set $0 - ;; NO_BOTH-NEXT: (i32.const 0) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (call $import) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (call $full-maybe-inline - ;; NO_BOTH-NEXT: (i32.const 1) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-yes-inline$1 - ;; NO_BOTH-NEXT: (local.set $1 - ;; NO_BOTH-NEXT: (i32.const 2) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (if - ;; NO_BOTH-NEXT: (i32.eqz - ;; NO_BOTH-NEXT: (local.get $1) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (then - ;; NO_BOTH-NEXT: (call $byn-split-outlined-A$partial-yes-inline - ;; NO_BOTH-NEXT: (local.get $1) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (call $partial-maybe-inline - ;; NO_BOTH-NEXT: (i32.const 3) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: ) + (local.get $x) + ) + + ;; CHECK: (type $0 (func)) + + ;; CHECK: (func $caller + ;; CHECK-NEXT: (local $0 i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block $__inlined_func$never (result i32) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block $__inlined_func$never-unannotated$1 (result i32) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block $__inlined_func$always$2 (result i32) + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (loop $loop + ;; CHECK-NEXT: (local.set $0 + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br_if $loop + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block $__inlined_func$always-unannotated$3 (result i32) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block (result i32) + ;; CHECK-NEXT: (loop $loop0 + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br_if $loop0 + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) (func $caller - (call $full-yes-inline - (i32.const 0) - ) - (call $full-maybe-inline - (i32.const 1) + (drop + (call $never) ) - (call $partial-yes-inline - (i32.const 2) - ) - (call $partial-maybe-inline - (i32.const 3) - ) - ) - - ;; YES_ALL: (func $caller-2 - ;; YES_ALL-NEXT: (local $0 i32) - ;; YES_ALL-NEXT: (local $1 i32) - ;; YES_ALL-NEXT: (local $2 i32) - ;; YES_ALL-NEXT: (local $3 i32) - ;; YES_ALL-NEXT: (block $__inlined_func$full-yes-inline$4 - ;; YES_ALL-NEXT: (local.set $0 - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (call $import) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (block $__inlined_func$full-maybe-inline$5 - ;; YES_ALL-NEXT: (local.set $1 - ;; YES_ALL-NEXT: (i32.const 1) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (call $import) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-yes-inline$6 - ;; YES_ALL-NEXT: (local.set $2 - ;; YES_ALL-NEXT: (i32.const 2) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (if - ;; YES_ALL-NEXT: (i32.eqz - ;; YES_ALL-NEXT: (local.get $2) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (then - ;; YES_ALL-NEXT: (call $byn-split-outlined-A$partial-yes-inline - ;; YES_ALL-NEXT: (local.get $2) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-maybe-inline$7 - ;; YES_ALL-NEXT: (local.set $3 - ;; YES_ALL-NEXT: (i32.const 3) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (if - ;; YES_ALL-NEXT: (i32.eqz - ;; YES_ALL-NEXT: (local.get $3) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (then - ;; YES_ALL-NEXT: (call $byn-split-outlined-A$partial-maybe-inline - ;; YES_ALL-NEXT: (local.get $3) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; NO_PART: (func $caller-2 - ;; NO_PART-NEXT: (local $0 i32) - ;; NO_PART-NEXT: (local $1 i32) - ;; NO_PART-NEXT: (local $2 i32) - ;; NO_PART-NEXT: (block $__inlined_func$full-yes-inline$3 - ;; NO_PART-NEXT: (local.set $0 - ;; NO_PART-NEXT: (i32.const 0) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (call $import) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (block $__inlined_func$full-maybe-inline$4 - ;; NO_PART-NEXT: (local.set $1 - ;; NO_PART-NEXT: (i32.const 1) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (call $import) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-yes-inline$5 - ;; NO_PART-NEXT: (local.set $2 - ;; NO_PART-NEXT: (i32.const 2) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (if - ;; NO_PART-NEXT: (i32.eqz - ;; NO_PART-NEXT: (local.get $2) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (then - ;; NO_PART-NEXT: (call $byn-split-outlined-A$partial-yes-inline - ;; NO_PART-NEXT: (local.get $2) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (call $partial-maybe-inline - ;; NO_PART-NEXT: (i32.const 3) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_FULL: (func $caller-2 - ;; NO_FULL-NEXT: (local $0 i32) - ;; NO_FULL-NEXT: (local $1 i32) - ;; NO_FULL-NEXT: (local $2 i32) - ;; NO_FULL-NEXT: (block $__inlined_func$full-yes-inline$3 - ;; NO_FULL-NEXT: (local.set $0 - ;; NO_FULL-NEXT: (i32.const 0) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (call $import) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (call $full-maybe-inline - ;; NO_FULL-NEXT: (i32.const 1) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-yes-inline$4 - ;; NO_FULL-NEXT: (local.set $1 - ;; NO_FULL-NEXT: (i32.const 2) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (if - ;; NO_FULL-NEXT: (i32.eqz - ;; NO_FULL-NEXT: (local.get $1) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (then - ;; NO_FULL-NEXT: (call $byn-split-outlined-A$partial-yes-inline - ;; NO_FULL-NEXT: (local.get $1) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-maybe-inline$5 - ;; NO_FULL-NEXT: (local.set $2 - ;; NO_FULL-NEXT: (i32.const 3) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (if - ;; NO_FULL-NEXT: (i32.eqz - ;; NO_FULL-NEXT: (local.get $2) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (then - ;; NO_FULL-NEXT: (call $byn-split-outlined-A$partial-maybe-inline - ;; NO_FULL-NEXT: (local.get $2) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_BOTH: (func $caller-2 - ;; NO_BOTH-NEXT: (local $0 i32) - ;; NO_BOTH-NEXT: (local $1 i32) - ;; NO_BOTH-NEXT: (block $__inlined_func$full-yes-inline$2 - ;; NO_BOTH-NEXT: (local.set $0 - ;; NO_BOTH-NEXT: (i32.const 0) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (call $import) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (call $full-maybe-inline - ;; NO_BOTH-NEXT: (i32.const 1) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (block $__inlined_func$byn-split-inlineable-A$partial-yes-inline$3 - ;; NO_BOTH-NEXT: (local.set $1 - ;; NO_BOTH-NEXT: (i32.const 2) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (if - ;; NO_BOTH-NEXT: (i32.eqz - ;; NO_BOTH-NEXT: (local.get $1) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (then - ;; NO_BOTH-NEXT: (call $byn-split-outlined-A$partial-yes-inline - ;; NO_BOTH-NEXT: (local.get $1) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (call $partial-maybe-inline - ;; NO_BOTH-NEXT: (i32.const 3) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: ) - (func $caller-2 - ;; Same as $caller, to prevent the functions from having a single use (which - ;; is always inlined). - (call $full-yes-inline - (i32.const 0) + (drop + (call $never-unannotated) ) - (call $full-maybe-inline - (i32.const 1) + (drop + (call $always) ) - (call $partial-yes-inline - (i32.const 2) + (drop + (call $always-unannotated) ) - (call $partial-maybe-inline - (i32.const 3) - ) - ) -) - -;; YES_ALL: (func $byn-split-outlined-A$partial-yes-inline (param $x i32) -;; YES_ALL-NEXT: (loop $l -;; YES_ALL-NEXT: (call $import) -;; YES_ALL-NEXT: (br $l) -;; YES_ALL-NEXT: ) -;; YES_ALL-NEXT: ) - -;; YES_ALL: (func $byn-split-outlined-A$partial-maybe-inline (param $x i32) -;; YES_ALL-NEXT: (loop $l -;; YES_ALL-NEXT: (call $import) -;; YES_ALL-NEXT: (br $l) -;; YES_ALL-NEXT: ) -;; YES_ALL-NEXT: ) - -;; NO_PART: (func $byn-split-outlined-A$partial-yes-inline (param $x i32) -;; NO_PART-NEXT: (loop $l -;; NO_PART-NEXT: (call $import) -;; NO_PART-NEXT: (br $l) -;; NO_PART-NEXT: ) -;; NO_PART-NEXT: ) - -;; NO_FULL: (func $byn-split-outlined-A$partial-yes-inline (param $x i32) -;; NO_FULL-NEXT: (loop $l -;; NO_FULL-NEXT: (call $import) -;; NO_FULL-NEXT: (br $l) -;; NO_FULL-NEXT: ) -;; NO_FULL-NEXT: ) - -;; NO_FULL: (func $byn-split-outlined-A$partial-maybe-inline (param $x i32) -;; NO_FULL-NEXT: (loop $l -;; NO_FULL-NEXT: (call $import) -;; NO_FULL-NEXT: (br $l) -;; NO_FULL-NEXT: ) -;; NO_FULL-NEXT: ) - -;; NO_BOTH: (func $byn-split-outlined-A$partial-yes-inline (param $x i32) -;; NO_BOTH-NEXT: (loop $l -;; NO_BOTH-NEXT: (call $import) -;; NO_BOTH-NEXT: (br $l) -;; NO_BOTH-NEXT: ) -;; NO_BOTH-NEXT: ) -(module - ;; YES_ALL: (type $0 (func)) - - ;; YES_ALL: (import "out" "func" (func $import)) - ;; NO_PART: (type $0 (func)) - - ;; NO_PART: (type $1 (func (param i32))) - - ;; NO_PART: (import "out" "func" (func $import)) - ;; NO_FULL: (type $0 (func)) - - ;; NO_FULL: (type $1 (func (param i32))) - - ;; NO_FULL: (import "out" "func" (func $import)) - ;; NO_BOTH: (type $0 (func)) - - ;; NO_BOTH: (type $1 (func (param i32))) - - ;; NO_BOTH: (import "out" "func" (func $import)) - (import "out" "func" (func $import)) - - ;; NO_PART: (func $maybe-partial-or-full-1 (param $x i32) - ;; NO_PART-NEXT: (if - ;; NO_PART-NEXT: (local.get $x) - ;; NO_PART-NEXT: (then - ;; NO_PART-NEXT: (call $import) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_BOTH: (func $maybe-partial-or-full-1 (param $x i32) - ;; NO_BOTH-NEXT: (if - ;; NO_BOTH-NEXT: (local.get $x) - ;; NO_BOTH-NEXT: (then - ;; NO_BOTH-NEXT: (call $import) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: ) - (func $maybe-partial-or-full-1 (param $x i32) - ;; This function can be partially inlined, but after doing so we want to - ;; inline the remainder as well, so instead we fully inline it. When full - ;; inlining is disabled but partial inlining is enabled, we should only - ;; partially inline it. - (if - (local.get $x) - (then - (call $import) - ) - ) - ) - - ;; NO_PART: (func $maybe-partial-or-full-2 (param $x i32) - ;; NO_PART-NEXT: (if - ;; NO_PART-NEXT: (local.get $x) - ;; NO_PART-NEXT: (then - ;; NO_PART-NEXT: (return) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (nop) - ;; NO_PART-NEXT: (drop - ;; NO_PART-NEXT: (i32.const 0) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (drop - ;; NO_PART-NEXT: (i32.const 0) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (drop - ;; NO_PART-NEXT: (i32.const 0) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (drop - ;; NO_PART-NEXT: (i32.const 0) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (drop - ;; NO_PART-NEXT: (i32.const 0) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (drop - ;; NO_PART-NEXT: (i32.const 0) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (drop - ;; NO_PART-NEXT: (i32.const 0) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (drop - ;; NO_PART-NEXT: (i32.const 0) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_BOTH: (func $maybe-partial-or-full-2 (param $x i32) - ;; NO_BOTH-NEXT: (if - ;; NO_BOTH-NEXT: (local.get $x) - ;; NO_BOTH-NEXT: (then - ;; NO_BOTH-NEXT: (return) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (nop) - ;; NO_BOTH-NEXT: (drop - ;; NO_BOTH-NEXT: (i32.const 0) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (drop - ;; NO_BOTH-NEXT: (i32.const 0) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (drop - ;; NO_BOTH-NEXT: (i32.const 0) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (drop - ;; NO_BOTH-NEXT: (i32.const 0) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (drop - ;; NO_BOTH-NEXT: (i32.const 0) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (drop - ;; NO_BOTH-NEXT: (i32.const 0) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (drop - ;; NO_BOTH-NEXT: (i32.const 0) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (drop - ;; NO_BOTH-NEXT: (i32.const 0) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: ) - (func $maybe-partial-or-full-2 (param $x i32) - ;; As above, but for another form of partial inlining. Here we need to add - ;; some extra things to the function size for partial inlining to kick in. - (if - (local.get $x) - (then - (return) - ) - ) - (nop) - (drop - (i32.const 0) - ) - (drop - (i32.const 0) - ) - (drop - (i32.const 0) - ) - (drop - (i32.const 0) - ) - (drop - (i32.const 0) - ) - (drop - (i32.const 0) ) - (drop - (i32.const 0) - ) - (drop - (i32.const 0) - ) - ) - -(func $very-long-name-we-should-not-error-on-even-though-it-is-very-very-long - ;; Test a long name. - ) - - ;; NO_FULL: (func $very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long - ;; NO_FULL-NEXT: ) - ;; NO_BOTH: (func $very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long - ;; NO_BOTH-NEXT: ) - (func $very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long - ;; Test a long name with "maybe" in it. - ) - - ;; NO_FULL: (func $very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long - ;; NO_FULL-NEXT: ) - ;; NO_BOTH: (func $very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long - ;; NO_BOTH-NEXT: ) - (func $very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long - ;; Test a long name with "maybe" in it, and a partial match earlier ("may"). - ) - -(func $maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmayb - ;; Test a long name with many partial matches but no real match. -) - -;; NO_FULL: (func $maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybe -;; NO_FULL-NEXT: ) -;; NO_BOTH: (func $maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybe -;; NO_BOTH-NEXT: ) -(func $maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybe - ;; Test a long name with many partial matches and one real match right at the end. ) - -(func $mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm - ;; Test a long name with even more tiny partial matches but no real match. -) - - ;; YES_ALL: (func $caller - ;; YES_ALL-NEXT: (local $0 i32) - ;; YES_ALL-NEXT: (local $1 i32) - ;; YES_ALL-NEXT: (local $2 i32) - ;; YES_ALL-NEXT: (local $3 i32) - ;; YES_ALL-NEXT: (block $__inlined_func$maybe-partial-or-full-1 - ;; YES_ALL-NEXT: (local.set $0 - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (if - ;; YES_ALL-NEXT: (local.get $0) - ;; YES_ALL-NEXT: (then - ;; YES_ALL-NEXT: (call $import) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (block $__inlined_func$maybe-partial-or-full-1$1 - ;; YES_ALL-NEXT: (local.set $1 - ;; YES_ALL-NEXT: (i32.const 1) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (if - ;; YES_ALL-NEXT: (local.get $1) - ;; YES_ALL-NEXT: (then - ;; YES_ALL-NEXT: (call $import) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (block $__inlined_func$maybe-partial-or-full-2$2 - ;; YES_ALL-NEXT: (local.set $2 - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (block - ;; YES_ALL-NEXT: (if - ;; YES_ALL-NEXT: (local.get $2) - ;; YES_ALL-NEXT: (then - ;; YES_ALL-NEXT: (br $__inlined_func$maybe-partial-or-full-2$2) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (nop) - ;; YES_ALL-NEXT: (drop - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (drop - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (drop - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (drop - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (drop - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (drop - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (drop - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (drop - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (block $__inlined_func$maybe-partial-or-full-2$3 - ;; YES_ALL-NEXT: (local.set $3 - ;; YES_ALL-NEXT: (i32.const 1) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (block - ;; YES_ALL-NEXT: (if - ;; YES_ALL-NEXT: (local.get $3) - ;; YES_ALL-NEXT: (then - ;; YES_ALL-NEXT: (br $__inlined_func$maybe-partial-or-full-2$3) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (nop) - ;; YES_ALL-NEXT: (drop - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (drop - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (drop - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (drop - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (drop - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (drop - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (drop - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (drop - ;; YES_ALL-NEXT: (i32.const 0) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (block $__inlined_func$very-long-name-we-should-not-error-on-even-though-it-is-very-very-long$4 - ;; YES_ALL-NEXT: (block - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (block $__inlined_func$very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long$5 - ;; YES_ALL-NEXT: (block - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (block $__inlined_func$very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long$6 - ;; YES_ALL-NEXT: (block - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (block $__inlined_func$maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmayb$7 - ;; YES_ALL-NEXT: (block - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (block $__inlined_func$maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybe$8 - ;; YES_ALL-NEXT: (block - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: (block $__inlined_func$mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm$9 - ;; YES_ALL-NEXT: (block - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; YES_ALL-NEXT: ) - ;; NO_PART: (func $caller - ;; NO_PART-NEXT: (call $maybe-partial-or-full-1 - ;; NO_PART-NEXT: (i32.const 0) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (call $maybe-partial-or-full-1 - ;; NO_PART-NEXT: (i32.const 1) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (call $maybe-partial-or-full-2 - ;; NO_PART-NEXT: (i32.const 0) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (call $maybe-partial-or-full-2 - ;; NO_PART-NEXT: (i32.const 1) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (block $__inlined_func$very-long-name-we-should-not-error-on-even-though-it-is-very-very-long - ;; NO_PART-NEXT: (block - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (block $__inlined_func$very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long$1 - ;; NO_PART-NEXT: (block - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (block $__inlined_func$very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long$2 - ;; NO_PART-NEXT: (block - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (block $__inlined_func$maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmayb$3 - ;; NO_PART-NEXT: (block - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (block $__inlined_func$maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybe$4 - ;; NO_PART-NEXT: (block - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: (block $__inlined_func$mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm$5 - ;; NO_PART-NEXT: (block - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_PART-NEXT: ) - ;; NO_FULL: (func $caller - ;; NO_FULL-NEXT: (local $0 i32) - ;; NO_FULL-NEXT: (local $1 i32) - ;; NO_FULL-NEXT: (local $2 i32) - ;; NO_FULL-NEXT: (local $3 i32) - ;; NO_FULL-NEXT: (block $__inlined_func$byn-split-inlineable-B$maybe-partial-or-full-1 - ;; NO_FULL-NEXT: (local.set $0 - ;; NO_FULL-NEXT: (i32.const 0) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (if - ;; NO_FULL-NEXT: (local.get $0) - ;; NO_FULL-NEXT: (then - ;; NO_FULL-NEXT: (call $byn-split-outlined-B$maybe-partial-or-full-1 - ;; NO_FULL-NEXT: (local.get $0) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (block $__inlined_func$byn-split-inlineable-B$maybe-partial-or-full-1$1 - ;; NO_FULL-NEXT: (local.set $1 - ;; NO_FULL-NEXT: (i32.const 1) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (if - ;; NO_FULL-NEXT: (local.get $1) - ;; NO_FULL-NEXT: (then - ;; NO_FULL-NEXT: (call $byn-split-outlined-B$maybe-partial-or-full-1 - ;; NO_FULL-NEXT: (local.get $1) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (block $__inlined_func$byn-split-inlineable-A$maybe-partial-or-full-2$2 - ;; NO_FULL-NEXT: (local.set $2 - ;; NO_FULL-NEXT: (i32.const 0) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (if - ;; NO_FULL-NEXT: (i32.eqz - ;; NO_FULL-NEXT: (local.get $2) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (then - ;; NO_FULL-NEXT: (call $byn-split-outlined-A$maybe-partial-or-full-2 - ;; NO_FULL-NEXT: (local.get $2) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (block $__inlined_func$byn-split-inlineable-A$maybe-partial-or-full-2$3 - ;; NO_FULL-NEXT: (local.set $3 - ;; NO_FULL-NEXT: (i32.const 1) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (if - ;; NO_FULL-NEXT: (i32.eqz - ;; NO_FULL-NEXT: (local.get $3) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (then - ;; NO_FULL-NEXT: (call $byn-split-outlined-A$maybe-partial-or-full-2 - ;; NO_FULL-NEXT: (local.get $3) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (block $__inlined_func$very-long-name-we-should-not-error-on-even-though-it-is-very-very-long$4 - ;; NO_FULL-NEXT: (block - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (call $very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long) - ;; NO_FULL-NEXT: (call $very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long) - ;; NO_FULL-NEXT: (block $__inlined_func$maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmayb$5 - ;; NO_FULL-NEXT: (block - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: (call $maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybe) - ;; NO_FULL-NEXT: (block $__inlined_func$mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm$6 - ;; NO_FULL-NEXT: (block - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_FULL-NEXT: ) - ;; NO_BOTH: (func $caller - ;; NO_BOTH-NEXT: (call $maybe-partial-or-full-1 - ;; NO_BOTH-NEXT: (i32.const 0) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (call $maybe-partial-or-full-1 - ;; NO_BOTH-NEXT: (i32.const 1) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (call $maybe-partial-or-full-2 - ;; NO_BOTH-NEXT: (i32.const 0) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (call $maybe-partial-or-full-2 - ;; NO_BOTH-NEXT: (i32.const 1) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (block $__inlined_func$very-long-name-we-should-not-error-on-even-though-it-is-very-very-long - ;; NO_BOTH-NEXT: (block - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (call $very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long) - ;; NO_BOTH-NEXT: (call $very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long) - ;; NO_BOTH-NEXT: (block $__inlined_func$maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmayb$1 - ;; NO_BOTH-NEXT: (block - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: (call $maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybe) - ;; NO_BOTH-NEXT: (block $__inlined_func$mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm$2 - ;; NO_BOTH-NEXT: (block - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: ) - ;; NO_BOTH-NEXT: ) - (func $caller - ;; In YES_ALL we will fully inline all of these. In NO_FULL we will partially - ;; inline. In NO_PART and NO_BOTH we will not inline at all (in theory we - ;; could do full inlining when only partial inlining is disabled, but we only - ;; see that full inlining is possible as a result of partial inlining, so if - ;; partial is disabled we don't get there, and full inlining is not - ;; justified). - (call $maybe-partial-or-full-1 - (i32.const 0) - ) - (call $maybe-partial-or-full-1 - (i32.const 1) - ) - (call $maybe-partial-or-full-2 - (i32.const 0) - ) - (call $maybe-partial-or-full-2 - (i32.const 1) - ) - (call $very-long-name-we-should-not-error-on-even-though-it-is-very-very-long) - (call $very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long) - (call $very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long) - (call $maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmayb) - (call $maybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybmaybe) - (call $mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm) - ) -) -;; NO_FULL: (func $byn-split-outlined-B$maybe-partial-or-full-1 (param $x i32) -;; NO_FULL-NEXT: (call $import) -;; NO_FULL-NEXT: ) - -;; NO_FULL: (func $byn-split-outlined-A$maybe-partial-or-full-2 (param $x i32) -;; NO_FULL-NEXT: (nop) -;; NO_FULL-NEXT: (drop -;; NO_FULL-NEXT: (i32.const 0) -;; NO_FULL-NEXT: ) -;; NO_FULL-NEXT: (drop -;; NO_FULL-NEXT: (i32.const 0) -;; NO_FULL-NEXT: ) -;; NO_FULL-NEXT: (drop -;; NO_FULL-NEXT: (i32.const 0) -;; NO_FULL-NEXT: ) -;; NO_FULL-NEXT: (drop -;; NO_FULL-NEXT: (i32.const 0) -;; NO_FULL-NEXT: ) -;; NO_FULL-NEXT: (drop -;; NO_FULL-NEXT: (i32.const 0) -;; NO_FULL-NEXT: ) -;; NO_FULL-NEXT: (drop -;; NO_FULL-NEXT: (i32.const 0) -;; NO_FULL-NEXT: ) -;; NO_FULL-NEXT: (drop -;; NO_FULL-NEXT: (i32.const 0) -;; NO_FULL-NEXT: ) -;; NO_FULL-NEXT: (drop -;; NO_FULL-NEXT: (i32.const 0) -;; NO_FULL-NEXT: ) -;; NO_FULL-NEXT: ) diff --git a/test/lit/toolchain-inline-hints-func.wast b/test/lit/toolchain-inline-hints-func.wast index 363ef512729..653a9232813 100644 --- a/test/lit/toolchain-inline-hints-func.wast +++ b/test/lit/toolchain-inline-hints-func.wast @@ -3,7 +3,7 @@ ;; RUN: wasm-opt -all --roundtrip %s -S -o - | filecheck %s (module - ;; CHECK: (@binaryen.inline "\12") + ;; CHECK: (@binaryen.inline "\00") ;; CHECK-NEXT: (func $func-annotation (type $0) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 0) diff --git a/test/lit/toolchain-inline-hints.wast b/test/lit/toolchain-inline-hints.wast index a61d9ff5392..2f8823e37a4 100644 --- a/test/lit/toolchain-inline-hints.wast +++ b/test/lit/toolchain-inline-hints.wast @@ -18,11 +18,11 @@ ;; CHECK: (func $func (type $func) ;; CHECK-NEXT: (@binaryen.inline "\00") ;; CHECK-NEXT: (call $func) - ;; CHECK-NEXT: (@binaryen.inline "\01") + ;; CHECK-NEXT: (@binaryen.inline "\00") ;; CHECK-NEXT: (call $func) - ;; CHECK-NEXT: (@binaryen.inline "\7e") + ;; CHECK-NEXT: (@binaryen.inline "\00") ;; CHECK-NEXT: (call $func) - ;; CHECK-NEXT: (@binaryen.inline "\7f") + ;; CHECK-NEXT: (@binaryen.inline "\00") ;; CHECK-NEXT: (call $func) ;; CHECK-NEXT: (call $func) ;; CHECK-NEXT: ) @@ -31,11 +31,11 @@ ;; RTRIP: (func $func (type $func) ;; RTRIP-NEXT: (@binaryen.inline "\00") ;; RTRIP-NEXT: (call $func) - ;; RTRIP-NEXT: (@binaryen.inline "\01") + ;; RTRIP-NEXT: (@binaryen.inline "\00") ;; RTRIP-NEXT: (call $func) - ;; RTRIP-NEXT: (@binaryen.inline "\7e") + ;; RTRIP-NEXT: (@binaryen.inline "\00") ;; RTRIP-NEXT: (call $func) - ;; RTRIP-NEXT: (@binaryen.inline "\7f") + ;; RTRIP-NEXT: (@binaryen.inline "\00") ;; RTRIP-NEXT: (call $func) ;; RTRIP-NEXT: (call $func) ;; RTRIP-NEXT: ) @@ -53,21 +53,21 @@ ) ;; CHECK: (func $other-calls (type $func) - ;; CHECK-NEXT: (@binaryen.inline "\12") + ;; CHECK-NEXT: (@binaryen.inline "\00") ;; CHECK-NEXT: (call_indirect $table (type $func) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (@binaryen.inline "\34") + ;; CHECK-NEXT: (@binaryen.inline "\00") ;; CHECK-NEXT: (call_ref $func ;; CHECK-NEXT: (ref.func $func) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; RTRIP: (func $other-calls (type $func) - ;; RTRIP-NEXT: (@binaryen.inline "\12") + ;; RTRIP-NEXT: (@binaryen.inline "\00") ;; RTRIP-NEXT: (call_indirect $table (type $func) ;; RTRIP-NEXT: (i32.const 0) ;; RTRIP-NEXT: ) - ;; RTRIP-NEXT: (@binaryen.inline "\34") + ;; RTRIP-NEXT: (@binaryen.inline "\00") ;; RTRIP-NEXT: (call_ref $func ;; RTRIP-NEXT: (ref.func $func) ;; RTRIP-NEXT: ) From 0b2bef7367e0adc2c38f6f12395fcb1a45bfadd1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 12:16:29 -0700 Subject: [PATCH 10/21] work --- test/lit/passes/strip-toolchain-annotations-func.wast | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/lit/passes/strip-toolchain-annotations-func.wast b/test/lit/passes/strip-toolchain-annotations-func.wast index 755166a4baa..c59586e4da9 100644 --- a/test/lit/passes/strip-toolchain-annotations-func.wast +++ b/test/lit/passes/strip-toolchain-annotations-func.wast @@ -43,8 +43,10 @@ ;; This hint should be removed too. ) + ;; CHECK: (func $test-binaryen-inline (type $0) + ;; CHECK-NEXT: ) (@binaryen.inline "\00") - (func $test-func-a + (func $test-binaryen-inline ;; This hint should be removed too. ) ) From 7bbabd94e6e3b0165545625797a7253000014f9c Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 12:24:25 -0700 Subject: [PATCH 11/21] work --- src/parser/contexts.h | 28 +++++++++++++++-------- src/passes/Print.cpp | 2 +- test/lit/toolchain-inline-hints-func.wast | 2 +- test/lit/toolchain-inline-hints.wast | 24 +++++++++---------- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/parser/contexts.h b/src/parser/contexts.h index 3ea68de0b76..e2dd4f3820c 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -1361,6 +1361,7 @@ struct AnnotationParserCtx { // overrides the others. const Annotation* branchHint = nullptr; const Annotation* inlineHint = nullptr; + const Annotation* toolchainInlineHint = nullptr; for (auto& a : annotations) { if (a.kind == Annotations::BranchHint) { branchHint = &a; @@ -1373,7 +1374,7 @@ struct AnnotationParserCtx { } else if (a.kind == Annotations::IdempotentHint) { ret.idempotent = true; } else if (a.kind == Annotations::ToolchainInlineHint) { - ret.toolchainInline = true; + toolchainInlineHint = &a; } } @@ -1397,25 +1398,34 @@ struct AnnotationParserCtx { } } - // Apply the last inline hint, if valid. - if (inlineHint) { - Lexer lexer(inlineHint->contents); + auto parseI7Hint = [&](Annotation* hint, const char* name) -> std::optional { + if (!hint) { + return {}; + } + + Lexer lexer(hint->contents); if (lexer.empty()) { - std::cerr << "warning: empty InlineHint\n"; + std::cerr << "warning: empty " << name << "\n"; } else { auto str = lexer.takeString(); if (!str || str->size() != 1) { - std::cerr << "warning: invalid InlineHint string\n"; + std::cerr << "warning: invalid " << name << " string\n"; } else { uint8_t value = (*str)[0]; if (value > 127) { - std::cerr << "warning: invalid InlineHint value\n"; + std::cerr << "warning: invalid " << name << " value\n"; } else { - ret.inline_ = value; + return value; } } } - } + + return {}; + }; + + // Apply the last inline hints, if valid. + ret.inline_ = parseI7Hint(inlineHint); + ret.toolchainInline = parseI7Hint(toolchainInlineHint); return ret; } diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index b899e979453..83e7baa580d 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2843,7 +2843,7 @@ void PrintSExpression::printCodeAnnotations(const CodeAnnotation& annotation) { std::ofstream saved; saved.copyfmt(o); o << "(@" << Annotations::ToolchainInlineHint << " \"\\" << std::hex - << std::setfill('0') << std::setw(2) << int(*annotation.inline_) + << std::setfill('0') << std::setw(2) << int(*annotation.toolchainInline) << "\")\n"; o.copyfmt(saved); restoreNormalColor(o); diff --git a/test/lit/toolchain-inline-hints-func.wast b/test/lit/toolchain-inline-hints-func.wast index 653a9232813..967bcf08895 100644 --- a/test/lit/toolchain-inline-hints-func.wast +++ b/test/lit/toolchain-inline-hints-func.wast @@ -3,7 +3,7 @@ ;; RUN: wasm-opt -all --roundtrip %s -S -o - | filecheck %s (module - ;; CHECK: (@binaryen.inline "\00") + ;; CHECK: (@binaryen.inline "\01") ;; CHECK-NEXT: (func $func-annotation (type $0) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 0) diff --git a/test/lit/toolchain-inline-hints.wast b/test/lit/toolchain-inline-hints.wast index 2f8823e37a4..309969ee6ea 100644 --- a/test/lit/toolchain-inline-hints.wast +++ b/test/lit/toolchain-inline-hints.wast @@ -16,26 +16,26 @@ ;; CHECK: (elem declare func $func) ;; CHECK: (func $func (type $func) - ;; CHECK-NEXT: (@binaryen.inline "\00") + ;; CHECK-NEXT: (@binaryen.inline "\01") ;; CHECK-NEXT: (call $func) - ;; CHECK-NEXT: (@binaryen.inline "\00") + ;; CHECK-NEXT: (@binaryen.inline "\01") ;; CHECK-NEXT: (call $func) - ;; CHECK-NEXT: (@binaryen.inline "\00") + ;; CHECK-NEXT: (@binaryen.inline "\01") ;; CHECK-NEXT: (call $func) - ;; CHECK-NEXT: (@binaryen.inline "\00") + ;; CHECK-NEXT: (@binaryen.inline "\01") ;; CHECK-NEXT: (call $func) ;; CHECK-NEXT: (call $func) ;; CHECK-NEXT: ) ;; RTRIP: (elem declare func $func) ;; RTRIP: (func $func (type $func) - ;; RTRIP-NEXT: (@binaryen.inline "\00") + ;; RTRIP-NEXT: (@binaryen.inline "\01") ;; RTRIP-NEXT: (call $func) - ;; RTRIP-NEXT: (@binaryen.inline "\00") + ;; RTRIP-NEXT: (@binaryen.inline "\01") ;; RTRIP-NEXT: (call $func) - ;; RTRIP-NEXT: (@binaryen.inline "\00") + ;; RTRIP-NEXT: (@binaryen.inline "\01") ;; RTRIP-NEXT: (call $func) - ;; RTRIP-NEXT: (@binaryen.inline "\00") + ;; RTRIP-NEXT: (@binaryen.inline "\01") ;; RTRIP-NEXT: (call $func) ;; RTRIP-NEXT: (call $func) ;; RTRIP-NEXT: ) @@ -53,21 +53,21 @@ ) ;; CHECK: (func $other-calls (type $func) - ;; CHECK-NEXT: (@binaryen.inline "\00") + ;; CHECK-NEXT: (@binaryen.inline "\01") ;; CHECK-NEXT: (call_indirect $table (type $func) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (@binaryen.inline "\00") + ;; CHECK-NEXT: (@binaryen.inline "\01") ;; CHECK-NEXT: (call_ref $func ;; CHECK-NEXT: (ref.func $func) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; RTRIP: (func $other-calls (type $func) - ;; RTRIP-NEXT: (@binaryen.inline "\00") + ;; RTRIP-NEXT: (@binaryen.inline "\01") ;; RTRIP-NEXT: (call_indirect $table (type $func) ;; RTRIP-NEXT: (i32.const 0) ;; RTRIP-NEXT: ) - ;; RTRIP-NEXT: (@binaryen.inline "\00") + ;; RTRIP-NEXT: (@binaryen.inline "\01") ;; RTRIP-NEXT: (call_ref $func ;; RTRIP-NEXT: (ref.func $func) ;; RTRIP-NEXT: ) From d09c68328dd397d168d35bc1d713f196ade95621 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 12:24:52 -0700 Subject: [PATCH 12/21] work --- src/parser/contexts.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parser/contexts.h b/src/parser/contexts.h index e2dd4f3820c..49aa27a6538 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -1424,8 +1424,8 @@ struct AnnotationParserCtx { }; // Apply the last inline hints, if valid. - ret.inline_ = parseI7Hint(inlineHint); - ret.toolchainInline = parseI7Hint(toolchainInlineHint); + ret.inline_ = parseI7Hint(inlineHint, "InlineHint"); + ret.toolchainInline = parseI7Hint(toolchainInlineHint, "Toolchain InlineHint"); return ret; } From 4261db71e649c349ab5b29f5942b332987a08bf1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 12:25:17 -0700 Subject: [PATCH 13/21] work --- src/parser/contexts.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser/contexts.h b/src/parser/contexts.h index 49aa27a6538..4a8ecf1fba4 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -1398,7 +1398,7 @@ struct AnnotationParserCtx { } } - auto parseI7Hint = [&](Annotation* hint, const char* name) -> std::optional { + auto parseI7Hint = [&](const Annotation* hint, const char* name) -> std::optional { if (!hint) { return {}; } From 0e4863e8faa22d705e1fe300a993c9ff7ef22078 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 13:03:05 -0700 Subject: [PATCH 14/21] work --- test/lit/passes/toolchain-inlining.wast | 20 ++++++++++++-------- test/lit/toolchain-inline-hints-func.wast | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/test/lit/passes/toolchain-inlining.wast b/test/lit/passes/toolchain-inlining.wast index 5afc0f9446f..2de9d466fd4 100644 --- a/test/lit/passes/toolchain-inlining.wast +++ b/test/lit/passes/toolchain-inlining.wast @@ -3,6 +3,14 @@ ;; RUN: foreach %s %t wasm-opt --inlining --optimize-level=3 -S -o - | filecheck %s (module + ;; CHECK: (type $0 (func (result i32))) + + ;; CHECK: (type $1 (func)) + + ;; CHECK: (@binaryen.inline "\00") + ;; CHECK-NEXT: (func $never (result i32) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: ) (@binaryen.inline "\00") (func $never (result i32) (i32.const 42) @@ -54,23 +62,19 @@ (local.get $x) ) - ;; CHECK: (type $0 (func)) - ;; CHECK: (func $caller ;; CHECK-NEXT: (local $0 i32) ;; CHECK-NEXT: (local $1 i32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block $__inlined_func$never (result i32) - ;; CHECK-NEXT: (i32.const 42) - ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $never) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block $__inlined_func$never-unannotated$1 (result i32) + ;; CHECK-NEXT: (block $__inlined_func$never-unannotated (result i32) ;; CHECK-NEXT: (i32.const 42) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block $__inlined_func$always$2 (result i32) + ;; CHECK-NEXT: (block $__inlined_func$always$1 (result i32) ;; CHECK-NEXT: (local.set $0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -93,7 +97,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block $__inlined_func$always-unannotated$3 (result i32) + ;; CHECK-NEXT: (block $__inlined_func$always-unannotated$2 (result i32) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) diff --git a/test/lit/toolchain-inline-hints-func.wast b/test/lit/toolchain-inline-hints-func.wast index 967bcf08895..363ef512729 100644 --- a/test/lit/toolchain-inline-hints-func.wast +++ b/test/lit/toolchain-inline-hints-func.wast @@ -3,7 +3,7 @@ ;; RUN: wasm-opt -all --roundtrip %s -S -o - | filecheck %s (module - ;; CHECK: (@binaryen.inline "\01") + ;; CHECK: (@binaryen.inline "\12") ;; CHECK-NEXT: (func $func-annotation (type $0) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 0) From 0efd7d1885034e76840125d9cdf5ce818522def5 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 13:09:02 -0700 Subject: [PATCH 15/21] work --- src/wasm/wasm-ir-builder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index c729796d0a9..9a66bb50fe2 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -2866,7 +2866,7 @@ void IRBuilder::applyAnnotations(Expression* expr, if (annotation.toolchainInline) { assert(func); - func->codeAnnotations[expr].toolchainInline = true; + func->codeAnnotations[expr].toolchainInline = annotation.toolchainInline; } } From bb75d303800a6a28f4c6db4111d09507c684dedc Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 13:10:06 -0700 Subject: [PATCH 16/21] work --- test/lit/toolchain-inline-hints.wast | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/lit/toolchain-inline-hints.wast b/test/lit/toolchain-inline-hints.wast index 309969ee6ea..a61d9ff5392 100644 --- a/test/lit/toolchain-inline-hints.wast +++ b/test/lit/toolchain-inline-hints.wast @@ -16,26 +16,26 @@ ;; CHECK: (elem declare func $func) ;; CHECK: (func $func (type $func) - ;; CHECK-NEXT: (@binaryen.inline "\01") + ;; CHECK-NEXT: (@binaryen.inline "\00") ;; CHECK-NEXT: (call $func) ;; CHECK-NEXT: (@binaryen.inline "\01") ;; CHECK-NEXT: (call $func) - ;; CHECK-NEXT: (@binaryen.inline "\01") + ;; CHECK-NEXT: (@binaryen.inline "\7e") ;; CHECK-NEXT: (call $func) - ;; CHECK-NEXT: (@binaryen.inline "\01") + ;; CHECK-NEXT: (@binaryen.inline "\7f") ;; CHECK-NEXT: (call $func) ;; CHECK-NEXT: (call $func) ;; CHECK-NEXT: ) ;; RTRIP: (elem declare func $func) ;; RTRIP: (func $func (type $func) - ;; RTRIP-NEXT: (@binaryen.inline "\01") + ;; RTRIP-NEXT: (@binaryen.inline "\00") ;; RTRIP-NEXT: (call $func) ;; RTRIP-NEXT: (@binaryen.inline "\01") ;; RTRIP-NEXT: (call $func) - ;; RTRIP-NEXT: (@binaryen.inline "\01") + ;; RTRIP-NEXT: (@binaryen.inline "\7e") ;; RTRIP-NEXT: (call $func) - ;; RTRIP-NEXT: (@binaryen.inline "\01") + ;; RTRIP-NEXT: (@binaryen.inline "\7f") ;; RTRIP-NEXT: (call $func) ;; RTRIP-NEXT: (call $func) ;; RTRIP-NEXT: ) @@ -53,21 +53,21 @@ ) ;; CHECK: (func $other-calls (type $func) - ;; CHECK-NEXT: (@binaryen.inline "\01") + ;; CHECK-NEXT: (@binaryen.inline "\12") ;; CHECK-NEXT: (call_indirect $table (type $func) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (@binaryen.inline "\01") + ;; CHECK-NEXT: (@binaryen.inline "\34") ;; CHECK-NEXT: (call_ref $func ;; CHECK-NEXT: (ref.func $func) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; RTRIP: (func $other-calls (type $func) - ;; RTRIP-NEXT: (@binaryen.inline "\01") + ;; RTRIP-NEXT: (@binaryen.inline "\12") ;; RTRIP-NEXT: (call_indirect $table (type $func) ;; RTRIP-NEXT: (i32.const 0) ;; RTRIP-NEXT: ) - ;; RTRIP-NEXT: (@binaryen.inline "\01") + ;; RTRIP-NEXT: (@binaryen.inline "\34") ;; RTRIP-NEXT: (call_ref $func ;; RTRIP-NEXT: (ref.func $func) ;; RTRIP-NEXT: ) From 69fb2bd6038ef5480374a5f01ef0c53be3078e7d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 13:13:53 -0700 Subject: [PATCH 17/21] work --- test/lit/passes/toolchain-inlining.wast | 35 +++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/test/lit/passes/toolchain-inlining.wast b/test/lit/passes/toolchain-inlining.wast index 2de9d466fd4..cc6cfb9364f 100644 --- a/test/lit/passes/toolchain-inlining.wast +++ b/test/lit/passes/toolchain-inlining.wast @@ -1,6 +1,6 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: foreach %s %t wasm-opt --inlining --optimize-level=3 -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt --inlining -S -o - | filecheck %s (module ;; CHECK: (type $0 (func (result i32))) @@ -42,6 +42,23 @@ (local.get $x) ) + ;; CHECK: (func $always-unannotated (result i32) + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: (loop $loop + ;; CHECK-NEXT: (local.set $x + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br_if $loop + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) (func $always-unannotated (result i32) (local $x i32) ;; Idential to above, but without the annotation. This will not be inlined, @@ -97,7 +114,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (block $__inlined_func$always-unannotated$2 (result i32) + ;; CHECK-NEXT: (block $__inlined_func$always$2 (result i32) ;; CHECK-NEXT: (local.set $1 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) @@ -119,6 +136,12 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $always-unannotated) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $always-unannotated) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $caller (drop @@ -127,11 +150,19 @@ (drop (call $never-unannotated) ) + ;; We need multiple calls here, to avoid the 'single-caller rule that + ;; always inlines anyhow. + (drop + (call $always) + ) (drop (call $always) ) (drop (call $always-unannotated) ) + (drop + (call $always-unannotated) + ) ) ) From d52accd3d2d6351850c9dad23ebf3a2ee19a5a9d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 13:14:00 -0700 Subject: [PATCH 18/21] work --- src/parser/contexts.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/parser/contexts.h b/src/parser/contexts.h index 4a8ecf1fba4..39c37d07530 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -1398,7 +1398,8 @@ struct AnnotationParserCtx { } } - auto parseI7Hint = [&](const Annotation* hint, const char* name) -> std::optional { + auto parseI7Hint = [&](const Annotation* hint, + const char* name) -> std::optional { if (!hint) { return {}; } @@ -1425,7 +1426,8 @@ struct AnnotationParserCtx { // Apply the last inline hints, if valid. ret.inline_ = parseI7Hint(inlineHint, "InlineHint"); - ret.toolchainInline = parseI7Hint(toolchainInlineHint, "Toolchain InlineHint"); + ret.toolchainInline = + parseI7Hint(toolchainInlineHint, "Toolchain InlineHint"); return ret; } From 6738ec1a746a93539eeee9a0baa92b17b670f417 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 13:19:12 -0700 Subject: [PATCH 19/21] clean --- src/wasm.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/wasm.h b/src/wasm.h index b9357819981..26b51ea8296 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -2482,12 +2482,6 @@ class Function : public Importable { bool noFullInline = false; bool noPartialInline = false; - // Whether to always inline a function, despite other flags like optimizing - // for size (except for things that prevent inlining for correctness reasons, - // like infinite recursion). - bool alwaysFullInline = false; - // TODO: alwaysPartialInline, if that is useful? - // Methods Signature getSig() { return type.getHeapType().getSignature(); } Type getParams() { return getSig().params; } From a4b837fcc879abcf0273ba61f8896c338bd245a3 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 13:41:26 -0700 Subject: [PATCH 20/21] try to avoid a compilation error --- src/wasm.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/wasm.h b/src/wasm.h index 26b51ea8296..b1a2de2e98c 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -2347,8 +2347,10 @@ struct CodeAnnotation { std::optional branchLikely; // Compilation Hints proposal. - static const uint8_t NeverInline = 0; - static const uint8_t AlwaysInline = 127; + enum { + NeverInline = 0, + AlwaysInline = 127 + }; std::optional inline_; // Toolchain hints, see From ed0d09d9f5a3a5dc01662215de46423e88889980 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 9 Jun 2026 13:41:42 -0700 Subject: [PATCH 21/21] form --- src/wasm.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/wasm.h b/src/wasm.h index b1a2de2e98c..0eff9071b70 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -2347,10 +2347,7 @@ struct CodeAnnotation { std::optional branchLikely; // Compilation Hints proposal. - enum { - NeverInline = 0, - AlwaysInline = 127 - }; + enum { NeverInline = 0, AlwaysInline = 127 }; std::optional inline_; // Toolchain hints, see