Skip to content

[wasm] CORINFO_HELP_CHECK_OBJ is modeled TYP_VOID in fgMorphInit but JIT_CheckObj returns Object* #131378

Closed

Description

@lewing

Description

CORINFO_HELP_CHECK_OBJ is implemented as HCIMPL1_RAW(Object*, JIT_CheckObj, Object* obj) in src/coreclr/vm/jithelpers.cpp — it returns Object*. The JIT models it inconsistently:

  • src/coreclr/jit/importer.cpp (return-value check): gtNewHelperCallNode(CORINFO_HELP_CHECK_OBJ, TYP_REF, op2) — correct.
  • src/coreclr/jit/morph.cpp (fgMorphInit, argument check): gtNewHelperCallNode(CORINFO_HELP_CHECK_OBJ, TYP_VOID, op) — models a value-returning helper as void.

On register-based targets the second form is benign (the returned reference is simply left in the return register and dropped). On wasm the call_indirect / call signature is derived from the JIT-modeled return type in CodeGen::genCallInstruction, so a TYP_VOID model for a value-returning helper emits a mismatched signature and the engine traps with function signature mismatch — the same class of defect as #130813 and the inline-unbox slow-path helper.

This only fires under DOTNET_JitGCChecks=1 (opts.compGcChecks) in a Debug/Checked JIT, so it is low severity, but it is the same modeling bug and is cheap to fix.

Reproduction Steps

Checked/Debug JIT, DOTNET_JitGCChecks=1, wasm target, on any method with a TYP_REF argument.

Expected behavior

The helper is modeled with a return type matching JIT_CheckObj (TYP_REF), and the unused result is discarded (e.g. via gtUnusedValNode) so the enclosing statement stays void.

Actual behavior

fgMorphInit creates the call as TYP_VOID; on wasm the emitted call signature does not match the helper.

Configuration

browser-wasm / wasi-wasm, Checked/Debug, DOTNET_JitGCChecks=1, main.

Related: #129622, #130813

Note

This issue was created with assistance from GitHub Copilot (AI).

Activity

  1. added
    arch-wasmWebAssembly architecture
    area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
    on Jul 26, 2026
  2. dotnet-policy-service commented on Jul 26, 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 Jul 26, 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. lewing commented on Jul 26, 2026

    @lewing
    MemberAuthor

    Correction to the original report: this issue overstates the impact. The modeling bug is real, but the function signature mismatch trap I described is not reachable.

    CORINFO_HELP_CHECK_OBJ is not handled anywhere in CorInfoImpl.ReadyToRun.cs. It falls through to default: throw new NotImplementedException, and it is not in the graceful RequiresRuntimeJitException list either. So any method that would emit CHECK_OBJ fails R2R codegen outright, on every target, before return-arity modeling ever reaches call_indirect emission:

    ILCompiler.CodeGenerationFailedException: Code generation failed for method 'GcCheck.Foo(object)'
     ---> System.NotImplementedException: CORINFO_HELP_CHECK_OBJ
       at CorInfoImpl.GetHelperFtnUncached(...) CorInfoImpl.ReadyToRun.cs:1342
    

    Confirmed by building a Checked wasm JIT (which does insert CHECK_OBJ under JitGCChecks), deploying it to a Checked crossgen2, and attempting to compile a trivial static void Foo(object o) with --codegenopt:JitGCChecks=1.

    So there is no R2R image that can contain a CHECK_OBJ call, and wasm has no runtime JIT (R2R + interpreter only), so there is no other path where the wrong return arity would bite. The interpreter does not derive a call signature from the modeled return type.

    What remains accurate:

    • fgMorphInit models the helper TYP_VOID while JIT_CheckObj returns Object* (vm/jithelpers.cpp:1210).
    • importer.cpp already models the same helper TYP_REF, so the two sites disagree.

    The fix in #131383 is therefore defensive: it aligns the model with the helper's real return type and with the importer, and would become live if CHECK_OBJ ever gains R2R support. It should not be treated as fixing an observable trap, and this issue is not a good candidate for a regression test — there is nothing to reproduce.

    Note

    This comment was created with assistance from GitHub Copilot (AI).

  5. added this to the 11.0.0 milestone on Jul 27, 2026
  6. JulieLeeMSFT commented on Jul 27, 2026

    @JulieLeeMSFT
    Member
  7. added a commit that references this issue on Jul 29, 2026
    950fea3
  8. locked and limited conversation to collaborators on Aug 28, 2026
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