diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index dd42db7aca28b6..24c2672ebcc0f4 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -3123,6 +3123,7 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree) assert(!op2->isUsedFromMemory()); emitAttr cmpSize = EA_ATTR(genTypeSize(op1Type)); + assert(cmpSize == EA_4BYTE || cmpSize == EA_8BYTE); assert(genTypeSize(op1Type) == genTypeSize(op2Type)); @@ -3205,133 +3206,82 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree) { ssize_t imm = op2->AsIntCon()->gtIconVal; - switch (cmpSize) + bool useAddSub = !(!tree->OperIs(GT_EQ, GT_NE) || (imm == -2048)); + bool useShiftRight = + !isUnsigned && ((tree->OperIs(GT_LT) && (imm == 0)) || (tree->OperIs(GT_LE) && (imm == -1))); + bool useLoadImm = isUnsigned && ((tree->OperIs(GT_LT, GT_GE) && (imm == 0)) || + (tree->OperIs(GT_LE, GT_GT) && (imm == -1))); + + if (cmpSize == EA_4BYTE) { - case EA_4BYTE: + if (!useAddSub && !useShiftRight && !useLoadImm) { regNumber tmpRegOp1 = internalRegisters.GetSingle(tree); assert(regOp1 != tmpRegOp1); - if (isUnsigned) - { - imm = static_cast(imm); - emit->emitIns_R_R_I(INS_slli, EA_8BYTE, tmpRegOp1, regOp1, 32); - emit->emitIns_R_R_I(INS_srli, EA_8BYTE, tmpRegOp1, tmpRegOp1, 32); - } - else - { - imm = static_cast(imm); - emit->emitIns_R_R(INS_sext_w, EA_8BYTE, tmpRegOp1, regOp1); - } + imm = static_cast(imm); + emit->emitIns_R_R(INS_sext_w, EA_8BYTE, tmpRegOp1, regOp1); regOp1 = tmpRegOp1; - break; } - case EA_8BYTE: - break; - default: - unreached(); } - if (tree->OperIs(GT_LT)) - { - if (!isUnsigned && emitter::isValidSimm12(imm)) - { - emit->emitIns_R_R_I(INS_slti, EA_PTRSIZE, targetReg, regOp1, imm); - } - else if (isUnsigned && emitter::isValidUimm11(imm)) - { - emit->emitIns_R_R_I(INS_sltiu, EA_PTRSIZE, targetReg, regOp1, imm); - } - else - { - emit->emitLoadImmediate(EA_PTRSIZE, REG_RA, imm); - emit->emitIns_R_R_R(isUnsigned ? INS_sltu : INS_slt, EA_PTRSIZE, targetReg, regOp1, REG_RA); - } - } - else if (tree->OperIs(GT_LE)) - { - if (!isUnsigned && emitter::isValidSimm12(imm + 1)) - { - emit->emitIns_R_R_I(INS_slti, EA_PTRSIZE, targetReg, regOp1, imm + 1); - } - else if (isUnsigned && emitter::isValidUimm11(imm + 1) && (imm != (~0))) - { - emit->emitIns_R_R_I(INS_sltiu, EA_PTRSIZE, targetReg, regOp1, imm + 1); - } - else - { - emit->emitLoadImmediate(EA_PTRSIZE, REG_RA, imm + 1); - emit->emitIns_R_R_R(isUnsigned ? INS_sltu : INS_slt, EA_PTRSIZE, targetReg, regOp1, REG_RA); - } - } - else if (tree->OperIs(GT_GT)) + if (tree->OperIs(GT_EQ, GT_NE)) { - if (!isUnsigned && emitter::isValidSimm12(imm + 1)) + if ((imm != 0) || (cmpSize == EA_4BYTE)) { - emit->emitIns_R_R_I(INS_slti, EA_PTRSIZE, targetReg, regOp1, imm + 1); - emit->emitIns_R_R_I(INS_xori, EA_PTRSIZE, targetReg, targetReg, 1); + instruction diff = INS_xori; + if (imm != -2048) + { + assert(useAddSub); + diff = (cmpSize == EA_4BYTE) ? INS_addiw : INS_addi; + imm = -imm; + } + emit->emitIns_R_R_I(diff, cmpSize, targetReg, regOp1, imm); + regOp1 = targetReg; } - else if (isUnsigned && emitter::isValidUimm11(imm + 1) && (imm != (~0))) + assert(emitter::isValidSimm12(imm)); + + if (tree->OperIs(GT_EQ)) { - emit->emitIns_R_R_I(INS_sltiu, EA_PTRSIZE, targetReg, regOp1, imm + 1); - emit->emitIns_R_R_I(INS_xori, EA_PTRSIZE, targetReg, targetReg, 1); + emit->emitIns_R_R_I(INS_sltiu, EA_PTRSIZE, targetReg, regOp1, 1); } else { - emit->emitLoadImmediate(EA_PTRSIZE, REG_RA, imm); - emit->emitIns_R_R_R(isUnsigned ? INS_sltu : INS_slt, EA_PTRSIZE, targetReg, REG_RA, regOp1); + assert(tree->OperIs(GT_NE)); + emit->emitIns_R_R_R(INS_sltu, EA_PTRSIZE, targetReg, REG_ZERO, regOp1); } } - else if (tree->OperIs(GT_GE)) + else { - if (!isUnsigned && emitter::isValidSimm12(imm)) - { - emit->emitIns_R_R_I(INS_slti, EA_PTRSIZE, targetReg, regOp1, imm); - } - else if (isUnsigned && emitter::isValidUimm11(imm)) - { - emit->emitIns_R_R_I(INS_sltiu, EA_PTRSIZE, targetReg, regOp1, imm); - } - else + assert(tree->OperIs(GT_LT, GT_LE, GT_GT, GT_GE)); + if (useLoadImm) { - emit->emitLoadImmediate(EA_PTRSIZE, REG_RA, imm); - emit->emitIns_R_R_R(isUnsigned ? INS_sltu : INS_slt, EA_PTRSIZE, targetReg, regOp1, REG_RA); + // unsigned (a <= ~0), (a >= 0) / (a > ~0), (a < 0) is always true / false + imm = tree->OperIs(GT_GE, GT_LE) ? 1 : 0; + emit->emitIns_R_R_I(INS_addi, EA_PTRSIZE, targetReg, REG_ZERO, imm); } - emit->emitIns_R_R_I(INS_xori, EA_PTRSIZE, targetReg, targetReg, 1); - } - else if (tree->OperIs(GT_NE)) - { - if (!imm) + else if (useShiftRight) { - emit->emitIns_R_R_R(INS_sltu, EA_PTRSIZE, targetReg, REG_R0, regOp1); + // signed (a < 0) or (a <= -1) is just the sign bit + instruction srli = (cmpSize == EA_4BYTE) ? INS_srliw : INS_srli; + emit->emitIns_R_R_I(srli, cmpSize, targetReg, regOp1, cmpSize * 8 - 1); } - else if (emitter::isValidUimm12(imm)) + else if ((tree->OperIs(GT_GT) && (imm == 0)) || (tree->OperIs(GT_GE) && (imm == 1))) { - emit->emitIns_R_R_I(INS_xori, EA_PTRSIZE, targetReg, regOp1, imm); - emit->emitIns_R_R_R(INS_sltu, EA_PTRSIZE, targetReg, REG_R0, targetReg); + instruction slt = isUnsigned ? INS_sltu : INS_slt; + emit->emitIns_R_R_R(slt, EA_PTRSIZE, targetReg, REG_ZERO, regOp1); } else { - emit->emitLoadImmediate(EA_PTRSIZE, REG_RA, imm); - emit->emitIns_R_R_R(INS_xor, EA_PTRSIZE, targetReg, regOp1, REG_RA); - emit->emitIns_R_R_R(INS_sltu, EA_PTRSIZE, targetReg, REG_R0, targetReg); - } - } - else if (tree->OperIs(GT_EQ)) - { - if (!imm) - { - emit->emitIns_R_R_I(INS_sltiu, EA_PTRSIZE, targetReg, regOp1, 1); - } - else if (emitter::isValidUimm12(imm)) - { - emit->emitIns_R_R_I(INS_xori, EA_PTRSIZE, targetReg, regOp1, imm); - emit->emitIns_R_R_I(INS_sltiu, EA_PTRSIZE, targetReg, targetReg, 1); - } - else - { - emit->emitLoadImmediate(EA_PTRSIZE, REG_RA, imm); - emit->emitIns_R_R_R(INS_xor, EA_PTRSIZE, targetReg, regOp1, REG_RA); - emit->emitIns_R_R_I(INS_sltiu, EA_PTRSIZE, targetReg, targetReg, 1); + instruction slti = isUnsigned ? INS_sltiu : INS_slti; + if (tree->OperIs(GT_LE, GT_GT)) + imm += 1; + assert(emitter::isValidSimm12(imm)); + assert(!isUnsigned || (imm != 0)); // should be handled in useLoadImm + + emit->emitIns_R_R_I(slti, EA_PTRSIZE, targetReg, regOp1, imm); + + if (tree->OperIs(GT_GT, GT_GE)) + emit->emitIns_R_R_I(INS_xori, EA_PTRSIZE, targetReg, targetReg, 1); } } } @@ -3339,58 +3289,43 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree) { regNumber regOp2 = op2->GetRegNum(); - if (cmpSize == EA_4BYTE) + if (tree->OperIs(GT_EQ, GT_NE)) { - regNumber tmpRegOp1 = REG_RA; - regNumber tmpRegOp2 = internalRegisters.GetSingle(tree); - assert(regOp1 != tmpRegOp2); - assert(regOp2 != tmpRegOp2); - - if (isUnsigned) + instruction sub = (cmpSize == EA_4BYTE) ? INS_subw : INS_sub; + emit->emitIns_R_R_R(sub, EA_PTRSIZE, targetReg, regOp1, regOp2); + if (tree->OperIs(GT_EQ)) { - emit->emitIns_R_R_I(INS_slli, EA_8BYTE, tmpRegOp1, regOp1, 32); - emit->emitIns_R_R_I(INS_srli, EA_8BYTE, tmpRegOp1, tmpRegOp1, 32); - - emit->emitIns_R_R_I(INS_slli, EA_8BYTE, tmpRegOp2, regOp2, 32); - emit->emitIns_R_R_I(INS_srli, EA_8BYTE, tmpRegOp2, tmpRegOp2, 32); + emit->emitIns_R_R_I(INS_sltiu, EA_PTRSIZE, targetReg, targetReg, 1); } else { - emit->emitIns_R_R_I(INS_slliw, EA_8BYTE, tmpRegOp1, regOp1, 0); - emit->emitIns_R_R_I(INS_slliw, EA_8BYTE, tmpRegOp2, regOp2, 0); + assert(tree->OperIs(GT_NE)); + emit->emitIns_R_R_R(INS_sltu, EA_PTRSIZE, targetReg, REG_ZERO, targetReg); } - - regOp1 = tmpRegOp1; - regOp2 = tmpRegOp2; - } - - if (tree->OperIs(GT_LT)) - { - emit->emitIns_R_R_R(isUnsigned ? INS_sltu : INS_slt, EA_8BYTE, targetReg, regOp1, regOp2); - } - else if (tree->OperIs(GT_LE)) - { - emit->emitIns_R_R_R(isUnsigned ? INS_sltu : INS_slt, EA_8BYTE, targetReg, regOp2, regOp1); - emit->emitIns_R_R_I(INS_xori, EA_PTRSIZE, targetReg, targetReg, 1); - } - else if (tree->OperIs(GT_GT)) - { - emit->emitIns_R_R_R(isUnsigned ? INS_sltu : INS_slt, EA_8BYTE, targetReg, regOp2, regOp1); } - else if (tree->OperIs(GT_GE)) - { - emit->emitIns_R_R_R(isUnsigned ? INS_sltu : INS_slt, EA_8BYTE, targetReg, regOp1, regOp2); - emit->emitIns_R_R_I(INS_xori, EA_PTRSIZE, targetReg, targetReg, 1); - } - else if (tree->OperIs(GT_NE)) - { - emit->emitIns_R_R_R(INS_xor, EA_PTRSIZE, targetReg, regOp1, regOp2); - emit->emitIns_R_R_R(INS_sltu, EA_PTRSIZE, targetReg, REG_R0, targetReg); - } - else if (tree->OperIs(GT_EQ)) + else { - emit->emitIns_R_R_R(INS_xor, EA_PTRSIZE, targetReg, regOp1, regOp2); - emit->emitIns_R_R_I(INS_sltiu, EA_PTRSIZE, targetReg, targetReg, 1); + assert(tree->OperIs(GT_LT, GT_LE, GT_GT, GT_GE)); + if (cmpSize == EA_4BYTE) + { + regNumber tmpRegOp1 = REG_RA; + regNumber tmpRegOp2 = internalRegisters.GetSingle(tree); + assert(regOp1 != tmpRegOp2); + assert(regOp2 != tmpRegOp2); + emit->emitIns_R_R(INS_sext_w, EA_8BYTE, tmpRegOp1, regOp1); + emit->emitIns_R_R(INS_sext_w, EA_8BYTE, tmpRegOp2, regOp2); + regOp1 = tmpRegOp1; + regOp2 = tmpRegOp2; + } + + instruction slt = isUnsigned ? INS_sltu : INS_slt; + if (tree->OperIs(GT_LE, GT_GT)) + std::swap(regOp1, regOp2); + + emit->emitIns_R_R_R(slt, EA_8BYTE, targetReg, regOp1, regOp2); + + if (tree->OperIs(GT_LE, GT_GE)) + emit->emitIns_R_R_I(INS_xori, EA_PTRSIZE, targetReg, targetReg, 1); } } } @@ -3452,19 +3387,8 @@ void CodeGen::genCodeForJumpCompare(GenTreeOpCC* tree) { regNumber tmpRegOp1 = rsGetRsvdReg(); assert(regOp1 != tmpRegOp1); - if (cond.IsUnsigned()) - { - imm = static_cast(imm); - - assert(regOp1 != tmpRegOp1); - emit->emitIns_R_R_I(INS_slli, EA_8BYTE, tmpRegOp1, regOp1, 32); - emit->emitIns_R_R_I(INS_srli, EA_8BYTE, tmpRegOp1, tmpRegOp1, 32); - } - else - { - imm = static_cast(imm); - emit->emitIns_R_R(INS_sext_w, EA_8BYTE, tmpRegOp1, regOp1); - } + imm = static_cast(imm); + emit->emitIns_R_R(INS_sext_w, EA_8BYTE, tmpRegOp1, regOp1); regOp1 = tmpRegOp1; break; } @@ -3500,15 +3424,7 @@ void CodeGen::genCodeForJumpCompare(GenTreeOpCC* tree) { regNumber tmpRegOp1 = rsGetRsvdReg(); assert(regOp1 != tmpRegOp1); - if (cond.IsUnsigned()) - { - emit->emitIns_R_R_I(INS_slli, EA_8BYTE, tmpRegOp1, regOp1, 32); - emit->emitIns_R_R_I(INS_srli, EA_8BYTE, tmpRegOp1, tmpRegOp1, 32); - } - else - { - emit->emitIns_R_R(INS_sext_w, EA_8BYTE, tmpRegOp1, regOp1); - } + emit->emitIns_R_R(INS_sext_w, EA_8BYTE, tmpRegOp1, regOp1); regOp1 = tmpRegOp1; } } @@ -3557,20 +3473,8 @@ void CodeGen::genCodeForJumpCompare(GenTreeOpCC* tree) regNumber tmpRegOp2 = rsGetRsvdReg(); assert(regOp1 != tmpRegOp2); assert(regOp2 != tmpRegOp2); - - if (cond.IsUnsigned()) - { - emit->emitIns_R_R_I(INS_slli, EA_8BYTE, tmpRegOp1, regOp1, 32); - emit->emitIns_R_R_I(INS_srli, EA_8BYTE, tmpRegOp1, tmpRegOp1, 32); - emit->emitIns_R_R_I(INS_slli, EA_8BYTE, tmpRegOp2, regOp2, 32); - emit->emitIns_R_R_I(INS_srli, EA_8BYTE, tmpRegOp2, tmpRegOp2, 32); - } - else - { - emit->emitIns_R_R_I(INS_slliw, EA_8BYTE, tmpRegOp1, regOp1, 0); - emit->emitIns_R_R_I(INS_slliw, EA_8BYTE, tmpRegOp2, regOp2, 0); - } - + emit->emitIns_R_R(INS_sext_w, EA_8BYTE, tmpRegOp1, regOp1); + emit->emitIns_R_R(INS_sext_w, EA_8BYTE, tmpRegOp2, regOp2); regOp1 = tmpRegOp1; regOp2 = tmpRegOp2; } diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 4c9bd307938855..56952ccf44fae9 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -5492,7 +5492,7 @@ regNumber emitter::emitInsTernary(instruction ins, emitAttr attr, GenTree* dst, // TODO-RISCV64-CQ: here sign-extend dst when deal with 32bit data is too conservative. if (EA_SIZE(attr) == EA_4BYTE) - emitIns_R_R_I(INS_slliw, attr, dstReg, dstReg, 0); + emitIns_R_R(INS_sext_w, attr, dstReg, dstReg); } break; diff --git a/src/coreclr/jit/lowerriscv64.cpp b/src/coreclr/jit/lowerriscv64.cpp index 9f410e1e09ab4e..effb025c8168ca 100644 --- a/src/coreclr/jit/lowerriscv64.cpp +++ b/src/coreclr/jit/lowerriscv64.cpp @@ -64,13 +64,17 @@ bool Lowering::IsContainableImmed(GenTree* parentNode, GenTree* childNode) const switch (parentNode->OperGet()) { - case GT_ADD: case GT_EQ: case GT_NE: + return emitter::isValidSimm12(-immVal) || (immVal == -2048); + + case GT_LE: // a <= N -> a < N+1 + case GT_GT: // a > N -> !(a <= N) -> !(a < N+1) + immVal += 1; + FALLTHROUGH; case GT_LT: - case GT_LE: case GT_GE: - case GT_GT: + case GT_ADD: case GT_AND: case GT_OR: case GT_XOR: @@ -85,9 +89,7 @@ bool Lowering::IsContainableImmed(GenTree* parentNode, GenTree* childNode) const case GT_XCHG: case GT_STORE_LCL_FLD: case GT_STORE_LCL_VAR: - if (immVal == 0) - return true; - break; + return (immVal == 0); default: break; diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index 28c2bea643bff2..dbf8f0978b11db 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -4611,6 +4611,8 @@ int LinearScan::BuildCmp(GenTree* tree) assert(tree->OperIsCompare() || tree->OperIs(GT_CMP, GT_TEST, GT_BT)); #elif defined(TARGET_ARM64) assert(tree->OperIsCompare() || tree->OperIs(GT_CMP, GT_TEST, GT_JCMP, GT_JTEST, GT_CCMP)); +#elif defined(TARGET_RISCV64) + assert(tree->OperIsCmpCompare() || tree->OperIs(GT_JCMP)); #else assert(tree->OperIsCompare() || tree->OperIs(GT_CMP, GT_TEST, GT_JCMP)); #endif diff --git a/src/coreclr/jit/lsrariscv64.cpp b/src/coreclr/jit/lsrariscv64.cpp index 37f3bd2e59087f..8e67fc9183f102 100644 --- a/src/coreclr/jit/lsrariscv64.cpp +++ b/src/coreclr/jit/lsrariscv64.cpp @@ -446,7 +446,19 @@ int LinearScan::BuildNode(GenTree* tree) { emitAttr cmpSize = EA_ATTR(genTypeSize(op1Type)); if (cmpSize == EA_4BYTE) - buildInternalIntRegisterDefForNode(tree); + { + GenTree* op2 = tree->gtGetOp2(); + + bool isUnsigned = (tree->gtFlags & GTF_UNSIGNED) != 0; + bool useAddSub = !(!tree->OperIs(GT_EQ, GT_NE) || op2->IsIntegralConst(-2048)); + bool useShiftRight = !isUnsigned && ((tree->OperIs(GT_LT) && op2->IsIntegralConst(0)) || + (tree->OperIs(GT_LE) && op2->IsIntegralConst(-1))); + bool useLoadImm = isUnsigned && ((tree->OperIs(GT_LT, GT_GE) && op2->IsIntegralConst(0)) || + (tree->OperIs(GT_LE, GT_GT) && op2->IsIntegralConst(-1))); + + if (!useAddSub && !useShiftRight && !useLoadImm) + buildInternalIntRegisterDefForNode(tree); + } } buildInternalRegisterUses(); } diff --git a/src/tests/JIT/Directed/compare/int32.cs b/src/tests/JIT/Directed/compare/int32.cs index d631ad5071fe58..7bb555b7c2e635 100644 --- a/src/tests/JIT/Directed/compare/int32.cs +++ b/src/tests/JIT/Directed/compare/int32.cs @@ -5,40 +5,253 @@ using System.Runtime.CompilerServices; using Xunit; -public class CompareTest + +// The integer field of a struct passed in a register according to RISC-V floating-point calling convention is +// not extended. Comparisons on RISC-V, however, always operate on full registers. +// Note: reflection calls poison the undefined bits in runtime debug mode making it a better repro. + +public static class CompareTestInt { - [MethodImplAttribute(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + [MethodImplAttribute(MethodImplOptions.NoInlining)] public static bool Lt2((int, float) x) => (x.Item1 < 2); - - [MethodImplAttribute(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + + [MethodImplAttribute(MethodImplOptions.NoInlining)] public static bool Gt2((int, float) x) => (x.Item1 > 2); - [MethodImplAttribute(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + [MethodImplAttribute(MethodImplOptions.NoInlining)] public static bool Le2((int, float) x) => (x.Item1 <= 2); - - [MethodImplAttribute(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + + [MethodImplAttribute(MethodImplOptions.NoInlining)] public static bool Ge2((int, float) x) => (x.Item1 >= 2); - [MethodImplAttribute(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] - public static bool Eq0((int, float) x) => (x.Item1 == 0); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Lt0((int, float) x) => (x.Item1 < 0); - [MethodImplAttribute(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Gt0((int, float) x) => (x.Item1 > 0); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Le0((int, float) x) => (x.Item1 <= 0); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Ge0((int, float) x) => (x.Item1 >= 0); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Eq0((int, float) x) => (x.Item1 == 0); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] public static bool Ne0((int, float) x) => (x.Item1 != 0); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Lt1((int, float) x) => (x.Item1 < 1); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Gt1((int, float) x) => (x.Item1 > 1); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Le1((int, float) x) => (x.Item1 <= 1); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Ge1((int, float) x) => (x.Item1 >= 1); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Eq1((int, float) x) => (x.Item1 == 1); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Ne1((int, float) x) => (x.Item1 != 1); + + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool LtMinus1((int, float) x) => (x.Item1 < -1); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool GtMinus1((int, float) x) => (x.Item1 > -1); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool LeMinus1((int, float) x) => (x.Item1 <= -1); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool GeMinus1((int, float) x) => (x.Item1 >= -1); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool EqMinus1((int, float) x) => (x.Item1 == -1); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool NeMinus1((int, float) x) => (x.Item1 != -1); + + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Eq2048((int, float) x) => (x.Item1 == 2048); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Ne2048((int, float) x) => (x.Item1 != 2048); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool EqMinus2048((int, float) x) => (x.Item1 == -2048); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool NeMinus2048((int, float) x) => (x.Item1 != -2048); + [Fact] public static void Test() { - // The integer field of a struct passed in a register according to RISC-V floating-point calling convention is - // not extended. Comparisons on RISC-V, however, always operate on full registers. - // Note: reflection calls poison the undefined bits in runtime debug mode making it a better repro. - var type = typeof(CompareTest); + var type = typeof(CompareTestInt); var args = new object[] {(2, 0f)}; Assert.False((bool)type.GetMethod("Lt2").Invoke(null, args)); Assert.False((bool)type.GetMethod("Gt2").Invoke(null, args)); Assert.True((bool)type.GetMethod("Le2").Invoke(null, args)); Assert.True((bool)type.GetMethod("Ge2").Invoke(null, args)); args = new object[] {(0, 0f)}; + Assert.False((bool)type.GetMethod("Lt0").Invoke(null, args)); + Assert.False((bool)type.GetMethod("Gt0").Invoke(null, args)); + Assert.True((bool)type.GetMethod("Le0").Invoke(null, args)); + Assert.True((bool)type.GetMethod("Ge0").Invoke(null, args)); + Assert.True((bool)type.GetMethod("Eq0").Invoke(null, args)); + Assert.False((bool)type.GetMethod("Ne0").Invoke(null, args)); + args = new object[] {(1, 0f)}; + Assert.False((bool)type.GetMethod("Lt1").Invoke(null, args)); + Assert.False((bool)type.GetMethod("Gt1").Invoke(null, args)); + Assert.True((bool)type.GetMethod("Le1").Invoke(null, args)); + Assert.True((bool)type.GetMethod("Ge1").Invoke(null, args)); + Assert.True((bool)type.GetMethod("Eq1").Invoke(null, args)); + Assert.False((bool)type.GetMethod("Ne1").Invoke(null, args)); + args = new object[] {(-1, 0f)}; + Assert.False((bool)type.GetMethod("LtMinus1").Invoke(null, args)); + Assert.False((bool)type.GetMethod("GtMinus1").Invoke(null, args)); + Assert.True((bool)type.GetMethod("LeMinus1").Invoke(null, args)); + Assert.True((bool)type.GetMethod("GeMinus1").Invoke(null, args)); + Assert.True((bool)type.GetMethod("EqMinus1").Invoke(null, args)); + Assert.False((bool)type.GetMethod("NeMinus1").Invoke(null, args)); + args = new object[] {(2048, 0f)}; + Assert.True((bool)type.GetMethod("Eq2048").Invoke(null, args)); + Assert.False((bool)type.GetMethod("Ne2048").Invoke(null, args)); + args = new object[] {(-2048, 0f)}; + Assert.True((bool)type.GetMethod("EqMinus2048").Invoke(null, args)); + Assert.False((bool)type.GetMethod("NeMinus2048").Invoke(null, args)); + } +} + +public static class CompareTestUint +{ + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Lt2((uint, float) x) => (x.Item1 < 2); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Gt2((uint, float) x) => (x.Item1 > 2); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Le2((uint, float) x) => (x.Item1 <= 2); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Ge2((uint, float) x) => (x.Item1 >= 2); + + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Lt0((uint, float) x) => (x.Item1 < 0); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Gt0((uint, float) x) => (x.Item1 > 0); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Le0((uint, float) x) => (x.Item1 <= 0); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Ge0((uint, float) x) => (x.Item1 >= 0); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Eq0((uint, float) x) => (x.Item1 == 0); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Ne0((uint, float) x) => (x.Item1 != 0); + + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Lt1((uint, float) x) => (x.Item1 < 1); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Gt1((uint, float) x) => (x.Item1 > 1); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Le1((uint, float) x) => (x.Item1 <= 1); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Ge1((uint, float) x) => (x.Item1 >= 1); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Eq1((uint, float) x) => (x.Item1 == 1); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Ne1((uint, float) x) => (x.Item1 != 1); + + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool LtMinus1((uint, float) x) => (x.Item1 < 0xFFFF_FFFFu); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool GtMinus1((uint, float) x) => (x.Item1 > 0xFFFF_FFFFu); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool LeMinus1((uint, float) x) => (x.Item1 <= 0xFFFF_FFFFu); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool GeMinus1((uint, float) x) => (x.Item1 >= 0xFFFF_FFFFu); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool EqMinus1((uint, float) x) => (x.Item1 == 0xFFFF_FFFFu); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool NeMinus1((uint, float) x) => (x.Item1 != 0xFFFF_FFFFu); + + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Eq2048((uint, float) x) => (x.Item1 == 2048); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool Ne2048((uint, float) x) => (x.Item1 != 2048); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool EqMinus2048((uint, float) x) => (x.Item1 == 0xFFFF_F800u); + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static bool NeMinus2048((uint, float) x) => (x.Item1 != 0xFFFF_F800u); + + [Fact] + public static void Test() + { + var type = typeof(CompareTestUint); + var args = new object[] {(2u, 0f)}; + Assert.False((bool)type.GetMethod("Lt2").Invoke(null, args)); + Assert.False((bool)type.GetMethod("Gt2").Invoke(null, args)); + Assert.True((bool)type.GetMethod("Le2").Invoke(null, args)); + Assert.True((bool)type.GetMethod("Ge2").Invoke(null, args)); + args = new object[] {(0u, 0f)}; + Assert.False((bool)type.GetMethod("Lt0").Invoke(null, args)); + Assert.False((bool)type.GetMethod("Gt0").Invoke(null, args)); + Assert.True((bool)type.GetMethod("Le0").Invoke(null, args)); + Assert.True((bool)type.GetMethod("Ge0").Invoke(null, args)); Assert.True((bool)type.GetMethod("Eq0").Invoke(null, args)); Assert.False((bool)type.GetMethod("Ne0").Invoke(null, args)); + args = new object[] {(1u, 0f)}; + Assert.False((bool)type.GetMethod("Lt1").Invoke(null, args)); + Assert.False((bool)type.GetMethod("Gt1").Invoke(null, args)); + Assert.True((bool)type.GetMethod("Le1").Invoke(null, args)); + Assert.True((bool)type.GetMethod("Ge1").Invoke(null, args)); + Assert.True((bool)type.GetMethod("Eq1").Invoke(null, args)); + Assert.False((bool)type.GetMethod("Ne1").Invoke(null, args)); + args = new object[] {(0xFFFF_FFFFu, 0f)}; + Assert.False((bool)type.GetMethod("LtMinus1").Invoke(null, args)); + Assert.False((bool)type.GetMethod("GtMinus1").Invoke(null, args)); + Assert.True((bool)type.GetMethod("LeMinus1").Invoke(null, args)); + Assert.True((bool)type.GetMethod("GeMinus1").Invoke(null, args)); + Assert.True((bool)type.GetMethod("EqMinus1").Invoke(null, args)); + Assert.False((bool)type.GetMethod("NeMinus1").Invoke(null, args)); + args = new object[] {(2048u, 0f)}; + Assert.True((bool)type.GetMethod("Eq2048").Invoke(null, args)); + Assert.False((bool)type.GetMethod("Ne2048").Invoke(null, args)); + args = new object[] {(0xFFFF_F800u, 0f)}; + Assert.True((bool)type.GetMethod("EqMinus2048").Invoke(null, args)); + Assert.False((bool)type.GetMethod("NeMinus2048").Invoke(null, args)); } } diff --git a/src/tests/JIT/Directed/compare/int32_d.csproj b/src/tests/JIT/Directed/compare/int32_d.csproj new file mode 100644 index 00000000000000..2dad18d2666756 --- /dev/null +++ b/src/tests/JIT/Directed/compare/int32_d.csproj @@ -0,0 +1,12 @@ + + + 1 + + + Full + False + + + + + diff --git a/src/tests/JIT/Directed/compare/int32.csproj b/src/tests/JIT/Directed/compare/int32_do.csproj similarity index 88% rename from src/tests/JIT/Directed/compare/int32.csproj rename to src/tests/JIT/Directed/compare/int32_do.csproj index 2929b8f6e68b31..c3111ebcf133cc 100644 --- a/src/tests/JIT/Directed/compare/int32.csproj +++ b/src/tests/JIT/Directed/compare/int32_do.csproj @@ -3,6 +3,7 @@ 1 + Full True diff --git a/src/tests/JIT/Directed/compare/int32_r.csproj b/src/tests/JIT/Directed/compare/int32_r.csproj new file mode 100644 index 00000000000000..58c5761e60fff5 --- /dev/null +++ b/src/tests/JIT/Directed/compare/int32_r.csproj @@ -0,0 +1,12 @@ + + + 1 + + + None + False + + + + + diff --git a/src/tests/JIT/Directed/compare/int32_ro.csproj b/src/tests/JIT/Directed/compare/int32_ro.csproj new file mode 100644 index 00000000000000..127b1b02b5d51f --- /dev/null +++ b/src/tests/JIT/Directed/compare/int32_ro.csproj @@ -0,0 +1,12 @@ + + + 1 + + + None + True + + + + +