Skip to content

Conversation

@kg
Copy link
Member

@kg kg commented Nov 24, 2025

My first JIT PR so I'm keeping it simple.

Copilot AI review requested due to automatic review settings November 24, 2025 19:32
@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 Nov 24, 2025
@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.

@kg
Copy link
Member Author

kg commented Nov 24, 2025

@dotnet/jit-contrib

Copilot finished reviewing on behalf of kg November 24, 2025 19:36
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements basic arithmetic operations (loads and adds) for i64, f32, and f64 types in the RyuJIT WebAssembly backend. The changes enable the JIT compiler to generate WebAssembly instructions for these fundamental operations across 64-bit integers, 32-bit floats, and 64-bit floats.

  • Implements register validation functions for integer and float registers based on WASM value types
  • Adds WASM instruction definitions for i64/f32/f64 load and add operations
  • Updates code generation to handle typed binary operations using a new helper function

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/coreclr/jit/targetwasm.h Replaces NYI stubs with actual implementations of register validation functions that check WASM value types (I32, I64, F32, F64)
src/coreclr/jit/instrswasm.h Adds instruction definitions for i64_load, f32_load, f64_load, i64_add, f32_add, and f64_add with their corresponding opcodes
src/coreclr/jit/instr.cpp Extends ins_Load to support TYP_LONG, TYP_FLOAT, and TYP_DOUBLE by mapping them to appropriate WASM load instructions
src/coreclr/jit/codegenwasm.cpp Introduces OperAndType helper to combine operation and type into a switch value, refactors genCodeForBinary to handle GT_ADD for INT, LONG, FLOAT, and DOUBLE types
src/coreclr/jit/lower.cpp Adds workaround for NumSegments being 0 on WASM to prevent crashes when creating GT_PUTARG_STK nodes

const ABIPassingSegment& stackSeg = abiInfo.Segment(0);
const bool putInIncomingArgArea = call->IsFastTailCall();
// FIXME: Workaround for NumSegments being 0 on Wasm and causing crashes
if (abiInfo.NumSegments > 0) {
Copy link
Member

Choose a reason for hiding this comment

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

When does this happen?

WasmClassifier::Classify always returns a single register segment so this seems odd.
Also with the assert above this doesn't make sense.

Copy link
Member Author

Choose a reason for hiding this comment

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

While debugging crossgen we get past the assert into this code and we get an AV because Segment(0) is an invalid pointer.

Copy link
Member

Choose a reason for hiding this comment

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

The fix should be to make sure there is some reasonable ABI info there for WASM. But given the implementation of WasmClassifier it is odd that there isn't, so we should figure out why.

The assert above will keep firing for people running the JIT in debug/checked so some other fix is needed.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll look into it further, but with how little I know about the JIT I don't expect to get very far. Should I pair with someone?

Copy link
Member

Choose a reason for hiding this comment

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

That sounds like a good idea. A good place to start would be by obtaining the JITDUMP for the function where the AV occurs. The example at https://github.com/dotnet/runtime/blob/main/docs/workflow/debugging/coreclr/debugging-aot-compilers.md#example-of-debugging-a-test-application-in-crossgen2 might help with that.
Also feel free to hit me up internally.

Copy link
Contributor

@SingleAccretion SingleAccretion Nov 24, 2025

Choose a reason for hiding this comment

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

Also, is this needed for the current work? This code is part of support for calls.

Copy link
Member Author

Choose a reason for hiding this comment

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

It gets hit when I debug crossgen2. It works fine outside of the debugger.

Comment on lines 118 to 121
constexpr uint64_t PackOperAndType(genTreeOps oper, var_types type)
{
return ((uint64_t)oper) | ((uint64_t)type << 32);
}
Copy link
Contributor

@SingleAccretion SingleAccretion Nov 24, 2025

Choose a reason for hiding this comment

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

Suggested change
constexpr uint64_t PackOperAndType(genTreeOps oper, var_types type)
{
return ((uint64_t)oper) | ((uint64_t)type << 32);
}
static constexpr uint64_t PackOperAndType(genTreeOps oper, var_types type)
{
return ((uint64_t)oper) | ((uint64_t)type << 32);
}

You could make this more likely to produce a dense series (and fit into an int32) by shifting oper by ConstLog2<GT_COUNT>::value + 1 and ORing the type, since type is the part with the dense lower bits. That said, I don't know how much that would help; the series will still be sparse. It may help on RISC architectures that don't have instructions for loading large constants, I suppose.

case TYP_INT:
ins = INS_i32_load;
break;
case TYP_LONG:
Copy link
Contributor

@SingleAccretion SingleAccretion Nov 24, 2025

Choose a reason for hiding this comment

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

The way this function is currently structured is leaving performance on the table. It would be best to remove this and lower part that is dealing with FP and integers separately, and have one switch for both at the beginning.

(Under a WASM ifdef, of course)

{
NYI_WASM("isValidIntArgReg");
return false;
return genIsValidIntReg(reg);
Copy link
Contributor

Choose a reason for hiding this comment

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

This function lacks the context to determine whether it is an arg reg or not. What stack is this being called from? The correct fix may be to put the caller under HAS_FIXED_REGISTER_SET. Or propagate the context (on the number of args) here.

Copy link
Member Author

Choose a reason for hiding this comment

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

I just decided to fill out all of them while I was here. I can revert it.

Copy link
Contributor

@SingleAccretion SingleAccretion Nov 24, 2025

Choose a reason for hiding this comment

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

Yes, that would be best.

The genIsValidInt/FloatReg have clear semantics under !HAS_FIXED_REGISTER_SET. These Arg functions don't, so the best thing would be to make them unreachable at some point, unless we find it necessary to properly implement them.

Separately, genIsValidIntReg/etc should be moved to registeropswasm.cpp (you will need to add prototypes for them to registeropswasm.h, like with genIsValidReg).

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