Skip to content

Conversation

@kg
Copy link
Member

@kg kg commented Jan 9, 2026

Depends on #123021

Starting to put together all the code we need for helper calls and calls in general.

@kg kg added arch-wasm WebAssembly architecture area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI labels Jan 9, 2026
@dotnet-policy-service
Copy link
Contributor

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

Copy link
Member

@AndyAyersMS AndyAyersMS left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need to figure out in detail how to specify what method we want to call.

We know roughly what kind of Wasm we want to produce (per the calling convention doc we'll be making lots of indirect calls) but we need help from the JIT host to make this all work out.

I think it might be ok for now to just defer this part of the work and try and get everything else lined up?

// Generate a direct call to a non-virtual user defined or helper method
assert(call->IsHelperCall() || (call->gtCallType == CT_USER_FUNC));

if (call->gtEntryPoint.addr != NULL)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per the calling convention, for user and helper calls we'll also be invoking them indirectly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. I'm not clear on whether that means there won't be an addr filled in

@kg kg force-pushed the wasm-genemithelpercall branch from b95ce18 to 499127d Compare January 16, 2026 04:26
@kg
Copy link
Member Author

kg commented Jan 16, 2026

This branch now hits this in crossgen2:

Z:\runtime\src\coreclr\jit\codegenwasm.cpp:1123
Assertion failed 'NYI_WASM: load call target from indirection cell in register' in 'Program:callVoidFunc()' during 'Generate code' (IL size 8; hash 0x4bd04ee2; MinOpts)

@kg
Copy link
Member Author

kg commented Jan 16, 2026

For this:

    [MethodImpl(MethodImplOptions.NoInlining)]
    static void voidFunc () {
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static void callVoidFunc () {
        voidFunc();
    }

R2R now compiles without crashing or asserting, and disasmo generates this:

; Method Program:callVoidFunc() (FullOpts)
G_M45341_IG01:  ;; offset=0x0000
            local.cnt 4
            local[0] type=i32
            local[1] type=i64
            local[2] type=f32
            local[3] type=f64
						;; size=9 bbWeight=1 PerfScore 5.00

G_M45341_IG02:  ;; offset=0x0009
            i32.const 140722950757336
            i32.load 0 0
            call_indirect 0 0
						;; size=14 bbWeight=1 PerfScore 3.00

G_M45341_IG03:  ;; offset=0x0017
            end
						;; size=1 bbWeight=1 PerfScore 1.00
; Total bytes of code: 24

It's not correct but it's a start.

@kg
Copy link
Member Author

kg commented Jan 16, 2026

cc @dotnet/jit-contrib need to figure out how this should actually be factored and shaped and implemented, now that it "works". I don't understand 75% of the code I touched here, for example whether we use EA_8BYTE or a different one still makes no sense to me, and the way we historically separate out emitNewInstrXxx from emitIns_Xxx seems arbitrary. But this is my best guess at how everything should be arranged to make sense for wasm.


genConsumeRegs(op1);

genEmitHelperCall(CORINFO_HELP_THROWNULLREF, 0, EA_UNKNOWN, REG_NA);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does GT_NULLCHECK just unconditionally calls CORINFO_HELP_THROWNULLREF?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was trying to just get the JIT to emit a helper call, since Andy is working on actual null checks. But I never found a way to get a GT_NULLCHECK node

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like that should?

unsafe void Foo(int* x)
{
    _ = *x;
}

TransformUnusedIndirection is expected to convert this unused GT_IND into GT_NULLCHECK

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing this out though, I had forgotten to put a comment in

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like that should?

unsafe void Foo(int* x)
{
    _ = *x;
}

TransformUnusedIndirection is expected to convert this unused GT_IND into GT_NULLCHECK

This indeed appears to work! Thank you very much

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants