mirror of https://github.com/dotnet/runtime
Merge 5a6742935a
into 02596ba8d9
This commit is contained in:
commit
d520a5a189
|
@ -706,6 +706,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableArm64Sve2, W("EnableArm64Sv
|
|||
#elif defined(TARGET_RISCV64)
|
||||
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zba, W("EnableRiscV64Zba"), 1, "Allows RiscV64 Zba hardware intrinsics to be disabled")
|
||||
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zbb, W("EnableRiscV64Zbb"), 1, "Allows RiscV64 Zbb hardware intrinsics to be disabled")
|
||||
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zbs, W("EnableRiscV64Zbs"), 1, "Allows RiscV64 Zbs hardware intrinsics to be disabled")
|
||||
#endif
|
||||
|
||||
// Runtime-async
|
||||
|
|
|
@ -48,6 +48,7 @@ enum CORINFO_InstructionSet
|
|||
InstructionSet_RiscV64Base=1,
|
||||
InstructionSet_Zba=2,
|
||||
InstructionSet_Zbb=3,
|
||||
InstructionSet_Zbs=4,
|
||||
#endif // TARGET_RISCV64
|
||||
#ifdef TARGET_AMD64
|
||||
InstructionSet_X86Base=1,
|
||||
|
@ -389,6 +390,8 @@ inline CORINFO_InstructionSetFlags EnsureInstructionSetFlagsAreValid(CORINFO_Ins
|
|||
resultflags.RemoveInstructionSet(InstructionSet_Zbb);
|
||||
if (resultflags.HasInstructionSet(InstructionSet_Zba) && !resultflags.HasInstructionSet(InstructionSet_RiscV64Base))
|
||||
resultflags.RemoveInstructionSet(InstructionSet_Zba);
|
||||
if (resultflags.HasInstructionSet(InstructionSet_Zbs) && !resultflags.HasInstructionSet(InstructionSet_RiscV64Base))
|
||||
resultflags.RemoveInstructionSet(InstructionSet_Zbs);
|
||||
#endif // TARGET_RISCV64
|
||||
#ifdef TARGET_AMD64
|
||||
if (resultflags.HasInstructionSet(InstructionSet_X86Base) && !resultflags.HasInstructionSet(InstructionSet_X86Base_X64))
|
||||
|
@ -667,6 +670,8 @@ inline const char *InstructionSetToString(CORINFO_InstructionSet instructionSet)
|
|||
return "Zba";
|
||||
case InstructionSet_Zbb :
|
||||
return "Zbb";
|
||||
case InstructionSet_Zbs :
|
||||
return "Zbs";
|
||||
#endif // TARGET_RISCV64
|
||||
#ifdef TARGET_AMD64
|
||||
case InstructionSet_X86Base :
|
||||
|
@ -864,6 +869,7 @@ inline CORINFO_InstructionSet InstructionSetFromR2RInstructionSet(ReadyToRunInst
|
|||
case READYTORUN_INSTRUCTION_RiscV64Base: return InstructionSet_RiscV64Base;
|
||||
case READYTORUN_INSTRUCTION_Zba: return InstructionSet_Zba;
|
||||
case READYTORUN_INSTRUCTION_Zbb: return InstructionSet_Zbb;
|
||||
case READYTORUN_INSTRUCTION_Zbs: return InstructionSet_Zbs;
|
||||
#endif // TARGET_RISCV64
|
||||
#ifdef TARGET_AMD64
|
||||
case READYTORUN_INSTRUCTION_X86Base: return InstructionSet_X86Base;
|
||||
|
|
|
@ -37,11 +37,11 @@
|
|||
|
||||
#include <minipal/guid.h>
|
||||
|
||||
constexpr GUID JITEEVersionIdentifier = { /* 2d40ec46-2e41-4a8b-8349-3c1267b95821 */
|
||||
0x2d40ec46,
|
||||
0x2e41,
|
||||
0x4a8b,
|
||||
{0x83, 0x49, 0x3c, 0x12, 0x67, 0xb9, 0x58, 0x21}
|
||||
constexpr GUID JITEEVersionIdentifier = { /* 6c4c75a1-33ba-48c5-a49d-59e8b92cef0d */
|
||||
0x6c4c75a1,
|
||||
0x33ba,
|
||||
0x48c5,
|
||||
{0xa4, 0x9d, 0x59, 0xe8, 0xb9, 0x2c, 0xef, 0x0d}
|
||||
};
|
||||
|
||||
#endif // JIT_EE_VERSIONING_GUID_H
|
||||
|
|
|
@ -91,6 +91,7 @@ enum ReadyToRunInstructionSet
|
|||
READYTORUN_INSTRUCTION_Avx512Vp2intersect_VL=81,
|
||||
READYTORUN_INSTRUCTION_Avx512Vpopcntdq=82,
|
||||
READYTORUN_INSTRUCTION_Avx512Vpopcntdq_VL=83,
|
||||
READYTORUN_INSTRUCTION_Zbs=84,
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -1074,7 +1074,8 @@ 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_OR_NOT, GT_XOR, GT_XOR_NOT));
|
||||
assert(treeNode->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_AND_NOT, GT_OR, GT_OR_NOT, GT_XOR, GT_XOR_NOT,
|
||||
GT_BIT_SET, GT_BIT_CLEAR, GT_BIT_INVERT));
|
||||
|
||||
GenTree* op1 = treeNode->gtGetOp1();
|
||||
GenTree* op2 = treeNode->gtGetOp2();
|
||||
|
@ -2683,6 +2684,21 @@ instruction CodeGen::genGetInsForOper(GenTree* treeNode)
|
|||
ins = INS_xnor;
|
||||
break;
|
||||
|
||||
case GT_BIT_SET:
|
||||
assert(compiler->compOpportunisticallyDependsOn(InstructionSet_Zbs));
|
||||
ins = isImmed(treeNode) ? INS_bseti : INS_bset;
|
||||
break;
|
||||
|
||||
case GT_BIT_CLEAR:
|
||||
assert(compiler->compOpportunisticallyDependsOn(InstructionSet_Zbs));
|
||||
ins = isImmed(treeNode) ? INS_bclri : INS_bclr;
|
||||
break;
|
||||
|
||||
case GT_BIT_INVERT:
|
||||
assert(compiler->compOpportunisticallyDependsOn(InstructionSet_Zbs));
|
||||
ins = isImmed(treeNode) ? INS_binvi : INS_binv;
|
||||
break;
|
||||
|
||||
default:
|
||||
NO_WAY("Unhandled oper in genGetInsForOper() - integer");
|
||||
break;
|
||||
|
@ -4120,6 +4136,9 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
|
|||
case GT_AND_NOT:
|
||||
case GT_OR_NOT:
|
||||
case GT_XOR_NOT:
|
||||
case GT_BIT_SET:
|
||||
case GT_BIT_CLEAR:
|
||||
case GT_BIT_INVERT:
|
||||
assert(varTypeIsIntegralOrI(treeNode));
|
||||
|
||||
FALLTHROUGH;
|
||||
|
|
|
@ -728,7 +728,8 @@ 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_slli_uw == ins || INS_rori == ins || INS_roriw == ins)
|
||||
INS_flw == ins || INS_slli_uw == ins || INS_rori == ins || INS_roriw == ins ||
|
||||
(INS_bseti <= ins && ins <= INS_binvi))
|
||||
{
|
||||
assert(isGeneralRegister(reg2));
|
||||
code |= (reg1 & 0x1f) << 7; // rd
|
||||
|
@ -827,7 +828,8 @@ 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_sh1add <= ins && ins <= INS_sh3add_uw) || (INS_rol <= ins && ins <= INS_maxu))
|
||||
(INS_sh1add <= ins && ins <= INS_sh3add_uw) || (INS_rol <= ins && ins <= INS_maxu) ||
|
||||
(INS_bset <= ins && ins <= INS_binv))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
switch (ins)
|
||||
|
@ -934,6 +936,11 @@ void emitter::emitIns_R_R_R(
|
|||
case INS_minu:
|
||||
case INS_max:
|
||||
case INS_maxu:
|
||||
|
||||
case INS_bset:
|
||||
case INS_bclr:
|
||||
case INS_bext:
|
||||
case INS_binv:
|
||||
break;
|
||||
default:
|
||||
NYI_RISCV64("illegal ins within emitIns_R_R_R!");
|
||||
|
@ -3675,7 +3682,7 @@ void emitter::emitDispInsName(
|
|||
case 0x1:
|
||||
{
|
||||
unsigned funct6 = (imm12 >> 6) & 0x3f;
|
||||
unsigned shamt = imm12 & 0x3f; // 6 BITS for SHAMT in RISCV6
|
||||
unsigned shamt = imm12 & 0x3f; // 6 BITS for SHAMT in RISCV64
|
||||
switch (funct6)
|
||||
{
|
||||
case 0b011000:
|
||||
|
@ -3692,12 +3699,20 @@ void emitter::emitDispInsName(
|
|||
}
|
||||
case 0b000000:
|
||||
printLength = printf("slli");
|
||||
imm12 = shamt;
|
||||
break;
|
||||
|
||||
case 0b001010:
|
||||
printLength = printf("bseti");
|
||||
break;
|
||||
case 0b010010:
|
||||
printLength = printf("bclri");
|
||||
break;
|
||||
case 0b011010:
|
||||
printLength = printf("binvi");
|
||||
break;
|
||||
default:
|
||||
return emitDispIllegalInstruction(code);
|
||||
}
|
||||
imm12 = shamt;
|
||||
}
|
||||
break;
|
||||
case 0x2: // SLTI
|
||||
|
@ -3717,7 +3732,7 @@ void emitter::emitDispInsName(
|
|||
printLength = printf("xori");
|
||||
}
|
||||
break;
|
||||
case 0x5: // SRLI & SRAI
|
||||
case 0x5:
|
||||
{
|
||||
unsigned funct6 = (imm12 >> 6) & 0x3f;
|
||||
imm12 &= 0x3f; // 6BITS for SHAMT in RISCV64
|
||||
|
@ -3732,6 +3747,9 @@ void emitter::emitDispInsName(
|
|||
case 0b011000:
|
||||
printLength = printf("rori");
|
||||
break;
|
||||
case 0b010010:
|
||||
printLength = printf("bexti");
|
||||
break;
|
||||
case 0b011010:
|
||||
if (imm12 != 0b111000) // shift amount is treated as additional funct opcode
|
||||
return emitDispIllegalInstruction(code);
|
||||
|
@ -3982,6 +4000,28 @@ void emitter::emitDispInsName(
|
|||
printf("%s %s, %s, %s\n", names[opcode3 & 0b11], rd, rs1, rs2);
|
||||
return;
|
||||
}
|
||||
case 0b0010100:
|
||||
if (opcode3 != 0b001)
|
||||
return emitDispIllegalInstruction(code);
|
||||
printf("bset %s, %s, %s\n", rd, rs1, rs2);
|
||||
return;
|
||||
case 0b0100100:
|
||||
switch (opcode3)
|
||||
{
|
||||
case 0b001:
|
||||
printf("bclr %s, %s, %s\n", rd, rs1, rs2);
|
||||
return;
|
||||
case 0b101:
|
||||
printf("bext %s, %s, %s\n", rd, rs1, rs2);
|
||||
return;
|
||||
default:
|
||||
return emitDispIllegalInstruction(code);
|
||||
}
|
||||
case 0b0110100:
|
||||
if (opcode3 != 0b001)
|
||||
return emitDispIllegalInstruction(code);
|
||||
printf("binv %s, %s, %s\n", rd, rs1, rs2);
|
||||
return;
|
||||
default:
|
||||
return emitDispIllegalInstruction(code);
|
||||
}
|
||||
|
@ -5142,8 +5182,22 @@ regNumber emitter::emitInsTernary(instruction ins, emitAttr attr, GenTree* dst,
|
|||
ins = INS_addiw;
|
||||
imm = -imm;
|
||||
}
|
||||
else if (ins == INS_bseti || ins == INS_bclri || ins == INS_bexti || ins == INS_binvi)
|
||||
{
|
||||
// Use base instructions where possible:
|
||||
// "bexti rd, rs, 0" is equivalent to "andi rd, rs, 1"
|
||||
// "bseti/bclri/binvi rd, rs, imm" are equivalent to "ori/andi/xori rd, rs, (~)(1 << imm)" for imm < 11
|
||||
int minBitIndex = (ins == INS_bexti) ? 1 : 11;
|
||||
int maxBitIndex = emitActualTypeSize(src1) * 8;
|
||||
if (ins != INS_bexti && attr == EA_4BYTE)
|
||||
maxBitIndex--; // can't touch the sign bit alone, it affects sign extension
|
||||
|
||||
assert(ins == INS_addi || ins == INS_addiw || ins == INS_andi || ins == INS_ori || ins == INS_xori);
|
||||
assert(imm >= minBitIndex);
|
||||
assert(imm < maxBitIndex);
|
||||
}
|
||||
|
||||
assert(ins == INS_addi || ins == INS_addiw || ins == INS_andi || ins == INS_ori || ins == INS_xori ||
|
||||
ins == INS_bseti || ins == INS_bclri || ins == INS_bexti || ins == INS_binvi);
|
||||
|
||||
regNumber tempReg = needCheckOv ? codeGen->internalRegisters.Extract(dst) : REG_NA;
|
||||
|
||||
|
@ -5291,6 +5345,9 @@ regNumber emitter::emitInsTernary(instruction ins, emitAttr attr, GenTree* dst,
|
|||
case GT_OR_NOT:
|
||||
case GT_XOR:
|
||||
case GT_XOR_NOT:
|
||||
case GT_BIT_SET:
|
||||
case GT_BIT_CLEAR:
|
||||
case GT_BIT_INVERT:
|
||||
{
|
||||
emitIns_R_R_R(ins, attr, dstReg, src1Reg, src2Reg);
|
||||
|
||||
|
|
|
@ -294,6 +294,13 @@ GTNODE(SH3ADD_UW , GenTreeOp ,0,0,GTK_BINOP|DBK_NOTHIR)
|
|||
GTNODE(ADD_UW , GenTreeOp ,0,0,GTK_BINOP|DBK_NOTHIR)
|
||||
// Maps to riscv64 slli.uw instruction. Computes result = zext(op1[31..0]) << imm.
|
||||
GTNODE(SLLI_UW , GenTreeOp ,0,0,GTK_BINOP|DBK_NOTHIR)
|
||||
|
||||
// Maps to bset/bseti instruction. Computes result = op1 | (1 << op2)
|
||||
GTNODE(BIT_SET , GenTreeOp ,0,0,GTK_BINOP|DBK_NOTHIR)
|
||||
// Maps to bclr/bclri instruction. Computes result = op1 & ~(1 << op2)
|
||||
GTNODE(BIT_CLEAR , GenTreeOp ,0,0,GTK_BINOP|DBK_NOTHIR)
|
||||
// Maps to binv/binvi instruction. Computes result = op1 ^ (1 << op2)
|
||||
GTNODE(BIT_INVERT , GenTreeOp ,0,0,GTK_BINOP|DBK_NOTHIR)
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -307,6 +307,19 @@ INST(sh2add_uw, "sh2add_uw", 0, 0x2000403b)
|
|||
INST(sh3add_uw, "sh3add_uw", 0, 0x2000603b)
|
||||
INST(slli_uw, "slli_uw", 0, 0x0800101b)
|
||||
|
||||
// Zbs (RV32 + RV64)
|
||||
//// R_R_R
|
||||
INST(bset, "bset", 0, 0x28001033)
|
||||
INST(bclr, "bclr", 0, 0x48001033)
|
||||
INST(bext, "bext", 0, 0x48005033)
|
||||
INST(binv, "binv", 0, 0x68001033)
|
||||
|
||||
//// R_R_I
|
||||
INST(bseti, "bseti", 0, 0x28001013)
|
||||
INST(bclri, "bclri", 0, 0x48001013)
|
||||
INST(bexti, "bexti", 0, 0x48005013)
|
||||
INST(binvi, "binvi", 0, 0x68001013)
|
||||
|
||||
// clang-format on
|
||||
/*****************************************************************************/
|
||||
#undef INST
|
||||
|
|
|
@ -429,6 +429,7 @@ RELEASE_CONFIG_INTEGER(EnableArm64Sve2, "EnableArm64Sve2",
|
|||
#elif defined(TARGET_RISCV64)
|
||||
RELEASE_CONFIG_INTEGER(EnableRiscV64Zba, "EnableRiscV64Zba", 1) // Allows RiscV64 Zba hardware intrinsics to be disabled
|
||||
RELEASE_CONFIG_INTEGER(EnableRiscV64Zbb, "EnableRiscV64Zbb", 1) // Allows RiscV64 Zbb hardware intrinsics to be disabled
|
||||
RELEASE_CONFIG_INTEGER(EnableRiscV64Zbs, "EnableRiscV64Zbs", 1) // Allows RiscV64 Zbs hardware intrinsics to be disabled
|
||||
#endif
|
||||
|
||||
RELEASE_CONFIG_INTEGER(EnableEmbeddedBroadcast, "EnableEmbeddedBroadcast", 1) // Allows embedded broadcasts to be disabled
|
||||
|
|
|
@ -197,74 +197,149 @@ 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);
|
||||
|
||||
ContainCheckBinary(binOp);
|
||||
|
||||
if (!comp->opts.OptimizationEnabled())
|
||||
return binOp->gtNext;
|
||||
|
||||
if (comp->compOpportunisticallyDependsOn(InstructionSet_Zbs) && binOp->OperIs(GT_OR, GT_XOR, GT_AND))
|
||||
{
|
||||
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 (op2->IsIntegralConst())
|
||||
{
|
||||
if ((isOp1Negated && isOp2Negated) || comp->compOpportunisticallyDependsOn(InstructionSet_Zbb))
|
||||
GenTreeIntConCommon* constant = op2->AsIntConCommon();
|
||||
UINT64 bit = (UINT64)constant->IntegralValue();
|
||||
if (binOp->OperIs(GT_AND))
|
||||
bit = ~bit;
|
||||
|
||||
if (!op2->isContained() && isPow2(bit) &&
|
||||
(!op1->TypeIs(TYP_INT) || BitOperations::Log2(bit) != 31)) // don't change the sign bit alone
|
||||
{
|
||||
if (isOp1Negated)
|
||||
{
|
||||
BlockRange().Remove(op1);
|
||||
op1 = op1->AsUnOp()->gtGetOp1();
|
||||
}
|
||||
if (isOp2Negated)
|
||||
assert(binOp->OperIs(GT_OR, GT_XOR, GT_AND));
|
||||
static_assert(AreContiguous(GT_OR, GT_XOR, GT_AND), "");
|
||||
constexpr genTreeOps singleBitOpers[] = {GT_BIT_SET, GT_BIT_INVERT, GT_BIT_CLEAR};
|
||||
binOp->ChangeOper(singleBitOpers[binOp->OperGet() - GT_OR]);
|
||||
|
||||
bit = BitOperations::Log2(bit);
|
||||
assert(bit >= 11); // smaller single-bit masks fit into ori/xori/andi
|
||||
constant->SetIntegralValue(bit);
|
||||
constant->SetContained();
|
||||
}
|
||||
}
|
||||
else // op2 is not constant
|
||||
{
|
||||
GenTree* opp1 = isOp1Negated ? op1->gtGetOp1() : op1;
|
||||
GenTree* opp2 = isOp2Negated ? op2->gtGetOp1() : op2;
|
||||
|
||||
bool isOp1SingleBit =
|
||||
(isOp1Negated == binOp->OperIs(GT_AND)) && opp1->OperIs(GT_LSH) && opp1->gtGetOp1()->IsIntegralConst(1);
|
||||
bool isOp2SingleBit =
|
||||
(isOp2Negated == binOp->OperIs(GT_AND)) && opp2->OperIs(GT_LSH) && opp2->gtGetOp1()->IsIntegralConst(1);
|
||||
|
||||
if (isOp1SingleBit || isOp2SingleBit)
|
||||
{
|
||||
// a | (1 << b), a ^ (1 << b), a & ~(1 << b) => BIT_{SET,INVERT,CLEAR}(a, b)
|
||||
|
||||
if (isOp1SingleBit)
|
||||
std::swap(op1, op2);
|
||||
|
||||
if (binOp->OperIs(GT_AND))
|
||||
{
|
||||
assert(op2->OperIs(GT_NOT));
|
||||
BlockRange().Remove(op2);
|
||||
op2 = op2->AsUnOp()->gtGetOp1();
|
||||
op2 = op2->gtGetOp1();
|
||||
}
|
||||
|
||||
if (isOp1Negated != isOp2Negated)
|
||||
{
|
||||
assert(comp->compOpportunisticallyDependsOn(InstructionSet_Zbb));
|
||||
if (isOp1Negated)
|
||||
std::swap(op1, op2);
|
||||
assert(binOp->OperIs(GT_OR, GT_XOR, GT_AND));
|
||||
static_assert(AreContiguous(GT_OR, GT_XOR, GT_AND), "");
|
||||
constexpr genTreeOps singleBitOpers[] = {GT_BIT_SET, GT_BIT_INVERT, GT_BIT_CLEAR};
|
||||
binOp->ChangeOper(singleBitOpers[binOp->OperGet() - GT_OR]);
|
||||
|
||||
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);
|
||||
assert(op2->OperIs(GT_LSH));
|
||||
assert(op2->gtGetOp1()->IsIntegralConst(1));
|
||||
assert(!op2->gtGetOp2()->IsIntegralConst());
|
||||
BlockRange().Remove(op2->gtGetOp1());
|
||||
BlockRange().Remove(op2);
|
||||
op2 = op2->gtGetOp2(); // shift amount becomes bit index
|
||||
|
||||
GenTreeUnOp* negation = comp->gtNewOperNode(GT_NOT, binOp->gtType, binOp);
|
||||
BlockRange().InsertAfter(binOp, negation);
|
||||
use.ReplaceWith(negation);
|
||||
}
|
||||
else
|
||||
{
|
||||
binOp->SetUnusedValue();
|
||||
}
|
||||
assert(op1->TypeIs(TYP_INT, TYP_LONG));
|
||||
if (!op2->IsIntegralConst() && op1->TypeIs(TYP_INT))
|
||||
{
|
||||
// Zbs instructions don't have *w variants so wrap the bit index / shift amount to 0-31 manually
|
||||
GenTreeIntCon* mask = comp->gtNewIconNode(0x1F);
|
||||
mask->SetContained();
|
||||
BlockRange().InsertAfter(op2, mask);
|
||||
op2 = comp->gtNewOperNode(GT_AND, op2->TypeGet(), op2, mask);
|
||||
BlockRange().InsertAfter(mask, op2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContainCheckBinary(binOp);
|
||||
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));
|
||||
op2->ClearContained(); // negated binary instructions don't have immediate variants
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return binOp->gtNext;
|
||||
}
|
||||
|
|
|
@ -269,6 +269,9 @@ int LinearScan::BuildNode(GenTree* tree)
|
|||
case GT_SH3ADD_UW:
|
||||
case GT_ADD_UW:
|
||||
case GT_SLLI_UW:
|
||||
case GT_BIT_SET:
|
||||
case GT_BIT_CLEAR:
|
||||
case GT_BIT_INVERT:
|
||||
if (tree->OperIs(GT_ROR, GT_ROL) && !compiler->compOpportunisticallyDependsOn(InstructionSet_Zbb))
|
||||
buildInternalIntRegisterDefForNode(tree);
|
||||
srcCount = BuildBinaryUses(tree->AsOp());
|
||||
|
|
|
@ -306,6 +306,7 @@ namespace ILCompiler
|
|||
{
|
||||
public const int Zba = (1 << 0);
|
||||
public const int Zbb = (1 << 1);
|
||||
public const int Zbs = (1 << 2);
|
||||
|
||||
public static void AddToBuilder(InstructionSetSupportBuilder builder, int flags)
|
||||
{
|
||||
|
@ -313,6 +314,8 @@ namespace ILCompiler
|
|||
builder.AddSupportedInstructionSet("zba");
|
||||
if ((flags & Zbb) != 0)
|
||||
builder.AddSupportedInstructionSet("zbb");
|
||||
if ((flags & Zbs) != 0)
|
||||
builder.AddSupportedInstructionSet("zbs");
|
||||
}
|
||||
|
||||
public static int FromInstructionSet(InstructionSet instructionSet)
|
||||
|
@ -325,6 +328,7 @@ namespace ILCompiler
|
|||
// Optional ISAs - only available via opt-in or opportunistic light-up
|
||||
InstructionSet.RiscV64_Zba => Zba,
|
||||
InstructionSet.RiscV64_Zbb => Zbb,
|
||||
InstructionSet.RiscV64_Zbs => Zbs,
|
||||
|
||||
_ => throw new NotSupportedException(((InstructionSet_RiscV64)instructionSet).ToString())
|
||||
};
|
||||
|
|
|
@ -94,6 +94,7 @@ namespace Internal.ReadyToRunConstants
|
|||
Avx512Vp2intersect_VL=81,
|
||||
Avx512Vpopcntdq=82,
|
||||
Avx512Vpopcntdq_VL=83,
|
||||
Zbs=84,
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ namespace Internal.ReadyToRunConstants
|
|||
case InstructionSet.RiscV64_RiscV64Base: return ReadyToRunInstructionSet.RiscV64Base;
|
||||
case InstructionSet.RiscV64_Zba: return ReadyToRunInstructionSet.Zba;
|
||||
case InstructionSet.RiscV64_Zbb: return ReadyToRunInstructionSet.Zbb;
|
||||
case InstructionSet.RiscV64_Zbs: return ReadyToRunInstructionSet.Zbs;
|
||||
|
||||
default: throw new Exception("Unknown instruction set");
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ namespace Internal.JitInterface
|
|||
RiscV64_RiscV64Base = InstructionSet_RiscV64.RiscV64Base,
|
||||
RiscV64_Zba = InstructionSet_RiscV64.Zba,
|
||||
RiscV64_Zbb = InstructionSet_RiscV64.Zbb,
|
||||
RiscV64_Zbs = InstructionSet_RiscV64.Zbs,
|
||||
X64_X86Base = InstructionSet_X64.X86Base,
|
||||
X64_SSE42 = InstructionSet_X64.SSE42,
|
||||
X64_AVX = InstructionSet_X64.AVX,
|
||||
|
@ -181,6 +182,7 @@ namespace Internal.JitInterface
|
|||
RiscV64Base = 1,
|
||||
Zba = 2,
|
||||
Zbb = 3,
|
||||
Zbs = 4,
|
||||
}
|
||||
|
||||
public enum InstructionSet_X64
|
||||
|
@ -518,6 +520,8 @@ namespace Internal.JitInterface
|
|||
resultflags.AddInstructionSet(InstructionSet.RiscV64_RiscV64Base);
|
||||
if (resultflags.HasInstructionSet(InstructionSet.RiscV64_Zba))
|
||||
resultflags.AddInstructionSet(InstructionSet.RiscV64_RiscV64Base);
|
||||
if (resultflags.HasInstructionSet(InstructionSet.RiscV64_Zbs))
|
||||
resultflags.AddInstructionSet(InstructionSet.RiscV64_RiscV64Base);
|
||||
break;
|
||||
|
||||
case TargetArchitecture.X64:
|
||||
|
@ -794,6 +798,8 @@ namespace Internal.JitInterface
|
|||
resultflags.AddInstructionSet(InstructionSet.RiscV64_Zbb);
|
||||
if (resultflags.HasInstructionSet(InstructionSet.RiscV64_RiscV64Base))
|
||||
resultflags.AddInstructionSet(InstructionSet.RiscV64_Zba);
|
||||
if (resultflags.HasInstructionSet(InstructionSet.RiscV64_RiscV64Base))
|
||||
resultflags.AddInstructionSet(InstructionSet.RiscV64_Zbs);
|
||||
break;
|
||||
|
||||
case TargetArchitecture.X64:
|
||||
|
@ -1040,6 +1046,7 @@ namespace Internal.JitInterface
|
|||
yield return new InstructionSetInfo("base", "RiscV64Base", InstructionSet.RiscV64_RiscV64Base, true);
|
||||
yield return new InstructionSetInfo("zba", "Zba", InstructionSet.RiscV64_Zba, true);
|
||||
yield return new InstructionSetInfo("zbb", "Zbb", InstructionSet.RiscV64_Zbb, true);
|
||||
yield return new InstructionSetInfo("zbs", "Zbs", InstructionSet.RiscV64_Zbs, true);
|
||||
break;
|
||||
|
||||
case TargetArchitecture.X64:
|
||||
|
@ -1452,6 +1459,9 @@ namespace Internal.JitInterface
|
|||
case "Zbb":
|
||||
{ return InstructionSet.RiscV64_Zbb; }
|
||||
|
||||
case "Zbs":
|
||||
{ return InstructionSet.RiscV64_Zbs; }
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -259,9 +259,11 @@ definearch ,RiscV64 ,64Bit ,RiscV64, RiscV64
|
|||
instructionset ,RiscV64 ,RiscV64Base , ,56 ,RiscV64Base ,base
|
||||
instructionset ,RiscV64 ,Zba , ,57 ,Zba ,zba
|
||||
instructionset ,RiscV64 ,Zbb , ,58 ,Zbb ,zbb
|
||||
instructionset ,RiscV64 ,Zbs , ,84 ,Zbs ,zbs
|
||||
|
||||
implication ,RiscV64 ,Zbb ,RiscV64Base
|
||||
implication ,RiscV64 ,Zba ,RiscV64Base
|
||||
implication ,RiscV64 ,Zbs ,RiscV64Base
|
||||
|
||||
; ,name and aliases ,archs ,lower baselines included by implication
|
||||
instructionsetgroup ,x86-64 ,X64 X86 ,base
|
||||
|
|
|
@ -1416,6 +1416,11 @@ void EEJitManager::SetCpuInfo()
|
|||
{
|
||||
CPUCompileFlags.Set(InstructionSet_Zbb);
|
||||
}
|
||||
|
||||
if (((cpuFeatures & RiscV64IntrinsicConstants_Zbs) != 0) && CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_EnableRiscV64Zbs))
|
||||
{
|
||||
CPUCompileFlags.Set(InstructionSet_Zbs);
|
||||
}
|
||||
#endif
|
||||
|
||||
// These calls are very important as it ensures the flags are consistent with any
|
||||
|
|
|
@ -606,6 +606,11 @@ int minipal_getcpufeatures(void)
|
|||
{
|
||||
result |= RiscV64IntrinsicConstants_Zbb;
|
||||
}
|
||||
|
||||
if (pairs[0].value & RISCV_HWPROBE_EXT_ZBS)
|
||||
{
|
||||
result |= RiscV64IntrinsicConstants_Zbs;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAVE_HWPROBE_H
|
||||
|
|
|
@ -56,6 +56,7 @@ static_assert((1 << ARM64_ATOMICS_FEATURE_FLAG_BIT) == ARM64IntrinsicConstants_A
|
|||
#if defined(HOST_RISCV64)
|
||||
#define RiscV64IntrinsicConstants_Zba (1 << 0)
|
||||
#define RiscV64IntrinsicConstants_Zbb (1 << 1)
|
||||
#define RiscV64IntrinsicConstants_Zbs (1 << 2)
|
||||
#endif // HOST_RISCV64
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Xunit;
|
||||
|
||||
public static class SingleBit
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int Set(int a, int b) => a | (1 << b);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int SetSwap(int a, int b) => (1 << b) | a ;
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int Set10(int a) => a | (1 << 10);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int Set11(int a) => a | (1 << 11);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int Set31(int a) => a | (1 << 31);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int SetNegatedBit(int a, int b) => ~(1 << a) | (1 << b);
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int Clear(int a, int b) => a & ~(1 << b);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int ClearSwap(int a, int b) => ~(1 << b) & a;
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int Clear10(int a) => a & ~(1 << 10);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int Clear11(int a) => a & ~(1 << 11);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int Clear31(int a) => a & ~(1 << 31);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int ClearNegatedBit(int a, int b) => ~(1 << a) & ~(1 << b);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int ClearPositiveBit(int a, int b) => (1 << a) & ~(1 << b);
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int Invert(int a, int b) => a ^ (1 << b);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int InvertSwap(int a, int b) => (1 << b) ^ a;
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int Invert10(int a) => a ^ (1 << 10);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int Invert11(int a) => a ^ (1 << 11);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int Invert31(int a) => a ^ (1 << 31);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static int InvertNegatedBit(int a, int b) => ~(1 << a) ^ (1 << b);
|
||||
|
||||
[Fact]
|
||||
public static void Test()
|
||||
{
|
||||
Assert.Equal(0x12345478, Set(0x12345078, 10 + 32));
|
||||
Assert.Equal(0x12345878, Set(0x12345078, 11 + 32));
|
||||
Assert.Equal(0x12345478, SetSwap(0x12345078, 10 + 32));
|
||||
Assert.Equal(0x12345878, SetSwap(0x12345078, 11 + 32));
|
||||
Assert.Equal(0x12345478, Set10(0x12345078));
|
||||
Assert.Equal(0x12345878, Set11(0x12345078));
|
||||
Assert.Equal(int.MinValue, Set31(0));
|
||||
Assert.Equal(-1, SetNegatedBit(0, 0 + 32));
|
||||
|
||||
Assert.Equal(0x12345078, Clear(0x12345478, 10 + 32));
|
||||
Assert.Equal(0x12345078, Clear(0x12345878, 11 + 32));
|
||||
Assert.Equal(0x12345078, ClearSwap(0x12345478, 10 + 32));
|
||||
Assert.Equal(0x12345078, ClearSwap(0x12345878, 11 + 32));
|
||||
Assert.Equal(0x12345078, Clear10(0x12345478));
|
||||
Assert.Equal(0x12345078, Clear11(0x12345878));
|
||||
Assert.Equal(0, Clear31(int.MinValue));
|
||||
Assert.Equal(-4, ClearNegatedBit(0, 1 + 32));
|
||||
Assert.Equal(0, ClearPositiveBit(0, 0 + 32));
|
||||
|
||||
Assert.Equal(0x12345478, Invert(0x12345078, 10 + 32));
|
||||
Assert.Equal(0x12345078, Invert(0x12345478, 10 + 32));
|
||||
Assert.Equal(0x12345878, Invert(0x12345078, 11 + 32));
|
||||
Assert.Equal(0x12345078, Invert(0x12345878, 11 + 32));
|
||||
Assert.Equal(0x12345478, InvertSwap(0x12345078, 10 + 32));
|
||||
Assert.Equal(0x12345078, InvertSwap(0x12345478, 10 + 32));
|
||||
Assert.Equal(0x12345878, InvertSwap(0x12345078, 11 + 32));
|
||||
Assert.Equal(0x12345078, InvertSwap(0x12345878, 11 + 32));
|
||||
Assert.Equal(0x12345478, Invert10(0x12345078));
|
||||
Assert.Equal(0x12345078, Invert10(0x12345478));
|
||||
Assert.Equal(0x12345878, Invert11(0x12345078));
|
||||
Assert.Equal(0x12345078, Invert11(0x12345878));
|
||||
Assert.Equal(0, Invert31(int.MinValue));
|
||||
Assert.Equal(int.MinValue, Invert31(0));
|
||||
Assert.Equal(-1, InvertNegatedBit(0, 0 + 32));
|
||||
Assert.Equal(-4, InvertNegatedBit(0, 1 + 32));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<!-- Needed for CLRTestEnvironmentVariable -->
|
||||
<RequiresProcessIsolation>true</RequiresProcessIsolation>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<DebugType>None</DebugType>
|
||||
<Optimize>True</Optimize>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="SingleBit.cs"/>
|
||||
<CLRTestEnvironmentVariable Include="DOTNET_TieredCompilation" Value="0" />
|
||||
<CLRTestEnvironmentVariable Include="DOTNET_JITMinOpts" Value="0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
Reference in New Issue