diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 6ca6564379a748..9be48ea01507a8 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -1187,7 +1187,7 @@ void CodeGen::genCodeForMulHi(GenTreeOp* treeNode) genProduceReg(treeNode); } -// Generate code for ADD, SUB, MUL, AND, AND_NOT, OR and XOR +// Generate code for ADD, SUB, MUL, AND, AND_NOT, OR, OR_NOT, XOR, and XOR_NOT // This method is expected to have called genConsumeOperands() before calling it. void CodeGen::genCodeForBinary(GenTreeOp* treeNode) { @@ -1195,7 +1195,7 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode) regNumber targetReg = treeNode->GetRegNum(); emitter* emit = GetEmitter(); - assert(treeNode->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_AND_NOT, GT_OR, GT_XOR)); + assert(treeNode->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_AND_NOT, GT_OR, GT_OR_NOT, GT_XOR, GT_XOR_NOT)); GenTree* op1 = treeNode->gtGetOp1(); GenTree* op2 = treeNode->gtGetOp2(); @@ -1827,7 +1827,25 @@ void CodeGen::genCodeForNegNot(GenTree* tree) // void CodeGen::genCodeForBswap(GenTree* tree) { - NYI_RISCV64("genCodeForBswap-----unimplemented on RISCV64 yet----"); + assert(tree->OperIs(GT_BSWAP, GT_BSWAP16)); + var_types type = tree->gtGetOp1()->TypeGet(); + emitAttr size = emitTypeSize(type); + regNumber dest = tree->GetRegNum(); + regNumber src = genConsumeReg(tree->gtGetOp1()); + + assert(compiler->compOpportunisticallyDependsOn(InstructionSet_Zbb)); + emitter& emit = *GetEmitter(); + emit.emitIns_R_R(INS_rev8, size, dest, src); + if (size < EA_PTRSIZE) + { + int shiftAmount = tree->OperIs(GT_BSWAP16) ? 48 : 32; + // TODO: we need to right-shift the byte-reversed register anyway. Remove the cast (in Lowering::LowerCast?) + // wrapping GT_BSWAP16 and pass the exact destination type here, so that this codegen could leave the register + // properly extended. + emit.emitIns_R_R_I(INS_srli, size, dest, dest, shiftAmount); + } + + genProduceReg(tree); } //------------------------------------------------------------------------ @@ -2637,7 +2655,9 @@ instruction CodeGen::genGetInsForOper(GenTree* treeNode) break; case GT_AND_NOT: - NYI_RISCV64("GT_AND_NOT-----unimplemented/unused on RISCV64 yet----"); + assert(compiler->compOpportunisticallyDependsOn(InstructionSet_Zbb)); + assert(!isImmed(treeNode)); + ins = INS_andn; break; case GT_OR: @@ -2652,6 +2672,12 @@ instruction CodeGen::genGetInsForOper(GenTree* treeNode) } break; + case GT_OR_NOT: + assert(compiler->compOpportunisticallyDependsOn(InstructionSet_Zbb)); + assert(!isImmed(treeNode)); + ins = INS_orn; + break; + case GT_LSH: isImm = isImmed(treeNode); if (isImm) @@ -2749,6 +2775,12 @@ instruction CodeGen::genGetInsForOper(GenTree* treeNode) } break; + case GT_XOR_NOT: + assert(compiler->compOpportunisticallyDependsOn(InstructionSet_Zbb)); + assert(!isImmed(treeNode)); + ins = INS_xnor; + break; + default: NO_WAY("Unhandled oper in genGetInsForOper() - integer"); break; @@ -4301,6 +4333,8 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) case GT_XOR: case GT_AND: case GT_AND_NOT: + case GT_OR_NOT: + case GT_XOR_NOT: assert(varTypeIsIntegralOrI(treeNode)); FALLTHROUGH; @@ -5336,48 +5370,81 @@ void CodeGen::genCodeForShift(GenTree* tree) GenTree* operand = tree->gtGetOp1(); GenTree* shiftBy = tree->gtGetOp2(); + unsigned immWidth = emitter::getBitWidth(size); // For RISCV64, immWidth will be set to 32 or 64 + if (tree->OperIs(GT_ROR, GT_ROL)) { - regNumber tempReg = internalRegisters.GetSingle(tree); - unsigned immWidth = emitter::getBitWidth(size); // For RISCV64, immWidth will be set to 32 or 64 - if (!shiftBy->IsCnsIntOrI()) + if (compiler->compOpportunisticallyDependsOn(InstructionSet_Zbb)) { - regNumber shiftRight = tree->OperIs(GT_ROR) ? shiftBy->GetRegNum() : tempReg; - regNumber shiftLeft = tree->OperIs(GT_ROR) ? tempReg : shiftBy->GetRegNum(); - GetEmitter()->emitIns_R_R_I(INS_addi, size, tempReg, REG_R0, immWidth); - GetEmitter()->emitIns_R_R_R(INS_sub, size, tempReg, tempReg, shiftBy->GetRegNum()); - if (size == EA_8BYTE) + bool is4 = (size == EA_4BYTE); + bool isR = tree->OperIs(GT_ROR); + if (!shiftBy->IsCnsIntOrI()) { - GetEmitter()->emitIns_R_R_R(INS_srl, size, REG_RA, operand->GetRegNum(), shiftRight); - GetEmitter()->emitIns_R_R_R(INS_sll, size, tempReg, operand->GetRegNum(), shiftLeft); + instruction ins; + if (isR) + { + ins = is4 ? INS_rorw : INS_ror; + } + else + { + ins = is4 ? INS_rolw : INS_rol; + } + GetEmitter()->emitIns_R_R_R(ins, size, tree->GetRegNum(), operand->GetRegNum(), shiftBy->GetRegNum()); } else { - GetEmitter()->emitIns_R_R_R(INS_srlw, size, REG_RA, operand->GetRegNum(), shiftRight); - GetEmitter()->emitIns_R_R_R(INS_sllw, size, tempReg, operand->GetRegNum(), shiftLeft); + unsigned shiftByImm = (unsigned)shiftBy->AsIntCon()->gtIconVal; + assert(shiftByImm < immWidth); + if (!isR) + { + shiftByImm = immWidth - shiftByImm; + } + instruction ins = is4 ? INS_roriw : INS_rori; + GetEmitter()->emitIns_R_R_I(ins, size, tree->GetRegNum(), operand->GetRegNum(), shiftByImm); } } else { - unsigned shiftByImm = (unsigned)shiftBy->AsIntCon()->gtIconVal; - if (shiftByImm >= 32 && shiftByImm < 64) + regNumber tempReg = internalRegisters.GetSingle(tree); + if (!shiftBy->IsCnsIntOrI()) { - immWidth = 64; - } - unsigned shiftRight = tree->OperIs(GT_ROR) ? shiftByImm : immWidth - shiftByImm; - unsigned shiftLeft = tree->OperIs(GT_ROR) ? immWidth - shiftByImm : shiftByImm; - if ((shiftByImm >= 32 && shiftByImm < 64) || size == EA_8BYTE) - { - GetEmitter()->emitIns_R_R_I(INS_srli, size, REG_RA, operand->GetRegNum(), shiftRight); - GetEmitter()->emitIns_R_R_I(INS_slli, size, tempReg, operand->GetRegNum(), shiftLeft); + regNumber shiftRight = tree->OperIs(GT_ROR) ? shiftBy->GetRegNum() : tempReg; + regNumber shiftLeft = tree->OperIs(GT_ROR) ? tempReg : shiftBy->GetRegNum(); + GetEmitter()->emitIns_R_R_I(INS_addi, size, tempReg, REG_R0, immWidth); + GetEmitter()->emitIns_R_R_R(INS_sub, size, tempReg, tempReg, shiftBy->GetRegNum()); + if (size == EA_8BYTE) + { + GetEmitter()->emitIns_R_R_R(INS_srl, size, REG_RA, operand->GetRegNum(), shiftRight); + GetEmitter()->emitIns_R_R_R(INS_sll, size, tempReg, operand->GetRegNum(), shiftLeft); + } + else + { + GetEmitter()->emitIns_R_R_R(INS_srlw, size, REG_RA, operand->GetRegNum(), shiftRight); + GetEmitter()->emitIns_R_R_R(INS_sllw, size, tempReg, operand->GetRegNum(), shiftLeft); + } } else { - GetEmitter()->emitIns_R_R_I(INS_srliw, size, REG_RA, operand->GetRegNum(), shiftRight); - GetEmitter()->emitIns_R_R_I(INS_slliw, size, tempReg, operand->GetRegNum(), shiftLeft); + unsigned shiftByImm = (unsigned)shiftBy->AsIntCon()->gtIconVal; + if (shiftByImm >= 32 && shiftByImm < 64) + { + immWidth = 64; + } + unsigned shiftRight = tree->OperIs(GT_ROR) ? shiftByImm : immWidth - shiftByImm; + unsigned shiftLeft = tree->OperIs(GT_ROR) ? immWidth - shiftByImm : shiftByImm; + if ((shiftByImm >= 32 && shiftByImm < 64) || size == EA_8BYTE) + { + GetEmitter()->emitIns_R_R_I(INS_srli, size, REG_RA, operand->GetRegNum(), shiftRight); + GetEmitter()->emitIns_R_R_I(INS_slli, size, tempReg, operand->GetRegNum(), shiftLeft); + } + else + { + GetEmitter()->emitIns_R_R_I(INS_srliw, size, REG_RA, operand->GetRegNum(), shiftRight); + GetEmitter()->emitIns_R_R_I(INS_slliw, size, tempReg, operand->GetRegNum(), shiftLeft); + } } + GetEmitter()->emitIns_R_R_R(INS_or, size, tree->GetRegNum(), REG_RA, tempReg); } - GetEmitter()->emitIns_R_R_R(INS_or, size, tree->GetRegNum(), REG_RA, tempReg); } else { @@ -5392,7 +5459,6 @@ void CodeGen::genCodeForShift(GenTree* tree) unsigned shiftByImm = (unsigned)shiftBy->AsIntCon()->gtIconVal; // should check shiftByImm for riscv64-ins. - unsigned immWidth = emitter::getBitWidth(size); // For RISCV64, immWidth will be set to 32 or 64 shiftByImm &= (immWidth - 1); if (ins == INS_slliw && shiftByImm >= 32) @@ -6329,28 +6395,37 @@ void CodeGen::genIntToIntCast(GenTreeCast* cast) case GenIntCastDesc::ZERO_EXTEND_SMALL_INT: if (desc.ExtendSrcSize() == 1) { - emit->emitIns_R_R_I(INS_slli, EA_PTRSIZE, dstReg, srcReg, 64 - 8); - emit->emitIns_R_R_I(INS_srli, EA_PTRSIZE, dstReg, dstReg, 64 - 8); - } - else - { - - emit->emitIns_R_R_I(INS_slli, EA_PTRSIZE, dstReg, srcReg, 64 - 16); - emit->emitIns_R_R_I(INS_srli, EA_PTRSIZE, dstReg, dstReg, 64 - 16); + emit->emitIns_R_R_I(INS_andi, EA_PTRSIZE, dstReg, srcReg, 0xff); + break; } - break; + FALLTHROUGH; case GenIntCastDesc::SIGN_EXTEND_SMALL_INT: - if (desc.ExtendSrcSize() == 1) + { + bool isSignExtend = (desc.ExtendKind() == GenIntCastDesc::SIGN_EXTEND_SMALL_INT); + if (compiler->compOpportunisticallyDependsOn(InstructionSet_Zbb)) { - emit->emitIns_R_R_I(INS_slli, EA_PTRSIZE, dstReg, srcReg, 64 - 8); - emit->emitIns_R_R_I(INS_srai, EA_PTRSIZE, dstReg, dstReg, 64 - 8); + instruction extend = INS_none; + bool isHalf = (desc.ExtendSrcSize() == 2); + if (isSignExtend) + { + extend = isHalf ? INS_sext_h : INS_sext_b; + } + else + { + assert(isHalf); + extend = INS_zext_h; + } + emit->emitIns_R_R(extend, EA_PTRSIZE, dstReg, srcReg); } else { - emit->emitIns_R_R_I(INS_slli, EA_PTRSIZE, dstReg, srcReg, 64 - 16); - emit->emitIns_R_R_I(INS_srai, EA_PTRSIZE, dstReg, dstReg, 64 - 16); + instruction shiftRight = isSignExtend ? INS_srai : INS_srli; + unsigned shiftAmount = 64 - desc.ExtendSrcSize() * 8; + emit->emitIns_R_R_I(INS_slli, EA_PTRSIZE, dstReg, srcReg, shiftAmount); + emit->emitIns_R_R_I(shiftRight, EA_PTRSIZE, dstReg, dstReg, shiftAmount); } break; + } case GenIntCastDesc::ZERO_EXTEND_INT: diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index a67f125c90b84f..3784cd7e6cc437 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -650,7 +650,7 @@ void emitter::emitIns_R_R( { code_t code = emitInsCode(ins); - if (INS_mov == ins || INS_sext_w == ins || (INS_clz <= ins && ins <= INS_cpopw)) + if (INS_mov == ins || INS_sext_w == ins || (INS_clz <= ins && ins <= INS_rev8)) { assert(isGeneralRegisterOrR0(reg1)); assert(isGeneralRegisterOrR0(reg2)); @@ -728,7 +728,7 @@ void emitter::emitIns_R_R_I( if ((INS_addi <= ins && INS_srai >= ins) || (INS_addiw <= ins && INS_sraiw >= ins) || (INS_lb <= ins && INS_lhu >= ins) || INS_ld == ins || INS_lw == ins || INS_jalr == ins || INS_fld == ins || - INS_flw == ins) + INS_flw == ins || INS_rori == ins || INS_roriw == ins) { assert(isGeneralRegister(reg2)); code |= (reg1 & 0x1f) << 7; // rd @@ -827,7 +827,7 @@ void emitter::emitIns_R_R_R( (INS_addw <= ins && ins <= INS_sraw) || (INS_fadd_s <= ins && ins <= INS_fmax_s) || (INS_fadd_d <= ins && ins <= INS_fmax_d) || (INS_feq_s <= ins && ins <= INS_fle_s) || (INS_feq_d <= ins && ins <= INS_fle_d) || (INS_lr_w <= ins && ins <= INS_amomaxu_d) || - (INS_min <= ins && ins <= INS_maxu)) + (INS_rol <= ins && ins <= INS_maxu)) { #ifdef DEBUG switch (ins) @@ -915,6 +915,13 @@ void emitter::emitIns_R_R_R( case INS_amomaxu_w: case INS_amomaxu_d: + case INS_rol: + case INS_rolw: + case INS_ror: + case INS_rorw: + case INS_xnor: + case INS_orn: + case INS_andn: case INS_min: case INS_minu: case INS_max: @@ -3843,16 +3850,17 @@ void emitter::emitDispInsName( case 0x1: { unsigned funct6 = (imm12 >> 6) & 0x3f; - unsigned shamt = imm12 & 0x3f; // 6 BITS for SHAMT in RISCV64 + unsigned shamt = imm12 & 0x3f; // 6 BITS for SHAMT in RISCV6 switch (funct6) { case 0b011000: { - static const char* names[] = {"clz", "ctz", "cpop"}; + static const char* names[] = {"clz", "ctz", "cpop", nullptr, "sext.b", "sext.h"}; // shift amount is treated as additional funct opcode - if (shamt >= ARRAY_SIZE(names)) + if (shamt >= ARRAY_SIZE(names) || shamt == 3) return emitDispIllegalInstruction(code); + assert(names[shamt] != nullptr); printLength = printf("%s", names[shamt]); hasImmediate = false; break; @@ -3874,21 +3882,41 @@ void emitter::emitDispInsName( printLength = printf("sltiu"); break; case 0x4: // XORI - printLength = printf("xori"); + if (imm12 == -1) + { + printLength = printf("not"); + hasImmediate = false; + } + else + { + printLength = printf("xori"); + } break; case 0x5: // SRLI & SRAI { - static constexpr unsigned kLogicalShiftFunct6 = 0b000000; - static constexpr unsigned kArithmeticShiftFunct6 = 0b010000; - - unsigned funct6 = (imm12 >> 6) & 0x3f; - bool isLogicalShift = funct6 == kLogicalShiftFunct6; - if ((!isLogicalShift) && (funct6 != kArithmeticShiftFunct6)) + unsigned funct6 = (imm12 >> 6) & 0x3f; + imm12 &= 0x3f; // 6BITS for SHAMT in RISCV64 + switch (funct6) { - return emitDispIllegalInstruction(code); + case 0b000000: + printLength = printf("srli"); + break; + case 0b010000: + printLength = printf("srai"); + break; + case 0b011000: + printLength = printf("rori"); + break; + case 0b011010: + if (imm12 != 0b111000) // shift amount is treated as additional funct opcode + return emitDispIllegalInstruction(code); + + printLength = printf("rev8"); + hasImmediate = false; + break; + default: + return emitDispIllegalInstruction(code); } - printLength = printf(isLogicalShift ? "srli" : "srai"); - imm12 &= 0x3f; // 6BITS for SHAMT in RISCV64 } break; case 0x6: // ORI @@ -3969,21 +3997,21 @@ void emitter::emitDispInsName( return; case 0x5: // SRLIW & SRAIW { - static constexpr unsigned kLogicalShiftFunct7 = 0b0000000; - static constexpr unsigned kArithmeticShiftFunct7 = 0b0100000; - unsigned funct7 = (imm12 >> 5) & 0x7f; - if (funct7 == kLogicalShiftFunct7) - { - printf("srliw %s, %s, %d\n", rd, rs1, imm12 & 0x1f); // 5BITS for SHAMT in RISCV64 - } - else if (funct7 == kArithmeticShiftFunct7) - { - printf("sraiw %s, %s, %d\n", rd, rs1, imm12 & 0x1f); // 5BITS for SHAMT in RISCV64 - } - else + imm12 &= 0x1f; // 5BITS for SHAMT in RISCV64 + switch (funct7) { - emitDispIllegalInstruction(code); + case 0b0000000: + printf("srliw %s, %s, %d\n", rd, rs1, imm12); + return; + case 0b0100000: + printf("sraiw %s, %s, %d\n", rd, rs1, imm12); + return; + case 0b0110000: + printf("roriw %s, %s, %d\n", rd, rs1, imm12); + return; + default: + return emitDispIllegalInstruction(code); } } return; @@ -4038,9 +4066,18 @@ void emitter::emitDispInsName( case 0x0: // SUB printf("sub %s, %s, %s\n", rd, rs1, rs2); return; + case 0x4: // XNOR + printf("xnor %s, %s, %s\n", rd, rs1, rs2); + return; case 0x5: // SRA printf("sra %s, %s, %s\n", rd, rs1, rs2); return; + case 0x6: // ORN + printf("orn %s, %s, %s\n", rd, rs1, rs2); + return; + case 0x7: // ANDN + printf("andn %s, %s, %s\n", rd, rs1, rs2); + return; default: return emitDispIllegalInstruction(code); } @@ -4076,6 +4113,19 @@ void emitter::emitDispInsName( return emitDispIllegalInstruction(code); } return; + case 0b0110000: + switch (opcode3) + { + case 0b001: + printf("rol %s, %s, %s\n", rd, rs1, rs2); + return; + case 0b101: + printf("ror %s, %s, %s\n", rd, rs1, rs2); + return; + default: + return emitDispIllegalInstruction(code); + } + return; case 0b0000101: { if ((opcode3 >> 2) != 1) // clmul[h] unsupported @@ -4093,9 +4143,10 @@ void emitter::emitDispInsName( { unsigned int opcode2 = (code >> 25) & 0x7f; unsigned int opcode3 = (code >> 12) & 0x7; + unsigned int rs2Num = (code >> 20) & 0x1f; const char* rd = RegNames[(code >> 7) & 0x1f]; const char* rs1 = RegNames[(code >> 15) & 0x1f]; - const char* rs2 = RegNames[(code >> 20) & 0x1f]; + const char* rs2 = RegNames[rs2Num]; switch (opcode2) { @@ -4150,6 +4201,28 @@ void emitter::emitDispInsName( return emitDispIllegalInstruction(code); } return; + case 0b0110000: + switch (opcode3) + { + case 0b001: + printf("rolw %s, %s, %s\n", rd, rs1, rs2); + return; + case 0b101: + printf("rorw %s, %s, %s\n", rd, rs1, rs2); + return; + default: + return emitDispIllegalInstruction(code); + } + return; + case 0b0000100: + // Currently only zext.h for this opcode2. + // Note: zext.h is encoded as a pseudo for 'packw rd, rs1, zero' which is not in Zbb. + if (opcode3 != 0b100 || rs2Num != REG_ZERO) + return emitDispIllegalInstruction(code); + + printf("zext.h %s, %s\n", rd, rs1); + return; + default: return emitDispIllegalInstruction(code); } @@ -5325,7 +5398,9 @@ regNumber emitter::emitInsTernary(instruction ins, emitAttr attr, GenTree* dst, case GT_AND: case GT_AND_NOT: case GT_OR: + case GT_OR_NOT: case GT_XOR: + case GT_XOR_NOT: { emitIns_R_R_R(ins, attr, dstReg, src1Reg, src2Reg); diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 2dd1ebd574a93b..ae3989f2f2d22c 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -4668,9 +4668,11 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd, { case CorInfoType::CORINFO_TYPE_SHORT: case CorInfoType::CORINFO_TYPE_USHORT: + { retNode = gtNewCastNode(TYP_INT, gtNewOperNode(GT_BSWAP16, TYP_INT, impPopStack().val), false, callType); break; + } case CorInfoType::CORINFO_TYPE_INT: case CorInfoType::CORINFO_TYPE_UINT: @@ -10772,19 +10774,22 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) { namespaceName += 1; -#if defined(TARGET_XARCH) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) +#if defined(TARGET_XARCH) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) if (strcmp(namespaceName, "Buffers.Binary") == 0) { if (strcmp(className, "BinaryPrimitives") == 0) { if (strcmp(methodName, "ReverseEndianness") == 0) { - result = NI_System_Buffers_Binary_BinaryPrimitives_ReverseEndianness; + RISCV64_ONLY(if (compOpportunisticallyDependsOn(InstructionSet_Zbb))) + { + result = NI_System_Buffers_Binary_BinaryPrimitives_ReverseEndianness; + } } } } else -#endif // defined(TARGET_XARCH) || defined(TARGET_ARM64) +#endif // defined(TARGET_XARCH) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) if (strcmp(namespaceName, "Collections.Generic") == 0) { if (strcmp(className, "Comparer`1") == 0) diff --git a/src/coreclr/jit/instrsriscv64.h b/src/coreclr/jit/instrsriscv64.h index 1f72938548b451..fe643015e11e79 100644 --- a/src/coreclr/jit/instrsriscv64.h +++ b/src/coreclr/jit/instrsriscv64.h @@ -269,13 +269,28 @@ INST(ctz, "ctz", 0, 0x60101013) INST(ctzw, "ctzw", 0, 0x6010101b) INST(cpop, "cpop", 0, 0x60201013) INST(cpopw, "cpopw", 0, 0x6020101b) +INST(sext_b, "sext.b", 0, 0x60401013) +INST(sext_h, "sext.h", 0, 0x60501013) +INST(zext_h, "zext.h", 0, 0x0800403b) +INST(rev8, "rev8", 0, 0x6b805013) //// R_R_R +INST(rol, "rol", 0, 0x60001033) +INST(rolw, "rolw", 0, 0x6000103b) +INST(ror, "ror", 0, 0x60005033) +INST(rorw, "rorw", 0, 0x6000503b) +INST(xnor, "xnor", 0, 0x40004033) +INST(orn, "orn", 0, 0x40006033) +INST(andn, "andn", 0, 0x40007033) INST(min, "min", 0, 0x0a004033) INST(minu, "minu", 0, 0x0a005033) INST(max, "max", 0, 0x0a006033) INST(maxu, "maxu", 0, 0x0a007033) +//// R_R_I +INST(rori, "rori", 0, 0x60005013) +INST(roriw, "roriw", 0, 0x6000501b) + // clang-format on /*****************************************************************************/ #undef INST diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 9784dd6d55bd5d..b5acc593c337ef 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -730,7 +730,6 @@ GenTree* Lowering::LowerNode(GenTree* node) case GT_MDARR_LENGTH: case GT_MDARR_LOWER_BOUND: return LowerArrLength(node->AsArrCommon()); - break; default: break; diff --git a/src/coreclr/jit/lowerriscv64.cpp b/src/coreclr/jit/lowerriscv64.cpp index 6a1cffd5ca0426..5c7db04953a836 100644 --- a/src/coreclr/jit/lowerriscv64.cpp +++ b/src/coreclr/jit/lowerriscv64.cpp @@ -187,6 +187,73 @@ GenTree* Lowering::LowerJTrue(GenTreeOp* jtrue) // GenTree* Lowering::LowerBinaryArithmetic(GenTreeOp* binOp) { + if (comp->opts.OptimizationEnabled()) + { + GenTree*& op1 = binOp->gtOp1; + GenTree*& op2 = binOp->gtOp2; + + bool isOp1Negated = op1->OperIs(GT_NOT); + bool isOp2Negated = op2->OperIs(GT_NOT); + if (binOp->OperIs(GT_AND, GT_OR, GT_XOR) && (isOp1Negated || isOp2Negated)) + { + if ((isOp1Negated && isOp2Negated) || comp->compOpportunisticallyDependsOn(InstructionSet_Zbb)) + { + if (isOp1Negated) + { + BlockRange().Remove(op1); + op1 = op1->AsUnOp()->gtGetOp1(); + } + if (isOp2Negated) + { + BlockRange().Remove(op2); + op2 = op2->AsUnOp()->gtGetOp1(); + } + + if (isOp1Negated != isOp2Negated) + { + assert(comp->compOpportunisticallyDependsOn(InstructionSet_Zbb)); + if (isOp1Negated) + std::swap(op1, op2); + + genTreeOps operNot = GT_NONE; + switch (binOp->OperGet()) + { + case GT_AND: + operNot = GT_AND_NOT; + break; + case GT_OR: + operNot = GT_OR_NOT; + break; + default: + assert(binOp->OperIs(GT_XOR)); + operNot = GT_XOR_NOT; + break; + } + binOp->ChangeOper(operNot); + } + else if (binOp->OperIs(GT_AND, GT_OR)) // XOR is good after negation removal, (~a ^ ~b) == (a ^ b) + { + assert(isOp1Negated && isOp2Negated); + LIR::Use use; + if (BlockRange().TryGetUse(binOp, &use)) + { + // (~a | ~b) == ~(a & b), (~a & ~b) == ~(a | b) + genTreeOps reverseOper = binOp->OperIs(GT_AND) ? GT_OR : GT_AND; + binOp->ChangeOper(reverseOper); + + GenTreeUnOp* negation = comp->gtNewOperNode(GT_NOT, binOp->gtType, binOp); + BlockRange().InsertAfter(binOp, negation); + use.ReplaceWith(negation); + } + else + { + binOp->SetUnusedValue(); + } + } + } + } + } + ContainCheckBinary(binOp); return binOp->gtNext; diff --git a/src/coreclr/jit/lsrariscv64.cpp b/src/coreclr/jit/lsrariscv64.cpp index c307148b17769b..19da9096bfc790 100644 --- a/src/coreclr/jit/lsrariscv64.cpp +++ b/src/coreclr/jit/lsrariscv64.cpp @@ -261,7 +261,7 @@ int LinearScan::BuildNode(GenTree* tree) case GT_RSZ: case GT_ROR: case GT_ROL: - if (tree->OperIs(GT_ROR, GT_ROL)) + if (tree->OperIs(GT_ROR, GT_ROL) && !compiler->compOpportunisticallyDependsOn(InstructionSet_Zbb)) buildInternalIntRegisterDefForNode(tree); srcCount = BuildBinaryUses(tree->AsOp()); buildInternalRegisterUses();