From 4dc58dac6e1f815844692ba4c04586363c5eefe0 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 18 Feb 2026 15:11:36 -0800 Subject: [PATCH 1/2] [Wasm RyuJit] codegen for some intrinsics Handle some math intrinsics that map to Wasm operations. --- src/coreclr/jit/codegenwasm.cpp | 107 ++++++++++++++++++++++++++++++ src/coreclr/jit/importercalls.cpp | 19 ++++++ 2 files changed, 126 insertions(+) diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index a23105ca5b1a1b..d7ec97ab7380d7 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -605,6 +605,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) JITDUMP("Ignoring GT_MEMORYBARRIER; single-threaded codegen\n"); break; + case GT_INTRINSIC: + genIntrinsic(treeNode->AsIntrinsic()); + break; + default: #ifdef DEBUG NYIRAW(GenTree::OpName(treeNode->OperGet())); @@ -1471,6 +1475,109 @@ void CodeGen::genLeaInstruction(GenTreeAddrMode* lea) WasmProduceReg(lea); } +//------------------------------------------------------------------------ +// PackIntrinsicAndType: Pack a intrinsic and var_types into a uint32_t +// +// Arguments: +// ni - a NamedIntrinsic to pack +// type - a var_types to pack +// +// Return Value: +// intrinsic and type packed into an integer that can be used as a switch value/case +// +static constexpr uint32_t PackIntrinsicAndType(NamedIntrinsic ni, var_types type) +{ + if ((type == TYP_BYREF) || (type == TYP_REF)) + { + type = TYP_I_IMPL; + } + const int shift1 = ConstLog2::value + 1; + return ((uint32_t)ni << shift1) | ((uint32_t)type); +} + +//--------------------------------------------------------------------- +// genIntrinsic - generate code for a given intrinsic +// +// Arguments +// treeNode - the GT_INTRINSIC node +// +// Return value: +// None +// +void CodeGen::genIntrinsic(GenTreeIntrinsic* treeNode) +{ + genConsumeOperands(treeNode); + + // Handle intrinsics that can be implemented by target-specific instructions + instruction ins; + switch (PackIntrinsicAndType(treeNode->gtIntrinsicName, treeNode->TypeGet())) + { + case PackIntrinsicAndType(NI_System_Math_Abs, TYP_FLOAT): + ins = INS_f32_abs; + break; + case PackIntrinsicAndType(NI_System_Math_Abs, TYP_DOUBLE): + ins = INS_f64_abs; + break; + + case PackIntrinsicAndType(NI_System_Math_Ceiling, TYP_FLOAT): + ins = INS_f32_ceil; + break; + case PackIntrinsicAndType(NI_System_Math_Ceiling, TYP_DOUBLE): + ins = INS_f64_ceil; + break; + + case PackIntrinsicAndType(NI_System_Math_Floor, TYP_FLOAT): + ins = INS_f32_floor; + break; + case PackIntrinsicAndType(NI_System_Math_Floor, TYP_DOUBLE): + ins = INS_f64_floor; + break; + + case PackIntrinsicAndType(NI_System_Math_Max, TYP_FLOAT): + ins = INS_f32_max; + break; + case PackIntrinsicAndType(NI_System_Math_Max, TYP_DOUBLE): + ins = INS_f64_max; + break; + + case PackIntrinsicAndType(NI_System_Math_Min, TYP_FLOAT): + ins = INS_f32_min; + break; + case PackIntrinsicAndType(NI_System_Math_Min, TYP_DOUBLE): + ins = INS_f64_min; + break; + + case PackIntrinsicAndType(NI_System_Math_Round, TYP_FLOAT): + ins = INS_f32_nearest; + break; + case PackIntrinsicAndType(NI_System_Math_Round, TYP_DOUBLE): + ins = INS_f64_nearest; + break; + + case PackIntrinsicAndType(NI_System_Math_Sqrt, TYP_FLOAT): + ins = INS_f32_sqrt; + break; + case PackIntrinsicAndType(NI_System_Math_Sqrt, TYP_DOUBLE): + ins = INS_f64_sqrt; + break; + + case PackIntrinsicAndType(NI_System_Math_Truncate, TYP_FLOAT): + ins = INS_f32_trunc; + break; + case PackIntrinsicAndType(NI_System_Math_Truncate, TYP_DOUBLE): + ins = INS_f64_trunc; + break; + + default: + assert(!"genIntrinsic: Unsupported intrinsic"); + unreached(); + } + + GetEmitter()->emitIns(ins); + + genProduceReg(treeNode); +} + //------------------------------------------------------------------------ // genCodeForLclAddr: Generates the code for GT_LCL_ADDR. // diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 62b79e697788ff..14e1092a87c6b9 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -8477,6 +8477,25 @@ bool Compiler::IsTargetIntrinsic(NamedIntrinsic intrinsicName) case NI_System_Math_ReciprocalEstimate: return true; + default: + return false; + } +} +#elif defined(TARGET_WASM) + + // TODO-WASM-CQ: we can likely support more intrinsics here + switch (intrinsicName) + { + case NI_System_Math_Abs: + case NI_System_Math_Ceiling: + case NI_System_Math_Floor: + case NI_System_Math_Max: + case NI_System_Math_Min: + case NI_System_Math_Round: + case NI_System_Math_Sqrt: + case NI_System_Math_Truncate: + return true; + default: return false; } From 652ae4ceeb5b551a0ea85bc5b3e4e2883a3b0f77 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 18 Feb 2026 15:43:00 -0800 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreclr/jit/codegenwasm.cpp | 2 +- src/coreclr/jit/importercalls.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index d7ec97ab7380d7..7be643600d93cb 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -1575,7 +1575,7 @@ void CodeGen::genIntrinsic(GenTreeIntrinsic* treeNode) GetEmitter()->emitIns(ins); - genProduceReg(treeNode); + WasmProduceReg(treeNode); } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 14e1092a87c6b9..54d6ef04963f15 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -8480,7 +8480,7 @@ bool Compiler::IsTargetIntrinsic(NamedIntrinsic intrinsicName) default: return false; } -} + #elif defined(TARGET_WASM) // TODO-WASM-CQ: we can likely support more intrinsics here