Repository: dotnet/runtime
File: src/coreclr/jit/codegenwasm.cpp (C++)

Code before:
            regNumber resultReg = regs->Extract();
            assert(WasmRegToType(resultReg) == TypeToWasmValueType(treeNode->TypeGet()));

            // Add and save the sum
            GetEmitter()->emitIns(is64BitOp ? INS_i64_add : INS_i32_add);
            GetEmitter()->emitIns_I(INS_local_set, emitActualTypeSize(treeNode), WasmRegToIndex(resultReg));
            // See if addends had the same sign. XOR leaves a non-negative result if they had the same sign.
            GetEmitter()->emitIns_I(INS_local_get, emitActualTypeSize(treeNode), WasmRegToIndex(op1Reg));
            GetEmitter()->emitIns_I(INS_local_get, emitActualTypeSize(treeNode), WasmRegToIndex(op2Reg));
            GetEmitter()->emitIns(is64BitOp ? INS_i64_xor : INS_i32_xor);
            GetEmitter()->emitIns_I(is64BitOp ? INS_i64_const : INS_i32_const, emitActualTypeSize(treeNode), 0);
            GetEmitter()->emitIns(is64BitOp ? INS_i64_ge_s : INS_i32_ge_s);
            GetEmitter()->emitIns(INS_if);
            {
                // Operands have the same sign. If the sum has a different sign, then the add overflowed.
                GetEmitter()->emitIns_I(INS_local_get, emitActualTypeSize(treeNode), WasmRegToIndex(resultReg));
                GetEmitter()->emitIns_I(INS_local_get, emitActualTypeSize(treeNode), WasmRegToIndex(op1Reg));
                GetEmitter()->emitIns(is64BitOp ? INS_i64_xor : INS_i32_xor);
                GetEmitter()->emitIns_I(is64BitOp ? INS_i64_const : INS_i32_const, emitActualTypeSize(treeNode), 0);
                GetEmitter()->emitIns(is64BitOp ? INS_i64_lt_s : INS_i32_lt_s);
                genJumpToThrowHlpBlk(SCK_OVERFLOW);
            }
            GetEmitter()->emitIns(INS_end);
            GetEmitter()->emitIns_I(INS_local_get, emitActualTypeSize(treeNode), WasmRegToIndex(resultReg));
            break;
        }

        case GT_SUB:
        {
            // We require an internal register.
            assert(regs->Count() == 1);
            regNumber resultReg = regs->Extract();
            assert(WasmRegToType(resultReg) == TypeToWasmValueType(treeNode->TypeGet()));

            // Subtract and save the difference
            GetEmitter()->emitIns(is64BitOp ? INS_i64_sub : INS_i32_sub);
            GetEmitter()->emitIns_I(INS_local_set, emitActualTypeSize(treeNode), WasmRegToIndex(resultReg));
            // See if operands had a different sign. XOR leaves a negative result if they had different signs.
            GetEmitter()->emitIns_I(INS_local_get, emitActualTypeSize(treeNode), WasmRegToIndex(op1Reg));
            GetEmitter()->emitIns_I(INS_local_get, emitActualTypeSize(treeNode), WasmRegToIndex(op2Reg));
            GetEmitter()->emitIns(is64BitOp ? INS_i64_xor : INS_i32_xor);
            GetEmitter()->emitIns_I(is64BitOp ? INS_i64_const : INS_i32_const, emitActualTypeSize(treeNode), 0);
            GetEmitter()->emitIns(is64BitOp ? INS_i64_lt_s : INS_i32_lt_s);
            GetEmitter()->emitIns(INS_if);
            {
                // Operands have different signs. If the difference has a different sign than op1, then the subtraction
                // overflowed.
                GetEmitter()->emitIns_I(INS_local_get, emitActualTypeSize(treeNode), WasmRegToIndex(resultReg));
                GetEmitter()->emitIns_I(INS_local_get, emitActualTypeSize(treeNode), WasmRegToIndex(op1Reg));
                GetEmitter()->emitIns(is64BitOp ? INS_i64_xor : INS_i32_xor);
                GetEmitter()->emitIns_I(is64BitOp ? INS_i64_const : INS_i32_const, emitActualTypeSize(treeNode), 0);

Review comment (bug):
I asked AI and it suggested this WASM for `int Foo(int a, int b) => checked(a + b)`:
```wasm
(local $res i32)
  
  ;; 1. Perform the addition
  local.get $a
  local.get $b
  i32.add
  local.tee $res  ;; Store result in local and keep on stack

  ;; 2. Overflow Detection Logic (Signed)
  ;; Formula: (res ^ a) & (res ^ b) & 0x80000000
  local.get $res
  local.get $a
  i32.xor
  local.get $res
  local.get $b
  i32.xor
  i32.and
  i32.const 0x80000000
  i32.and
  
  ;; 3. If the sign bit is set, it's an overflow
  if
    unreachable ;; This causes a WASM trap/exception
  end

  ;; 4. Return result if no overflow
  local.get $res
)
```
so just one branch instead of two, but feel free to ignore as long as it unblocks you

Code after:
        case GT_ADD:
        {
            // We require an internal register.
            assert(regs->Count() == 1);
            regNumber resultReg = regs->Extract();
            assert(WasmRegToType(resultReg) == TypeToWasmValueType(treeNode->TypeGet()));

            // Add and save the sum
            GetEmitter()->emitIns(is64BitOp ? INS_i64_add : INS_i32_add);
            GetEmitter()->emitIns_I(INS_local_set, emitActualTypeSize(treeNode), WasmRegToIndex(resultReg));
            // See if addends had the same sign. XOR leaves a non-negative result if they had the same sign.
            GetEmitter()->emitIns_I(INS_local_get, emitActualTypeSize(treeNode), WasmRegToIndex(op1Reg));
            GetEmitter()->emitIns_I(INS_local_get, emitActualTypeSize(treeNode), WasmRegToIndex(op2Reg));
            GetEmitter()->emitIns(is64BitOp ? INS_i64_xor : INS_i32_xor);

            // TODO-WASM-CQ: consider branchless alternative here (and for sub)
            GetEmitter()->emitIns_I(is64BitOp ? INS_i64_const : INS_i32_const, emitActualTypeSize(treeNode), 0);
            GetEmitter()->emitIns(is64BitOp ? INS_i64_ge_s : INS_i32_ge_s);
            GetEmitter()->emitIns(INS_if);
            {
                // Operands have the same sign. If the sum has a different sign, then the add overflowed.
                GetEmitter()->emitIns_I(INS_local_get, emitActualTypeSize(treeNode), WasmRegToIndex(resultReg));
                GetEmitter()->emitIns_I(INS_local_get, emitActualTypeSize(treeNode), WasmRegToIndex(op1Reg));
                GetEmitter()->emitIns(is64BitOp ? INS_i64_xor : INS_i32_xor);
                GetEmitter()->emitIns_I(is64BitOp ? INS_i64_const : INS_i32_const, emitActualTypeSize(treeNode), 0);
                GetEmitter()->emitIns(is64BitOp ? INS_i64_lt_s : INS_i32_lt_s);
                genJumpToThrowHlpBlk(SCK_OVERFLOW);
            }
            GetEmitter()->emitIns(INS_end);
            GetEmitter()->emitIns_I(INS_local_get, emitActualTypeSize(treeNode), WasmRegToIndex(resultReg));
            break;
        }

        case GT_SUB:
        {
            // We require an internal register.
            assert(regs->Count() == 1);
            regNumber resultReg = regs->Extract();
            assert(WasmRegToType(resultReg) == TypeToWasmValueType(treeNode->TypeGet()));

            // Subtract and save the difference
            GetEmitter()->emitIns(is64BitOp ? INS_i64_sub : INS_i32_sub);
            GetEmitter()->emitIns_I(INS_local_set, emitActualTypeSize(treeNode), WasmRegToIndex(resultReg));
            // See if operands had a different sign. XOR leaves a negative result if they had different signs.
            GetEmitter()->emitIns_I(INS_local_get, emitActualTypeSize(treeNode), WasmRegToIndex(op1Reg));
            GetEmitter()->emitIns_I(INS_local_get, emitActualTypeSize(treeNode), WasmRegToIndex(op2Reg));
            GetEmitter()->emitIns(is64BitOp ? INS_i64_xor : INS_i32_xor);
            GetEmitter()->emitIns_I(is64BitOp ? INS_i64_const : INS_i32_const, emitActualTypeSize(treeNode), 0);
            GetEmitter()->emitIns(is64BitOp ? INS_i64_lt_s : INS_i32_lt_s);
            GetEmitter()->emitIns(INS_if);
            {