diff --git a/src/inc/corjitflags.h b/src/inc/corjitflags.h index da303b6a0383..84fb42f08306 100644 --- a/src/inc/corjitflags.h +++ b/src/inc/corjitflags.h @@ -120,7 +120,7 @@ class CORJIT_FLAGS CORJIT_FLAG_HAS_ARM64_LRCPC = 52, // ID_AA64ISAR1_EL1.LRCPC is 1 or better CORJIT_FLAG_HAS_ARM64_PMULL = 53, // ID_AA64ISAR0_EL1.AES is 2 or better CORJIT_FLAG_HAS_ARM64_SHA1 = 54, // ID_AA64ISAR0_EL1.SHA1 is 1 or better - CORJIT_FLAG_HAS_ARM64_SHA2 = 55, // ID_AA64ISAR0_EL1.SHA2 is 1 or better + CORJIT_FLAG_HAS_ARM64_SHA256 = 55, // ID_AA64ISAR0_EL1.SHA2 is 1 or better CORJIT_FLAG_HAS_ARM64_SHA512 = 56, // ID_AA64ISAR0_EL1.SHA2 is 2 or better CORJIT_FLAG_HAS_ARM64_SHA3 = 57, // ID_AA64ISAR0_EL1.SHA3 is 1 or better CORJIT_FLAG_HAS_ARM64_SIMD = 58, // ID_AA64PFR0_EL1.AdvSIMD is 0 or better diff --git a/src/jit/codegenarm64.cpp b/src/jit/codegenarm64.cpp index 5463e2a5f464..fa50983de683 100644 --- a/src/jit/codegenarm64.cpp +++ b/src/jit/codegenarm64.cpp @@ -5011,6 +5011,19 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) case HWIntrinsicInfo::SimdUnaryOp: genHWIntrinsicSimdUnaryOp(node); break; + case HWIntrinsicInfo::SimdBinaryRMWOp: + genHWIntrinsicSimdBinaryRMWOp(node); + break; + case HWIntrinsicInfo::SimdTernaryRMWOp: + genHWIntrinsicSimdTernaryRMWOp(node); + break; + case HWIntrinsicInfo::Sha1HashOp: + genHWIntrinsicShaHashOp(node); + break; + case HWIntrinsicInfo::Sha1RotateOp: + genHWIntrinsicShaRotateOp(node); + break; + default: NYI("HWIntrinsic form not implemented"); } @@ -5579,6 +5592,214 @@ void CodeGen::genHWIntrinsicSimdUnaryOp(GenTreeHWIntrinsic* node) genProduceReg(node); } +//------------------------------------------------------------------------ +// genHWIntrinsicSimdBinaryRMWOp: +// +// Produce code for a GT_HWIntrinsic node with form SimdBinaryRMWOp. +// +// Consumes two SIMD operands and produces a SIMD result. +// First operand is both source and destination. +// +// Arguments: +// node - the GT_HWIntrinsic node +// +// Return Value: +// None. +// +void CodeGen::genHWIntrinsicSimdBinaryRMWOp(GenTreeHWIntrinsic* node) +{ + GenTree* op1 = node->gtGetOp1(); + GenTree* op2 = node->gtGetOp2(); + var_types baseType = node->gtSIMDBaseType; + regNumber targetReg = node->gtRegNum; + + assert(targetReg != REG_NA); + + genConsumeOperands(node); + + regNumber op1Reg = op1->gtRegNum; + regNumber op2Reg = op2->gtRegNum; + + assert(genIsValidFloatReg(op1Reg)); + assert(genIsValidFloatReg(op2Reg)); + assert(genIsValidFloatReg(targetReg)); + + instruction ins = getOpForHWIntrinsic(node, baseType); + assert(ins != INS_invalid); + + bool is16Byte = (node->gtSIMDSize > 8); + emitAttr attr = is16Byte ? EA_16BYTE : EA_8BYTE; + insOpts opt = genGetSimdInsOpt(is16Byte, baseType); + + if (targetReg != op1Reg) + { + getEmitter()->emitIns_R_R(INS_mov, attr, targetReg, op1Reg); + } + getEmitter()->emitIns_R_R(ins, attr, targetReg, op2Reg, opt); + + genProduceReg(node); +} + +//------------------------------------------------------------------------ +// genHWIntrinsicSimdTernaryRMWOp: +// +// Produce code for a GT_HWIntrinsic node with form SimdTernaryRMWOp +// +// Consumes three SIMD operands and produces a SIMD result. +// First operand is both source and destination. +// +// Arguments: +// node - the GT_HWIntrinsic node +// +// Return Value: +// None. +// +void CodeGen::genHWIntrinsicSimdTernaryRMWOp(GenTreeHWIntrinsic* node) +{ + GenTreeArgList* argList = node->gtGetOp1()->AsArgList(); + GenTree* op1 = argList->Current(); + GenTree* op2 = argList->Rest()->Current(); + GenTree* op3 = argList->Rest()->Rest()->Current(); + var_types baseType = node->gtSIMDBaseType; + regNumber targetReg = node->gtRegNum; + + assert(targetReg != REG_NA); + var_types targetType = node->TypeGet(); + + genConsumeRegs(op1); + genConsumeRegs(op2); + genConsumeRegs(op3); + + regNumber op1Reg = op1->gtRegNum; + regNumber op2Reg = op2->gtRegNum; + regNumber op3Reg = op3->gtRegNum; + + assert(genIsValidFloatReg(op1Reg)); + assert(genIsValidFloatReg(op2Reg)); + assert(genIsValidFloatReg(op3Reg)); + assert(genIsValidFloatReg(targetReg)); + assert(targetReg != op2Reg); + assert(targetReg != op3Reg); + + instruction ins = getOpForHWIntrinsic(node, baseType); + assert(ins != INS_invalid); + + bool is16Byte = (node->gtSIMDSize > 8); + emitAttr attr = is16Byte ? EA_16BYTE : EA_8BYTE; + + if (targetReg != op1Reg) + { + getEmitter()->emitIns_R_R(INS_mov, attr, targetReg, op1Reg); + } + + getEmitter()->emitIns_R_R_R(ins, attr, targetReg, op2Reg, op3Reg); + + genProduceReg(node); +} + +//------------------------------------------------------------------------ +// genHWIntrinsicShaHashOp: +// +// Produce code for a GT_HWIntrinsic node with form Sha1HashOp. +// Used in Arm64 SHA1 Hash operations. +// +// Consumes three operands and returns a Simd result. +// First Simd operand is both source and destination. +// Second Operand is an unsigned int. +// Third operand is a simd operand. + +// Arguments: +// node - the GT_HWIntrinsic node +// +// Return Value: +// None. +// +void CodeGen::genHWIntrinsicShaHashOp(GenTreeHWIntrinsic* node) +{ + GenTreeArgList* argList = node->gtGetOp1()->AsArgList(); + GenTree* op1 = argList->Current(); + GenTree* op2 = argList->Rest()->Current(); + GenTree* op3 = argList->Rest()->Rest()->Current(); + var_types baseType = node->gtSIMDBaseType; + regNumber targetReg = node->gtRegNum; + + assert(targetReg != REG_NA); + var_types targetType = node->TypeGet(); + + genConsumeRegs(op1); + genConsumeRegs(op2); + genConsumeRegs(op3); + + regNumber op1Reg = op1->gtRegNum; + regNumber op2Reg = op2->gtRegNum; + regNumber op3Reg = op3->gtRegNum; + + assert(genIsValidFloatReg(op1Reg)); + assert(genIsValidFloatReg(op3Reg)); + assert(targetReg != op2Reg); + assert(targetReg != op3Reg); + + instruction ins = getOpForHWIntrinsic(node, baseType); + assert(ins != INS_invalid); + + bool is16Byte = (node->gtSIMDSize > 8); + emitAttr attr = is16Byte ? EA_16BYTE : EA_8BYTE; + + assert(genIsValidIntReg(op2Reg)); + regNumber elementReg = op2->gtRegNum; + regNumber tmpReg = node->GetSingleTempReg(RBM_ALLFLOAT); + + getEmitter()->emitIns_R_R(INS_fmov, EA_4BYTE, tmpReg, elementReg); + + if (targetReg != op1Reg) + { + getEmitter()->emitIns_R_R(INS_mov, attr, targetReg, op1Reg); + } + + getEmitter()->emitIns_R_R_R(ins, attr, targetReg, tmpReg, op3Reg); + + genProduceReg(node); +} + +//------------------------------------------------------------------------ +// genHWIntrinsicShaRotateOp: +// +// Produce code for a GT_HWIntrinsic node with form Sha1RotateOp. +// Used in Arm64 SHA1 Rotate operations. +// +// Consumes one integer operand and returns unsigned int result. +// +// Arguments: +// node - the GT_HWIntrinsic node +// +// Return Value: +// None. +// +void CodeGen::genHWIntrinsicShaRotateOp(GenTreeHWIntrinsic* node) +{ + GenTree* op1 = node->gtGetOp1(); + regNumber targetReg = node->gtRegNum; + emitAttr attr = emitActualTypeSize(node); + + assert(targetReg != REG_NA); + var_types targetType = node->TypeGet(); + + genConsumeOperands(node); + + regNumber op1Reg = op1->gtRegNum; + + instruction ins = getOpForHWIntrinsic(node, node->TypeGet()); + assert(ins != INS_invalid); + + regNumber elementReg = op1->gtRegNum; + regNumber tmpReg = node->GetSingleTempReg(RBM_ALLFLOAT); + getEmitter()->emitIns_R_R(INS_fmov, EA_4BYTE, tmpReg, elementReg); + getEmitter()->emitIns_R_R(ins, EA_4BYTE, tmpReg, tmpReg); + getEmitter()->emitIns_R_R(INS_fmov, attr, targetReg, tmpReg); + + genProduceReg(node); +} + #endif // FEATURE_HW_INTRINSICS /***************************************************************************** diff --git a/src/jit/codegenlinear.h b/src/jit/codegenlinear.h index 3b37684fff2e..0d99178639df 100644 --- a/src/jit/codegenlinear.h +++ b/src/jit/codegenlinear.h @@ -145,6 +145,10 @@ void genHWIntrinsicSimdInsertOp(GenTreeHWIntrinsic* node); void genHWIntrinsicSimdSelectOp(GenTreeHWIntrinsic* node); void genHWIntrinsicSimdSetAllOp(GenTreeHWIntrinsic* node); void genHWIntrinsicSimdUnaryOp(GenTreeHWIntrinsic* node); +void genHWIntrinsicSimdBinaryRMWOp(GenTreeHWIntrinsic* node); +void genHWIntrinsicSimdTernaryRMWOp(GenTreeHWIntrinsic* node); +void genHWIntrinsicShaHashOp(GenTreeHWIntrinsic* node); +void genHWIntrinsicShaRotateOp(GenTreeHWIntrinsic* node); template void genHWIntrinsicSwitchTable(regNumber swReg, regNumber tmpReg, int swMax, HWIntrinsicSwitchCaseBody emitSwCase); #endif // defined(_TARGET_XARCH_) diff --git a/src/jit/emitarm64.cpp b/src/jit/emitarm64.cpp index 46782aefb3fb..7d5af2e0069a 100644 --- a/src/jit/emitarm64.cpp +++ b/src/jit/emitarm64.cpp @@ -427,6 +427,12 @@ void emitter::emitInsSanityCheck(instrDesc* id) assert(isValidImmCondFlags(emitGetInsSC(id))); break; + case IF_DR_2J: // DR_2J ................ ......nnnnnddddd Sd Sn (sha1h) + assert(isValidGeneralDatasize(id->idOpSize())); + assert(isVectorRegister(id->idReg1())); + assert(isVectorRegister(id->idReg2())); + break; + case IF_DR_3A: // DR_3A X..........mmmmm ......nnnnnmmmmm Rd Rn Rm assert(isValidGeneralDatasize(id->idOpSize())); assert(isIntegerRegister(id->idReg1())); // SP @@ -548,6 +554,7 @@ void emitter::emitInsSanityCheck(instrDesc* id) case IF_DV_2A: // DV_2A .Q.......X...... ......nnnnnddddd Vd Vn (fabs, fcvt - vector) case IF_DV_2M: // DV_2M .Q......XX...... ......nnnnnddddd Vd Vn (abs, neg - vector) + case IF_DV_2P: // DV_2P ................ ......nnnnnddddd Vd Vn (aes*, sha1su1) assert(isValidVectorDatasize(id->idOpSize())); assert(isValidArrangement(id->idOpSize(), id->idInsOpt())); assert(isVectorRegister(id->idReg1())); @@ -758,6 +765,14 @@ void emitter::emitInsSanityCheck(instrDesc* id) assert(isVectorRegister(id->idReg3())); break; + case IF_DV_3F: // DV_3F ...........mmmmm ......nnnnnddddd Vd Vn Vm + assert(isValidVectorDatasize(id->idOpSize())); + assert(isValidArrangement(id->idOpSize(), id->idInsOpt())); + assert(isVectorRegister(id->idReg1())); + assert(isVectorRegister(id->idReg2())); + assert(isVectorRegister(id->idReg3())); + break; + case IF_DV_4A: // DR_4A .........X.mmmmm .aaaaannnnnddddd Rd Rn Rm Ra (scalar) assert(isValidGeneralDatasize(id->idOpSize())); assert(isVectorRegister(id->idReg1())); @@ -811,6 +826,7 @@ bool emitter::emitInsMayWriteToGCReg(instrDesc* id) case IF_DR_3C: // DR_3C X..........mmmmm xxxsssnnnnnddddd Rd Rn Rm ext(Rm) LSL imm(0-4) case IF_DR_3D: // DR_3D X..........mmmmm cccc..nnnnnddddd Rd Rn Rm cond case IF_DR_3E: // DR_3E X........X.mmmmm ssssssnnnnnddddd Rd Rn Rm imm(0-63) + case IF_DV_3F: // DV_3F ...........mmmmm ......nnnnnddddd Vd Vn Vm (vector) - Vd both source and dest case IF_DR_4A: // DR_4A X..........mmmmm .aaaaannnnnddddd Rd Rn Rm Ra @@ -819,16 +835,19 @@ bool emitter::emitInsMayWriteToGCReg(instrDesc* id) return true; - case IF_DV_2C: // DV_2C .Q.........iiiii ......nnnnnddddd Vd Rn (dup/ins - vector from general) - case IF_DV_2D: // DV_2D .Q.........iiiii ......nnnnnddddd Vd Vn[] (dup - vector) - case IF_DV_2E: // DV_2E ...........iiiii ......nnnnnddddd Vd Vn[] (dup - scalar) - case IF_DV_2F: // DV_2F ...........iiiii .jjjj.nnnnnddddd Vd[] Vn[] (ins - element) - case IF_DV_2G: // DV_2G .........X...... ......nnnnnddddd Vd Vn (fmov, fcvtXX - register) - case IF_DV_2I: // DV_2I X........X...... ......nnnnnddddd Vd Rn (fmov - from general) - case IF_DV_2J: // DV_2J ........SS.....D D.....nnnnnddddd Vd Vn (fcvt) - case IF_DV_2K: // DV_2K .........X.mmmmm ......nnnnn..... Vn Vm (fcmp) - case IF_DV_2L: // DV_2L ........XX...... ......nnnnnddddd Vd Vn (abs, neg - scalar) - case IF_DV_2M: // DV_2M .Q......XX...... ......nnnnnddddd Vd Vn (abs, neg - vector) + case IF_DV_2C: // DV_2C .Q.........iiiii ......nnnnnddddd Vd Rn (dup/ins - vector from general) + case IF_DV_2D: // DV_2D .Q.........iiiii ......nnnnnddddd Vd Vn[] (dup - vector) + case IF_DV_2E: // DV_2E ...........iiiii ......nnnnnddddd Vd Vn[] (dup - scalar) + case IF_DV_2F: // DV_2F ...........iiiii .jjjj.nnnnnddddd Vd[] Vn[] (ins - element) + case IF_DV_2G: // DV_2G .........X...... ......nnnnnddddd Vd Vn (fmov, fcvtXX - register) + case IF_DV_2I: // DV_2I X........X...... ......nnnnnddddd Vd Rn (fmov - from general) + case IF_DV_2J: // DV_2J ........SS.....D D.....nnnnnddddd Vd Vn (fcvt) + case IF_DV_2K: // DV_2K .........X.mmmmm ......nnnnn..... Vn Vm (fcmp) + case IF_DV_2L: // DV_2L ........XX...... ......nnnnnddddd Vd Vn (abs, neg - scalar) + case IF_DV_2M: // DV_2M .Q......XX...... ......nnnnnddddd Vd Vn (abs, neg - vector) + case IF_DV_2P: // DV_2P ................ ......nnnnnddddd Vd Vn (aes*, sha1su1) - Vd both source and + // destination + case IF_DV_3A: // DV_3A .Q......XX.mmmmm ......nnnnnddddd Vd Vn Vm (vector) case IF_DV_3AI: // DV_3AI .Q......XXLMmmmm ....H.nnnnnddddd Vd Vn Vm[] (vector) case IF_DV_3B: // DV_3B .Q.......X.mmmmm ......nnnnnddddd Vd Vn Vm (vector) @@ -1951,6 +1970,7 @@ emitter::code_t emitter::emitInsCode(instruction ins, insFormat fmt) case IF_DR_2G: case IF_DR_2H: case IF_DR_2I: + case IF_DR_2J: case IF_DR_3A: case IF_DR_3B: case IF_DR_3C: @@ -1975,6 +1995,7 @@ emitter::code_t emitter::emitInsCode(instruction ins, insFormat fmt) case IF_DV_2M: case IF_DV_2N: case IF_DV_2O: + case IF_DV_2P: case IF_DV_3A: case IF_DV_3AI: case IF_DV_3B: @@ -1983,6 +2004,7 @@ emitter::code_t emitter::emitInsCode(instruction ins, insFormat fmt) case IF_DV_3D: case IF_DV_3DI: case IF_DV_3E: + case IF_DV_3F: case IF_DV_4A: case IF_SN_0A: case IF_SI_0A: @@ -4275,6 +4297,34 @@ void emitter::emitIns_R_R( fmt = IF_DV_2G; } break; + case INS_aesd: + case INS_aese: + case INS_aesmc: + case INS_aesimc: + assert(isVectorRegister(reg1)); + assert(isVectorRegister(reg2)); + assert(isValidVectorDatasize(size)); + elemsize = optGetElemsize(opt); + assert(elemsize == EA_1BYTE); + fmt = IF_DV_2P; + break; + + case INS_sha1h: + assert(insOptsNone(opt)); + assert(isVectorRegister(reg1)); + assert(isVectorRegister(reg2)); + fmt = IF_DR_2J; + break; + + case INS_sha256su0: + case INS_sha1su1: + assert(isVectorRegister(reg1)); + assert(isVectorRegister(reg2)); + assert(isValidVectorDatasize(size)); + elemsize = optGetElemsize(opt); + assert(elemsize == EA_4BYTE); + fmt = IF_DV_2P; + break; default: unreached(); @@ -5338,6 +5388,26 @@ void emitter::emitIns_R_R_R( fmt = IF_LS_3D; break; + case INS_sha256h: + case INS_sha256h2: + case INS_sha256su1: + case INS_sha1su0: + case INS_sha1c: + case INS_sha1p: + case INS_sha1m: + assert(isValidVectorDatasize(size)); + assert(isVectorRegister(reg1)); + assert(isVectorRegister(reg2)); + assert(isVectorRegister(reg3)); + if (opt == INS_OPTS_NONE) + { + elemsize = EA_4BYTE; + opt = optMakeArrangement(size, elemsize); + } + assert(isValidArrangement(size, opt)); + fmt = IF_DV_3F; + break; + default: unreached(); break; @@ -9491,6 +9561,13 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) } break; + case IF_DR_2J: // DR_2J ................ ......nnnnnddddd Sd Sn (sha1h) + code = emitInsCode(ins, fmt); + code |= insEncodeReg_Vd(id->idReg1()); // ddddd + code |= insEncodeReg_Vn(id->idReg2()); // nnnnn + dst += emitOutput_Instr(dst, code); + break; + case IF_DR_3A: // DR_3A X..........mmmmm ......nnnnnmmmmm Rd Rn Rm code = emitInsCode(ins, fmt); code |= insEncodeDatasize(id->idOpSize()); // X @@ -9808,6 +9885,14 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) dst += emitOutput_Instr(dst, code); break; + case IF_DV_2P: // DV_2P ............... ......nnnnnddddd Vd Vn (aes*, sha1su1) + elemsize = optGetElemsize(id->idInsOpt()); + code = emitInsCode(ins, fmt); + code |= insEncodeReg_Vd(id->idReg1()); // ddddd + code |= insEncodeReg_Vn(id->idReg2()); // nnnnn + dst += emitOutput_Instr(dst, code); + break; + case IF_DV_3A: // DV_3A .Q......XX.mmmmm ......nnnnnddddd Vd Vn Vm (vector) code = emitInsCode(ins, fmt); elemsize = optGetElemsize(id->idInsOpt()); @@ -9890,6 +9975,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) break; case IF_DV_3E: // DV_3E ...........mmmmm ......nnnnnddddd Vd Vn Vm (scalar) + case IF_DV_3F: // DV_3F ...........mmmmm ......nnnnnddddd Vd Vn Vm (vector) - source dest regs overlap code = emitInsCode(ins, fmt); code |= insEncodeReg_Vd(id->idReg1()); // ddddd code |= insEncodeReg_Vn(id->idReg2()); // nnnnn @@ -11039,6 +11125,7 @@ void emitter::emitDispIns( break; case IF_DR_2E: // DR_2E X..........mmmmm ...........ddddd Rd Rm + case IF_DR_2J: // DR_2J ................ ......nnnnnddddd Sd Sn emitDispReg(id->idReg1(), size, true); emitDispReg(id->idReg2(), size, false); break; @@ -11196,6 +11283,7 @@ void emitter::emitDispIns( case IF_DV_2A: // DV_2A .Q.......X...... ......nnnnnddddd Vd Vn (fabs, fcvt - vector) case IF_DV_2M: // DV_2M .Q......XX...... ......nnnnnddddd Vd Vn (abs, neg - vector) + case IF_DV_2P: // DV_2P ................ ......nnnnnddddd Vd Vn (aes*, sha1su1) emitDispVectorReg(id->idReg1(), id->idInsOpt(), true); emitDispVectorReg(id->idReg2(), id->idInsOpt(), false); break; @@ -11346,6 +11434,29 @@ void emitter::emitDispIns( emitDispReg(id->idReg3(), size, false); break; + case IF_DV_3F: // DV_3F ..........mmmmm ......nnnnnddddd Vd Vn Vm (vector) + if ((ins == INS_sha1c) || (ins == INS_sha1m) || (ins == INS_sha1p)) + { + // Qd, Sn, Vm (vector) + emitDispReg(id->idReg1(), size, true); + emitDispReg(id->idReg2(), EA_4BYTE, true); + emitDispVectorReg(id->idReg3(), id->idInsOpt(), false); + } + else if ((ins == INS_sha256h) || (ins == INS_sha256h2)) + { + // Qd Qn Vm (vector) + emitDispReg(id->idReg1(), size, true); + emitDispReg(id->idReg2(), size, true); + emitDispVectorReg(id->idReg3(), id->idInsOpt(), false); + } + else + { + emitDispVectorReg(id->idReg1(), id->idInsOpt(), true); + emitDispVectorReg(id->idReg2(), id->idInsOpt(), true); + emitDispVectorReg(id->idReg3(), id->idInsOpt(), false); + } + break; + case IF_DV_3DI: // DV_3DI .........XLmmmmm ....H.nnnnnddddd Vd Vn Vm[] (scalar by elem) emitDispReg(id->idReg1(), size, true); emitDispReg(id->idReg2(), size, true); diff --git a/src/jit/emitfmtsarm64.h b/src/jit/emitfmtsarm64.h index 49b2dffd601b..891f5b343050 100644 --- a/src/jit/emitfmtsarm64.h +++ b/src/jit/emitfmtsarm64.h @@ -159,6 +159,7 @@ IF_DEF(DR_2F, IS_NONE, NONE) // DR_2F X.......sh.mmmmm ssssss.....ddddd R IF_DEF(DR_2G, IS_NONE, NONE) // DR_2G X............... ......nnnnnddddd Rd Rn IF_DEF(DR_2H, IS_NONE, NONE) // DR_2H X........X...... ......nnnnnddddd Rd Rn IF_DEF(DR_2I, IS_NONE, NONE) // DR_2I X..........mmmmm cccc..nnnnn.nzcv Rn Rm nzcv cond +IF_DEF(DR_2J, IS_NONE, NONE) // DR_2J ................ ......nnnnnddddd Sd Sn IF_DEF(DR_3A, IS_NONE, NONE) // DR_3A X..........mmmmm ......nnnnnddddd Rd Rn Rm IF_DEF(DR_3B, IS_NONE, NONE) // DR_3B X.......sh.mmmmm ssssssnnnnnddddd Rd Rn Rm {LSL,LSR,ASR} imm(0-63) @@ -188,6 +189,7 @@ IF_DEF(DV_2L, IS_NONE, NONE) // DV_2L ........XX...... ......nnnnnddddd V IF_DEF(DV_2M, IS_NONE, NONE) // DV_2M .Q......XX...... ......nnnnnddddd Vd Vn (abs, neg - vector) IF_DEF(DV_2N, IS_NONE, NONE) // DV_2N .........iiiiiii ......nnnnnddddd Vd Vn imm (shift - scalar) IF_DEF(DV_2O, IS_NONE, NONE) // DV_2O .Q.......iiiiiii ......nnnnnddddd Vd Vn imm (shift - vector) +IF_DEF(DV_2P, IS_NONE, NONE) // DV_2P .,.............. ......nnnnnddddd Vd Vn (Vd used as both source and destination) IF_DEF(DV_3A, IS_NONE, NONE) // DV_3A .Q......XX.mmmmm ......nnnnnddddd Vd Vn Vm (vector) IF_DEF(DV_3AI, IS_NONE, NONE) // DV_3AI .Q......XXLMmmmm ....H.nnnnnddddd Vd Vn Vm[] (vector by elem) @@ -197,6 +199,7 @@ IF_DEF(DV_3C, IS_NONE, NONE) // DV_3C .Q.........mmmmm ......nnnnnddddd IF_DEF(DV_3D, IS_NONE, NONE) // DV_3D .........X.mmmmm ......nnnnnddddd Vd Vn Vm (scalar) IF_DEF(DV_3DI, IS_NONE, NONE) // DV_3DI .........XLmmmmm ....H.nnnnnddddd Vd Vn Vm[] (scalar by elem) IF_DEF(DV_3E, IS_NONE, NONE) // DV_3E ...........mmmmm ......nnnnnddddd Vd Vn Vm (scalar) +IF_DEF(DV_3F, IS_NONE, NONE) // DV_3F ...........mmmmm ......nnnnnddddd Qd Sn Vm (Qd used as both source and destination) IF_DEF(DV_4A, IS_NONE, NONE) // DV_4A .........X.mmmmm .aaaaannnnnddddd Vd Vn Vm Va (scalar) diff --git a/src/jit/hwintrinsicArm64.cpp b/src/jit/hwintrinsicArm64.cpp index 394ab21c979f..7f5f53220c2a 100644 --- a/src/jit/hwintrinsicArm64.cpp +++ b/src/jit/hwintrinsicArm64.cpp @@ -166,6 +166,9 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, case HWIntrinsicInfo::SimdSelectOp: case HWIntrinsicInfo::SimdSetAllOp: case HWIntrinsicInfo::SimdUnaryOp: + case HWIntrinsicInfo::SimdBinaryRMWOp: + case HWIntrinsicInfo::SimdTernaryRMWOp: + case HWIntrinsicInfo::Sha1HashOp: simdClass = sig->retTypeClass; break; case HWIntrinsicInfo::SimdExtractOp: @@ -196,6 +199,7 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, return impUnsupportedHWIntrinsic(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, method, sig, mustExpand); case HWIntrinsicInfo::SimdBinaryOp: + case HWIntrinsicInfo::SimdBinaryRMWOp: // op1 is the first operand // op2 is the second operand op2 = impSIMDPopStack(simdType); @@ -203,6 +207,7 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, return gtNewSimdHWIntrinsicNode(simdType, op1, op2, intrinsic, simdBaseType, simdSizeBytes); + case HWIntrinsicInfo::SimdTernaryRMWOp: case HWIntrinsicInfo::SimdSelectOp: // op1 is the first operand // op2 is the second operand @@ -246,6 +251,17 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, return gtNewSimdHWIntrinsicNode(simdType, op1, op2, op3, intrinsic, simdBaseType, simdSizeBytes); + case HWIntrinsicInfo::Sha1HashOp: + op3 = impSIMDPopStack(simdType); + op2 = impPopStack().val; + op1 = impSIMDPopStack(simdType); + + return gtNewSimdHWIntrinsicNode(simdType, op1, op2, op3, intrinsic, simdBaseType, simdSizeBytes); + + case HWIntrinsicInfo::Sha1RotateOp: + assert(sig->numArgs == 1); + return gtNewScalarHWIntrinsicNode(TYP_UINT, impPopStack().val, NI_ARM64_Sha1FixedRotate); + default: JITDUMP("Not implemented hardware intrinsic form"); assert(!"Unimplemented SIMD Intrinsic form"); diff --git a/src/jit/hwintrinsicArm64.h b/src/jit/hwintrinsicArm64.h index 7fcde605b8b8..b7bc6e7f4c27 100644 --- a/src/jit/hwintrinsicArm64.h +++ b/src/jit/hwintrinsicArm64.h @@ -18,16 +18,22 @@ struct HWIntrinsicInfo IsSupported, // The IsSupported property will use this form Unsupported, // Any intrisic which is unsupported and must throw PlatformNotSupportException will use this form // Non SIMD forms - UnaryOp, // Non SIMD intrinsics which take a single argument - CrcOp, // Crc intrinsics. + UnaryOp, // Non SIMD intrinsics which take a single argument + CrcOp, // Crc intrinsics. + Sha1RotateOp, // SHA1 Hash Rotate intrinsics. Takes hash index unsigned int and returns unsigned int. + // SIMD common forms - SimdBinaryOp, // SIMD intrinsics which take two vector operands and return a vector - SimdUnaryOp, // SIMD intrinsics which take one vector operand and return a vector + SimdBinaryOp, // SIMD intrinsics which take two vector operands and return a vector + SimdUnaryOp, // SIMD intrinsics which take one vector operand and return a vector + SimdBinaryRMWOp, // Same as SimdBinaryOp , with first source vector used as destination vector also. + SimdTernaryRMWOp, // SIMD intrinsics which take three vector operands and return a vector , + // with destination vector same as first source vector // SIMD custom forms SimdExtractOp, // SIMD intrinsics which take one vector operand and a lane index and return an element SimdInsertOp, // SIMD intrinsics which take one vector operand and a lane index and value and return a vector SimdSelectOp, // BitwiseSelect intrinsic which takes three vector operands and returns a vector SimdSetAllOp, // Simd intrinsics which take one numeric operand and return a vector + Sha1HashOp // SIMD instrisics for SHA1 Hash operations. Takes two vectors and hash index and returns vector }; // Flags will be used to handle secondary meta-data which will help diff --git a/src/jit/hwintrinsiclistArm64.h b/src/jit/hwintrinsiclistArm64.h index 082bfe15944c..ffc8d39c526c 100644 --- a/src/jit/hwintrinsiclistArm64.h +++ b/src/jit/hwintrinsiclistArm64.h @@ -23,7 +23,7 @@ HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_JSCVT , Jscvt ) HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_LRCPC , Lrcpc ) HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_PMULL , Pmull ) HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SHA1 , Sha1 ) -HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SHA2 , Sha2 ) +HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SHA256 , Sha256 ) HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SHA512 , Sha512 ) HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SHA3 , Sha3 ) HARDWARE_INTRINSIC_CLASS(JIT_FLAG_HAS_ARM64_SIMD , Simd ) @@ -80,7 +80,25 @@ HARDWARE_INTRINSIC(NI_ARM64_SIMD_GetItem, Simd, Extract, HARDWARE_INTRINSIC(NI_ARM64_SIMD_SetItem, Simd, Insert, SimdInsertOp, INS_mov, INS_mov, INS_mov, None ) HARDWARE_INTRINSIC(NI_ARM64_SIMD_SetAllVector64, Simd, SetAllVector64, SimdSetAllOp, INS_dup, INS_dup, INS_dup, None ) HARDWARE_INTRINSIC(NI_ARM64_SIMD_SetAllVector128, Simd, SetAllVector128, SimdSetAllOp, INS_dup, INS_dup, INS_dup, None ) +//Aes +HARDWARE_INTRINSIC(NI_ARM64_AesEncrypt, Aes, Encrypt, SimdBinaryRMWOp, INS_invalid, INS_invalid, INS_aese, None ) +HARDWARE_INTRINSIC(NI_ARM64_AesDecrypt, Aes, Decrypt, SimdBinaryRMWOp, INS_invalid, INS_invalid, INS_aesd, None ) +HARDWARE_INTRINSIC(NI_ARM64_AesMixColumns, Aes, MixColumns, SimdUnaryOp, INS_invalid, INS_invalid, INS_aesmc, None ) +HARDWARE_INTRINSIC(NI_ARM64_AesInvMixColumns, Aes, InverseMixColumns, SimdUnaryOp, INS_invalid, INS_invalid, INS_aesimc, None ) +//Sha1 +HARDWARE_INTRINSIC(NI_ARM64_Sha1Choose, Sha1, HashChoose, Sha1HashOp, INS_invalid, INS_invalid, INS_sha1c, None ) +HARDWARE_INTRINSIC(NI_ARM64_Sha1Parity, Sha1, HashParity, Sha1HashOp, INS_invalid, INS_invalid, INS_sha1p, None ) +HARDWARE_INTRINSIC(NI_ARM64_Sha1Majority, Sha1, HashMajority, Sha1HashOp, INS_invalid, INS_invalid, INS_sha1m, None ) +HARDWARE_INTRINSIC(NI_ARM64_Sha1FixedRotate, Sha1, FixedRotate, Sha1RotateOp, INS_invalid, INS_invalid, INS_sha1h, None ) +HARDWARE_INTRINSIC(NI_ARM64_Sha1SchedulePart1, Sha1, SchedulePart1, SimdTernaryRMWOp, INS_invalid, INS_invalid, INS_sha1su0, None ) +HARDWARE_INTRINSIC(NI_ARM64_Sha1SchedulePart2, Sha1, SchedulePart2, SimdBinaryRMWOp, INS_invalid, INS_invalid, INS_sha1su1, None ) + +//Sha256 +HARDWARE_INTRINSIC(NI_ARM64_Sha256HashLower, Sha256, HashLower, SimdTernaryRMWOp, INS_invalid, INS_invalid, INS_sha256h, None ) +HARDWARE_INTRINSIC(NI_ARM64_Sha256HashUpper, Sha256, HashUpper, SimdTernaryRMWOp, INS_invalid, INS_invalid, INS_sha256h2, None ) +HARDWARE_INTRINSIC(NI_ARM64_Sha256SchedulePart1, Sha256, SchedulePart1, SimdBinaryRMWOp, INS_invalid, INS_invalid, INS_sha256su0, None ) +HARDWARE_INTRINSIC(NI_ARM64_Sha256SchedulePart2, Sha256, SchedulePart2, SimdTernaryRMWOp, INS_invalid, INS_invalid, INS_sha256su1, None ) #endif diff --git a/src/jit/instr.h b/src/jit/instr.h index 7838fa44e4f8..c7be413d0578 100644 --- a/src/jit/instr.h +++ b/src/jit/instr.h @@ -314,7 +314,7 @@ enum InstructionSet InstructionSet_Lrcpc, // ID_AA64ISAR1_EL1.LRCPC is 1 or better InstructionSet_Pmull, // ID_AA64ISAR0_EL1.AES is 2 or better InstructionSet_Sha1, // ID_AA64ISAR0_EL1.SHA1 is 1 or better - InstructionSet_Sha2, // ID_AA64ISAR0_EL1.SHA2 is 1 or better + InstructionSet_Sha256, // ID_AA64ISAR0_EL1.SHA2 is 1 or better InstructionSet_Sha512, // ID_AA64ISAR0_EL1.SHA2 is 2 or better InstructionSet_Sha3, // ID_AA64ISAR0_EL1.SHA3 is 1 or better InstructionSet_Simd, // ID_AA64PFR0_EL1.AdvSIMD is 0 or better diff --git a/src/jit/instrsarm64.h b/src/jit/instrsarm64.h index 433bde7f5f12..8f5971bcb79a 100644 --- a/src/jit/instrsarm64.h +++ b/src/jit/instrsarm64.h @@ -828,6 +828,18 @@ INST1(cset, "cset", 0, 0, IF_DR_1D, 0x1A9F07E0) INST1(csetm, "csetm", 0, 0, IF_DR_1D, 0x5A9F03E0) // csetm Rd,cond DR_1D X101101010011111 cccc0011111ddddd 5A9F 03E0 Rd cond +INST1(aese, "aese", 0, 0, IF_DV_2P, 0x4E284800) + // aese Vd.16B,Vn.16B DV_2P 0100111000101000 010010nnnnnddddd 4E28 4800 Vd.16B Vn.16B (vector) + +INST1(aesd, "aesd", 0, 0, IF_DV_2P, 0x4E285800) + // aesd Vd.16B,Vn.16B DV_2P 0100111000101000 010110nnnnnddddd 4E28 5800 Vd.16B Vn.16B (vector) + +INST1(aesmc, "aesmc", 0, 0, IF_DV_2P, 0x4E286800) + // aesmc Vd.16B,Vn.16B DV_2P 0100111000101000 011010nnnnnddddd 4E28 6800 Vd.16B Vn.16B (vector) + +INST1(aesimc, "aesimc", 0, 0, IF_DV_2P, 0x4E287800) + // aesimc Vd.16B,Vn.16B DV_2P 0100111000101000 011110nnnnnddddd 4E28 7800 Vd.16B Vn.16B (vector) + INST1(rev, "rev", 0, 0, IF_DR_2G, 0x5AC00800) // rev Rd,Rm DR_2G X101101011000000 00001Xnnnnnddddd 5AC0 0800 Rd Rn @@ -905,6 +917,36 @@ INST1(asrv, "asrv", 0, 0, IF_DR_3A, 0x1AC02800) INST1(rorv, "rorv", 0, 0, IF_DR_3A, 0x1AC02C00) // rorv Rd,Rn,Rm DR_3A X0011010110mmmmm 001011nnnnnddddd 1AC0 2C00 + +INST1(sha1c, "sha1c", 0, 0, IF_DV_3F, 0x5E000000) + // sha1c Qd, Sn Vm.4S DV_3F 01011110000mmmmm 000000nnnnnddddd 5E00 0000 Qd Sn Vm.4S (vector) + +INST1(sha1m, "sha1m", 0, 0, IF_DV_3F, 0x5E002000) + // sha1m Qd, Sn Vm.4S DV_3F 01011110000mmmmm 001000nnnnnddddd 5E00 0000 Qd Sn Vm.4S (vector) + +INST1(sha1p, "sha1p", 0, 0, IF_DV_3F, 0x5E001000) + // sha1m Qd, Sn Vm.4S DV_3F 01011110000mmmmm 000100nnnnnddddd 5E00 0000 Qd Sn Vm.4S (vector) + +INST1(sha1h, "sha1h", 0, 0, IF_DR_2J, 0x5E280800) + // sha1h Sd, Sn DR_2H 0101111000101000 000010nnnnnddddd 5E28 0800 Sn Sn + +INST1(sha1su0, "sha1su0", 0, 0, IF_DV_3F, 0x5E003000) + // sha1su0 Vd.4S,Vn.4S,Vm.4S DV_3F 01011110000mmmmm 001100nnnnnddddd 5E00 3000 Vd.4S Vn.4S Vm.4S (vector) + +INST1(sha1su1, "sha1su1", 0, 0, IF_DV_2P, 0x5E281800) + // sha1su1 Vd.4S, Vn.4S DV_2P 0101111000101000 000110nnnnnddddd 5E28 1800 Vd.4S Vn.4S (vector) + +INST1(sha256h, "sha256h", 0, 0, IF_DV_3F, 0x5E004000) + // sha256h Qd,Qn,Vm.4S DV_3F 01011110000mmmmm 010000nnnnnddddd 5E00 4000 Qd Qn Vm.4S (vector) + +INST1(sha256h2, "sha256h2", 0, 0, IF_DV_3F, 0x5E005000) + // sha256h Qd,Qn,Vm.4S DV_3F 01011110000mmmmm 010100nnnnnddddd 5E00 5000 Qd Qn Vm.4S (vector) + +INST1(sha256su0, "sha256su0", 0, 0, IF_DV_2P, 0x5E282800) + // sha256su0 Vd.4S,Vn.4S DV_2P 0101111000101000 001010nnnnnddddd 5E28 2800 Vd.4S Vn.4S (vector) + +INST1(sha256su1, "sha256su1", 0, 0, IF_DV_3F, 0x5E006000) + // sha256su1 Vd.4S,Vn.4S,Vm.4S DV_3F 01011110000mmmmm 011000nnnnnddddd 5E00 6000 Vd.4S Vn.4S Vm.4S (vector) INST1(sbfm, "sbfm", 0, 0, IF_DI_2D, 0x13000000) // sbfm Rd,Rn,imr,ims DI_2D X00100110Nrrrrrr ssssssnnnnnddddd 1300 0000 imr, ims diff --git a/src/jit/jitee.h b/src/jit/jitee.h index b36e7661fdf3..903b3cffd91d 100644 --- a/src/jit/jitee.h +++ b/src/jit/jitee.h @@ -107,7 +107,7 @@ class JitFlags JIT_FLAG_HAS_ARM64_LRCPC = 52, // ID_AA64ISAR1_EL1.LRCPC is 1 or better JIT_FLAG_HAS_ARM64_PMULL = 53, // ID_AA64ISAR0_EL1.AES is 2 or better JIT_FLAG_HAS_ARM64_SHA1 = 54, // ID_AA64ISAR0_EL1.SHA1 is 1 or better - JIT_FLAG_HAS_ARM64_SHA2 = 55, // ID_AA64ISAR0_EL1.SHA2 is 1 or better + JIT_FLAG_HAS_ARM64_SHA256 = 55, // ID_AA64ISAR0_EL1.SHA2 is 1 or better JIT_FLAG_HAS_ARM64_SHA512 = 56, // ID_AA64ISAR0_EL1.SHA2 is 2 or better JIT_FLAG_HAS_ARM64_SHA3 = 57, // ID_AA64ISAR0_EL1.SHA3 is 1 or better JIT_FLAG_HAS_ARM64_SIMD = 58, // ID_AA64PFR0_EL1.AdvSIMD is 0 or better diff --git a/src/jit/lsraarm64.cpp b/src/jit/lsraarm64.cpp index 534694518ac7..1f367915d126 100644 --- a/src/jit/lsraarm64.cpp +++ b/src/jit/lsraarm64.cpp @@ -957,6 +957,38 @@ void LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree) switch (compiler->getHWIntrinsicInfo(intrinsicID).form) { + case HWIntrinsicInfo::Sha1HashOp: + info->setInternalCandidates(this, RBM_ALLFLOAT); + info->internalFloatCount = 1; + if (!op2->isContained()) + { + LocationInfoListNode* op2Info = useList.Begin()->Next(); + op2Info->info.isDelayFree = true; + GenTree* op3 = intrinsicTree->gtOp.gtOp1->AsArgList()->Rest()->Rest()->Current(); + assert(!op3->isContained()); + LocationInfoListNode* op3Info = op2Info->Next(); + op3Info->info.isDelayFree = true; + info->hasDelayFreeSrc = true; + info->isInternalRegDelayFree = true; + } + break; + case HWIntrinsicInfo::SimdTernaryRMWOp: + if (!op2->isContained()) + { + LocationInfoListNode* op2Info = useList.Begin()->Next(); + op2Info->info.isDelayFree = true; + GenTree* op3 = intrinsicTree->gtOp.gtOp1->AsArgList()->Rest()->Rest()->Current(); + assert(!op3->isContained()); + LocationInfoListNode* op3Info = op2Info->Next(); + op3Info->info.isDelayFree = true; + info->hasDelayFreeSrc = true; + } + break; + case HWIntrinsicInfo::Sha1RotateOp: + info->setInternalCandidates(this, RBM_ALLFLOAT); + info->internalFloatCount = 1; + break; + case HWIntrinsicInfo::SimdExtractOp: case HWIntrinsicInfo::SimdInsertOp: if (!op2->isContained()) diff --git a/src/mscorlib/System.Private.CoreLib.csproj b/src/mscorlib/System.Private.CoreLib.csproj index aec27ca03244..1da5ccba252c 100644 --- a/src/mscorlib/System.Private.CoreLib.csproj +++ b/src/mscorlib/System.Private.CoreLib.csproj @@ -298,9 +298,15 @@ + + + + + + @@ -669,4 +675,4 @@ - \ No newline at end of file + diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/Arm/Arm64/Aes.PlatformNotSupported.cs b/src/mscorlib/src/System/Runtime/Intrinsics/Arm/Arm64/Aes.PlatformNotSupported.cs new file mode 100644 index 000000000000..0ad9634d192f --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Intrinsics/Arm/Arm64/Aes.PlatformNotSupported.cs @@ -0,0 +1,40 @@ +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace System.Runtime.Intrinsics.Arm.Arm64 +{ + /// + /// This class provides access to the Arm64 AES Crypto intrinsics + /// + /// Arm64 CPU indicate support for this feature by setting + /// ID_AA64ISAR0_EL1.AES is 1 or better + /// + [CLSCompliant(false)] + public static class Aes + { + public static bool IsSupported { get { return false; } } + // + /// Performs AES single round decryption + /// vaesdq_u8 (uint8x16_t data, uint8x16_t key) + /// + public static Vector128 Decrypt(Vector128 value, Vector128 roundKey) { throw new PlatformNotSupportedException(); } + + // + /// Performs AES single round encryption + /// vaeseq_u8 (uint8x16_t data, uint8x16_t key) + /// + public static Vector128 Encrypt(Vector128 value, Vector128 roundKey) { throw new PlatformNotSupportedException(); } + + // + /// Performs AES Mix Columns + /// vaesmcq_u8 (uint8x16_t data) + /// + public static Vector128 MixColumns(Vector128 value) { throw new PlatformNotSupportedException(); } + + // + /// Performs AES inverse mix columns + /// vaesimcq_u8 (uint8x16_t data) + /// + public static Vector128 InverseMixColumns(Vector128 value) { throw new PlatformNotSupportedException(); } + } +} diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/Arm/Arm64/Aes.cs b/src/mscorlib/src/System/Runtime/Intrinsics/Arm/Arm64/Aes.cs new file mode 100644 index 000000000000..24c9342d9180 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Intrinsics/Arm/Arm64/Aes.cs @@ -0,0 +1,40 @@ +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace System.Runtime.Intrinsics.Arm.Arm64 +{ + /// + /// This class provides access to the Arm64 AES Crypto intrinsics + /// + /// Arm64 CPU indicate support for this feature by setting + /// ID_AA64ISAR0_EL1.AES is 1 or better + /// + [CLSCompliant(false)] + public static class Aes + { + public static bool IsSupported { get => IsSupported; } + // + /// Performs AES single round decryption + /// vaesdq_u8 (uint8x16_t data, uint8x16_t key) + /// + public static Vector128 Decrypt(Vector128 value, Vector128 roundKey) => Decrypt(value, roundKey); + + // + /// Performs AES single round encryption + /// vaeseq_u8 (uint8x16_t data, uint8x16_t key) + /// + public static Vector128 Encrypt(Vector128 value, Vector128 roundKey) => Encrypt(value, roundKey); + + // + /// Performs AES Mix Columns + /// vaesmcq_u8 (uint8x16_t data) + /// + public static Vector128 MixColumns(Vector128 value) => MixColumns(value); + + // + /// Performs AES inverse mix columns + /// vaesimcq_u8 (uint8x16_t data) + /// + public static Vector128 InverseMixColumns(Vector128 value) => InverseMixColumns(value); + } +} diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/Arm/Arm64/Sha1.PlatformNotSupported.cs b/src/mscorlib/src/System/Runtime/Intrinsics/Arm/Arm64/Sha1.PlatformNotSupported.cs new file mode 100644 index 000000000000..9b6c5aac6326 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Intrinsics/Arm/Arm64/Sha1.PlatformNotSupported.cs @@ -0,0 +1,54 @@ +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace System.Runtime.Intrinsics.Arm.Arm64 +{ + /// + /// This class provides access to the Arm64 SHA1 Crypto intrinsics + /// + /// Arm64 CPU indicate support for this feature by setting + /// ID_AA64ISAR0_EL1.SHA1 is 1 or better + /// + [CLSCompliant(false)] + public static class Sha1 + { + + public static bool IsSupported { get { return false; } } + + // + /// Performs SHA1 hash update choose form. + /// vsha1cq_u32 (uint32x4_t hash_abcd, uint32_t hash_e, uint32x4_t wk) + /// + public static Vector128 HashChoose(Vector128 hash_abcd, uint hash_e, Vector128wk) { throw new PlatformNotSupportedException(); } + + // + /// Performs SHA1 hash update majority form. + /// vsha1mq_u32 (uint32x4_t hash_abcd, uint32_t hash_e, uint32x4_t wk) + /// + public static Vector128 HashMajority(Vector128 hash_abcd, uint hash_e, Vector128wk) { throw new PlatformNotSupportedException(); } + + // + /// Performs SHA1 hash update parity form. + /// vsha1pq_u32 (uint32x4_t hash_abcd, uint32_t hash_e, uint32x4_t wk) + /// + public static Vector128 HashParity(Vector128 hash_abcd, uint hash_e, Vector128wk) { throw new PlatformNotSupportedException(); } + + // + /// Performs SHA1 fixed rotate + /// vsha1h_u32 (uint32_t hash_e) + /// + public static uint FixedRotate(uint hash_e) { throw new PlatformNotSupportedException(); } + + // + /// Performs SHA1 schedule update 0 + /// vsha1su0q_u32 (uint32x4_t w0_3, uint32x4_t w4_7, uint32x4_t w8_11) + /// + public static Vector128 SchedulePart1(Vector128 w0_3, Vector128 w4_7, Vector128 w8_11) { throw new PlatformNotSupportedException(); } + + // + /// Performs SHA1 schedule update 1 + /// vsha1su1q_u32 (uint32x4_t tw0_3, uint32x4_t w12_15) + /// + public static Vector128 SchedulePart2(Vector128 tw0_3, Vector128 w12_15) { throw new PlatformNotSupportedException(); } + } +} diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/Arm/Arm64/Sha1.cs b/src/mscorlib/src/System/Runtime/Intrinsics/Arm/Arm64/Sha1.cs new file mode 100644 index 000000000000..28cc397ceb6a --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Intrinsics/Arm/Arm64/Sha1.cs @@ -0,0 +1,53 @@ +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace System.Runtime.Intrinsics.Arm.Arm64 +{ + /// + /// This class provides access to the Arm64 SHA1 Crypto intrinsics + /// + /// Arm64 CPU indicate support for this feature by setting + /// ID_AA64ISAR0_EL1.SHA1 is 1 or better + /// + [CLSCompliant(false)] + public static class Sha1 + { + public static bool IsSupported { get => IsSupported; } + + // + /// Performs SHA1 hash update choose form. + /// vsha1cq_u32 (uint32x4_t hash_abcd, uint32_t hash_e, uint32x4_t wk) + /// + public static Vector128 HashChoose(Vector128 hash_abcd, uint hash_e, Vector128wk) => HashChoose(hash_abcd, hash_e, wk); + + // + /// Performs SHA1 hash update majority form. + /// vsha1mq_u32 (uint32x4_t hash_abcd, uint32_t hash_e, uint32x4_t wk) + /// + public static Vector128 HashMajority(Vector128 hash_abcd, uint hash_e, Vector128wk) => HashMajority(hash_abcd, hash_e, wk); + + // + /// Performs SHA1 hash update parity form. + /// vsha1pq_u32 (uint32x4_t hash_abcd, uint32_t hash_e, uint32x4_t wk) + /// + public static Vector128 HashParity(Vector128 hash_abcd, uint hash_e, Vector128wk) => HashParity(hash_abcd, hash_e, wk); + + // + /// Performs SHA1 fixed rotate + /// vsha1h_u32 (uint32_t hash_e) + /// + public static uint FixedRotate(uint hash_e) => FixedRotate(hash_e); + + // + /// Performs SHA1 schedule update 0 + /// vsha1su0q_u32 (uint32x4_t w0_3, uint32x4_t w4_7, uint32x4_t w8_11) + /// + public static Vector128 SchedulePart1(Vector128 w0_3, Vector128 w4_7, Vector128 w8_11) => SchedulePart1(w0_3, w4_7, w8_11); + + // + /// Performs SHA1 schedule update 1 + /// vsha1su1q_u32 (uint32x4_t tw0_3, uint32x4_t w12_15) + /// + public static Vector128 SchedulePart2(Vector128 tw0_3, Vector128 w12_15) => SchedulePart2(tw0_3, w12_15); + } +} diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/Arm/Arm64/Sha256.PlatformNotSupported.cs b/src/mscorlib/src/System/Runtime/Intrinsics/Arm/Arm64/Sha256.PlatformNotSupported.cs new file mode 100644 index 000000000000..052a1d236b5f --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Intrinsics/Arm/Arm64/Sha256.PlatformNotSupported.cs @@ -0,0 +1,41 @@ +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace System.Runtime.Intrinsics.Arm.Arm64 +{ + /// + /// This class provides access to the Arm64 SHA256 Crypto intrinsics + /// + /// Arm64 CPU indicate support for this feature by setting + /// ID_AA64ISAR0_EL1.SHA2 is 1 or better + /// + [CLSCompliant(false)] + public static class Sha256 + { + public static bool IsSupported { get { return false; } } + + // + /// Performs SHA256 hash update (part 1). + /// vsha256hq_u32 (uint32x4_t hash_abcd, uint32x4_t hash_efgh, uint32x4_t wk) + /// + public static Vector128 HashLower(Vector128 hash_abcd, Vector128 hash_efgh, Vector128 wk) { throw new PlatformNotSupportedException(); } + + // + /// Performs SHA256 hash update (part 2). + /// vsha256h2q_u32 (uint32x4_t hash_efgh, uint32x4_t hash_abcd, uint32x4_t wk) + /// + public static Vector128 HashUpper(Vector128 hash_efgh, Vector128 hash_abcd, Vector128 wk) { throw new PlatformNotSupportedException(); } + + // + /// Performs SHA256 schedule update 0 + /// vsha256su0q_u32 (uint32x4_t w0_3, uint32x4_t w4_7) + /// + public static Vector128 SchedulePart1(Vector128 w0_3, Vector128 w4_7) { throw new PlatformNotSupportedException(); } + + // + /// Performs SHA256 schedule update 1 + /// vsha256su1q_u32 (uint32x4_t w0_3, uint32x4_t w8_11, uint32x4_t w12_15) + /// + public static Vector128 SchedulePart2(Vector128 w0_3, Vector128 w8_11, Vector128 w12_15) { throw new PlatformNotSupportedException(); } + } +} diff --git a/src/mscorlib/src/System/Runtime/Intrinsics/Arm/Arm64/Sha256.cs b/src/mscorlib/src/System/Runtime/Intrinsics/Arm/Arm64/Sha256.cs new file mode 100644 index 000000000000..4e7e51db30f3 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Intrinsics/Arm/Arm64/Sha256.cs @@ -0,0 +1,41 @@ +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace System.Runtime.Intrinsics.Arm.Arm64 +{ + /// + /// This class provides access to the Arm64 SHA256 Crypto intrinsics + /// + /// Arm64 CPU indicate support for this feature by setting + /// ID_AA64ISAR0_EL1.SHA2 is 1 or better + /// + [CLSCompliant(false)] + public static class Sha256 + { + public static bool IsSupported { get => IsSupported; } + + // + /// Performs SHA256 hash update (part 1). + /// vsha256hq_u32 (uint32x4_t hash_abcd, uint32x4_t hash_efgh, uint32x4_t wk) + /// + public static Vector128 HashLower(Vector128 hash_abcd, Vector128 hash_efgh, Vector128 wk) => HashLower(hash_abcd, hash_efgh, wk); + + // + /// Performs SHA256 hash update (part 2). + /// vsha256h2q_u32 (uint32x4_t hash_efgh, uint32x4_t hash_abcd, uint32x4_t wk) + /// + public static Vector128 HashUpper(Vector128 hash_efgh, Vector128 hash_abcd, Vector128 wk) => HashUpper(hash_efgh, hash_abcd, wk); + + // + /// Performs SHA256 schedule update 0 + /// vsha256su0q_u32 (uint32x4_t w0_3, uint32x4_t w4_7) + /// + public static Vector128 SchedulePart1(Vector128 w0_3, Vector128 w4_7) => SchedulePart1(w0_3, w4_7); + + // + /// Performs SHA256 schedule update 1 + /// vsha256su1q_u32 (uint32x4_t tw0_3, uint32x4_t w8_11, uint32x4_t w12_15) + /// + public static Vector128 SchedulePart2(Vector128 w0_3, Vector128 w8_11, Vector128 w12_15) => SchedulePart2(w0_3, w8_11, w12_15); + } +} diff --git a/src/pal/src/misc/jitsupport.cpp b/src/pal/src/misc/jitsupport.cpp index 917319d7c508..d3d4f1e5c526 100644 --- a/src/pal/src/misc/jitsupport.cpp +++ b/src/pal/src/misc/jitsupport.cpp @@ -86,7 +86,7 @@ PAL_GetJitCpuCapabilityFlags(CORJIT_FLAGS *flags) #endif #ifdef HWCAP_SHA2 if (hwCap & HWCAP_SHA2) - CPUCompileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_HAS_ARM64_SHA2); + CPUCompileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_HAS_ARM64_SHA256); #endif #ifdef HWCAP_SHA512 if (hwCap & HWCAP_SHA512) diff --git a/tests/src/JIT/HardwareIntrinsics/Arm64/Crypto.cs b/tests/src/JIT/HardwareIntrinsics/Arm64/Crypto.cs new file mode 100644 index 000000000000..a1720d971560 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/Arm64/Crypto.cs @@ -0,0 +1,338 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +#if ARM64_SIMD_API_PENDING_APPROVAL_AND_OR_COREFX_MERGE +using System.Runtime.Intrinsics.Arm.Arm64; +#endif //ARM64_SIMD_API_PENDING_APPROVAL_AND_OR_COREFX_MERGE + +namespace Arm64intrisicsTest +{ + + class Program + { + + struct DataSet + where TBaseType : struct + where TVectorType : new() + { + private static TVectorType _vectorX; + private static TVectorType _vectorY; + private static TVectorType _vectorZ; + + public static TVectorType vectorX { get { return _vectorX; }} + public static TVectorType vectorY { get { return _vectorY; }} + public static TVectorType vectorZ { get { return _vectorZ; }} + + public static TBaseType[] arrayX { get; private set; } + public static TBaseType[] arrayY { get; private set; } + public static TBaseType[] arrayZ { get; private set; } + + public static unsafe void setData(TBaseType[] x, TBaseType[] y) + { + arrayX = x; + arrayY = y; + + GCHandle handleSrc = GCHandle.Alloc(x, GCHandleType.Pinned); + + try + { + var ptrSrc = (byte*) handleSrc.AddrOfPinnedObject().ToPointer(); + + _vectorX = Unsafe.Read(ptrSrc); + } + finally + { + handleSrc.Free(); + } + + handleSrc = GCHandle.Alloc(y, GCHandleType.Pinned); + + try + { + var ptrSrc = (byte*) handleSrc.AddrOfPinnedObject().ToPointer(); + + _vectorY = Unsafe.Read(ptrSrc); + } + finally + { + handleSrc.Free(); + } + } + + public static unsafe void setData(TBaseType[] x, TBaseType[] y, TBaseType[] z) + { + setData(x, y); + arrayZ = z; + + GCHandle handleSrc = GCHandle.Alloc(z, GCHandleType.Pinned); + + try + { + var ptrSrc = (byte*) handleSrc.AddrOfPinnedObject().ToPointer(); + + _vectorZ = Unsafe.Read(ptrSrc); + } + finally + { + handleSrc.Free(); + } + + } + + } + + static unsafe TBaseType[] writeVector(TVectorType src) + where TBaseType : struct + where TVectorType : new() + { + var length = Unsafe.SizeOf() / Unsafe.SizeOf(); + var dst = new TBaseType[length]; + + GCHandle handleSrc = GCHandle.Alloc(src, GCHandleType.Pinned); + GCHandle handleDst = GCHandle.Alloc(dst, GCHandleType.Pinned); + + try + { + var ptrSrc = (byte*) handleSrc.AddrOfPinnedObject().ToPointer(); + var ptrDst = (byte*) handleDst.AddrOfPinnedObject().ToPointer(); + + for (int i = 0; i < Unsafe.SizeOf(); ++i) + { + ptrDst[i] = ptrSrc[i]; + } + } + finally + { + handleSrc.Free(); + handleDst.Free(); + } + + return dst; + } + + static void testCryptoOp(String testCaseDescription, + Func cryptoOp, + TBaseType[] check) + where TBaseType : struct, IComparable + where TVectorType : new() + where TBaseReturnType : struct, IComparable + where TVectorReturnType : new() + { + bool failed = false; + try + { + var vX = DataSet.vectorX; + var vY = DataSet.vectorY; + var vZ = DataSet.vectorZ; + var vResult = cryptoOp(vX, vY, vZ); + + var result = writeVector(vResult); + //Console.WriteLine("res [{0}]", string.Join(", ", result)); + + + for (int i = 0; i < result.Length; i++) + { + + var expected = check[i]; + + if (result[i].CompareTo(expected) != 0) + { + if(!failed) + { + Console.WriteLine($"testCryptoOp<{typeof(TBaseType).Name}, {typeof(TVectorType).Name} >{testCaseDescription}: Check Failed"); + } + Console.WriteLine($"check[{i}] : result[{i}] = {result[i]}, expected {expected}"); + failed = true; + } + } + } + catch + { + Console.WriteLine($"testCryptoOp<{typeof(TBaseType).Name}, {typeof(TVectorType).Name} >{testCaseDescription}: Unexpected exception"); + throw; + } + + if (failed) + { + throw new Exception($"testCryptoOp<{typeof(TBaseType).Name}, {typeof(TVectorType).Name} >{testCaseDescription}: Failed"); + } + else + { + Console.WriteLine($"testCryptoOp<{typeof(TBaseType).Name}, {typeof(TVectorType).Name} >{testCaseDescription}: Check Passed"); + } + } + + static void testThrowsTypeNotSupported(String testCaseDescription, + Func cryptoOp) + where TVectorType : new() + { + TVectorType v = new TVectorType(); + + bool notSupported = false; + + try + { + cryptoOp(v,v,v); + } + catch (PlatformNotSupportedException) + { + notSupported = true; + } + finally + { + Debug.Assert(notSupported, $"{typeof(TVectorType).Name} {testCaseDescription}: Failed to throw PlatformNotSupportedException"); + } + } + + static void testThrowsPlatformNotSupported(String testCaseDescription, + Func cryptoOp) + where TVectorType : new() + { + testThrowsPlatformNotSupported(testCaseDescription, cryptoOp); + } + + static void testThrowsPlatformNotSupported(String testCaseDescription, + Func cryptoOp) + where TVectorType : new() + { + TVectorType v = new TVectorType(); + + bool notSupported = false; + + try + { + cryptoOp(v,v,v); + } + catch (PlatformNotSupportedException) // TODO-Fixme check for Type not supported exception + { + notSupported = true; + } + finally + { + Debug.Assert(notSupported, $"{typeof(TVectorType).Name} {testCaseDescription}: Failed to throw TypeNotSupportedException"); + } + } + + + static void TestAes() + { +#if ARM64_SIMD_API_PENDING_APPROVAL_AND_OR_COREFX_MERGE + String name = "Aes"; + + if (Aes.IsSupported) + { + testCryptoOp, byte, Vector128 >(name, (x, y, z) => Aes.Encrypt(x, y), aesEncRes); + testCryptoOp, byte, Vector128 >(name, (x, y, z) => Aes.Decrypt(x, y), aesDecRes); + testCryptoOp, byte, Vector128 >(name, (x, y, z) => Aes.MixColumns(x), aesMixRes ); + testCryptoOp, byte, Vector128 >(name, (x, y, z) => Aes.InverseMixColumns(x), aesInvMixRes ); + + } + else + { + testThrowsPlatformNotSupported , Vector128 >(name, (x, y, z) => Aes.Encrypt(x,y)); + testThrowsPlatformNotSupported , Vector128 >(name, (x, y, z) => Aes.Decrypt(x,y)); + testThrowsPlatformNotSupported , Vector128 >(name, (x, y, z) => Aes.MixColumns(x)); + testThrowsPlatformNotSupported , Vector128 >(name, (x, y, z) => Aes.InverseMixColumns(x)); + } +#endif //ARM64_SIMD_API_PENDING_APPROVAL_AND_OR_COREFX_MERGE + } + + static void TestSha256() + { +#if ARM64_SIMD_API_PENDING_APPROVAL_AND_OR_COREFX_MERGE + String name = "Sha256"; + if (Sha256.IsSupported) + { + testCryptoOp, uint, Vector128 >(name, (x, y, z) => Sha256.HashLower(x, y, z), sha256low); + testCryptoOp, uint, Vector128 >(name, (x, y, z) => Sha256.HashUpper(x, y, z), sha256high); + testCryptoOp, uint, Vector128 >(name, (x, y, z) => Sha256.SchedulePart1(x, y), sha256su1Res); + testCryptoOp, uint, Vector128 >(name, (x, y, z) => Sha256.SchedulePart2(x, y, z), sha256su2Res); + } + else + { + testThrowsPlatformNotSupported, Vector128 >(name, (x, y, z) => Sha256.HashLower(x, y, z)); + testThrowsPlatformNotSupported, Vector128 >(name, (x, y, z) => Sha256.HashUpper(x, y, z)); + testThrowsPlatformNotSupported, Vector128 >(name, (x, y, z) => Sha256.SchedulePart1(x, y)); + testThrowsPlatformNotSupported, Vector128 >(name, (x, y, z) => Sha256.SchedulePart2(x, y, z)); + } +#endif //ARM64_SIMD_API_PENDING_APPROVAL_AND_OR_COREFX_MERGE + } + + static void TestSha1() + { +#if ARM64_SIMD_API_PENDING_APPROVAL_AND_OR_COREFX_MERGE + String name = "Sha1"; + if (Sha1.IsSupported) + { + testCryptoOp, uint, Vector128 >(name, (x, y, z) => Sha1.HashChoose(x, 20, y), sha1cRes); + testCryptoOp, uint, Vector128 >(name, (x, y, z) => Sha1.HashParity(x, 20, y), sha1pRes); + testCryptoOp, uint, Vector128 >(name, (x, y, z) => Sha1.HashMajority(x, 20, y), sha1mRes); + testCryptoOp, uint, Vector128 >(name, (x, y, z) => Sha1.SchedulePart1(x, y, z), sha1su1Res); + testCryptoOp, uint, Vector128 >(name, (x, y, z) => Sha1.SchedulePart2(x, y), sha1su2Res); + if(Sha1.FixedRotate(100) != 25) + throw new Exception("Sha1 FixedRotate failed.\n"); + + + } + else + { + testThrowsPlatformNotSupported , Vector128 >(name, (x, y, z) => Sha1.HashChoose(x, 20, y)); + testThrowsPlatformNotSupported , Vector128 >(name, (x, y, z) => Sha1.HashParity(x, 20, y)); + testThrowsPlatformNotSupported , Vector128 >(name, (x, y, z) => Sha1.HashMajority(x, 20, y)); + testThrowsPlatformNotSupported , Vector128 >(name, (x, y, z) => Sha1.SchedulePart1(x, y, z)); + testThrowsPlatformNotSupported , Vector128 >(name, (x, y, z) => Sha1.SchedulePart2(x, y)); + } +#endif //ARM64_SIMD_API_PENDING_APPROVAL_AND_OR_COREFX_MERGE + } + + static void initializeDataSetDefault() + { + /// Data sets + DataSet >.setData(new byte[] { 1, 5, 100, 0, 7, 8, 2, 9, 1, 5, 100, 0, 7, 8, 2, 9 }, + new byte[] { 22, 1, 50, 0, 7, 5, 3, 33, 17, 4, 100, 120, 27, 6, 2, 6 }, + new byte[] { 1, 5, 10, 0, 17, 23, 14, 33, 15, 40, 0, 20, 22, 55, 12, 5 }); + DataSet >.setData(new uint[] {10, 44, 11, 81}, new uint[] {20, 41, 67, 59}, new uint[] {10, 20, 51, 96}); + } + + // Below result values are obtained by executing the corresponding GCC arm64 crypto intrinsics (defined in arm_neon.h) + // with the same input dataset on ARM64 platform. + + static byte[] aesEncRes = new byte[] {240, 215, 99, 118, 99, 124, 99, 99, 202, 171, 177, 52, 156, 242, 124, 188}; + static byte[] aesDecRes = new byte[] {135, 215, 82, 238, 82, 48, 82, 193, 124, 243, 185, 251, 196, 09, 09, 82}; + static byte[] aesMixRes = new byte[] {105, 167, 204, 98, 29, 24, 16, 17, 105, 167, 204, 98, 29, 24, 16, 17}; + static byte[] aesInvMixRes = new byte[] {203, 158, 110, 91, 41, 60, 36, 53, 203, 158, 110, 91, 41, 60, 36, 53}; + static uint[] sha1cRes = new uint[] {2162335592, 464120, 1073745449, 1073741936}; + static uint[] sha1pRes = new uint[] {15831335, 2147977893, 3857, 2147483767}; + static uint[] sha1mRes = new uint[] {12230250, 382193, 1073744809, 1073741916}; + static uint[] sha1su1Res = new uint[] {11, 105, 44, 24}; + static uint[] sha1su2Res = new uint[] {70,222,96,46}; + static uint[] sha256low = new uint[] {3870443882, 98061066, 1597900421, 3536859796}; + static uint[] sha256high = new uint[] {2024066181, 3259295072, 1866655758, 692061599}; + static uint[] sha256su1Res = new uint[] {1477115919, 369279021, 2719236117, 671416403}; + static uint[] sha256su2Res = new uint[] {2089011, 3932271, 203417658, 2151313268}; + + + static void ExecuteAllTests() + { + TestAes(); + TestSha1(); + TestSha256(); + } + + static int Main(string[] args) + { +#if ARM64_SIMD_API_PENDING_APPROVAL_AND_OR_COREFX_MERGE + Console.WriteLine($"System.Runtime.Intrinsics.Arm.Arm64.Aes.IsSupported = {Aes.IsSupported}"); + Console.WriteLine($"System.Runtime.Intrinsics.Arm.Arm64.Sha1.IsSupported = {Sha1.IsSupported}"); + Console.WriteLine($"System.Runtime.Intrinsics.Arm.Arm64.Sha2.IsSupported = {Sha256.IsSupported}"); +#endif //ARM64_SIMD_API_PENDING_APPROVAL_AND_OR_COREFX_MERGE + initializeDataSetDefault(); + Console.WriteLine("Running tests"); + ExecuteAllTests(); + + return 100; + } + } +} diff --git a/tests/src/JIT/HardwareIntrinsics/Arm64/Crypto.csproj b/tests/src/JIT/HardwareIntrinsics/Arm64/Crypto.csproj new file mode 100644 index 000000000000..fa5f43a97051 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/Arm64/Crypto.csproj @@ -0,0 +1,34 @@ + + + + + Debug + AnyCPU + 2.0 + Exe + ..\..\ + true + + + + + + + False + + + + None + True + + + + + + + + + + + +