From a0f658c18894560e59313d0e33e91fab64d1306e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Dec 2025 21:23:34 +0000 Subject: [PATCH 1/6] Initial plan From 8aa4db4cf6f175eb89e178971aa906ae5c96e924 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 2 Dec 2025 21:56:02 +0000 Subject: [PATCH 2/6] Add binary operations to codegenwasm.cpp Added implementations for binary operations in the Wasm RyuJit backend: - Added GT_SUB, GT_MUL, GT_DIV, GT_MOD, GT_UDIV, GT_UMOD to genCodeForTreeNode switch - Added GT_OR, GT_XOR, GT_AND, GT_LSH, GT_RSH, GT_RSZ, GT_ROL, GT_ROR to genCodeForTreeNode switch - Added all corresponding PackOperAndType cases in genCodeForBinary for: - Integer operations (i32/i64): sub, mul, div_s, div_u, rem_s, rem_u, and, or, xor, shl, shr_s, shr_u, rotl, rotr - Floating point operations (f32/f64): sub, mul, div These map WASM instructions from instrswasm.h to GT_xxx gentree enums from gtlist.h. Co-authored-by: kg <198130+kg@users.noreply.github.com> --- src/coreclr/jit/codegenwasm.cpp | 131 ++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index 1ed20a7634379c..9392482a047829 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -80,6 +80,20 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) switch (treeNode->OperGet()) { case GT_ADD: + case GT_SUB: + case GT_MUL: + case GT_DIV: + case GT_MOD: + case GT_UDIV: + case GT_UMOD: + case GT_OR: + case GT_XOR: + case GT_AND: + case GT_LSH: + case GT_RSH: + case GT_RSZ: + case GT_ROL: + case GT_ROR: genCodeForBinary(treeNode->AsOp()); break; @@ -164,6 +178,123 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode) case PackOperAndType(GT_ADD, TYP_DOUBLE): ins = INS_f64_add; break; + + case PackOperAndType(GT_SUB, TYP_INT): + ins = INS_i32_sub; + break; + case PackOperAndType(GT_SUB, TYP_LONG): + ins = INS_i64_sub; + break; + case PackOperAndType(GT_SUB, TYP_FLOAT): + ins = INS_f32_sub; + break; + case PackOperAndType(GT_SUB, TYP_DOUBLE): + ins = INS_f64_sub; + break; + + case PackOperAndType(GT_MUL, TYP_INT): + ins = INS_i32_mul; + break; + case PackOperAndType(GT_MUL, TYP_LONG): + ins = INS_i64_mul; + break; + case PackOperAndType(GT_MUL, TYP_FLOAT): + ins = INS_f32_mul; + break; + case PackOperAndType(GT_MUL, TYP_DOUBLE): + ins = INS_f64_mul; + break; + + case PackOperAndType(GT_DIV, TYP_INT): + ins = INS_i32_div_s; + break; + case PackOperAndType(GT_DIV, TYP_LONG): + ins = INS_i64_div_s; + break; + case PackOperAndType(GT_DIV, TYP_FLOAT): + ins = INS_f32_div; + break; + case PackOperAndType(GT_DIV, TYP_DOUBLE): + ins = INS_f64_div; + break; + + case PackOperAndType(GT_UDIV, TYP_INT): + ins = INS_i32_div_u; + break; + case PackOperAndType(GT_UDIV, TYP_LONG): + ins = INS_i64_div_u; + break; + + case PackOperAndType(GT_MOD, TYP_INT): + ins = INS_i32_rem_s; + break; + case PackOperAndType(GT_MOD, TYP_LONG): + ins = INS_i64_rem_s; + break; + + case PackOperAndType(GT_UMOD, TYP_INT): + ins = INS_i32_rem_u; + break; + case PackOperAndType(GT_UMOD, TYP_LONG): + ins = INS_i64_rem_u; + break; + + case PackOperAndType(GT_AND, TYP_INT): + ins = INS_i32_and; + break; + case PackOperAndType(GT_AND, TYP_LONG): + ins = INS_i64_and; + break; + + case PackOperAndType(GT_OR, TYP_INT): + ins = INS_i32_or; + break; + case PackOperAndType(GT_OR, TYP_LONG): + ins = INS_i64_or; + break; + + case PackOperAndType(GT_XOR, TYP_INT): + ins = INS_i32_xor; + break; + case PackOperAndType(GT_XOR, TYP_LONG): + ins = INS_i64_xor; + break; + + case PackOperAndType(GT_LSH, TYP_INT): + ins = INS_i32_shl; + break; + case PackOperAndType(GT_LSH, TYP_LONG): + ins = INS_i64_shl; + break; + + case PackOperAndType(GT_RSH, TYP_INT): + ins = INS_i32_shr_s; + break; + case PackOperAndType(GT_RSH, TYP_LONG): + ins = INS_i64_shr_s; + break; + + case PackOperAndType(GT_RSZ, TYP_INT): + ins = INS_i32_shr_u; + break; + case PackOperAndType(GT_RSZ, TYP_LONG): + ins = INS_i64_shr_u; + break; + + case PackOperAndType(GT_ROL, TYP_INT): + ins = INS_i32_rotl; + break; + case PackOperAndType(GT_ROL, TYP_LONG): + ins = INS_i64_rotl; + break; + + case PackOperAndType(GT_ROR, TYP_INT): + ins = INS_i32_rotr; + break; + case PackOperAndType(GT_ROR, TYP_LONG): + ins = INS_i64_rotr; + break; + default: ins = INS_none; NYI_WASM("genCodeForBinary"); From 7b223fca15e0a1207a60a8b1194b36daee91f816 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 01:10:35 +0000 Subject: [PATCH 3/6] Add overflow checks to arithmetic operations Added gtOverflow() checks with NYI_WASM("Overflow checks") for: - GT_ADD (TYP_INT, TYP_LONG) - GT_SUB (TYP_INT, TYP_LONG) - GT_MUL (TYP_INT, TYP_LONG) - GT_DIV (TYP_INT, TYP_LONG) - GT_MOD (TYP_INT, TYP_LONG) Floating-point operations do not check overflow as requested. Co-authored-by: kg <198130+kg@users.noreply.github.com> --- src/coreclr/jit/codegenwasm.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index 9392482a047829..6b2abf24894cd9 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -167,9 +167,13 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode) switch (PackOperAndType(treeNode)) { case PackOperAndType(GT_ADD, TYP_INT): + if (treeNode->gtOverflow()) + NYI_WASM("Overflow checks"); ins = INS_i32_add; break; case PackOperAndType(GT_ADD, TYP_LONG): + if (treeNode->gtOverflow()) + NYI_WASM("Overflow checks"); ins = INS_i64_add; break; case PackOperAndType(GT_ADD, TYP_FLOAT): @@ -180,9 +184,13 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode) break; case PackOperAndType(GT_SUB, TYP_INT): + if (treeNode->gtOverflow()) + NYI_WASM("Overflow checks"); ins = INS_i32_sub; break; case PackOperAndType(GT_SUB, TYP_LONG): + if (treeNode->gtOverflow()) + NYI_WASM("Overflow checks"); ins = INS_i64_sub; break; case PackOperAndType(GT_SUB, TYP_FLOAT): @@ -193,9 +201,13 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode) break; case PackOperAndType(GT_MUL, TYP_INT): + if (treeNode->gtOverflow()) + NYI_WASM("Overflow checks"); ins = INS_i32_mul; break; case PackOperAndType(GT_MUL, TYP_LONG): + if (treeNode->gtOverflow()) + NYI_WASM("Overflow checks"); ins = INS_i64_mul; break; case PackOperAndType(GT_MUL, TYP_FLOAT): @@ -206,9 +218,13 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode) break; case PackOperAndType(GT_DIV, TYP_INT): + if (treeNode->gtOverflow()) + NYI_WASM("Overflow checks"); ins = INS_i32_div_s; break; case PackOperAndType(GT_DIV, TYP_LONG): + if (treeNode->gtOverflow()) + NYI_WASM("Overflow checks"); ins = INS_i64_div_s; break; case PackOperAndType(GT_DIV, TYP_FLOAT): @@ -226,9 +242,13 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode) break; case PackOperAndType(GT_MOD, TYP_INT): + if (treeNode->gtOverflow()) + NYI_WASM("Overflow checks"); ins = INS_i32_rem_s; break; case PackOperAndType(GT_MOD, TYP_LONG): + if (treeNode->gtOverflow()) + NYI_WASM("Overflow checks"); ins = INS_i64_rem_s; break; From fa3c96e16f75896ca75a9bc1cb6083c42d893a45 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 05:35:27 +0000 Subject: [PATCH 4/6] Remove overflow checks from GT_DIV and GT_MOD Removed gtOverflow() checks from GT_DIV and GT_MOD operations as requested. Only GT_ADD, GT_SUB, and GT_MUL retain overflow checks per OperMayOverflow. Co-authored-by: kg <198130+kg@users.noreply.github.com> --- src/coreclr/jit/codegenwasm.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index 6b2abf24894cd9..de375333b82d89 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -218,13 +218,9 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode) break; case PackOperAndType(GT_DIV, TYP_INT): - if (treeNode->gtOverflow()) - NYI_WASM("Overflow checks"); ins = INS_i32_div_s; break; case PackOperAndType(GT_DIV, TYP_LONG): - if (treeNode->gtOverflow()) - NYI_WASM("Overflow checks"); ins = INS_i64_div_s; break; case PackOperAndType(GT_DIV, TYP_FLOAT): @@ -242,13 +238,9 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode) break; case PackOperAndType(GT_MOD, TYP_INT): - if (treeNode->gtOverflow()) - NYI_WASM("Overflow checks"); ins = INS_i32_rem_s; break; case PackOperAndType(GT_MOD, TYP_LONG): - if (treeNode->gtOverflow()) - NYI_WASM("Overflow checks"); ins = INS_i64_rem_s; break; From 8c63dfac42953cec3ccc7cebee2d732e23a6c1ab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 17:10:43 +0000 Subject: [PATCH 5/6] Move shift/rotate operations to genCodeForShiftOrRotate - Created new genCodeForShiftOrRotate member function - Moved GT_LSH, GT_RSH, GT_RSZ, GT_ROL, GT_ROR from genCodeForBinary - Added declaration in codegen.h with #ifdef TARGET_WASM - Updated genCodeForTreeNode to call genCodeForShiftOrRotate for shifts Co-authored-by: kg <198130+kg@users.noreply.github.com> --- src/coreclr/jit/codegen.h | 3 +++ src/coreclr/jit/codegenwasm.cpp | 30 ++++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 727b4688980020..5550d219719519 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -748,6 +748,9 @@ class CodeGen final : public CodeGenInterface #endif void genCodeForTreeNode(GenTree* treeNode); void genCodeForBinary(GenTreeOp* treeNode); +#ifdef TARGET_WASM + void genCodeForShiftOrRotate(GenTreeOp* treeNode); +#endif bool genIsSameLocalVar(GenTree* tree1, GenTree* tree2); #if defined(TARGET_X86) diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index de375333b82d89..fbf32015740424 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -89,12 +89,15 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) case GT_OR: case GT_XOR: case GT_AND: + genCodeForBinary(treeNode->AsOp()); + break; + case GT_LSH: case GT_RSH: case GT_RSZ: case GT_ROL: case GT_ROR: - genCodeForBinary(treeNode->AsOp()); + genCodeForShiftOrRotate(treeNode->AsOp()); break; case GT_LCL_VAR: @@ -272,6 +275,29 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode) ins = INS_i64_xor; break; + default: + ins = INS_none; + NYI_WASM("genCodeForBinary"); + break; + } + + GetEmitter()->emitIns(ins); + genProduceReg(treeNode); +} + +//------------------------------------------------------------------------ +// genCodeForShiftOrRotate: Generate code for a shift or rotate operator +// +// Arguments: +// treeNode - The shift or rotate operation for which we are generating code. +// +void CodeGen::genCodeForShiftOrRotate(GenTreeOp* treeNode) +{ + genConsumeOperands(treeNode); + + instruction ins; + switch (PackOperAndType(treeNode)) + { case PackOperAndType(GT_LSH, TYP_INT): ins = INS_i32_shl; break; @@ -309,7 +335,7 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode) default: ins = INS_none; - NYI_WASM("genCodeForBinary"); + NYI_WASM("genCodeForShiftOrRotate"); break; } From b60846406663cdf2e14d806e8117d803ef79e89e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 3 Dec 2025 17:48:28 +0000 Subject: [PATCH 6/6] Add TODO comment for shift/rotate operand extension Added TODO-WASM comment documenting the need to zero-extend the 2nd operand for shifts and rotates when operands have different types. The shift operand width in IR is TYP_INT but WASM operations require matching widths, so extension may be needed for TYP_LONG operations. Co-authored-by: kg <198130+kg@users.noreply.github.com> --- src/coreclr/jit/codegenwasm.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index fbf32015740424..84bfb2eb99199b 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -295,6 +295,10 @@ void CodeGen::genCodeForShiftOrRotate(GenTreeOp* treeNode) { genConsumeOperands(treeNode); + // TODO-WASM: Zero-extend the 2nd operand for shifts and rotates as needed when the 1st and 2nd operand are + // different types. The shift operand width in IR is always TYP_INT; the WASM operations have the same widths + // for both the shift and shiftee. So the shift may need to be extended (zero-extended) for TYP_LONG. + instruction ins; switch (PackOperAndType(treeNode)) {