From 3e6017a6333b8a191d8b16ef15968b3dade22d91 Mon Sep 17 00:00:00 2001 From: Julie Lee <63486087+JulieLeeMSFT@users.noreply.github.com> Date: Tue, 22 Mar 2022 10:32:48 -0700 Subject: [PATCH 01/15] Emit shlx, sarx, shrx: resolve merge conflict in emitxarch.cpp. --- src/coreclr/jit/codegenxarch.cpp | 31 +++++++++++++++++++++++++++++++ src/coreclr/jit/emitxarch.cpp | 26 +++++++++++++++++++++++++- src/coreclr/jit/instrsxarch.h | 3 +++ src/coreclr/jit/lsraxarch.cpp | 10 ++++++++++ 4 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index f495acf2c07e67..40f456fdc89d44 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -4384,6 +4384,7 @@ void CodeGen::genCodeForShift(GenTree* tree) int shiftByValue = (int)shiftBy->AsIntConCommon()->IconValue(); #if defined(TARGET_64BIT) + // Try to emit rorx if BMI2 is available instead of mov+rol // it makes sense only for 64bit integers if ((genActualType(targetType) == TYP_LONG) && (tree->GetRegNum() != operandReg) && @@ -4403,6 +4404,36 @@ void CodeGen::genCodeForShift(GenTree* tree) inst_RV_SH(ins, size, tree->GetRegNum(), shiftByValue); } } +#if defined(TARGET_64BIT) + else if (compiler->compOpportunisticallyDependsOn(InstructionSet_BMI2) && tree->OperIs(GT_LSH, GT_RSH, GT_RSZ)) + { + // Try to emit shlx, sarx, shrx if BMI2 is available instead of mov+shl, mov+sar, mov+shr. + switch (tree->OperGet()) + { + case GT_LSH: + ins = INS_shlx; + break; + + case GT_RSH: + ins = INS_sarx; + break; + + case GT_RSZ: + ins = INS_shrx; + break; + + default: + unreached(); + } + + regNumber shiftByReg = shiftBy->GetRegNum(); + emitAttr size = emitTypeSize(tree); + + GetEmitter()->emitIns_R_R_R(ins, size, tree->GetRegNum(), operandReg, shiftByReg); + genProduceReg(tree); + return; + } +#endif else { // We must have the number of bits to shift stored in ECX, since we constrained this node to diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index ebe6030a8d7093..e177c420dc164b 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -749,6 +749,9 @@ bool emitter::TakesRexWPrefix(instruction ins, emitAttr attr) case INS_pdep: case INS_pext: case INS_rorx: + case INS_shlx: + case INS_sarx: + case INS_shrx: return true; default: return false; @@ -987,17 +990,25 @@ unsigned emitter::emitOutputRexOrVexPrefixIfNeeded(instruction ins, BYTE* dst, c case INS_rorx: case INS_pdep: case INS_mulx: + case INS_shrx: { vexPrefix |= 0x03; break; } case INS_pext: + case INS_sarx: { vexPrefix |= 0x02; break; } + case INS_shlx: + { + vexPrefix |= 0x01; + break; + } + default: { vexPrefix |= 0x00; @@ -1484,6 +1495,9 @@ bool emitter::emitInsCanOnlyWriteSSE2OrAVXReg(instrDesc* id) case INS_pextrw: case INS_pextrw_sse41: case INS_rorx: + case INS_shlx: + case INS_sarx: + case INS_shrx: { // These SSE instructions write to a general purpose integer register. return false; @@ -9519,7 +9533,7 @@ void emitter::emitDispIns( assert(IsThreeOperandAVXInstruction(ins)); regNumber reg2 = id->idReg2(); regNumber reg3 = id->idReg3(); - if (ins == INS_bextr || ins == INS_bzhi) + if (ins == INS_bextr || ins == INS_bzhi || ins == INS_shrx || ins == INS_shlx || ins == INS_sarx) { // BMI bextr and bzhi encodes the reg2 in VEX.vvvv and reg3 in modRM, // which is different from most of other instructions @@ -10314,6 +10328,7 @@ BYTE* emitter::emitOutputAM(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc) // For this format, moves do not support a third operand, so we only need to handle the binary ops. if (TakesVexPrefix(ins)) { + if (IsDstDstSrcAVXInstruction(ins)) { regNumber src1 = REG_NA; @@ -16323,6 +16338,15 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins break; } + case INS_shlx: + case INS_sarx: + case INS_shrx: + { + result.insLatency = PERFSCORE_LATENCY_1C; + result.insThroughput = PERFSCORE_THROUGHPUT_1C; + break; + } + default: // unhandled instruction insFmt combination perfScoreUnhandledInstruction(id, &result); diff --git a/src/coreclr/jit/instrsxarch.h b/src/coreclr/jit/instrsxarch.h index 5a626ce26e96ec..eb3fcc9ad626b4 100644 --- a/src/coreclr/jit/instrsxarch.h +++ b/src/coreclr/jit/instrsxarch.h @@ -605,6 +605,9 @@ INST3(pdep, "pdep", IUM_WR, BAD_CODE, BAD_CODE, INST3(pext, "pext", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF5), INS_Flags_IsDstDstSrcAVXInstruction) // Parallel Bits Extract INST3(bzhi, "bzhi", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF5), Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_Flags_IsDstDstSrcAVXInstruction) // Zero High Bits Starting with Specified Bit Position INST3(mulx, "mulx", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF6), INS_Flags_IsDstDstSrcAVXInstruction) // Unsigned Multiply Without Affecting Flags +INST3(shlx, "shlx", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF7), INS_Flags_IsDstSrcSrcAVXInstruction) // Shift Logical Left Without Affecting Flags +INST3(sarx, "sarx", IUM_WR, BAD_CODE, BAD_CODE, PACK4(0xF3, 0x0F, 0x38, 0xF7), INS_Flags_IsDstSrcSrcAVXInstruction) // Shift Arithmetic Right Without Affecting Flags +INST3(shrx, "shrx", IUM_WR, BAD_CODE, BAD_CODE, PACK4(0xF2, 0x0F, 0x38, 0xF7), INS_Flags_IsDstSrcSrcAVXInstruction) // Shift Logical Right Without Affecting Flags INST3(LAST_BMI_INSTRUCTION, "LAST_BMI_INSTRUCTION", IUM_WR, BAD_CODE, BAD_CODE, BAD_CODE, INS_FLAGS_None) diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index 4f7cde2cc62f65..62719e5d32d6ad 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -925,6 +925,16 @@ int LinearScan::BuildShiftRotate(GenTree* tree) { assert(shiftBy->OperIsConst()); } +#if defined(TARGET_64BIT) + else if (compiler->compOpportunisticallyDependsOn(InstructionSet_BMI2) && tree->OperIs(GT_LSH, GT_RSH, GT_RSZ) && + !tree->isContained()) + { + srcCount += BuildOperandUses(source, srcCandidates); + srcCount += BuildOperandUses(shiftBy, srcCandidates); + BuildDef(tree, dstCandidates); + return srcCount; + } +#endif else { srcCandidates = allRegs(TYP_INT) & ~RBM_RCX; From 4a7b4043e558c70d8283b91a19db6560be62b900 Mon Sep 17 00:00:00 2001 From: Julie Lee <63486087+JulieLeeMSFT@users.noreply.github.com> Date: Fri, 25 Mar 2022 12:13:36 -0700 Subject: [PATCH 02/15] Shrx: Swap operand and ShiftBy registers to swap rcx and rdx in correct order. --- src/coreclr/jit/codegenxarch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 40f456fdc89d44..e41c3c8fb558bb 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -4429,7 +4429,7 @@ void CodeGen::genCodeForShift(GenTree* tree) regNumber shiftByReg = shiftBy->GetRegNum(); emitAttr size = emitTypeSize(tree); - GetEmitter()->emitIns_R_R_R(ins, size, tree->GetRegNum(), operandReg, shiftByReg); + GetEmitter()->emitIns_R_R_R(ins, size, tree->GetRegNum(), shiftByReg, operandReg); genProduceReg(tree); return; } From 23395b38cf169d5bc5d5a3763e95a0cb8e18e7b1 Mon Sep 17 00:00:00 2001 From: Julie Lee <63486087+JulieLeeMSFT@users.noreply.github.com> Date: Fri, 25 Mar 2022 17:27:24 -0700 Subject: [PATCH 03/15] Change only TYP_LONG to Shlx, Sarx and Shrx. --- src/coreclr/jit/codegenxarch.cpp | 7 ++++--- src/coreclr/jit/emitxarch.cpp | 21 +++++++++++++++++++++ src/coreclr/jit/emitxarch.h | 3 +++ src/coreclr/jit/instrsxarch.h | 6 +++--- src/coreclr/jit/lowerxarch.cpp | 4 ++-- src/coreclr/jit/lsraxarch.cpp | 2 +- 6 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index e41c3c8fb558bb..a6966dbc25d29c 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -4341,7 +4341,7 @@ instruction CodeGen::genGetInsForOper(genTreeOps oper, var_types type) // tree - the bit shift node (that specifies the type of bit shift to perform). // // Assumptions: -// a) All GenTrees are register allocated. +// a) All GenTrees are register allocated or source operand in tree->AsOp()->gtOp1 is contained memory address. // b) The shift-by-amount in tree->AsOp()->gtOp2 is either a contained constant or // it's a register-allocated expression. If it is in a register that is // not RCX, it will be moved to RCX (so RCX better not be in use!). @@ -4405,7 +4405,8 @@ void CodeGen::genCodeForShift(GenTree* tree) } } #if defined(TARGET_64BIT) - else if (compiler->compOpportunisticallyDependsOn(InstructionSet_BMI2) && tree->OperIs(GT_LSH, GT_RSH, GT_RSZ)) + else if (compiler->compOpportunisticallyDependsOn(InstructionSet_BMI2) && tree->OperIs(GT_LSH, GT_RSH, GT_RSZ) && + (genActualType(targetType) == TYP_LONG)) { // Try to emit shlx, sarx, shrx if BMI2 is available instead of mov+shl, mov+sar, mov+shr. switch (tree->OperGet()) @@ -4428,9 +4429,9 @@ void CodeGen::genCodeForShift(GenTree* tree) regNumber shiftByReg = shiftBy->GetRegNum(); emitAttr size = emitTypeSize(tree); - GetEmitter()->emitIns_R_R_R(ins, size, tree->GetRegNum(), shiftByReg, operandReg); genProduceReg(tree); + return; } #endif diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index e177c420dc164b..c28847419d1120 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -5086,6 +5086,27 @@ void emitter::emitIns_R_R_A(instruction ins, emitAttr attr, regNumber reg1, regN emitCurIGsize += sz; } +void emitter::emitIns_R_A_R(instruction ins, emitAttr attr, regNumber reg1, GenTreeIndir* indir, regNumber reg2) +{ + assert(IsSSEOrAVXInstruction(ins)); + assert(IsThreeOperandAVXInstruction(ins)); + + ssize_t offs = indir->Offset(); + instrDesc* id = emitNewInstrAmd(attr, offs); + + id->idIns(ins); + id->idReg1(reg1); + id->idReg2(reg2); + + emitHandleMemOp(indir, id, IF_RWR_RRD_ARD, ins); + + UNATIVE_OFFSET sz = emitInsSizeAM(id, insCodeRM(ins)); + id->idCodeSize(sz); + + dispIns(id); + emitCurIGsize += sz; +} + void emitter::emitIns_R_R_AR(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, regNumber base, int offs) { assert(IsSSEOrAVXInstruction(ins)); diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h index 3f1efdaac5e52b..552bf469f83154 100644 --- a/src/coreclr/jit/emitxarch.h +++ b/src/coreclr/jit/emitxarch.h @@ -347,6 +347,8 @@ void emitIns_R_S_I(instruction ins, emitAttr attr, regNumber reg1, int varx, int void emitIns_R_R_A(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, GenTreeIndir* indir); +void emitIns_R_A_R(instruction ins, emitAttr attr, regNumber reg1, GenTreeIndir* indir, regNumber reg2); + void emitIns_R_R_AR(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, regNumber base, int offs); void emitIns_R_AR_R(instruction ins, @@ -461,6 +463,7 @@ void emitIns_AX_R(instruction ins, emitAttr attr, regNumber ireg, regNumber reg, void emitIns_SIMD_R_R_I(instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, int ival); void emitIns_SIMD_R_R_A(instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, GenTreeIndir* indir); +void emitIns_SIMD_R_A_R(instruction ins, emitAttr attr, regNumber targetReg, GenTreeIndir* indir, regNumber op2Reg); void emitIns_SIMD_R_R_AR( instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, regNumber base, int offset); void emitIns_SIMD_R_R_C( diff --git a/src/coreclr/jit/instrsxarch.h b/src/coreclr/jit/instrsxarch.h index eb3fcc9ad626b4..06dd9ef0d2a505 100644 --- a/src/coreclr/jit/instrsxarch.h +++ b/src/coreclr/jit/instrsxarch.h @@ -605,9 +605,9 @@ INST3(pdep, "pdep", IUM_WR, BAD_CODE, BAD_CODE, INST3(pext, "pext", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF5), INS_Flags_IsDstDstSrcAVXInstruction) // Parallel Bits Extract INST3(bzhi, "bzhi", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF5), Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_Flags_IsDstDstSrcAVXInstruction) // Zero High Bits Starting with Specified Bit Position INST3(mulx, "mulx", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF6), INS_Flags_IsDstDstSrcAVXInstruction) // Unsigned Multiply Without Affecting Flags -INST3(shlx, "shlx", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF7), INS_Flags_IsDstSrcSrcAVXInstruction) // Shift Logical Left Without Affecting Flags -INST3(sarx, "sarx", IUM_WR, BAD_CODE, BAD_CODE, PACK4(0xF3, 0x0F, 0x38, 0xF7), INS_Flags_IsDstSrcSrcAVXInstruction) // Shift Arithmetic Right Without Affecting Flags -INST3(shrx, "shrx", IUM_WR, BAD_CODE, BAD_CODE, PACK4(0xF2, 0x0F, 0x38, 0xF7), INS_Flags_IsDstSrcSrcAVXInstruction) // Shift Logical Right Without Affecting Flags +INST3(shlx, "shlx", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF7), INS_Flags_IsDstDstSrcAVXInstruction) // Shift Logical Left Without Affecting Flags +INST3(sarx, "sarx", IUM_WR, BAD_CODE, BAD_CODE, PACK4(0xF3, 0x0F, 0x38, 0xF7), INS_Flags_IsDstDstSrcAVXInstruction) // Shift Arithmetic Right Without Affecting Flags +INST3(shrx, "shrx", IUM_WR, BAD_CODE, BAD_CODE, PACK4(0xF2, 0x0F, 0x38, 0xF7), INS_Flags_IsDstDstSrcAVXInstruction) // Shift Logical Right Without Affecting Flags INST3(LAST_BMI_INSTRUCTION, "LAST_BMI_INSTRUCTION", IUM_WR, BAD_CODE, BAD_CODE, BAD_CODE, INS_FLAGS_None) diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index ee94c6d9042ddf..7d919e463371ed 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -4843,14 +4843,14 @@ void Lowering::ContainCheckDivOrMod(GenTreeOp* node) void Lowering::ContainCheckShiftRotate(GenTreeOp* node) { assert(node->OperIsShiftOrRotate()); -#ifdef TARGET_X86 +#if defined(TARGET_X86) GenTree* source = node->gtOp1; if (node->OperIsShiftLong()) { assert(source->OperGet() == GT_LONG); MakeSrcContained(node, source); } -#endif // !TARGET_X86 +#endif GenTree* shiftBy = node->gtOp2; if (IsContainableImmed(node, shiftBy) && (shiftBy->AsIntConCommon()->IconValue() <= 255) && diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index 62719e5d32d6ad..cf2905dc8693dd 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -927,7 +927,7 @@ int LinearScan::BuildShiftRotate(GenTree* tree) } #if defined(TARGET_64BIT) else if (compiler->compOpportunisticallyDependsOn(InstructionSet_BMI2) && tree->OperIs(GT_LSH, GT_RSH, GT_RSZ) && - !tree->isContained()) + !tree->isContained() && (genActualType(tree->TypeGet()) == TYP_LONG)) { srcCount += BuildOperandUses(source, srcCandidates); srcCount += BuildOperandUses(shiftBy, srcCandidates); From 9c07d03294b363f436fd20953429e1bb013d9112 Mon Sep 17 00:00:00 2001 From: Julie Lee <63486087+JulieLeeMSFT@users.noreply.github.com> Date: Fri, 25 Mar 2022 17:35:23 -0700 Subject: [PATCH 04/15] Shlx, Sarx, Shrx test cases --- src/tests/JIT/SIMD/ShiftOperations.cs | 485 ++++++++++++++++++++++ src/tests/JIT/SIMD/ShiftOperations.csproj | 12 + 2 files changed, 497 insertions(+) create mode 100644 src/tests/JIT/SIMD/ShiftOperations.cs create mode 100644 src/tests/JIT/SIMD/ShiftOperations.csproj diff --git a/src/tests/JIT/SIMD/ShiftOperations.cs b/src/tests/JIT/SIMD/ShiftOperations.cs new file mode 100644 index 00000000000000..9893ee53ed02ad --- /dev/null +++ b/src/tests/JIT/SIMD/ShiftOperations.cs @@ -0,0 +1,485 @@ +using System; +using System.Runtime.CompilerServices; +using System.Numerics; + +public class Test +{ + + [MethodImpl(MethodImplOptions.NoInlining)] + private static uint Shl(uint x, int y) => x<< y; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static int Sar(int x, int y) => x >> y; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static uint Shr(uint x, int y) => x >> y; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static uint Ror(uint x) => BitOperations.RotateRight(x, 2); + + [MethodImpl(MethodImplOptions.NoInlining)] + private static ulong Shlx(ulong x, int y) => x << y; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static long Sarx(long x, int y) => x >> y; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static ulong Shrx(ulong x, int y) => x >> y; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static unsafe ulong ShrxRef(ulong *x, int y) => *x >> y; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static ulong Rorx(ulong x) => BitOperations.RotateRight(x, 2); + +public static unsafe int Main() +{ + try + { + uint valUInt = 0xFFFFFFFE; + int valInt = 8; + ulong valULong = 8; + long valLong = 8; + int shiftBy = 1; + uint resUInt = 0; + int resInt = 0; + ulong resULong = 0; + long resLong = 0; + uint expectedUInt = 0; + int expectedInt = 0; + ulong expectedULong = 0; + long expectedLong = 0; + int MOD32 = 32; + int MOD64 = 64; + + // + // shl tests + // + valUInt = 0; + shiftBy = 1; + resUInt = Shl(valUInt, shiftBy); + expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shl({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + valUInt = 8; + shiftBy = 1; + resUInt = Shl(valUInt, shiftBy); + expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shl({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + valUInt = 1; + shiftBy = 31; + resUInt = Shl(valUInt, shiftBy); + expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shl({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + valUInt = 1; + shiftBy = 33; + resUInt = Shl(valUInt, shiftBy); + expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shl({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + valUInt = 0xFFFFFFFF; + shiftBy = 1; + resUInt = Shl(valUInt, shiftBy); + expectedUInt = (uint) (valUInt * Math.Pow(2, shiftBy)); + Console.Write("UnitTest Shl({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + // + // sar tests + // + valInt = 0; + shiftBy = 1; + resInt = Sar(valInt, shiftBy); + expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Sar({0},{1}): {2}", valInt, shiftBy, resInt); + if (resInt != expectedInt) + { + Console.Write(" != {0} Failed.\n", expectedInt); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedInt); + + valInt = -8; + shiftBy = 1; + resInt = Sar(valInt, shiftBy); + expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Sar({0},{1}): {2}", valInt, shiftBy, resInt); + if (resInt != expectedInt) + { + Console.Write(" != {0} Failed.\n", expectedInt); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedInt); + + valInt = 1; + shiftBy = 33; + resInt = Sar(valInt, shiftBy); + expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Sar({0},{1}): {2}", valInt, shiftBy, resInt); + if (resInt != expectedInt) + { + Console.Write(" != {0} Failed.\n", expectedInt); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedInt); + + valInt = 0x7FFFFFFF; + shiftBy = 33; + resInt = Sar(valInt, shiftBy); + expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Sar({0},{1}): {2}", valInt, shiftBy, resInt); + if (resInt != expectedInt) + { + Console.Write(" != {0} Failed.\n", expectedInt); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedInt); + + valInt = 0x7FFFFFFF; + shiftBy = 30; + resInt = Sar(valInt, shiftBy); + expectedInt = (int) (valInt / Math.Pow(2, shiftBy)); + Console.Write("UnitTest Sar({0},{1}): {2}", valInt, shiftBy, resInt); + if (resInt != expectedInt) + { + Console.Write(" != {0} Failed.\n", expectedInt); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedInt); + + // + // shr tests + // + valUInt = 1; + shiftBy = 1; + resUInt = Shr(valUInt, shiftBy); + expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shr({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + valUInt = 8; + shiftBy = 2; + resUInt = Shr(valUInt, shiftBy); + expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shr({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + valUInt = 1; + shiftBy = 33; + resUInt = Shr(valUInt, shiftBy); + expectedUInt = (uint) (valUInt / Math.Pow(2, shiftBy)); + Console.Write("UnitTest Shr({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + valUInt = 0xFFFFFFFF; + shiftBy = 31; + resUInt = Shr(valUInt, shiftBy); + expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shr({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + valUInt = 0xFFFFFFFF; + shiftBy = 33; + resUInt = Shr(valUInt, shiftBy); + expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shr({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + // + // Ror tests + // + valUInt = 0xFF; + shiftBy = 2; + resUInt = Ror(valUInt); + Console.Write("UnitTest Ror({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != 0xC000003F) + { + Console.Write(" Failed.\n"); + return 101; + } + Console.Write(" Passed.\n"); + + // + // Shlx tests + // + valULong = 0; + shiftBy = 1; + resULong = Shlx(valULong, shiftBy); + expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Shlx({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + valULong = 8; + shiftBy = 1; + resULong = Shlx(valULong, shiftBy); + expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Shlx({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + valULong = 1; + shiftBy = 31; + resULong = Shlx(valULong, shiftBy); + expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Shlx({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + valULong = 1; + shiftBy = 33; + resULong = Shlx(valULong, shiftBy); + expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Shlx({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + valULong = 0xFFFFFFFF; + shiftBy = 1; + resULong = Shlx(valULong, shiftBy); + expectedULong = (ulong) (valULong * Math.Pow(2, shiftBy)); + Console.Write("UnitTest Shlx({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + // + // Sarx tests + // + valLong = 1; + shiftBy = 1; + resLong = Sarx(valLong, shiftBy); + expectedLong = (long) (valLong / Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Sarx({0},{1}): {2}", valLong, shiftBy, resLong); + if (resLong != expectedLong) + { + Console.Write(" != {0} Failed.\n", expectedLong); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedLong); + + valLong = -8; + shiftBy = 1; + resLong = Sarx(valLong, shiftBy); + expectedLong = (long) (valLong / Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Sarx({0},{1}): {2}", valLong, shiftBy, resLong); + if (resLong != expectedLong) + { + Console.Write(" != {0} Failed.\n", expectedLong); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedLong); + + valLong = -8; + shiftBy = 65; + resLong = Sarx(valLong, shiftBy); + expectedLong = (long) (valLong / Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Sarx({0},{1}): {2}", valLong, shiftBy, resLong); + if (resLong != expectedLong) + { + Console.Write(" != {0} Failed.\n", expectedLong); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedLong); + + valLong = 0x7FFFFFFFFFFFFFFF; + shiftBy = 63; + resLong = Sarx(valLong, shiftBy); + expectedLong = 0; + Console.Write("UnitTest Sarx({0},{1}): {2}", valLong, shiftBy, resLong); + if (resLong != 0) + { + Console.Write(" != {0} Failed.\n", expectedLong); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedLong); + + valLong = 0x7FFFFFFFFFFFFFFF; + shiftBy = 65; + resLong = Sarx(valLong, shiftBy); + expectedLong = 0x3FFFFFFFFFFFFFFF; + Console.Write("UnitTest Sarx({0},{1}): {2}", valLong, shiftBy, resLong); + if (resLong != expectedLong) + { + Console.Write(" != {0} Failed.\n", expectedLong); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedLong); + + // + // Shrx tests + // + valULong = 1; + shiftBy = 1; + resULong = Shrx(valULong, shiftBy); + expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Shrx({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + valULong = 8; + shiftBy = 2; + resULong = Shrx(valULong, shiftBy); + expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Shrx({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + valULong = 0xFFFFFFFFFFFFFFFF; + shiftBy = 63; + resULong = Shrx(valULong, shiftBy); + expectedULong = 1; + Console.Write("UnitTest Shrx({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + valULong = 0x7FFFFFFFFFFFFFFF; + shiftBy = 65; + resULong = Shrx(valULong, shiftBy); + expectedULong = 0x3FFFFFFFFFFFFFFF; + Console.Write("UnitTest Shrx({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + valULong = 8; + shiftBy = 65; + resULong = Shrx(valULong, shiftBy); + expectedULong = 4; + Console.Write("UnitTest Shrx({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + // + // ShrxRef + // + valULong = 8; + shiftBy = 1; + resULong = ShrxRef(&valULong, shiftBy); + expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest ShrxRef({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + return 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + // + // Rorx tests + // + valULong = 0xFF; + shiftBy = 2; + resULong = Rorx(valULong); + Console.Write("UnitTest Rorx({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != 0xC00000000000003F) + { + Console.Write(" Failed.\n"); + return 101; + } + Console.Write(" Passed.\n"); + + } + catch (Exception e) + { + Console.WriteLine(e.Message); + return 101; + } + Console.WriteLine("PASSED"); + return 100; +} +} \ No newline at end of file diff --git a/src/tests/JIT/SIMD/ShiftOperations.csproj b/src/tests/JIT/SIMD/ShiftOperations.csproj new file mode 100644 index 00000000000000..5e5fbae5cb863b --- /dev/null +++ b/src/tests/JIT/SIMD/ShiftOperations.csproj @@ -0,0 +1,12 @@ + + + Exe + + + PdbOnly + True + + + + + From adbfc48f2b8c68b5e87a0592c6d09468a419c726 Mon Sep 17 00:00:00 2001 From: Julie Lee <63486087+JulieLeeMSFT@users.noreply.github.com> Date: Fri, 25 Mar 2022 18:05:37 -0700 Subject: [PATCH 05/15] Shlx, sarx, shrx code clean-up --- src/coreclr/jit/codegenxarch.cpp | 2 +- src/coreclr/jit/emitxarch.cpp | 25 ++----------------------- src/coreclr/jit/emitxarch.h | 3 --- src/coreclr/jit/lowerxarch.cpp | 2 +- 4 files changed, 4 insertions(+), 28 deletions(-) diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index a6966dbc25d29c..cab21cbc112a37 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -4341,7 +4341,7 @@ instruction CodeGen::genGetInsForOper(genTreeOps oper, var_types type) // tree - the bit shift node (that specifies the type of bit shift to perform). // // Assumptions: -// a) All GenTrees are register allocated or source operand in tree->AsOp()->gtOp1 is contained memory address. +// a) All GenTrees are register allocated. // b) The shift-by-amount in tree->AsOp()->gtOp2 is either a contained constant or // it's a register-allocated expression. If it is in a register that is // not RCX, it will be moved to RCX (so RCX better not be in use!). diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index c28847419d1120..0d88485d082b55 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -5086,27 +5086,6 @@ void emitter::emitIns_R_R_A(instruction ins, emitAttr attr, regNumber reg1, regN emitCurIGsize += sz; } -void emitter::emitIns_R_A_R(instruction ins, emitAttr attr, regNumber reg1, GenTreeIndir* indir, regNumber reg2) -{ - assert(IsSSEOrAVXInstruction(ins)); - assert(IsThreeOperandAVXInstruction(ins)); - - ssize_t offs = indir->Offset(); - instrDesc* id = emitNewInstrAmd(attr, offs); - - id->idIns(ins); - id->idReg1(reg1); - id->idReg2(reg2); - - emitHandleMemOp(indir, id, IF_RWR_RRD_ARD, ins); - - UNATIVE_OFFSET sz = emitInsSizeAM(id, insCodeRM(ins)); - id->idCodeSize(sz); - - dispIns(id); - emitCurIGsize += sz; -} - void emitter::emitIns_R_R_AR(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, regNumber base, int offs) { assert(IsSSEOrAVXInstruction(ins)); @@ -16363,8 +16342,8 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins case INS_sarx: case INS_shrx: { - result.insLatency = PERFSCORE_LATENCY_1C; - result.insThroughput = PERFSCORE_THROUGHPUT_1C; + result.insLatency = PERFSCORE_LATENCY_2C; + result.insThroughput = PERFSCORE_THROUGHPUT_2X; break; } diff --git a/src/coreclr/jit/emitxarch.h b/src/coreclr/jit/emitxarch.h index 552bf469f83154..3f1efdaac5e52b 100644 --- a/src/coreclr/jit/emitxarch.h +++ b/src/coreclr/jit/emitxarch.h @@ -347,8 +347,6 @@ void emitIns_R_S_I(instruction ins, emitAttr attr, regNumber reg1, int varx, int void emitIns_R_R_A(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, GenTreeIndir* indir); -void emitIns_R_A_R(instruction ins, emitAttr attr, regNumber reg1, GenTreeIndir* indir, regNumber reg2); - void emitIns_R_R_AR(instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, regNumber base, int offs); void emitIns_R_AR_R(instruction ins, @@ -463,7 +461,6 @@ void emitIns_AX_R(instruction ins, emitAttr attr, regNumber ireg, regNumber reg, void emitIns_SIMD_R_R_I(instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, int ival); void emitIns_SIMD_R_R_A(instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, GenTreeIndir* indir); -void emitIns_SIMD_R_A_R(instruction ins, emitAttr attr, regNumber targetReg, GenTreeIndir* indir, regNumber op2Reg); void emitIns_SIMD_R_R_AR( instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, regNumber base, int offset); void emitIns_SIMD_R_R_C( diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 7d919e463371ed..d33fbc66fd44c9 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -4850,7 +4850,7 @@ void Lowering::ContainCheckShiftRotate(GenTreeOp* node) assert(source->OperGet() == GT_LONG); MakeSrcContained(node, source); } -#endif +#endif // !TARGET_X86 GenTree* shiftBy = node->gtOp2; if (IsContainableImmed(node, shiftBy) && (shiftBy->AsIntConCommon()->IconValue() <= 255) && From 53181e48feabe1fadf929f4c9e9340b633d5b04b Mon Sep 17 00:00:00 2001 From: Julie Lee <63486087+JulieLeeMSFT@users.noreply.github.com> Date: Tue, 29 Mar 2022 13:04:02 -0700 Subject: [PATCH 06/15] Emit shlx, sarx, shrx: Enable 32 bit shifts and addressed feedback. --- src/coreclr/jit/codegenxarch.cpp | 3 +- src/coreclr/jit/emitxarch.cpp | 2 +- src/coreclr/jit/lsraxarch.cpp | 4 +- src/tests/JIT/SIMD/ShiftOperations.cs | 184 +++++++++++----------- src/tests/JIT/SIMD/ShiftOperations.csproj | 1 + 5 files changed, 98 insertions(+), 96 deletions(-) diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index cab21cbc112a37..0d969b2fc4b163 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -4405,8 +4405,7 @@ void CodeGen::genCodeForShift(GenTree* tree) } } #if defined(TARGET_64BIT) - else if (compiler->compOpportunisticallyDependsOn(InstructionSet_BMI2) && tree->OperIs(GT_LSH, GT_RSH, GT_RSZ) && - (genActualType(targetType) == TYP_LONG)) + else if (tree->OperIsShift() && compiler->compOpportunisticallyDependsOn(InstructionSet_BMI2)) { // Try to emit shlx, sarx, shrx if BMI2 is available instead of mov+shl, mov+sar, mov+shr. switch (tree->OperGet()) diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index 0d88485d082b55..5ce70d652983bc 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -9535,7 +9535,7 @@ void emitter::emitDispIns( regNumber reg3 = id->idReg3(); if (ins == INS_bextr || ins == INS_bzhi || ins == INS_shrx || ins == INS_shlx || ins == INS_sarx) { - // BMI bextr and bzhi encodes the reg2 in VEX.vvvv and reg3 in modRM, + // BMI bextr,bzhi, shrx, shlx and sarx encode the reg2 in VEX.vvvv and reg3 in modRM, // which is different from most of other instructions regNumber tmp = reg2; reg2 = reg3; diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index cf2905dc8693dd..8ef1a739f3b29f 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -926,8 +926,8 @@ int LinearScan::BuildShiftRotate(GenTree* tree) assert(shiftBy->OperIsConst()); } #if defined(TARGET_64BIT) - else if (compiler->compOpportunisticallyDependsOn(InstructionSet_BMI2) && tree->OperIs(GT_LSH, GT_RSH, GT_RSZ) && - !tree->isContained() && (genActualType(tree->TypeGet()) == TYP_LONG)) + else if (tree->OperIsShift() && !tree->isContained() && + compiler->compOpportunisticallyDependsOn(InstructionSet_BMI2)) { srcCount += BuildOperandUses(source, srcCandidates); srcCount += BuildOperandUses(shiftBy, srcCandidates); diff --git a/src/tests/JIT/SIMD/ShiftOperations.cs b/src/tests/JIT/SIMD/ShiftOperations.cs index 9893ee53ed02ad..5f74fd949507e5 100644 --- a/src/tests/JIT/SIMD/ShiftOperations.cs +++ b/src/tests/JIT/SIMD/ShiftOperations.cs @@ -6,28 +6,28 @@ public class Test { [MethodImpl(MethodImplOptions.NoInlining)] - private static uint Shl(uint x, int y) => x<< y; + private static uint Shlx32bit(uint x, int y) => x<< y; [MethodImpl(MethodImplOptions.NoInlining)] - private static int Sar(int x, int y) => x >> y; + private static int Sarx32bit(int x, int y) => x >> y; [MethodImpl(MethodImplOptions.NoInlining)] - private static uint Shr(uint x, int y) => x >> y; + private static uint Shrx32bit(uint x, int y) => x >> y; [MethodImpl(MethodImplOptions.NoInlining)] private static uint Ror(uint x) => BitOperations.RotateRight(x, 2); [MethodImpl(MethodImplOptions.NoInlining)] - private static ulong Shlx(ulong x, int y) => x << y; + private static ulong Shlx64bit(ulong x, int y) => x << y; [MethodImpl(MethodImplOptions.NoInlining)] - private static long Sarx(long x, int y) => x >> y; + private static long Sarx64bit(long x, int y) => x >> y; [MethodImpl(MethodImplOptions.NoInlining)] - private static ulong Shrx(ulong x, int y) => x >> y; + private static ulong Shrx64bit(ulong x, int y) => x >> y; [MethodImpl(MethodImplOptions.NoInlining)] - private static unsafe ulong ShrxRef(ulong *x, int y) => *x >> y; + private static unsafe ulong ShrxRef64bit(ulong *x, int y) => *x >> y; [MethodImpl(MethodImplOptions.NoInlining)] private static ulong Rorx(ulong x) => BitOperations.RotateRight(x, 2); @@ -36,11 +36,11 @@ public static unsafe int Main() { try { - uint valUInt = 0xFFFFFFFE; - int valInt = 8; - ulong valULong = 8; - long valLong = 8; - int shiftBy = 1; + uint valUInt = 0; + int valInt = 0; + ulong valULong = 0; + long valLong = 0; + int shiftBy = 0; uint resUInt = 0; int resInt = 0; ulong resULong = 0; @@ -53,13 +53,13 @@ public static unsafe int Main() int MOD64 = 64; // - // shl tests + // Shlx32bit tests // valUInt = 0; shiftBy = 1; - resUInt = Shl(valUInt, shiftBy); + resUInt = Shlx32bit(valUInt, shiftBy); expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shl({0},{1}): {2}", valUInt, shiftBy, resUInt); + Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); if (resUInt != expectedUInt) { Console.Write(" != {0} Failed.\n", expectedUInt); @@ -69,9 +69,9 @@ public static unsafe int Main() valUInt = 8; shiftBy = 1; - resUInt = Shl(valUInt, shiftBy); + resUInt = Shlx32bit(valUInt, shiftBy); expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shl({0},{1}): {2}", valUInt, shiftBy, resUInt); + Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); if (resUInt != expectedUInt) { Console.Write(" != {0} Failed.\n", expectedUInt); @@ -81,9 +81,9 @@ public static unsafe int Main() valUInt = 1; shiftBy = 31; - resUInt = Shl(valUInt, shiftBy); + resUInt = Shlx32bit(valUInt, shiftBy); expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shl({0},{1}): {2}", valUInt, shiftBy, resUInt); + Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); if (resUInt != expectedUInt) { Console.Write(" != {0} Failed.\n", expectedUInt); @@ -93,9 +93,9 @@ public static unsafe int Main() valUInt = 1; shiftBy = 33; - resUInt = Shl(valUInt, shiftBy); + resUInt = Shlx32bit(valUInt, shiftBy); expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shl({0},{1}): {2}", valUInt, shiftBy, resUInt); + Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); if (resUInt != expectedUInt) { Console.Write(" != {0} Failed.\n", expectedUInt); @@ -105,9 +105,9 @@ public static unsafe int Main() valUInt = 0xFFFFFFFF; shiftBy = 1; - resUInt = Shl(valUInt, shiftBy); - expectedUInt = (uint) (valUInt * Math.Pow(2, shiftBy)); - Console.Write("UnitTest Shl({0},{1}): {2}", valUInt, shiftBy, resUInt); + resUInt = Shlx32bit(valUInt, shiftBy); + expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); if (resUInt != expectedUInt) { Console.Write(" != {0} Failed.\n", expectedUInt); @@ -116,13 +116,13 @@ public static unsafe int Main() Console.Write(" == {0} Passed.\n", expectedUInt); // - // sar tests + // Sarx32bit tests // valInt = 0; shiftBy = 1; - resInt = Sar(valInt, shiftBy); + resInt = Sarx32bit(valInt, shiftBy); expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Sar({0},{1}): {2}", valInt, shiftBy, resInt); + Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); if (resInt != expectedInt) { Console.Write(" != {0} Failed.\n", expectedInt); @@ -132,9 +132,9 @@ public static unsafe int Main() valInt = -8; shiftBy = 1; - resInt = Sar(valInt, shiftBy); + resInt = Sarx32bit(valInt, shiftBy); expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Sar({0},{1}): {2}", valInt, shiftBy, resInt); + Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); if (resInt != expectedInt) { Console.Write(" != {0} Failed.\n", expectedInt); @@ -144,9 +144,9 @@ public static unsafe int Main() valInt = 1; shiftBy = 33; - resInt = Sar(valInt, shiftBy); + resInt = Sarx32bit(valInt, shiftBy); expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Sar({0},{1}): {2}", valInt, shiftBy, resInt); + Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); if (resInt != expectedInt) { Console.Write(" != {0} Failed.\n", expectedInt); @@ -156,9 +156,9 @@ public static unsafe int Main() valInt = 0x7FFFFFFF; shiftBy = 33; - resInt = Sar(valInt, shiftBy); + resInt = Sarx32bit(valInt, shiftBy); expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Sar({0},{1}): {2}", valInt, shiftBy, resInt); + Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); if (resInt != expectedInt) { Console.Write(" != {0} Failed.\n", expectedInt); @@ -168,9 +168,9 @@ public static unsafe int Main() valInt = 0x7FFFFFFF; shiftBy = 30; - resInt = Sar(valInt, shiftBy); - expectedInt = (int) (valInt / Math.Pow(2, shiftBy)); - Console.Write("UnitTest Sar({0},{1}): {2}", valInt, shiftBy, resInt); + resInt = Sarx32bit(valInt, shiftBy); + expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); if (resInt != expectedInt) { Console.Write(" != {0} Failed.\n", expectedInt); @@ -179,13 +179,13 @@ public static unsafe int Main() Console.Write(" == {0} Passed.\n", expectedInt); // - // shr tests + // Shrx32bit tests // valUInt = 1; shiftBy = 1; - resUInt = Shr(valUInt, shiftBy); + resUInt = Shrx32bit(valUInt, shiftBy); expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shr({0},{1}): {2}", valUInt, shiftBy, resUInt); + Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); if (resUInt != expectedUInt) { Console.Write(" != {0} Failed.\n", expectedUInt); @@ -195,9 +195,9 @@ public static unsafe int Main() valUInt = 8; shiftBy = 2; - resUInt = Shr(valUInt, shiftBy); + resUInt = Shrx32bit(valUInt, shiftBy); expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shr({0},{1}): {2}", valUInt, shiftBy, resUInt); + Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); if (resUInt != expectedUInt) { Console.Write(" != {0} Failed.\n", expectedUInt); @@ -207,9 +207,9 @@ public static unsafe int Main() valUInt = 1; shiftBy = 33; - resUInt = Shr(valUInt, shiftBy); - expectedUInt = (uint) (valUInt / Math.Pow(2, shiftBy)); - Console.Write("UnitTest Shr({0},{1}): {2}", valUInt, shiftBy, resUInt); + resUInt = Shrx32bit(valUInt, shiftBy); + expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); if (resUInt != expectedUInt) { Console.Write(" != {0} Failed.\n", expectedUInt); @@ -219,9 +219,9 @@ public static unsafe int Main() valUInt = 0xFFFFFFFF; shiftBy = 31; - resUInt = Shr(valUInt, shiftBy); + resUInt = Shrx32bit(valUInt, shiftBy); expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shr({0},{1}): {2}", valUInt, shiftBy, resUInt); + Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); if (resUInt != expectedUInt) { Console.Write(" != {0} Failed.\n", expectedUInt); @@ -231,9 +231,9 @@ public static unsafe int Main() valUInt = 0xFFFFFFFF; shiftBy = 33; - resUInt = Shr(valUInt, shiftBy); + resUInt = Shrx32bit(valUInt, shiftBy); expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shr({0},{1}): {2}", valUInt, shiftBy, resUInt); + Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); if (resUInt != expectedUInt) { Console.Write(" != {0} Failed.\n", expectedUInt); @@ -256,13 +256,13 @@ public static unsafe int Main() Console.Write(" Passed.\n"); // - // Shlx tests + // Shlx64bit tests // valULong = 0; shiftBy = 1; - resULong = Shlx(valULong, shiftBy); + resULong = Shlx64bit(valULong, shiftBy); expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shlx({0},{1}): {2}", valULong, shiftBy, resULong); + Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); if (resULong != expectedULong) { Console.Write(" != {0} Failed.\n", expectedULong); @@ -272,9 +272,9 @@ public static unsafe int Main() valULong = 8; shiftBy = 1; - resULong = Shlx(valULong, shiftBy); + resULong = Shlx64bit(valULong, shiftBy); expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shlx({0},{1}): {2}", valULong, shiftBy, resULong); + Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); if (resULong != expectedULong) { Console.Write(" != {0} Failed.\n", expectedULong); @@ -283,10 +283,10 @@ public static unsafe int Main() Console.Write(" == {0} Passed.\n", expectedULong); valULong = 1; - shiftBy = 31; - resULong = Shlx(valULong, shiftBy); + shiftBy = 63; + resULong = Shlx64bit(valULong, shiftBy); expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shlx({0},{1}): {2}", valULong, shiftBy, resULong); + Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); if (resULong != expectedULong) { Console.Write(" != {0} Failed.\n", expectedULong); @@ -295,10 +295,10 @@ public static unsafe int Main() Console.Write(" == {0} Passed.\n", expectedULong); valULong = 1; - shiftBy = 33; - resULong = Shlx(valULong, shiftBy); + shiftBy = 65; + resULong = Shlx64bit(valULong, shiftBy); expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shlx({0},{1}): {2}", valULong, shiftBy, resULong); + Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); if (resULong != expectedULong) { Console.Write(" != {0} Failed.\n", expectedULong); @@ -306,11 +306,11 @@ public static unsafe int Main() } Console.Write(" == {0} Passed.\n", expectedULong); - valULong = 0xFFFFFFFF; + valULong = 0xFFFFFFFFFFFFFFFF; shiftBy = 1; - resULong = Shlx(valULong, shiftBy); - expectedULong = (ulong) (valULong * Math.Pow(2, shiftBy)); - Console.Write("UnitTest Shlx({0},{1}): {2}", valULong, shiftBy, resULong); + resULong = Shlx64bit(valULong, shiftBy); + expectedULong = (ulong) (valULong ^ 1); + Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); if (resULong != expectedULong) { Console.Write(" != {0} Failed.\n", expectedULong); @@ -319,13 +319,13 @@ public static unsafe int Main() Console.Write(" == {0} Passed.\n", expectedULong); // - // Sarx tests + // Sarx64bit tests // valLong = 1; shiftBy = 1; - resLong = Sarx(valLong, shiftBy); + resLong = Sarx64bit(valLong, shiftBy); expectedLong = (long) (valLong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Sarx({0},{1}): {2}", valLong, shiftBy, resLong); + Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); if (resLong != expectedLong) { Console.Write(" != {0} Failed.\n", expectedLong); @@ -335,9 +335,9 @@ public static unsafe int Main() valLong = -8; shiftBy = 1; - resLong = Sarx(valLong, shiftBy); + resLong = Sarx64bit(valLong, shiftBy); expectedLong = (long) (valLong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Sarx({0},{1}): {2}", valLong, shiftBy, resLong); + Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); if (resLong != expectedLong) { Console.Write(" != {0} Failed.\n", expectedLong); @@ -347,9 +347,9 @@ public static unsafe int Main() valLong = -8; shiftBy = 65; - resLong = Sarx(valLong, shiftBy); + resLong = Sarx64bit(valLong, shiftBy); expectedLong = (long) (valLong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Sarx({0},{1}): {2}", valLong, shiftBy, resLong); + Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); if (resLong != expectedLong) { Console.Write(" != {0} Failed.\n", expectedLong); @@ -359,10 +359,10 @@ public static unsafe int Main() valLong = 0x7FFFFFFFFFFFFFFF; shiftBy = 63; - resLong = Sarx(valLong, shiftBy); + resLong = Sarx64bit(valLong, shiftBy); expectedLong = 0; - Console.Write("UnitTest Sarx({0},{1}): {2}", valLong, shiftBy, resLong); - if (resLong != 0) + Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); + if (resLong != expectedLong) { Console.Write(" != {0} Failed.\n", expectedLong); return 101; @@ -371,9 +371,10 @@ public static unsafe int Main() valLong = 0x7FFFFFFFFFFFFFFF; shiftBy = 65; - resLong = Sarx(valLong, shiftBy); - expectedLong = 0x3FFFFFFFFFFFFFFF; - Console.Write("UnitTest Sarx({0},{1}): {2}", valLong, shiftBy, resLong); + shiftBy = (int) Math.Pow(2, (shiftBy % MOD64)); + resLong = Sarx64bit(valLong, shiftBy); + expectedLong = 0x1FFFFFFFFFFFFFFF; + Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); if (resLong != expectedLong) { Console.Write(" != {0} Failed.\n", expectedLong); @@ -382,13 +383,13 @@ public static unsafe int Main() Console.Write(" == {0} Passed.\n", expectedLong); // - // Shrx tests + // Shrx64bit tests // valULong = 1; shiftBy = 1; - resULong = Shrx(valULong, shiftBy); + resULong = Shrx64bit(valULong, shiftBy); expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shrx({0},{1}): {2}", valULong, shiftBy, resULong); + Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); if (resULong != expectedULong) { Console.Write(" != {0} Failed.\n", expectedULong); @@ -398,9 +399,9 @@ public static unsafe int Main() valULong = 8; shiftBy = 2; - resULong = Shrx(valULong, shiftBy); + resULong = Shrx64bit(valULong, shiftBy); expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shrx({0},{1}): {2}", valULong, shiftBy, resULong); + Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); if (resULong != expectedULong) { Console.Write(" != {0} Failed.\n", expectedULong); @@ -410,9 +411,9 @@ public static unsafe int Main() valULong = 0xFFFFFFFFFFFFFFFF; shiftBy = 63; - resULong = Shrx(valULong, shiftBy); + resULong = Shrx64bit(valULong, shiftBy); expectedULong = 1; - Console.Write("UnitTest Shrx({0},{1}): {2}", valULong, shiftBy, resULong); + Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); if (resULong != expectedULong) { Console.Write(" != {0} Failed.\n", expectedULong); @@ -422,9 +423,9 @@ public static unsafe int Main() valULong = 0x7FFFFFFFFFFFFFFF; shiftBy = 65; - resULong = Shrx(valULong, shiftBy); + resULong = Shrx64bit(valULong, shiftBy); expectedULong = 0x3FFFFFFFFFFFFFFF; - Console.Write("UnitTest Shrx({0},{1}): {2}", valULong, shiftBy, resULong); + Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); if (resULong != expectedULong) { Console.Write(" != {0} Failed.\n", expectedULong); @@ -434,9 +435,10 @@ public static unsafe int Main() valULong = 8; shiftBy = 65; - resULong = Shrx(valULong, shiftBy); - expectedULong = 4; - Console.Write("UnitTest Shrx({0},{1}): {2}", valULong, shiftBy, resULong); + resULong = Shrx64bit(valULong, shiftBy); + //expectedULong = 4; + expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); if (resULong != expectedULong) { Console.Write(" != {0} Failed.\n", expectedULong); @@ -445,13 +447,13 @@ public static unsafe int Main() Console.Write(" == {0} Passed.\n", expectedULong); // - // ShrxRef + // ShrxRef64bit // valULong = 8; shiftBy = 1; - resULong = ShrxRef(&valULong, shiftBy); + resULong = ShrxRef64bit(&valULong, shiftBy); expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest ShrxRef({0},{1}): {2}", valULong, shiftBy, resULong); + Console.Write("UnitTest ShrxRef64bit({0},{1}): {2}", valULong, shiftBy, resULong); if (resULong != expectedULong) { Console.Write(" != {0} Failed.\n", expectedULong); diff --git a/src/tests/JIT/SIMD/ShiftOperations.csproj b/src/tests/JIT/SIMD/ShiftOperations.csproj index 5e5fbae5cb863b..d7141b8f4b1601 100644 --- a/src/tests/JIT/SIMD/ShiftOperations.csproj +++ b/src/tests/JIT/SIMD/ShiftOperations.csproj @@ -1,6 +1,7 @@ Exe + true PdbOnly From 28438a0084147f5520880c4dee0d0a5aa961ef91 Mon Sep 17 00:00:00 2001 From: Julie Lee <63486087+JulieLeeMSFT@users.noreply.github.com> Date: Wed, 20 Apr 2022 16:19:57 -0700 Subject: [PATCH 07/15] [shlx/sarx/shrx] Addressed CR feedback. - Removed some tests from Arm/Arm64 due to a known undefined behavior - Excluded the test from Mono due to a known undefined behavior - Resolved merge conflict --- src/coreclr/jit/codegenxarch.cpp | 6 +- src/coreclr/jit/emitxarch.cpp | 18 +- src/coreclr/jit/lowerxarch.cpp | 2 +- src/tests/JIT/SIMD/ShiftOperations.cs | 946 +++++++++++++------------- src/tests/issues.targets | 5 +- 5 files changed, 500 insertions(+), 477 deletions(-) diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 0d969b2fc4b163..27688f5d9d4b24 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -4384,7 +4384,6 @@ void CodeGen::genCodeForShift(GenTree* tree) int shiftByValue = (int)shiftBy->AsIntConCommon()->IconValue(); #if defined(TARGET_64BIT) - // Try to emit rorx if BMI2 is available instead of mov+rol // it makes sense only for 64bit integers if ((genActualType(targetType) == TYP_LONG) && (tree->GetRegNum() != operandReg) && @@ -4426,12 +4425,11 @@ void CodeGen::genCodeForShift(GenTree* tree) unreached(); } + // It handles all register forms, but it does not handle contained form for memory operand. regNumber shiftByReg = shiftBy->GetRegNum(); emitAttr size = emitTypeSize(tree); + // The order of operandReg and shiftByReg are swapped to follow shlx, sarx and shrx encoding spec. GetEmitter()->emitIns_R_R_R(ins, size, tree->GetRegNum(), shiftByReg, operandReg); - genProduceReg(tree); - - return; } #endif else diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index 5ce70d652983bc..89cdb628d53443 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -990,25 +990,32 @@ unsigned emitter::emitOutputRexOrVexPrefixIfNeeded(instruction ins, BYTE* dst, c case INS_rorx: case INS_pdep: case INS_mulx: +// TODO: Unblock when enabled for x86 +#ifdef TARGET_AMD64 case INS_shrx: +#endif { vexPrefix |= 0x03; break; } case INS_pext: +// TODO: Unblock when enabled for x86 +#ifdef TARGET_AMD64 case INS_sarx: +#endif { vexPrefix |= 0x02; break; } - +// TODO: Unblock when enabled for x86 +#ifdef TARGET_AMD64 case INS_shlx: { vexPrefix |= 0x01; break; } - +#endif default: { vexPrefix |= 0x00; @@ -9537,6 +9544,7 @@ void emitter::emitDispIns( { // BMI bextr,bzhi, shrx, shlx and sarx encode the reg2 in VEX.vvvv and reg3 in modRM, // which is different from most of other instructions + // The order of operandReg and shiftByReg are swapped to follow shlx, sarx and shrx encoding spec. regNumber tmp = reg2; reg2 = reg3; reg3 = tmp; @@ -10328,7 +10336,6 @@ BYTE* emitter::emitOutputAM(BYTE* dst, instrDesc* id, code_t code, CnsVal* addc) // For this format, moves do not support a third operand, so we only need to handle the binary ops. if (TakesVexPrefix(ins)) { - if (IsDstDstSrcAVXInstruction(ins)) { regNumber src1 = REG_NA; @@ -16338,15 +16345,16 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins break; } +#ifdef TARGET_AMD64 case INS_shlx: case INS_sarx: case INS_shrx: { - result.insLatency = PERFSCORE_LATENCY_2C; + result.insLatency += PERFSCORE_LATENCY_1C; result.insThroughput = PERFSCORE_THROUGHPUT_2X; break; } - +#endif default: // unhandled instruction insFmt combination perfScoreUnhandledInstruction(id, &result); diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index d33fbc66fd44c9..ee94c6d9042ddf 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -4843,7 +4843,7 @@ void Lowering::ContainCheckDivOrMod(GenTreeOp* node) void Lowering::ContainCheckShiftRotate(GenTreeOp* node) { assert(node->OperIsShiftOrRotate()); -#if defined(TARGET_X86) +#ifdef TARGET_X86 GenTree* source = node->gtOp1; if (node->OperIsShiftLong()) { diff --git a/src/tests/JIT/SIMD/ShiftOperations.cs b/src/tests/JIT/SIMD/ShiftOperations.cs index 5f74fd949507e5..17c47da326ecb5 100644 --- a/src/tests/JIT/SIMD/ShiftOperations.cs +++ b/src/tests/JIT/SIMD/ShiftOperations.cs @@ -1,487 +1,501 @@ using System; using System.Runtime.CompilerServices; using System.Numerics; +using System.Runtime.InteropServices; public class Test -{ - - [MethodImpl(MethodImplOptions.NoInlining)] +{ + [MethodImpl(MethodImplOptions.NoInlining)] private static uint Shlx32bit(uint x, int y) => x<< y; - - [MethodImpl(MethodImplOptions.NoInlining)] + + [MethodImpl(MethodImplOptions.NoInlining)] private static int Sarx32bit(int x, int y) => x >> y; - + [MethodImpl(MethodImplOptions.NoInlining)] private static uint Shrx32bit(uint x, int y) => x >> y; - - [MethodImpl(MethodImplOptions.NoInlining)] + + [MethodImpl(MethodImplOptions.NoInlining)] private static uint Ror(uint x) => BitOperations.RotateRight(x, 2); - [MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.NoInlining)] private static ulong Shlx64bit(ulong x, int y) => x << y; - [MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.NoInlining)] private static long Sarx64bit(long x, int y) => x >> y; [MethodImpl(MethodImplOptions.NoInlining)] private static ulong Shrx64bit(ulong x, int y) => x >> y; - [MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.NoInlining)] private static unsafe ulong ShrxRef64bit(ulong *x, int y) => *x >> y; - - [MethodImpl(MethodImplOptions.NoInlining)] + + [MethodImpl(MethodImplOptions.NoInlining)] private static ulong Rorx(ulong x) => BitOperations.RotateRight(x, 2); - -public static unsafe int Main() -{ - try - { - uint valUInt = 0; - int valInt = 0; - ulong valULong = 0; - long valLong = 0; - int shiftBy = 0; - uint resUInt = 0; - int resInt = 0; - ulong resULong = 0; - long resLong = 0; - uint expectedUInt = 0; - int expectedInt = 0; - ulong expectedULong = 0; - long expectedLong = 0; - int MOD32 = 32; - int MOD64 = 64; - - // - // Shlx32bit tests - // - valUInt = 0; - shiftBy = 1; - resUInt = Shlx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) - { - Console.Write(" != {0} Failed.\n", expectedUInt); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedUInt); - - valUInt = 8; - shiftBy = 1; - resUInt = Shlx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) - { - Console.Write(" != {0} Failed.\n", expectedUInt); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedUInt); - - valUInt = 1; - shiftBy = 31; - resUInt = Shlx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) - { - Console.Write(" != {0} Failed.\n", expectedUInt); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedUInt); - - valUInt = 1; - shiftBy = 33; - resUInt = Shlx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) - { - Console.Write(" != {0} Failed.\n", expectedUInt); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedUInt); - - valUInt = 0xFFFFFFFF; - shiftBy = 1; - resUInt = Shlx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) - { - Console.Write(" != {0} Failed.\n", expectedUInt); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedUInt); - - // - // Sarx32bit tests - // - valInt = 0; - shiftBy = 1; - resInt = Sarx32bit(valInt, shiftBy); - expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); - if (resInt != expectedInt) - { - Console.Write(" != {0} Failed.\n", expectedInt); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedInt); - - valInt = -8; - shiftBy = 1; - resInt = Sarx32bit(valInt, shiftBy); - expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); - if (resInt != expectedInt) - { - Console.Write(" != {0} Failed.\n", expectedInt); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedInt); - - valInt = 1; - shiftBy = 33; - resInt = Sarx32bit(valInt, shiftBy); - expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); - if (resInt != expectedInt) - { - Console.Write(" != {0} Failed.\n", expectedInt); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedInt); - - valInt = 0x7FFFFFFF; - shiftBy = 33; - resInt = Sarx32bit(valInt, shiftBy); - expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); - if (resInt != expectedInt) - { - Console.Write(" != {0} Failed.\n", expectedInt); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedInt); - - valInt = 0x7FFFFFFF; - shiftBy = 30; - resInt = Sarx32bit(valInt, shiftBy); - expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); - if (resInt != expectedInt) - { - Console.Write(" != {0} Failed.\n", expectedInt); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedInt); - - // - // Shrx32bit tests - // - valUInt = 1; - shiftBy = 1; - resUInt = Shrx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) - { - Console.Write(" != {0} Failed.\n", expectedUInt); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedUInt); - - valUInt = 8; - shiftBy = 2; - resUInt = Shrx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) - { - Console.Write(" != {0} Failed.\n", expectedUInt); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedUInt); - - valUInt = 1; - shiftBy = 33; - resUInt = Shrx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) - { - Console.Write(" != {0} Failed.\n", expectedUInt); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedUInt); - - valUInt = 0xFFFFFFFF; - shiftBy = 31; - resUInt = Shrx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) - { - Console.Write(" != {0} Failed.\n", expectedUInt); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedUInt); - - valUInt = 0xFFFFFFFF; - shiftBy = 33; - resUInt = Shrx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) - { - Console.Write(" != {0} Failed.\n", expectedUInt); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedUInt); - - // - // Ror tests - // - valUInt = 0xFF; - shiftBy = 2; - resUInt = Ror(valUInt); - Console.Write("UnitTest Ror({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != 0xC000003F) - { - Console.Write(" Failed.\n"); - return 101; - } - Console.Write(" Passed.\n"); - - // - // Shlx64bit tests - // - valULong = 0; - shiftBy = 1; - resULong = Shlx64bit(valULong, shiftBy); - expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - - valULong = 8; - shiftBy = 1; - resULong = Shlx64bit(valULong, shiftBy); - expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - - valULong = 1; - shiftBy = 63; - resULong = Shlx64bit(valULong, shiftBy); - expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - - valULong = 1; - shiftBy = 65; - resULong = Shlx64bit(valULong, shiftBy); - expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - - valULong = 0xFFFFFFFFFFFFFFFF; - shiftBy = 1; - resULong = Shlx64bit(valULong, shiftBy); - expectedULong = (ulong) (valULong ^ 1); - Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - - // - // Sarx64bit tests - // - valLong = 1; - shiftBy = 1; - resLong = Sarx64bit(valLong, shiftBy); - expectedLong = (long) (valLong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); - if (resLong != expectedLong) - { - Console.Write(" != {0} Failed.\n", expectedLong); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedLong); - - valLong = -8; - shiftBy = 1; - resLong = Sarx64bit(valLong, shiftBy); - expectedLong = (long) (valLong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); - if (resLong != expectedLong) - { - Console.Write(" != {0} Failed.\n", expectedLong); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedLong); - - valLong = -8; - shiftBy = 65; - resLong = Sarx64bit(valLong, shiftBy); - expectedLong = (long) (valLong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); - if (resLong != expectedLong) - { - Console.Write(" != {0} Failed.\n", expectedLong); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedLong); - - valLong = 0x7FFFFFFFFFFFFFFF; - shiftBy = 63; - resLong = Sarx64bit(valLong, shiftBy); - expectedLong = 0; - Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); - if (resLong != expectedLong) - { - Console.Write(" != {0} Failed.\n", expectedLong); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedLong); - - valLong = 0x7FFFFFFFFFFFFFFF; - shiftBy = 65; - shiftBy = (int) Math.Pow(2, (shiftBy % MOD64)); - resLong = Sarx64bit(valLong, shiftBy); - expectedLong = 0x1FFFFFFFFFFFFFFF; - Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); - if (resLong != expectedLong) - { - Console.Write(" != {0} Failed.\n", expectedLong); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedLong); - - // - // Shrx64bit tests - // - valULong = 1; - shiftBy = 1; - resULong = Shrx64bit(valULong, shiftBy); - expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - - valULong = 8; - shiftBy = 2; - resULong = Shrx64bit(valULong, shiftBy); - expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - - valULong = 0xFFFFFFFFFFFFFFFF; - shiftBy = 63; - resULong = Shrx64bit(valULong, shiftBy); - expectedULong = 1; - Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - - valULong = 0x7FFFFFFFFFFFFFFF; - shiftBy = 65; - resULong = Shrx64bit(valULong, shiftBy); - expectedULong = 0x3FFFFFFFFFFFFFFF; - Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - - valULong = 8; - shiftBy = 65; - resULong = Shrx64bit(valULong, shiftBy); - //expectedULong = 4; - expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - - // - // ShrxRef64bit - // - valULong = 8; - shiftBy = 1; - resULong = ShrxRef64bit(&valULong, shiftBy); - expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest ShrxRef64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - return 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - - // - // Rorx tests - // - valULong = 0xFF; - shiftBy = 2; - resULong = Rorx(valULong); - Console.Write("UnitTest Rorx({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != 0xC00000000000003F) - { - Console.Write(" Failed.\n"); - return 101; - } - Console.Write(" Passed.\n"); - - } - catch (Exception e) - { - Console.WriteLine(e.Message); - return 101; - } - Console.WriteLine("PASSED"); - return 100; + + public static unsafe int Main() + { + int returnCode = 100; + + try + { + uint valUInt = 0; + int valInt = 0; + ulong valULong = 0; + long valLong = 0; + int shiftBy = 0; + uint resUInt = 0; + int resInt = 0; + ulong resULong = 0; + long resLong = 0; + uint expectedUInt = 0; + int expectedInt = 0; + ulong expectedULong = 0; + long expectedLong = 0; + int MOD32 = 32; + int MOD64 = 64; + + // + // Shlx32bit tests + // + valUInt = 0; + shiftBy = 1; + resUInt = Shlx32bit(valUInt, shiftBy); + expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + valUInt = 8; + shiftBy = 1; + resUInt = Shlx32bit(valUInt, shiftBy); + expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + valUInt = 1; + shiftBy = 31; + resUInt = Shlx32bit(valUInt, shiftBy); + expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + valUInt = 1; + shiftBy = 33; + resUInt = Shlx32bit(valUInt, shiftBy); + expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + // Test only on x64 and x86. There is a known undefined behavior for Arm64 and Arm. + if (RuntimeInformation.ProcessArchitecture == Architecture.X64 || RuntimeInformation.ProcessArchitecture == Architecture.X86) + { + valUInt = 0xFFFFFFFF; + shiftBy = 1; + resUInt = Shlx32bit(valUInt, shiftBy); + expectedUInt = (uint)(valUInt * Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + } + + // + // Sarx32bit tests + // + valInt = 0; + shiftBy = 1; + resInt = Sarx32bit(valInt, shiftBy); + expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); + if (resInt != expectedInt) + { + Console.Write(" != {0} Failed.\n", expectedInt); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedInt); + + valInt = -8; + shiftBy = 1; + resInt = Sarx32bit(valInt, shiftBy); + expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); + if (resInt != expectedInt) + { + Console.Write(" != {0} Failed.\n", expectedInt); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedInt); + + valInt = 1; + shiftBy = 33; + resInt = Sarx32bit(valInt, shiftBy); + expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); + if (resInt != expectedInt) + { + Console.Write(" != {0} Failed.\n", expectedInt); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedInt); + + valInt = 0x7FFFFFFF; + shiftBy = 33; + resInt = Sarx32bit(valInt, shiftBy); + expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); + if (resInt != expectedInt) + { + Console.Write(" != {0} Failed.\n", expectedInt); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedInt); + + valInt = 0x7FFFFFFF; + shiftBy = 30; + resInt = Sarx32bit(valInt, shiftBy); + expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); + if (resInt != expectedInt) + { + Console.Write(" != {0} Failed.\n", expectedInt); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedInt); + + // + // Shrx32bit tests + // + valUInt = 1; + shiftBy = 1; + resUInt = Shrx32bit(valUInt, shiftBy); + expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + valUInt = 8; + shiftBy = 2; + resUInt = Shrx32bit(valUInt, shiftBy); + expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + valUInt = 1; + shiftBy = 33; + resUInt = Shrx32bit(valUInt, shiftBy); + expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + valUInt = 0xFFFFFFFF; + shiftBy = 31; + resUInt = Shrx32bit(valUInt, shiftBy); + expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + valUInt = 0xFFFFFFFF; + shiftBy = 33; + resUInt = Shrx32bit(valUInt, shiftBy); + expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); + Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != expectedUInt) + { + Console.Write(" != {0} Failed.\n", expectedUInt); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedUInt); + + // + // Ror tests + // + valUInt = 0xFF; + shiftBy = 2; + resUInt = Ror(valUInt); + Console.Write("UnitTest Ror({0},{1}): {2}", valUInt, shiftBy, resUInt); + if (resUInt != 0xC000003F) + { + Console.Write(" Failed.\n"); + returnCode = 101; + } + Console.Write(" Passed.\n"); + + // + // Shlx64bit tests + // + valULong = 0; + shiftBy = 1; + resULong = Shlx64bit(valULong, shiftBy); + expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + valULong = 8; + shiftBy = 1; + resULong = Shlx64bit(valULong, shiftBy); + expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + valULong = 1; + shiftBy = 63; + resULong = Shlx64bit(valULong, shiftBy); + expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + valULong = 1; + shiftBy = 65; + resULong = Shlx64bit(valULong, shiftBy); + expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + valULong = 0xFFFFFFFFFFFFFFFF; + shiftBy = 1; + resULong = Shlx64bit(valULong, shiftBy); + expectedULong = (ulong) (valULong ^ 1); + Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + // + // Sarx64bit tests + // + valLong = 1; + shiftBy = 1; + resLong = Sarx64bit(valLong, shiftBy); + expectedLong = (long) (valLong / Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); + if (resLong != expectedLong) + { + Console.Write(" != {0} Failed.\n", expectedLong); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedLong); + + valLong = -8; + shiftBy = 1; + resLong = Sarx64bit(valLong, shiftBy); + expectedLong = (long) (valLong / Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); + if (resLong != expectedLong) + { + Console.Write(" != {0} Failed.\n", expectedLong); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedLong); + + valLong = -8; + shiftBy = 65; + resLong = Sarx64bit(valLong, shiftBy); + expectedLong = (long) (valLong / Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); + if (resLong != expectedLong) + { + Console.Write(" != {0} Failed.\n", expectedLong); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedLong); + + valLong = 0x7FFFFFFFFFFFFFFF; + shiftBy = 63; + resLong = Sarx64bit(valLong, shiftBy); + expectedLong = 0; + Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); + if (resLong != expectedLong) + { + Console.Write(" != {0} Failed.\n", expectedLong); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedLong); + + valLong = 0x7FFFFFFFFFFFFFFF; + shiftBy = 65; + shiftBy = (int) Math.Pow(2, (shiftBy % MOD64)); + resLong = Sarx64bit(valLong, shiftBy); + expectedLong = 0x1FFFFFFFFFFFFFFF; + Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); + if (resLong != expectedLong) + { + Console.Write(" != {0} Failed.\n", expectedLong); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedLong); + + // + // Shrx64bit tests + // + valULong = 1; + shiftBy = 1; + resULong = Shrx64bit(valULong, shiftBy); + expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + valULong = 8; + shiftBy = 2; + resULong = Shrx64bit(valULong, shiftBy); + expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + valULong = 0xFFFFFFFFFFFFFFFF; + shiftBy = 63; + resULong = Shrx64bit(valULong, shiftBy); + expectedULong = 1; + Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + valULong = 0x7FFFFFFFFFFFFFFF; + shiftBy = 65; + resULong = Shrx64bit(valULong, shiftBy); + expectedULong = 0x3FFFFFFFFFFFFFFF; + Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + valULong = 8; + shiftBy = 65; + resULong = Shrx64bit(valULong, shiftBy); + //expectedULong = 4; + expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + // + // ShrxRef64bit + // + valULong = 8; + shiftBy = 1; + resULong = ShrxRef64bit(&valULong, shiftBy); + expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); + Console.Write("UnitTest ShrxRef64bit({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != expectedULong) + { + Console.Write(" != {0} Failed.\n", expectedULong); + returnCode = 101; + } + Console.Write(" == {0} Passed.\n", expectedULong); + + // + // Rorx tests + // + valULong = 0xFF; + shiftBy = 2; + resULong = Rorx(valULong); + Console.Write("UnitTest Rorx({0},{1}): {2}", valULong, shiftBy, resULong); + if (resULong != 0xC00000000000003F) + { + Console.Write(" Failed.\n"); + returnCode = 101; + } + Console.Write(" Passed.\n"); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + return 101; + } + + if (returnCode == 101) + { + Console.WriteLine("FAILED"); + } + else if (returnCode == 100) + { + Console.WriteLine("PASSED"); + } + + return returnCode; + } } -} \ No newline at end of file diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 97b8df0f720d7b..844031498cd331 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1484,7 +1484,10 @@ https://github.com/dotnet/runtime/issues/46174 - + + There is a known undefined behavior with shifts and 0x0FFFFFFFF overflows, so skip the test for mono. + + Tests features specific to coreclr From 60742eef0d067d1f287166bf477302960b83ae7c Mon Sep 17 00:00:00 2001 From: Julie Lee <63486087+JulieLeeMSFT@users.noreply.github.com> Date: Fri, 22 Apr 2022 12:52:35 -0700 Subject: [PATCH 08/15] shlx, sarx, shrx: added ifdef to target x64 only --- src/coreclr/jit/emitxarch.cpp | 8 +++++++- src/coreclr/jit/instrsxarch.h | 2 ++ src/coreclr/jit/lowerxarch.cpp | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index 89cdb628d53443..fa04d3208d7dbc 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -1502,9 +1502,11 @@ bool emitter::emitInsCanOnlyWriteSSE2OrAVXReg(instrDesc* id) case INS_pextrw: case INS_pextrw_sse41: case INS_rorx: +#ifdef TARGET_AMD64 case INS_shlx: case INS_sarx: case INS_shrx: +#endif { // These SSE instructions write to a general purpose integer register. return false; @@ -9540,7 +9542,11 @@ void emitter::emitDispIns( assert(IsThreeOperandAVXInstruction(ins)); regNumber reg2 = id->idReg2(); regNumber reg3 = id->idReg3(); - if (ins == INS_bextr || ins == INS_bzhi || ins == INS_shrx || ins == INS_shlx || ins == INS_sarx) + if (ins == INS_bextr || ins == INS_bzhi +#ifdef TARGET_AMD64 + || ins == INS_shrx || ins == INS_shlx || ins == INS_sarx +#endif + ) { // BMI bextr,bzhi, shrx, shlx and sarx encode the reg2 in VEX.vvvv and reg3 in modRM, // which is different from most of other instructions diff --git a/src/coreclr/jit/instrsxarch.h b/src/coreclr/jit/instrsxarch.h index 06dd9ef0d2a505..0a8527a6393fe5 100644 --- a/src/coreclr/jit/instrsxarch.h +++ b/src/coreclr/jit/instrsxarch.h @@ -605,9 +605,11 @@ INST3(pdep, "pdep", IUM_WR, BAD_CODE, BAD_CODE, INST3(pext, "pext", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF5), INS_Flags_IsDstDstSrcAVXInstruction) // Parallel Bits Extract INST3(bzhi, "bzhi", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF5), Resets_OF | Writes_SF | Writes_ZF | Undefined_AF | Undefined_PF | Writes_CF | INS_Flags_IsDstDstSrcAVXInstruction) // Zero High Bits Starting with Specified Bit Position INST3(mulx, "mulx", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF6), INS_Flags_IsDstDstSrcAVXInstruction) // Unsigned Multiply Without Affecting Flags +#ifdef TARGET_AMD64 INST3(shlx, "shlx", IUM_WR, BAD_CODE, BAD_CODE, SSE38(0xF7), INS_Flags_IsDstDstSrcAVXInstruction) // Shift Logical Left Without Affecting Flags INST3(sarx, "sarx", IUM_WR, BAD_CODE, BAD_CODE, PACK4(0xF3, 0x0F, 0x38, 0xF7), INS_Flags_IsDstDstSrcAVXInstruction) // Shift Arithmetic Right Without Affecting Flags INST3(shrx, "shrx", IUM_WR, BAD_CODE, BAD_CODE, PACK4(0xF2, 0x0F, 0x38, 0xF7), INS_Flags_IsDstDstSrcAVXInstruction) // Shift Logical Right Without Affecting Flags +#endif INST3(LAST_BMI_INSTRUCTION, "LAST_BMI_INSTRUCTION", IUM_WR, BAD_CODE, BAD_CODE, BAD_CODE, INS_FLAGS_None) diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index ee94c6d9042ddf..65124bfc35533a 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -4850,7 +4850,7 @@ void Lowering::ContainCheckShiftRotate(GenTreeOp* node) assert(source->OperGet() == GT_LONG); MakeSrcContained(node, source); } -#endif // !TARGET_X86 +#endif GenTree* shiftBy = node->gtOp2; if (IsContainableImmed(node, shiftBy) && (shiftBy->AsIntConCommon()->IconValue() <= 255) && From ab2624fe9cb100588870aecdd11679075d4d4429 Mon Sep 17 00:00:00 2001 From: Julie Lee <63486087+JulieLeeMSFT@users.noreply.github.com> Date: Fri, 22 Apr 2022 14:35:14 -0700 Subject: [PATCH 09/15] shlx, sarx, shrx jit-format fix --- src/coreclr/jit/emitxarch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index fa04d3208d7dbc..93171241c18aab 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -16356,7 +16356,7 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins case INS_sarx: case INS_shrx: { - result.insLatency += PERFSCORE_LATENCY_1C; + result.insLatency += PERFSCORE_LATENCY_1C; result.insThroughput = PERFSCORE_THROUGHPUT_2X; break; } From 21983281d417efddbe47a41c99cd14566e498b3e Mon Sep 17 00:00:00 2001 From: Julie Lee <63486087+JulieLeeMSFT@users.noreply.github.com> Date: Wed, 11 May 2022 19:23:53 -0700 Subject: [PATCH 10/15] [shlx/sarx/shrx] Addressed feedback. --- src/coreclr/jit/codegenxarch.cpp | 1 - src/coreclr/jit/emitxarch.cpp | 1 - src/coreclr/jit/lsraxarch.cpp | 1 + src/tests/JIT/SIMD/ShiftOperations.cs | 466 ++++++-------------------- src/tests/issues.targets | 2 +- 5 files changed, 111 insertions(+), 360 deletions(-) diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 27688f5d9d4b24..837937d12db79d 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -4425,7 +4425,6 @@ void CodeGen::genCodeForShift(GenTree* tree) unreached(); } - // It handles all register forms, but it does not handle contained form for memory operand. regNumber shiftByReg = shiftBy->GetRegNum(); emitAttr size = emitTypeSize(tree); // The order of operandReg and shiftByReg are swapped to follow shlx, sarx and shrx encoding spec. diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index 93171241c18aab..7ca24ecf672675 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -9550,7 +9550,6 @@ void emitter::emitDispIns( { // BMI bextr,bzhi, shrx, shlx and sarx encode the reg2 in VEX.vvvv and reg3 in modRM, // which is different from most of other instructions - // The order of operandReg and shiftByReg are swapped to follow shlx, sarx and shrx encoding spec. regNumber tmp = reg2; reg2 = reg3; reg3 = tmp; diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index 8ef1a739f3b29f..3bf9945aece942 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -929,6 +929,7 @@ int LinearScan::BuildShiftRotate(GenTree* tree) else if (tree->OperIsShift() && !tree->isContained() && compiler->compOpportunisticallyDependsOn(InstructionSet_BMI2)) { + // It handles all register forms, but it does not handle contained form for memory operand. srcCount += BuildOperandUses(source, srcCandidates); srcCount += BuildOperandUses(shiftBy, srcCandidates); BuildDef(tree, dstCandidates); diff --git a/src/tests/JIT/SIMD/ShiftOperations.cs b/src/tests/JIT/SIMD/ShiftOperations.cs index 17c47da326ecb5..3333166293e1f4 100644 --- a/src/tests/JIT/SIMD/ShiftOperations.cs +++ b/src/tests/JIT/SIMD/ShiftOperations.cs @@ -1,3 +1,5 @@ +// 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 System.Numerics; @@ -32,9 +34,19 @@ public class Test [MethodImpl(MethodImplOptions.NoInlining)] private static ulong Rorx(ulong x) => BitOperations.RotateRight(x, 2); + public int Validate(actual, expected) + { + if (expected != actual) + { + Console.WriteLine("Fail"); + return 101; + } + return 100; + } + public static unsafe int Main() { - int returnCode = 100; + int returnCode = 0; try { @@ -57,53 +69,18 @@ public static unsafe int Main() // // Shlx32bit tests // - valUInt = 0; - shiftBy = 1; - resUInt = Shlx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) - { - Console.Write(" != {0} Failed.\n", expectedUInt); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedUInt); - valUInt = 8; - shiftBy = 1; - resUInt = Shlx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) - { - Console.Write(" != {0} Failed.\n", expectedUInt); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedUInt); - - valUInt = 1; - shiftBy = 31; - resUInt = Shlx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) + Console.Write("### UnitTest: Shlx32bit ###############\n"); + uint[] valShlx32bit = new uint[] { 0, 8, 1, 1 }; + int[] shiftByShlx32bit = new int[] { 1, 1, 31, 33 }; + for (int idx = 0; idx < valShlx32bit.Length; idx++) { - Console.Write(" != {0} Failed.\n", expectedUInt); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedUInt); - - valUInt = 1; - shiftBy = 33; - resUInt = Shlx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt * Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) - { - Console.Write(" != {0} Failed.\n", expectedUInt); - returnCode = 101; + valUInt = valShlx32bit[idx]; + shiftBy = shiftByShlx32bit[idx]; + resUInt = Shlx32bit(valUInt, shiftBy); + expectedUInt = (uint)(valUInt * Math.Pow(2, (shiftBy % MOD32))); + returnCode = Validate(valUInt, shiftBy, resUInt, expectedUInt); } - Console.Write(" == {0} Passed.\n", expectedUInt); // Test only on x64 and x86. There is a known undefined behavior for Arm64 and Arm. if (RuntimeInformation.ProcessArchitecture == Architecture.X64 || RuntimeInformation.ProcessArchitecture == Architecture.X86) @@ -112,374 +89,134 @@ public static unsafe int Main() shiftBy = 1; resUInt = Shlx32bit(valUInt, shiftBy); expectedUInt = (uint)(valUInt * Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shlx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) - { - Console.Write(" != {0} Failed.\n", expectedUInt); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedUInt); + returnCode = Validate(valUInt, shiftBy, resUInt, expectedUInt); } // // Sarx32bit tests // - valInt = 0; - shiftBy = 1; - resInt = Sarx32bit(valInt, shiftBy); - expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); - if (resInt != expectedInt) - { - Console.Write(" != {0} Failed.\n", expectedInt); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedInt); - valInt = -8; - shiftBy = 1; - resInt = Sarx32bit(valInt, shiftBy); - expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); - if (resInt != expectedInt) - { - Console.Write(" != {0} Failed.\n", expectedInt); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedInt); - - valInt = 1; - shiftBy = 33; - resInt = Sarx32bit(valInt, shiftBy); - expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); - if (resInt != expectedInt) - { - Console.Write(" != {0} Failed.\n", expectedInt); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedInt); - - valInt = 0x7FFFFFFF; - shiftBy = 33; - resInt = Sarx32bit(valInt, shiftBy); - expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); - if (resInt != expectedInt) - { - Console.Write(" != {0} Failed.\n", expectedInt); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedInt); - - valInt = 0x7FFFFFFF; - shiftBy = 30; - resInt = Sarx32bit(valInt, shiftBy); - expectedInt = (int) (valInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Sarx32bit({0},{1}): {2}", valInt, shiftBy, resInt); - if (resInt != expectedInt) + Console.Write("### UnitTest: Sarx32bit ###############\n"); + int[] valSarx32bit = new int[] { 0, -8, 1, 0x7FFFFFFF, 0x7FFFFFFF }; + int[] shiftBySarx32bit = new int[] { 1, 1, 33, 33, 30 }; + for (int idx = 0; idx < valSarx32bit.Length; idx++) { - Console.Write(" != {0} Failed.\n", expectedInt); - returnCode = 101; + valInt = valSarx32bit[idx]; + shiftBy = shiftBySarx32bit[idx]; + resInt = Sarx32bit(valInt, shiftBy); + expectedInt = (int)(valInt / Math.Pow(2, (shiftBy % MOD32))); + returnCode = Validate(valInt, shiftBy, resInt, expectedInt); } - Console.Write(" == {0} Passed.\n", expectedInt); // // Shrx32bit tests // - valUInt = 1; - shiftBy = 1; - resUInt = Shrx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) - { - Console.Write(" != {0} Failed.\n", expectedUInt); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedUInt); - valUInt = 8; - shiftBy = 2; - resUInt = Shrx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) - { - Console.Write(" != {0} Failed.\n", expectedUInt); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedUInt); - - valUInt = 1; - shiftBy = 33; - resUInt = Shrx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) + Console.Write("### UnitTest: Shrx32bit ###############\n"); + uint[] valShrx32bit = new uint[] { 1, 8, 1, 0xFFFFFFFF, 0xFFFFFFFF }; + int[] shiftByShrx32bit = new int[] { 1, 2, 33, 31, 33 }; + for (int idx = 0; idx < valShrx32bit.Length; idx++) { - Console.Write(" != {0} Failed.\n", expectedUInt); - returnCode = 101; + valUInt = valShrx32bit[idx]; + shiftBy = shiftByShrx32bit[idx]; + resUInt = Shrx32bit(valUInt, shiftBy); + expectedUInt = (uint)(valUInt / Math.Pow(2, (shiftBy % MOD32))); + returnCode = Validate(valUInt, shiftBy, resUInt, expectedUInt); } - Console.Write(" == {0} Passed.\n", expectedUInt); - - valUInt = 0xFFFFFFFF; - shiftBy = 31; - resUInt = Shrx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) - { - Console.Write(" != {0} Failed.\n", expectedUInt); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedUInt); - - valUInt = 0xFFFFFFFF; - shiftBy = 33; - resUInt = Shrx32bit(valUInt, shiftBy); - expectedUInt = (uint) (valUInt / Math.Pow(2, (shiftBy % MOD32))); - Console.Write("UnitTest Shrx32bit({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != expectedUInt) - { - Console.Write(" != {0} Failed.\n", expectedUInt); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedUInt); // // Ror tests // + + Console.Write("### UnitTest: Ror ###############\n"); valUInt = 0xFF; shiftBy = 2; resUInt = Ror(valUInt); - Console.Write("UnitTest Ror({0},{1}): {2}", valUInt, shiftBy, resUInt); - if (resUInt != 0xC000003F) - { - Console.Write(" Failed.\n"); - returnCode = 101; - } - Console.Write(" Passed.\n"); + expectedUInt = 0xC000003F; + returnCode = Validate(valUInt, shiftBy, resUInt, expectedUInt); // // Shlx64bit tests // - valULong = 0; - shiftBy = 1; - resULong = Shlx64bit(valULong, shiftBy); - expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - - valULong = 8; - shiftBy = 1; - resULong = Shlx64bit(valULong, shiftBy); - expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - - valULong = 1; - shiftBy = 63; - resULong = Shlx64bit(valULong, shiftBy); - expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - - valULong = 1; - shiftBy = 65; - resULong = Shlx64bit(valULong, shiftBy); - expectedULong = (ulong) (valULong * Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - valULong = 0xFFFFFFFFFFFFFFFF; - shiftBy = 1; - resULong = Shlx64bit(valULong, shiftBy); - expectedULong = (ulong) (valULong ^ 1); - Console.Write("UnitTest Shlx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) + Console.Write("### UnitTest: Shlx64bit ###############\n"); + ulong[] valShlx64bit = new ulong[] { 0, 8, 1, 1, 0xFFFFFFFFFFFFFFFF }; + int[] shiftByShlx64bit = new int[] { 1, 1, 63, 65, 1 }; + for (int idx = 0; idx < valShlx64bit.Length; idx++) { - Console.Write(" != {0} Failed.\n", expectedULong); - returnCode = 101; + valULong = valShlx64bit[idx]; + shiftBy = shiftByShlx64bit[idx]; + resULong = Shlx64bit(valULong, shiftBy); + if (idx == 4) + expectedULong = (ulong)(valULong ^ 1); + else + expectedULong = (ulong)(valULong * Math.Pow(2, (shiftBy % MOD64))); + returnCode = Validate(valULong, shiftBy, resULong, expectedULong); } - Console.Write(" == {0} Passed.\n", expectedULong); // // Sarx64bit tests // - valLong = 1; - shiftBy = 1; - resLong = Sarx64bit(valLong, shiftBy); - expectedLong = (long) (valLong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); - if (resLong != expectedLong) - { - Console.Write(" != {0} Failed.\n", expectedLong); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedLong); - valLong = -8; - shiftBy = 1; - resLong = Sarx64bit(valLong, shiftBy); - expectedLong = (long) (valLong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); - if (resLong != expectedLong) - { - Console.Write(" != {0} Failed.\n", expectedLong); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedLong); - - valLong = -8; - shiftBy = 65; - resLong = Sarx64bit(valLong, shiftBy); - expectedLong = (long) (valLong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); - if (resLong != expectedLong) + Console.Write("### UnitTest: Sarx64bit ###############\n"); + long[] valSarx64bit = new long[] { 1, -8, -8, 0x7FFFFFFFFFFFFFFF }; + int[] shiftBySarx64bit = new int[] { 1, 1, 65, 63 }; + for (int idx = 0; idx < valSarx64bit.Length; idx++) { - Console.Write(" != {0} Failed.\n", expectedLong); - returnCode = 101; + valLong = valSarx64bit[idx]; + shiftBy = shiftBySarx64bit[idx]; + resLong = Sarx64bit(valLong, shiftBy); + if (idx == 3) + expectedLong = 0; + else if (idx == 4) + expectedLong = 0x1FFFFFFFFFFFFFFF; + else + expectedLong = (long)(valLong / Math.Pow(2, (shiftBy % MOD64))); + returnCode = Validate(valLong, shiftBy, resLong, expectedLong); } - Console.Write(" == {0} Passed.\n", expectedLong); - - valLong = 0x7FFFFFFFFFFFFFFF; - shiftBy = 63; - resLong = Sarx64bit(valLong, shiftBy); - expectedLong = 0; - Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); - if (resLong != expectedLong) - { - Console.Write(" != {0} Failed.\n", expectedLong); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedLong); - - valLong = 0x7FFFFFFFFFFFFFFF; - shiftBy = 65; - shiftBy = (int) Math.Pow(2, (shiftBy % MOD64)); - resLong = Sarx64bit(valLong, shiftBy); - expectedLong = 0x1FFFFFFFFFFFFFFF; - Console.Write("UnitTest Sarx64bit({0},{1}): {2}", valLong, shiftBy, resLong); - if (resLong != expectedLong) - { - Console.Write(" != {0} Failed.\n", expectedLong); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedLong); // // Shrx64bit tests // - valULong = 1; - shiftBy = 1; - resULong = Shrx64bit(valULong, shiftBy); - expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - - valULong = 8; - shiftBy = 2; - resULong = Shrx64bit(valULong, shiftBy); - expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - - valULong = 0xFFFFFFFFFFFFFFFF; - shiftBy = 63; - resULong = Shrx64bit(valULong, shiftBy); - expectedULong = 1; - Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - - valULong = 0x7FFFFFFFFFFFFFFF; - shiftBy = 65; - resULong = Shrx64bit(valULong, shiftBy); - expectedULong = 0x3FFFFFFFFFFFFFFF; - Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); - valULong = 8; - shiftBy = 65; - resULong = Shrx64bit(valULong, shiftBy); - //expectedULong = 4; - expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest Shrx64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) + Console.Write("### UnitTest: Shrx64bit ###############\n"); + ulong[] valShrx64bit = new ulong[] { 1, 8, 8, 0xFFFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF }; + int[] shiftByShrx64bit = new int[] { 1, 2, 65, 63, 65 }; + for (int idx = 0; idx < valShrx64bit.Length; idx++) { - Console.Write(" != {0} Failed.\n", expectedULong); - returnCode = 101; + valULong = valShrx64bit[idx]; + shiftBy = shiftByShrx64bit[idx]; + resULong = Shrx64bit(valULong, shiftBy); + if (idx == 3) + expectedULong = 1; + else if (idx == 4) + expectedULong = 0x3FFFFFFFFFFFFFFF; + else + expectedULong = (ulong)(valULong / Math.Pow(2, (shiftBy % MOD64))); + returnCode = Validate(valULong, shiftBy, resULong, expectedULong); } - Console.Write(" == {0} Passed.\n", expectedULong); // // ShrxRef64bit // + + Console.Write("### UnitTest: ShrxRef64bit ###############\n"); valULong = 8; shiftBy = 1; resULong = ShrxRef64bit(&valULong, shiftBy); expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); - Console.Write("UnitTest ShrxRef64bit({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != expectedULong) - { - Console.Write(" != {0} Failed.\n", expectedULong); - returnCode = 101; - } - Console.Write(" == {0} Passed.\n", expectedULong); + returnCode = Validate(valULong, shiftBy, resULong, expectedULong); // // Rorx tests // + + Console.Write("### UnitTest: Rorx ###############\n"); valULong = 0xFF; shiftBy = 2; resULong = Rorx(valULong); - Console.Write("UnitTest Rorx({0},{1}): {2}", valULong, shiftBy, resULong); - if (resULong != 0xC00000000000003F) - { - Console.Write(" Failed.\n"); - returnCode = 101; - } - Console.Write(" Passed.\n"); + expectedULong = 0xC00000000000003F; + returnCode = Validate(valULong, shiftBy, resULong, expectedULong); } catch (Exception e) { @@ -489,13 +226,28 @@ public static unsafe int Main() if (returnCode == 101) { - Console.WriteLine("FAILED"); + Console.WriteLine("FAILED."); } else if (returnCode == 100) { - Console.WriteLine("PASSED"); + Console.WriteLine("PASSED."); } return returnCode; } + + private static int Validate(T value, int shiftBy, T actual, T expected) + { + Console.Write("(value, shiftBy) ({0},{1}): {2}", value, shiftBy, actual); + if (EqualityComparer.Default.Equals(actual, expected)) + { + Console.Write(" == {0} ==> Passed.\n", expected); + return 100; + } + else + { + Console.Write(" != {0} ==> Failed.\n", expected); + return 101; + } + } } diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 844031498cd331..458304bc17832f 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1485,7 +1485,7 @@ https://github.com/dotnet/runtime/issues/46174 - There is a known undefined behavior with shifts and 0x0FFFFFFFF overflows, so skip the test for mono. + There is a known undefined behavior with shifts and 0xFFFFFFFF overflows, so skip the test for mono. From f629bfd0d2582017c14d872bab96f99cf2e9d898 Mon Sep 17 00:00:00 2001 From: Julie Lee <63486087+JulieLeeMSFT@users.noreply.github.com> Date: Fri, 13 May 2022 12:11:12 -0700 Subject: [PATCH 11/15] [Emit shlx, sarx, shrx] addressed feedback on unit test --- src/tests/JIT/SIMD/ShiftOperations.cs | 116 +++++++++++++++----------- 1 file changed, 68 insertions(+), 48 deletions(-) diff --git a/src/tests/JIT/SIMD/ShiftOperations.cs b/src/tests/JIT/SIMD/ShiftOperations.cs index 3333166293e1f4..831d71fb35eac2 100644 --- a/src/tests/JIT/SIMD/ShiftOperations.cs +++ b/src/tests/JIT/SIMD/ShiftOperations.cs @@ -4,11 +4,12 @@ using System.Runtime.CompilerServices; using System.Numerics; using System.Runtime.InteropServices; +using System.Collections.Generic; public class Test { [MethodImpl(MethodImplOptions.NoInlining)] - private static uint Shlx32bit(uint x, int y) => x<< y; + private static uint Shlx32bit(uint x, int y) => x << y; [MethodImpl(MethodImplOptions.NoInlining)] private static int Sarx32bit(int x, int y) => x >> y; @@ -29,29 +30,21 @@ public class Test private static ulong Shrx64bit(ulong x, int y) => x >> y; [MethodImpl(MethodImplOptions.NoInlining)] - private static unsafe ulong ShrxRef64bit(ulong *x, int y) => *x >> y; + private static unsafe ulong ShrxRef64bit(ulong* x, int y) => *x >> y; [MethodImpl(MethodImplOptions.NoInlining)] private static ulong Rorx(ulong x) => BitOperations.RotateRight(x, 2); - public int Validate(actual, expected) - { - if (expected != actual) - { - Console.WriteLine("Fail"); - return 101; - } - return 100; - } - public static unsafe int Main() { - int returnCode = 0; + const int PASS = 100; + const int FAIL = 101; + int returnCode = PASS; try { - uint valUInt = 0; - int valInt = 0; + uint valUInt = 0; + int valInt = 0; ulong valULong = 0; long valLong = 0; int shiftBy = 0; @@ -70,7 +63,7 @@ public static unsafe int Main() // Shlx32bit tests // - Console.Write("### UnitTest: Shlx32bit ###############\n"); + Console.WriteLine("### UnitTest: Shlx32bit ###############"); uint[] valShlx32bit = new uint[] { 0, 8, 1, 1 }; int[] shiftByShlx32bit = new int[] { 1, 1, 31, 33 }; for (int idx = 0; idx < valShlx32bit.Length; idx++) @@ -79,7 +72,10 @@ public static unsafe int Main() shiftBy = shiftByShlx32bit[idx]; resUInt = Shlx32bit(valUInt, shiftBy); expectedUInt = (uint)(valUInt * Math.Pow(2, (shiftBy % MOD32))); - returnCode = Validate(valUInt, shiftBy, resUInt, expectedUInt); + if (!Validate(valUInt, shiftBy, resUInt, expectedUInt)) + { + returnCode = FAIL; + } } // Test only on x64 and x86. There is a known undefined behavior for Arm64 and Arm. @@ -89,14 +85,17 @@ public static unsafe int Main() shiftBy = 1; resUInt = Shlx32bit(valUInt, shiftBy); expectedUInt = (uint)(valUInt * Math.Pow(2, (shiftBy % MOD32))); - returnCode = Validate(valUInt, shiftBy, resUInt, expectedUInt); + if (!Validate(valUInt, shiftBy, resUInt, expectedUInt)) + { + returnCode = FAIL; + } } // // Sarx32bit tests // - Console.Write("### UnitTest: Sarx32bit ###############\n"); + Console.WriteLine("### UnitTest: Sarx32bit ###############"); int[] valSarx32bit = new int[] { 0, -8, 1, 0x7FFFFFFF, 0x7FFFFFFF }; int[] shiftBySarx32bit = new int[] { 1, 1, 33, 33, 30 }; for (int idx = 0; idx < valSarx32bit.Length; idx++) @@ -105,14 +104,17 @@ public static unsafe int Main() shiftBy = shiftBySarx32bit[idx]; resInt = Sarx32bit(valInt, shiftBy); expectedInt = (int)(valInt / Math.Pow(2, (shiftBy % MOD32))); - returnCode = Validate(valInt, shiftBy, resInt, expectedInt); + if (!Validate(valInt, shiftBy, resInt, expectedInt)) + { + returnCode = FAIL; + } } // // Shrx32bit tests // - Console.Write("### UnitTest: Shrx32bit ###############\n"); + Console.WriteLine("### UnitTest: Shrx32bit ###############"); uint[] valShrx32bit = new uint[] { 1, 8, 1, 0xFFFFFFFF, 0xFFFFFFFF }; int[] shiftByShrx32bit = new int[] { 1, 2, 33, 31, 33 }; for (int idx = 0; idx < valShrx32bit.Length; idx++) @@ -121,25 +123,31 @@ public static unsafe int Main() shiftBy = shiftByShrx32bit[idx]; resUInt = Shrx32bit(valUInt, shiftBy); expectedUInt = (uint)(valUInt / Math.Pow(2, (shiftBy % MOD32))); - returnCode = Validate(valUInt, shiftBy, resUInt, expectedUInt); + if (!Validate(valUInt, shiftBy, resUInt, expectedUInt)) + { + returnCode = FAIL; + } } // // Ror tests // - Console.Write("### UnitTest: Ror ###############\n"); + Console.WriteLine("### UnitTest: Ror ###############"); valUInt = 0xFF; shiftBy = 2; resUInt = Ror(valUInt); expectedUInt = 0xC000003F; - returnCode = Validate(valUInt, shiftBy, resUInt, expectedUInt); + if (!Validate(valUInt, shiftBy, resUInt, expectedUInt)) + { + returnCode = FAIL; + } // // Shlx64bit tests // - Console.Write("### UnitTest: Shlx64bit ###############\n"); + Console.WriteLine("### UnitTest: Shlx64bit ###############"); ulong[] valShlx64bit = new ulong[] { 0, 8, 1, 1, 0xFFFFFFFFFFFFFFFF }; int[] shiftByShlx64bit = new int[] { 1, 1, 63, 65, 1 }; for (int idx = 0; idx < valShlx64bit.Length; idx++) @@ -151,14 +159,17 @@ public static unsafe int Main() expectedULong = (ulong)(valULong ^ 1); else expectedULong = (ulong)(valULong * Math.Pow(2, (shiftBy % MOD64))); - returnCode = Validate(valULong, shiftBy, resULong, expectedULong); + if (!Validate(valULong, shiftBy, resULong, expectedULong)) + { + returnCode = FAIL; + } } // // Sarx64bit tests // - Console.Write("### UnitTest: Sarx64bit ###############\n"); + Console.WriteLine("### UnitTest: Sarx64bit ###############"); long[] valSarx64bit = new long[] { 1, -8, -8, 0x7FFFFFFFFFFFFFFF }; int[] shiftBySarx64bit = new int[] { 1, 1, 65, 63 }; for (int idx = 0; idx < valSarx64bit.Length; idx++) @@ -168,18 +179,19 @@ public static unsafe int Main() resLong = Sarx64bit(valLong, shiftBy); if (idx == 3) expectedLong = 0; - else if (idx == 4) - expectedLong = 0x1FFFFFFFFFFFFFFF; else expectedLong = (long)(valLong / Math.Pow(2, (shiftBy % MOD64))); - returnCode = Validate(valLong, shiftBy, resLong, expectedLong); + if (!Validate(valLong, shiftBy, resLong, expectedLong)) + { + returnCode = FAIL; + } } // // Shrx64bit tests // - Console.Write("### UnitTest: Shrx64bit ###############\n"); + Console.WriteLine("### UnitTest: Shrx64bit ###############"); ulong[] valShrx64bit = new ulong[] { 1, 8, 8, 0xFFFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF }; int[] shiftByShrx64bit = new int[] { 1, 2, 65, 63, 65 }; for (int idx = 0; idx < valShrx64bit.Length; idx++) @@ -193,61 +205,69 @@ public static unsafe int Main() expectedULong = 0x3FFFFFFFFFFFFFFF; else expectedULong = (ulong)(valULong / Math.Pow(2, (shiftBy % MOD64))); - returnCode = Validate(valULong, shiftBy, resULong, expectedULong); + if (!Validate(valULong, shiftBy, resULong, expectedULong)) + { + returnCode = FAIL; + } } // // ShrxRef64bit // - Console.Write("### UnitTest: ShrxRef64bit ###############\n"); + Console.WriteLine("### UnitTest: ShrxRef64bit ###############"); valULong = 8; shiftBy = 1; resULong = ShrxRef64bit(&valULong, shiftBy); - expectedULong = (ulong) (valULong / Math.Pow(2, (shiftBy % MOD64))); - returnCode = Validate(valULong, shiftBy, resULong, expectedULong); + expectedULong = (ulong)(valULong / Math.Pow(2, (shiftBy % MOD64))); + if (!Validate(valULong, shiftBy, resULong, expectedULong)) + { + returnCode = FAIL; + } // // Rorx tests // - Console.Write("### UnitTest: Rorx ###############\n"); + Console.WriteLine("### UnitTest: Rorx ###############"); valULong = 0xFF; shiftBy = 2; resULong = Rorx(valULong); expectedULong = 0xC00000000000003F; - returnCode = Validate(valULong, shiftBy, resULong, expectedULong); + if (!Validate(valULong, shiftBy, resULong, expectedULong)) + { + returnCode = FAIL; + } } catch (Exception e) { Console.WriteLine(e.Message); - return 101; + return FAIL; } - if (returnCode == 101) + if (returnCode == PASS) { - Console.WriteLine("FAILED."); + Console.WriteLine("PASSED."); } - else if (returnCode == 100) + else { - Console.WriteLine("PASSED."); + Console.WriteLine("FAILED."); } - return returnCode; } - private static int Validate(T value, int shiftBy, T actual, T expected) + private static bool Validate(T value, int shiftBy, T actual, T expected) { Console.Write("(value, shiftBy) ({0},{1}): {2}", value, shiftBy, actual); if (EqualityComparer.Default.Equals(actual, expected)) { - Console.Write(" == {0} ==> Passed.\n", expected); - return 100; + Console.WriteLine(" == {0} ==> Passed.", expected); + return true; } else { - Console.Write(" != {0} ==> Failed.\n", expected); - return 101; + Console.WriteLine(" != {0} ==> Failed.", expected); + return false; } } } From 2292e0582f03fe7bc223e124d279d32ea6aa6ee1 Mon Sep 17 00:00:00 2001 From: Julie Lee <63486087+JulieLeeMSFT@users.noreply.github.com> Date: Fri, 13 May 2022 12:49:25 -0700 Subject: [PATCH 12/15] [Emit shlx, sarx, shrx] Removed x86 unit tests. --- src/tests/JIT/SIMD/ShiftOperations.cs | 30 ++++++++------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/src/tests/JIT/SIMD/ShiftOperations.cs b/src/tests/JIT/SIMD/ShiftOperations.cs index 831d71fb35eac2..12160ccbd33f95 100644 --- a/src/tests/JIT/SIMD/ShiftOperations.cs +++ b/src/tests/JIT/SIMD/ShiftOperations.cs @@ -43,22 +43,23 @@ public static unsafe int Main() try { - uint valUInt = 0; - int valInt = 0; ulong valULong = 0; long valLong = 0; int shiftBy = 0; - uint resUInt = 0; - int resInt = 0; ulong resULong = 0; long resLong = 0; - uint expectedUInt = 0; - int expectedInt = 0; ulong expectedULong = 0; long expectedLong = 0; - int MOD32 = 32; int MOD64 = 64; +/* TODO: Enable 32bit test when x86 shift is enabled. + uint valUInt = 0; + int valInt = 0; + uint resUInt = 0; + int resInt = 0; + uint expectedUInt = 0; + int expectedInt = 0; + int MOD32 = 32; // // Shlx32bit tests // @@ -128,20 +129,7 @@ public static unsafe int Main() returnCode = FAIL; } } - - // - // Ror tests - // - - Console.WriteLine("### UnitTest: Ror ###############"); - valUInt = 0xFF; - shiftBy = 2; - resUInt = Ror(valUInt); - expectedUInt = 0xC000003F; - if (!Validate(valUInt, shiftBy, resUInt, expectedUInt)) - { - returnCode = FAIL; - } +*/ // End of x86 shift unit tests // // Shlx64bit tests From f85d34ab85bd6b3fa385781d79f33829f13d0091 Mon Sep 17 00:00:00 2001 From: Julie Lee <63486087+JulieLeeMSFT@users.noreply.github.com> Date: Mon, 16 May 2022 18:00:35 -0700 Subject: [PATCH 13/15] [Emit shlx, sarx, shrx] Remove 32bit test cases. --- src/tests/JIT/SIMD/ShiftOperations.cs | 91 --------------------------- 1 file changed, 91 deletions(-) diff --git a/src/tests/JIT/SIMD/ShiftOperations.cs b/src/tests/JIT/SIMD/ShiftOperations.cs index 12160ccbd33f95..5acc180a56d0c1 100644 --- a/src/tests/JIT/SIMD/ShiftOperations.cs +++ b/src/tests/JIT/SIMD/ShiftOperations.cs @@ -8,18 +8,6 @@ public class Test { - [MethodImpl(MethodImplOptions.NoInlining)] - private static uint Shlx32bit(uint x, int y) => x << y; - - [MethodImpl(MethodImplOptions.NoInlining)] - private static int Sarx32bit(int x, int y) => x >> y; - - [MethodImpl(MethodImplOptions.NoInlining)] - private static uint Shrx32bit(uint x, int y) => x >> y; - - [MethodImpl(MethodImplOptions.NoInlining)] - private static uint Ror(uint x) => BitOperations.RotateRight(x, 2); - [MethodImpl(MethodImplOptions.NoInlining)] private static ulong Shlx64bit(ulong x, int y) => x << y; @@ -52,85 +40,6 @@ public static unsafe int Main() long expectedLong = 0; int MOD64 = 64; -/* TODO: Enable 32bit test when x86 shift is enabled. - uint valUInt = 0; - int valInt = 0; - uint resUInt = 0; - int resInt = 0; - uint expectedUInt = 0; - int expectedInt = 0; - int MOD32 = 32; - // - // Shlx32bit tests - // - - Console.WriteLine("### UnitTest: Shlx32bit ###############"); - uint[] valShlx32bit = new uint[] { 0, 8, 1, 1 }; - int[] shiftByShlx32bit = new int[] { 1, 1, 31, 33 }; - for (int idx = 0; idx < valShlx32bit.Length; idx++) - { - valUInt = valShlx32bit[idx]; - shiftBy = shiftByShlx32bit[idx]; - resUInt = Shlx32bit(valUInt, shiftBy); - expectedUInt = (uint)(valUInt * Math.Pow(2, (shiftBy % MOD32))); - if (!Validate(valUInt, shiftBy, resUInt, expectedUInt)) - { - returnCode = FAIL; - } - } - - // Test only on x64 and x86. There is a known undefined behavior for Arm64 and Arm. - if (RuntimeInformation.ProcessArchitecture == Architecture.X64 || RuntimeInformation.ProcessArchitecture == Architecture.X86) - { - valUInt = 0xFFFFFFFF; - shiftBy = 1; - resUInt = Shlx32bit(valUInt, shiftBy); - expectedUInt = (uint)(valUInt * Math.Pow(2, (shiftBy % MOD32))); - if (!Validate(valUInt, shiftBy, resUInt, expectedUInt)) - { - returnCode = FAIL; - } - } - - // - // Sarx32bit tests - // - - Console.WriteLine("### UnitTest: Sarx32bit ###############"); - int[] valSarx32bit = new int[] { 0, -8, 1, 0x7FFFFFFF, 0x7FFFFFFF }; - int[] shiftBySarx32bit = new int[] { 1, 1, 33, 33, 30 }; - for (int idx = 0; idx < valSarx32bit.Length; idx++) - { - valInt = valSarx32bit[idx]; - shiftBy = shiftBySarx32bit[idx]; - resInt = Sarx32bit(valInt, shiftBy); - expectedInt = (int)(valInt / Math.Pow(2, (shiftBy % MOD32))); - if (!Validate(valInt, shiftBy, resInt, expectedInt)) - { - returnCode = FAIL; - } - } - - // - // Shrx32bit tests - // - - Console.WriteLine("### UnitTest: Shrx32bit ###############"); - uint[] valShrx32bit = new uint[] { 1, 8, 1, 0xFFFFFFFF, 0xFFFFFFFF }; - int[] shiftByShrx32bit = new int[] { 1, 2, 33, 31, 33 }; - for (int idx = 0; idx < valShrx32bit.Length; idx++) - { - valUInt = valShrx32bit[idx]; - shiftBy = shiftByShrx32bit[idx]; - resUInt = Shrx32bit(valUInt, shiftBy); - expectedUInt = (uint)(valUInt / Math.Pow(2, (shiftBy % MOD32))); - if (!Validate(valUInt, shiftBy, resUInt, expectedUInt)) - { - returnCode = FAIL; - } - } -*/ // End of x86 shift unit tests - // // Shlx64bit tests // From 36b26a27936f83ae1fcf23fc3d24ff6fbcc5bb3c Mon Sep 17 00:00:00 2001 From: Julie Lee <63486087+JulieLeeMSFT@users.noreply.github.com> Date: Wed, 18 May 2022 09:01:45 -0700 Subject: [PATCH 14/15] [Emit shlx, sarx, shrx] Update a comment in lsraxarch.cpp Co-authored-by: Kunal Pathak --- src/coreclr/jit/lsraxarch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index 3bf9945aece942..1250aba799c375 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -929,7 +929,7 @@ int LinearScan::BuildShiftRotate(GenTree* tree) else if (tree->OperIsShift() && !tree->isContained() && compiler->compOpportunisticallyDependsOn(InstructionSet_BMI2)) { - // It handles all register forms, but it does not handle contained form for memory operand. + // shlx (as opposed to mov+shl) instructions handles all register forms, but it does not handle contained form for memory operand. Likewise for sarx and shrx. srcCount += BuildOperandUses(source, srcCandidates); srcCount += BuildOperandUses(shiftBy, srcCandidates); BuildDef(tree, dstCandidates); From 5b454bb3072292b77a3cfe807f771a69d8055208 Mon Sep 17 00:00:00 2001 From: Julie Lee <63486087+JulieLeeMSFT@users.noreply.github.com> Date: Wed, 18 May 2022 20:22:22 -0700 Subject: [PATCH 15/15] [Emit shlx, sarx, shrx] Added uint, int, ushort and short test cases. --- src/coreclr/jit/lsraxarch.cpp | 3 +- src/tests/JIT/SIMD/ShiftOperations.cs | 280 +++++++++++++++++++------- 2 files changed, 214 insertions(+), 69 deletions(-) diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index 1250aba799c375..1e5f03463c8079 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -929,7 +929,8 @@ int LinearScan::BuildShiftRotate(GenTree* tree) else if (tree->OperIsShift() && !tree->isContained() && compiler->compOpportunisticallyDependsOn(InstructionSet_BMI2)) { - // shlx (as opposed to mov+shl) instructions handles all register forms, but it does not handle contained form for memory operand. Likewise for sarx and shrx. + // shlx (as opposed to mov+shl) instructions handles all register forms, but it does not handle contained form + // for memory operand. Likewise for sarx and shrx. srcCount += BuildOperandUses(source, srcCandidates); srcCount += BuildOperandUses(shiftBy, srcCandidates); BuildDef(tree, dstCandidates); diff --git a/src/tests/JIT/SIMD/ShiftOperations.cs b/src/tests/JIT/SIMD/ShiftOperations.cs index 5acc180a56d0c1..5122d261bdedac 100644 --- a/src/tests/JIT/SIMD/ShiftOperations.cs +++ b/src/tests/JIT/SIMD/ShiftOperations.cs @@ -9,19 +9,76 @@ public class Test { [MethodImpl(MethodImplOptions.NoInlining)] - private static ulong Shlx64bit(ulong x, int y) => x << y; + private static R Shlx64bit(T x, int y) + { + switch (x) + { + case ulong a: + ulong resUlong = ((ulong)a) << y; + return (R)Convert.ChangeType(resUlong, typeof(R)); + case uint b: + uint resUint = ((uint)b) << y; + return (R)Convert.ChangeType(resUint, typeof(R)); + case ushort c: + int resInt = ((ushort)c) << y; + return (R)Convert.ChangeType(resInt, typeof(R)); + default: + Console.WriteLine("Unsupported type."); + return default(R); + } + } [MethodImpl(MethodImplOptions.NoInlining)] - private static long Sarx64bit(long x, int y) => x >> y; + private static R Sarx64bit(T x, int y) + { + int resInt = 0; + switch (x) + { + case long a: + long resLong = ((long)a) >> y; + return (R)Convert.ChangeType(resLong, typeof(R)); + case int b: + resInt = ((int)b) >> y; + return (R)Convert.ChangeType(resInt, typeof(R)); + case short c: + Console.WriteLine($"Before: {Convert.ToString((short)c, toBase: 2)}"); + resInt = ((short)c) >> y; + Console.WriteLine($"After: {Convert.ToString(resInt, toBase: 2)}"); + return (R)Convert.ChangeType(resInt, typeof(R)); + default: + Console.WriteLine("Unsupported type."); + return default(R); + } + } [MethodImpl(MethodImplOptions.NoInlining)] - private static ulong Shrx64bit(ulong x, int y) => x >> y; + private static R Shrx64bit(T x, int y) + { + switch (x) + { + case ulong a: + ulong resUlong = ((ulong)a) >> y; + return (R)Convert.ChangeType(resUlong, typeof(R)); + case uint b: + uint resUint = ((uint)b) >> y; + return (R)Convert.ChangeType(resUint, typeof(R)); + case ushort c: + int resInt = ((ushort)c) >> y; + return (R)Convert.ChangeType(resInt, typeof(R)); + default: + Console.WriteLine("Unsupported type."); + return default(R); + } + } [MethodImpl(MethodImplOptions.NoInlining)] private static unsafe ulong ShrxRef64bit(ulong* x, int y) => *x >> y; [MethodImpl(MethodImplOptions.NoInlining)] - private static ulong Rorx(ulong x) => BitOperations.RotateRight(x, 2); + private static unsafe uint ShrxRef64bit(uint* x, int y) => *x >> y; + + [MethodImpl(MethodImplOptions.NoInlining)] + private static unsafe int ShrxRef64bit(ushort* x, int y) => *x >> y; public static unsafe int Main() { @@ -31,32 +88,54 @@ public static unsafe int Main() try { - ulong valULong = 0; - long valLong = 0; - int shiftBy = 0; - ulong resULong = 0; - long resLong = 0; - ulong expectedULong = 0; - long expectedLong = 0; - int MOD64 = 64; - // // Shlx64bit tests // - Console.WriteLine("### UnitTest: Shlx64bit ###############"); - ulong[] valShlx64bit = new ulong[] { 0, 8, 1, 1, 0xFFFFFFFFFFFFFFFF }; - int[] shiftByShlx64bit = new int[] { 1, 1, 63, 65, 1 }; - for (int idx = 0; idx < valShlx64bit.Length; idx++) + // ulong + int MOD64 = 64; + + Console.WriteLine(); + Console.WriteLine("### UnitTest: Shlx64bit (ulong) ###############"); + ulong[] valUlong = new ulong[] { 0, 8, 1, 1, 0xFFFFFFFFFFFFFFFF }; + int[] shiftBy = new int[] { 1, 1, 63, 65, 1 }; + for (int idx = 0; idx < valUlong.Length; idx++) + { + ulong resULong = (ulong)Shlx64bit(valUlong[idx], shiftBy[idx]); + ulong expectedUlong = (ulong)(valUlong[idx] << (shiftBy[idx] % MOD64)); + if (!Validate(valUlong[idx], shiftBy[idx], resULong, expectedUlong)) + { + returnCode = FAIL; + } + } + + // uint + int MOD32 = 32; + + Console.WriteLine(); + Console.WriteLine("### UnitTest: Shlx64bit (uint) ###############"); + uint[] valUint = new uint[] { 0, 8, 1, 1, 0xFFFFFFFF }; + shiftBy = new int[] { 1, 1, 32, 33, 1 }; + for (int idx = 0; idx < valUint.Length; idx++) + { + uint resUint = (uint)Shlx64bit(valUint[idx], shiftBy[idx]); + uint expectedUint = (uint)(valUint[idx] << (shiftBy[idx] % MOD32)); + if (!Validate(valUint[idx], shiftBy[idx], resUint, expectedUint)) + { + returnCode = FAIL; + } + } + + // ushort + Console.WriteLine(); + Console.WriteLine("### UnitTest: Shlx64bit (ushort) ###############"); + ushort[] valUshort = new ushort[] { 0, 8, 1, 1, 0b_0111_0001_1000_0010 }; + shiftBy = new int[] { 1, 1, 16, 18, 16 }; + for (int idx = 0; idx < valUshort.Length; idx++) { - valULong = valShlx64bit[idx]; - shiftBy = shiftByShlx64bit[idx]; - resULong = Shlx64bit(valULong, shiftBy); - if (idx == 4) - expectedULong = (ulong)(valULong ^ 1); - else - expectedULong = (ulong)(valULong * Math.Pow(2, (shiftBy % MOD64))); - if (!Validate(valULong, shiftBy, resULong, expectedULong)) + int resInt = (int)Shlx64bit(valUshort[idx], shiftBy[idx]); + int expectedInt = (int)(((int)valUshort[idx]) << (shiftBy[idx] % MOD32)); + if (!Validate(valUshort[idx], shiftBy[idx], resInt, expectedInt)) { returnCode = FAIL; } @@ -66,19 +145,46 @@ public static unsafe int Main() // Sarx64bit tests // - Console.WriteLine("### UnitTest: Sarx64bit ###############"); - long[] valSarx64bit = new long[] { 1, -8, -8, 0x7FFFFFFFFFFFFFFF }; - int[] shiftBySarx64bit = new int[] { 1, 1, 65, 63 }; - for (int idx = 0; idx < valSarx64bit.Length; idx++) + // long + Console.WriteLine(); + Console.WriteLine("### UnitTest: Sarx64bit (long) ###############"); + long[] valLong = new long[] { 1, -8, -8, 0x7FFFFFFFFFFFFFFF }; + shiftBy = new int[] { 1, 1, 65, 63 }; + for (int idx = 0; idx < valLong.Length; idx++) + { + long resLong = (long)Sarx64bit(valLong[idx], shiftBy[idx]); + long expectedLong = (long)(valLong[idx] >> (shiftBy[idx] % MOD64)); + if (!Validate(valLong[idx], shiftBy[idx], resLong, expectedLong)) + { + returnCode = FAIL; + } + } + + // int + Console.WriteLine(); + Console.WriteLine("### UnitTest: Sarx64bit (int) ###############"); + int[] valInt = new int[] { 1, -8, -8, 0x7FFFFFFF }; + shiftBy = new int[] { 1, 1, 32, 33 }; + for (int idx = 0; idx < valInt.Length; idx++) { - valLong = valSarx64bit[idx]; - shiftBy = shiftBySarx64bit[idx]; - resLong = Sarx64bit(valLong, shiftBy); - if (idx == 3) - expectedLong = 0; - else - expectedLong = (long)(valLong / Math.Pow(2, (shiftBy % MOD64))); - if (!Validate(valLong, shiftBy, resLong, expectedLong)) + int resInt = (int)Sarx64bit(valInt[idx], shiftBy[idx]); + int expectedInt = (int)(valInt[idx] >> (shiftBy[idx] % MOD32)); + if (!Validate(valInt[idx], shiftBy[idx], resInt, expectedInt)) + { + returnCode = FAIL; + } + } + + // short + Console.WriteLine(); + Console.WriteLine("### UnitTest: Sarx64bit (short) ###############"); + short[] valShort = new short[] { 1, -8, -8, 0b_0111_0001_1000_0010 }; + shiftBy = new int[] { 1, 1, 16, 18 }; + for (int idx = 0; idx < valShort.Length; idx++) + { + int resInt = (int)Sarx64bit(valShort[idx], shiftBy[idx]); + int expectedInt = (int)valShort[idx] >> (shiftBy[idx] % MOD32); + if (!Validate(valShort[idx], shiftBy[idx], resInt, expectedInt)) { returnCode = FAIL; } @@ -88,21 +194,46 @@ public static unsafe int Main() // Shrx64bit tests // - Console.WriteLine("### UnitTest: Shrx64bit ###############"); - ulong[] valShrx64bit = new ulong[] { 1, 8, 8, 0xFFFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF }; - int[] shiftByShrx64bit = new int[] { 1, 2, 65, 63, 65 }; - for (int idx = 0; idx < valShrx64bit.Length; idx++) + // ulong + Console.WriteLine(); + Console.WriteLine("### UnitTest: Shrx64bit (ulong) ###############"); + valUlong = new ulong[] { 1, 8, 8, 0xFFFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF }; + shiftBy = new int[] { 1, 2, 65, 63, 65 }; + for (int idx = 0; idx < valUlong.Length; idx++) + { + ulong resULong = (ulong)Shrx64bit(valUlong[idx], shiftBy[idx]); + ulong expectedUlong = (ulong)(valUlong[idx] >> (shiftBy[idx] % MOD64)); + if (!Validate(valUlong[idx], shiftBy[idx], resULong, expectedUlong)) + { + returnCode = FAIL; + } + } + + // uint + Console.WriteLine(); + Console.WriteLine("### UnitTest: Shrx64bit (uint) ###############"); + valUint = new uint[] { 1, 8, 8, 0xFFFFFFFF }; + shiftBy = new int[] { 1, 1, 32, 33 }; + for (int idx = 0; idx < valUint.Length; idx++) { - valULong = valShrx64bit[idx]; - shiftBy = shiftByShrx64bit[idx]; - resULong = Shrx64bit(valULong, shiftBy); - if (idx == 3) - expectedULong = 1; - else if (idx == 4) - expectedULong = 0x3FFFFFFFFFFFFFFF; - else - expectedULong = (ulong)(valULong / Math.Pow(2, (shiftBy % MOD64))); - if (!Validate(valULong, shiftBy, resULong, expectedULong)) + uint resUint = (uint)Shrx64bit(valUint[idx], shiftBy[idx]); + uint expectedUint = (uint)(valUint[idx] >> (shiftBy[idx] % MOD32)); + if (!Validate(valUint[idx], shiftBy[idx], resUint, expectedUint)) + { + returnCode = FAIL; + } + } + + // ushort + Console.WriteLine(); + Console.WriteLine("### UnitTest: Shrx64bit (ushort) ###############"); + valUshort = new ushort[] { 0, 8, 0b_1000_0000_0000_0000, 0b_1000_0000_0000_0000, 0b_1111_0001_1000_0010 }; + shiftBy = new int[] { 1, 1, 15, 18, 40 }; + for (int idx = 0; idx < valUshort.Length; idx++) + { + int resInt = (int)Shrx64bit(valUshort[idx], shiftBy[idx]); + int expectedInt = (int)(((int)valUshort[idx]) >> (shiftBy[idx] % MOD32)); + if (!Validate(valUshort[idx], shiftBy[idx], resInt, expectedInt)) { returnCode = FAIL; } @@ -112,26 +243,38 @@ public static unsafe int Main() // ShrxRef64bit // - Console.WriteLine("### UnitTest: ShrxRef64bit ###############"); - valULong = 8; - shiftBy = 1; - resULong = ShrxRef64bit(&valULong, shiftBy); - expectedULong = (ulong)(valULong / Math.Pow(2, (shiftBy % MOD64))); - if (!Validate(valULong, shiftBy, resULong, expectedULong)) + // ulong + Console.WriteLine(); + Console.WriteLine("### UnitTest: ShrxRef64bit (ulong) ###############"); + ulong valUlongRef = 8; + int shiftByRef = 1; + ulong resUlongRef = ShrxRef64bit(&valUlongRef, shiftByRef); + ulong expectedULongRef = (ulong)(valUlongRef >> (shiftByRef % MOD64)); + if (!Validate(valUlongRef, shiftByRef, resUlongRef, expectedULongRef)) { returnCode = FAIL; } - // - // Rorx tests - // + // uint + Console.WriteLine(); + Console.WriteLine("### UnitTest: ShrxRef64bit (uint) ###############"); + uint valUintRef = 0xFFFFFFFF; + shiftByRef = 1; + uint resUintRef = ShrxRef64bit(&valUintRef, shiftByRef); + uint expectedUintRef = (uint)(valUintRef >> (shiftByRef % MOD32)); + if (!Validate(valUintRef, shiftByRef, resUintRef, expectedUintRef)) + { + returnCode = FAIL; + } - Console.WriteLine("### UnitTest: Rorx ###############"); - valULong = 0xFF; - shiftBy = 2; - resULong = Rorx(valULong); - expectedULong = 0xC00000000000003F; - if (!Validate(valULong, shiftBy, resULong, expectedULong)) + // ushort + Console.WriteLine(); + Console.WriteLine("### UnitTest: ShrxRef64bit (ushort) ###############"); + ushort valUshortRef = 0xFFFF; + shiftByRef = 15; + int resUshortRef = ShrxRef64bit(&valUshortRef, shiftByRef); + int expectedUshortRef = (int)((uint)valUshortRef >> (shiftByRef % MOD32)); + if (!Validate(valUshortRef, shiftByRef, resUshortRef, expectedUshortRef)) { returnCode = FAIL; } @@ -142,6 +285,7 @@ public static unsafe int Main() return FAIL; } + Console.WriteLine(); if (returnCode == PASS) { Console.WriteLine("PASSED."); @@ -153,10 +297,10 @@ public static unsafe int Main() return returnCode; } - private static bool Validate(T value, int shiftBy, T actual, T expected) + private static bool Validate(TValue value, int shiftBy, TResult actual, TResult expected) { Console.Write("(value, shiftBy) ({0},{1}): {2}", value, shiftBy, actual); - if (EqualityComparer.Default.Equals(actual, expected)) + if (EqualityComparer.Default.Equals(actual, expected)) { Console.WriteLine(" == {0} ==> Passed.", expected); return true;