Skip to content

Wasm / R2R: malformed wasm (missing function-body end opcode) for try / throwing-finally pattern in b51875 #129335

Description

@AndyAyersMS

Note

This issue body was drafted by GitHub Copilot CLI based on local investigation.

The R2R wasm produced by crossgen2 for b51875.AA.Main1 is malformed:
function #6 in the resulting module has no closing end opcode, so V8
refuses to compile the module at load time:

Failed to construct WebAssembly module for Webcil image: {
  wasmPath: 'Regression_8.wasm',
  errorMessage: 'WebAssembly.Module(): Compiling function #6 failed:
    function body must end with "end" opcode @+12569'
}

wasm-objdump -x confirms function #6 is the offending method
Regression_8_b51875_AA__Main1. crossgen2 itself reports success
(exit 0, no warnings) — the malformed body is only detected when V8
tries to compile it.

Because the load fails, the corerun loader silently falls back to IL
for the affected wasm and the test still exits 100. Net effect: the
test passes via interpreted IL, but R2R coverage of this method is
zero. No "FAILED" appears in any test report.

Source

src\tests\JIT\Regression\CLR-x86-JIT\V1-M12-Beta2\b51875\b51875.cs

public struct AA
{
    public static int Main1()
    {
        AA[] local1 = new AA[10];
        try
        {
            goto EOM;
        }
        finally
        {
            throw new Exception();
        }
    EOM:
        if (((Array)new Object()).Clone() == null)
            return 1;
        return 0;
    }
    [Fact]
    public static int TestEntryPoint()
    {
        try { Main1(); return 101; }
        catch (Exception) { return 100; }
    }
}

Pattern: a try whose only contents is a goto out of the protected
region, and a finally that always throws. The code after the
finally (the EOM: label and beyond) is unreachable, but the JIT
still emits something for it that doesn't close the wasm function
properly.

A near-equivalent C# repro lives at
src\tests\JIT\Methodical\eh\basics\throwinfinally.cs — that wasm
also fails to load with the same V8 error.

Repro

:: build runtime + tests
.\build clr+libs -rc debug -c release -os browser
.\src\tests\build skipmanaged browser

:: put repo dotnet in path
set DOTNET_ROOT=<REPO_ROOT>\.dotnet
set PATH=<REPO_ROOT>\.dotnet;%PATH%

set CORE_ROOT=<REPO_ROOT>\artifacts\tests\coreclr\browser.wasm.Debug\Tests\Core_Root
set TESTDIR=<REPO_ROOT>\artifacts\tests\coreclr\browser.wasm.Debug\JIT\Regression\Regression_8
set CG2DIR=<REPO_ROOT>\artifacts\bin\crossgen2_inbuild\wasm\Debug

:: regenerate just the test wasm (forces fresh codegen)
cd %TESTDIR%
del IL-CG2\* /q & rmdir IL-CG2 & del *.wasm *.wasm.rsp *.wasm.r2rdump

dotnet %CG2DIR%\crossgen2.dll ^
  --targetos:browser --targetarch:wasm --obj-format:wasm ^
  --jitpath %CG2DIR%\clrjit_universal_wasm_x64.dll ^
  -O --verify-type-and-field-layout --method-layout:random ^
  -r:%CORE_ROOT%\System.*.dll -r:%CORE_ROOT%\Microsoft.*.dll ^
  -r:%CORE_ROOT%\xunit.*.dll -r:%CORE_ROOT%\mscorlib.dll ^
  -r:%CORE_ROOT%\netstandard.dll ^
  -o:%TESTDIR%\Regression_8.wasm ^
  %TESTDIR%\Regression_8.dll

:: confirm the wasm is malformed
node --stack-size=8192 %CORE_ROOT%\corerun.js -c <unix path to CORE_ROOT> ^
  <unix path to Regression_8.dll>

stderr will contain the Compiling function #6 failed: function body must end with "end" opcode line above; the test still exits 100
because the load failure triggers an IL fallback.

Why this matters

This isn't currently caught by xunit-style test sweeps because the IL
fallback succeeds. Any other test that hits the same codegen path is
silently running interpreted instead of R2R; the only way to surface
this today is to grep stderr for Failed to construct WebAssembly module.

Found while validating the catch_ref + throw_ref EH codegen stack on
pr-stack-rebased (commits up to 9bea6045863).

Activity

  1. added
    arch-wasmWebAssembly architecture
    area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
    on Jun 12, 2026
  2. dotnet-policy-service commented on Jun 12, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
    See info in area-owners.md if you want to be subscribed.

  3. dotnet-policy-service commented on Jun 12, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
    See info in area-owners.md if you want to be subscribed.

  4. self-assigned this
    on Jun 12, 2026
  5. removed
    untriagedNew issue has not been triaged by the area owner
    on Jun 12, 2026
  6. added this to the 11.0.0 milestone on Jun 12, 2026
  7. AndyAyersMS commented on Jun 15, 2026

    @AndyAyersMS
    MemberAuthor

    This is a retless callfinally. Fix is simple enough.

  8. added a commit that references this issue on Jun 16, 2026
    1704f34
  9. added a commit that references this issue on Jul 15, 2026
    f47d5ca
  10. locked and limited conversation to collaborators on Jul 17, 2026
  11. added a commit that references this issue on Jul 22, 2026
    0acf89d
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

arch-wasmWebAssembly architecturearea-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions