diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Interlocked.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Interlocked.CoreCLR.cs index 5fb05077081bbb..014da2f95b0a4c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Interlocked.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Interlocked.CoreCLR.cs @@ -52,7 +52,7 @@ public static long Decrement(ref long location) => [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Exchange(ref int location1, int value) { -#if TARGET_X86 || TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 +#if TARGET_X86 || TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 || TARGET_LOONGARCH64 return Exchange(ref location1, value); // Must expand intrinsic #else if (Unsafe.IsNullRef(ref location1)) @@ -73,7 +73,7 @@ public static int Exchange(ref int location1, int value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long Exchange(ref long location1, long value) { -#if TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 +#if TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 || TARGET_LOONGARCH64 return Exchange(ref location1, value); // Must expand intrinsic #else if (Unsafe.IsNullRef(ref location1)) @@ -116,7 +116,7 @@ public static long Exchange(ref long location1, long value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int CompareExchange(ref int location1, int value, int comparand) { -#if TARGET_X86 || TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 +#if TARGET_X86 || TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 || TARGET_LOONGARCH64 return CompareExchange(ref location1, value, comparand); // Must expand intrinsic #else if (Unsafe.IsNullRef(ref location1)) @@ -137,7 +137,7 @@ public static int CompareExchange(ref int location1, int value, int comparand) [Intrinsic] internal static unsafe int CompareExchange(int* location1, int value, int comparand) { -#if TARGET_X86 || TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 +#if TARGET_X86 || TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 || TARGET_LOONGARCH64 return CompareExchange(location1, value, comparand); // Must expand intrinsic #else Debug.Assert(location1 != null); @@ -158,7 +158,7 @@ internal static unsafe int CompareExchange(int* location1, int value, int compar [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long CompareExchange(ref long location1, long value, long comparand) { -#if TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 +#if TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 || TARGET_LOONGARCH64 return CompareExchange(ref location1, value, comparand); // Must expand intrinsic #else if (Unsafe.IsNullRef(ref location1)) @@ -212,7 +212,7 @@ public static long Add(ref long location1, long value) => [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int ExchangeAdd(ref int location1, int value) { -#if TARGET_X86 || TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 +#if TARGET_X86 || TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 || TARGET_LOONGARCH64 return ExchangeAdd(ref location1, value); // Must expand intrinsic #else if (Unsafe.IsNullRef(ref location1)) @@ -228,7 +228,7 @@ private static int ExchangeAdd(ref int location1, int value) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static long ExchangeAdd(ref long location1, long value) { -#if TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 +#if TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 || TARGET_LOONGARCH64 return ExchangeAdd(ref location1, value); // Must expand intrinsic #else if (Unsafe.IsNullRef(ref location1)) diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index cd29d9b9a91b49..78d1169791c68a 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -719,6 +719,9 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zba, W("EnableRiscV64 RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zbb, W("EnableRiscV64Zbb"), 1, "Allows RiscV64 Zbb hardware intrinsics to be disabled") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zbs, W("EnableRiscV64Zbs"), 1, "Allows RiscV64 Zbs hardware intrinsics to be disabled") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableRiscV64Zicond, W("EnableRiscV64Zicond"), 1, "Allows RiscV64 Zicond hardware intrinsics to be disabled") +#elif defined(TARGET_LOONGARCH64) +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableLoongArch64LAM_BH, W("EnableLoongArch64LAM_BH"), 1, "Allows LoongArch64 LAM_BH+ hardware intrinsics to be disabled") +RETAIL_CONFIG_DWORD_INFO(EXTERNAL_EnableLoongArch64LAM_CAS, W("EnableLoongArch64LAM_CAS"), 1, "Allows LoongArch64 LAM_CAS+ hardware intrinsics to be disabled") #endif /// diff --git a/src/coreclr/inc/corinfoinstructionset.h b/src/coreclr/inc/corinfoinstructionset.h index 6336d39dced220..781038ce79c17d 100644 --- a/src/coreclr/inc/corinfoinstructionset.h +++ b/src/coreclr/inc/corinfoinstructionset.h @@ -57,6 +57,10 @@ enum CORINFO_InstructionSet InstructionSet_SveSha3_Arm64=38, InstructionSet_SveSm4_Arm64=39, #endif // TARGET_ARM64 +#ifdef TARGET_LOONGARCH64 + InstructionSet_LAM_BH=1, + InstructionSet_LAM_CAS=2, +#endif // TARGET_LOONGARCH64 #ifdef TARGET_RISCV64 InstructionSet_RiscV64Base=1, InstructionSet_Zba=2, @@ -290,6 +294,8 @@ struct CORINFO_InstructionSetFlags if (HasInstructionSet(InstructionSet_SveSm4)) AddInstructionSet(InstructionSet_SveSm4_Arm64); #endif // TARGET_ARM64 +#ifdef TARGET_LOONGARCH64 +#endif // TARGET_LOONGARCH64 #ifdef TARGET_RISCV64 #endif // TARGET_RISCV64 #ifdef TARGET_WASM @@ -450,6 +456,8 @@ inline CORINFO_InstructionSetFlags EnsureInstructionSetFlagsAreValid(CORINFO_Ins if (resultflags.HasInstructionSet(InstructionSet_SveSm4) && !resultflags.HasInstructionSet(InstructionSet_Sm4)) resultflags.RemoveInstructionSet(InstructionSet_SveSm4); #endif // TARGET_ARM64 +#ifdef TARGET_LOONGARCH64 +#endif // TARGET_LOONGARCH64 #ifdef TARGET_RISCV64 if (resultflags.HasInstructionSet(InstructionSet_Zbb) && !resultflags.HasInstructionSet(InstructionSet_RiscV64Base)) resultflags.RemoveInstructionSet(InstructionSet_Zbb); @@ -752,6 +760,12 @@ inline const char *InstructionSetToString(CORINFO_InstructionSet instructionSet) case InstructionSet_Cssc : return "Cssc"; #endif // TARGET_ARM64 +#ifdef TARGET_LOONGARCH64 + case InstructionSet_LAM_BH : + return "LAM_BH"; + case InstructionSet_LAM_CAS : + return "LAM_CAS"; +#endif // TARGET_LOONGARCH64 #ifdef TARGET_RISCV64 case InstructionSet_RiscV64Base : return "RiscV64Base"; @@ -968,6 +982,10 @@ inline CORINFO_InstructionSet InstructionSetFromR2RInstructionSet(ReadyToRunInst case READYTORUN_INSTRUCTION_SveSm4: return InstructionSet_SveSm4; case READYTORUN_INSTRUCTION_Cssc: return InstructionSet_Cssc; #endif // TARGET_ARM64 +#ifdef TARGET_LOONGARCH64 + case READYTORUN_INSTRUCTION_LAM_BH: return InstructionSet_LAM_BH; + case READYTORUN_INSTRUCTION_LAM_CAS: return InstructionSet_LAM_CAS; +#endif // TARGET_LOONGARCH64 #ifdef TARGET_RISCV64 case READYTORUN_INSTRUCTION_RiscV64Base: return InstructionSet_RiscV64Base; case READYTORUN_INSTRUCTION_Zba: return InstructionSet_Zba; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index e48e375315e0f5..f1cc2d7dfbcf1b 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* 65743063-e8fa-41d4-9496-c436974c00f5 */ - 0x65743063, - 0xe8fa, - 0x41d4, - {0x94, 0x96, 0xc4, 0x36, 0x97, 0x4c, 0x00, 0xf5} +constexpr GUID JITEEVersionIdentifier = { /* 7f943c0c-aad7-4ef5-b7e7-c07d05321f8f */ + 0x7f943c0c, + 0xaad7, + 0x4ef5, + {0xb7, 0xe7, 0xc0, 0x7d, 0x05, 0x32, 0x1f, 0x8f} }; #endif // JIT_EE_VERSIONING_GUID_H diff --git a/src/coreclr/inc/readytoruninstructionset.h b/src/coreclr/inc/readytoruninstructionset.h index 19ffdad8863f2e..be598e84ad3864 100644 --- a/src/coreclr/inc/readytoruninstructionset.h +++ b/src/coreclr/inc/readytoruninstructionset.h @@ -102,6 +102,9 @@ enum ReadyToRunInstructionSet READYTORUN_INSTRUCTION_PackedSimd=92, READYTORUN_INSTRUCTION_Cssc=93, READYTORUN_INSTRUCTION_Zicond=94, + READYTORUN_INSTRUCTION_LAM_BH=95, + READYTORUN_INSTRUCTION_LAM_CAS=96, + }; #endif // READYTORUNINSTRUCTIONSET_H diff --git a/src/coreclr/jit/codegenloongarch64.cpp b/src/coreclr/jit/codegenloongarch64.cpp index 087e201f5d8225..ff23becc0867da 100644 --- a/src/coreclr/jit/codegenloongarch64.cpp +++ b/src/coreclr/jit/codegenloongarch64.cpp @@ -2167,14 +2167,74 @@ void CodeGen::genNonLocalJmp(GenTreeUnOp* tree) } //------------------------------------------------------------------------ -// genLockedInstructions: Generate code for a GT_XADD or GT_XCHG node. +// genLockedInstructions: Generate code for a GT_XADD, GT_XAND, GT_XORR or GT_XCHG node. // // Arguments: -// treeNode - the GT_XADD/XCHG node +// treeNode - the GT_XADD/XAND/XORR/XCHG node // void CodeGen::genLockedInstructions(GenTreeOp* treeNode) { - NYI("unimplemented on LOONGARCH64 yet"); + GenTree* data = treeNode->AsOp()->gtOp2; + GenTree* addr = treeNode->AsOp()->gtOp1; + regNumber dataReg = !data->isContained() ? data->GetRegNum() : REG_R0; + regNumber addrReg = addr->GetRegNum(); + regNumber targetReg = treeNode->GetRegNum(); + + genConsumeAddress(addr); + genConsumeRegs(data); + + assert(treeNode->OperIs(GT_XCHG) || !varTypeIsSmall(treeNode->TypeGet())); + + emitter* emit = GetEmitter(); + emitAttr dataSize = emitActualTypeSize(data); + bool is4 = (dataSize == EA_4BYTE); + + instruction ins = INS_none; + if (!varTypeIsSmall(treeNode->TypeGet())) + { + switch (treeNode->gtOper) + { + case GT_XORR: + ins = is4 ? INS_amor_db_w : INS_amor_db_d; + break; + case GT_XAND: + ins = is4 ? INS_amand_db_w : INS_amand_db_d; + break; + case GT_XCHG: + ins = is4 ? INS_amswap_db_w : INS_amswap_db_d; + break; + case GT_XADD: + ins = is4 ? INS_amadd_db_w : INS_amadd_db_d; + break; + default: + noway_assert(!"Unexpected treeNode->gtOper"); + } + emit->emitIns_R_R_R(ins, dataSize, targetReg, dataReg, addrReg); + } + else + { + // Smalltypes only support atomic instructions for GT_XCHG + assert(!treeNode->OperIs(GT_XORR, GT_XAND, GT_XADD)); + // When LAM_BH is not supported, it will back to the managed implementation or throws PlatformNotSupported. + assert(m_compiler->opts.compSupportsISA.HasInstructionSet(InstructionSet_LAM_BH)); + if (varTypeIsByte(treeNode->TypeGet())) + { + ins = INS_amswap_db_b; + } + else if (varTypeIsShort(treeNode->TypeGet())) + { + ins = INS_amswap_db_h; + } + emit->emitIns_R_R_R(ins, dataSize, targetReg, dataReg, addrReg); + } + + if (varTypeIsSmall(treeNode->TypeGet()) && varTypeIsUnsigned(treeNode->TypeGet())) + { + int imm1 = varTypeIsByte(treeNode->TypeGet()) ? 7 : 15; + emit->emitIns_R_R_I_I(INS_bstrpick_d, dataSize, targetReg, targetReg, imm1, 0); + } + + genProduceReg(treeNode); } //------------------------------------------------------------------------ @@ -2185,7 +2245,98 @@ void CodeGen::genLockedInstructions(GenTreeOp* treeNode) // void CodeGen::genCodeForCmpXchg(GenTreeCmpXchg* treeNode) { - NYI("unimplemented on LOONGARCH64 yet"); + assert(treeNode->OperIs(GT_CMPXCHG)); + + GenTree* addr = treeNode->Addr(); // arg1 + GenTree* data = treeNode->Data(); // arg2 + GenTree* comparand = treeNode->Comparand(); // arg3 + + regNumber targetReg = treeNode->GetRegNum(); + regNumber dataReg = data->GetRegNum(); + regNumber addrReg = addr->GetRegNum(); + regNumber comparandReg = !comparand->isContained() ? comparand->GetRegNum() : REG_R0; + + genConsumeAddress(addr); + genConsumeRegs(data); + genConsumeRegs(comparand); + + emitter* emit = GetEmitter(); + emitAttr dataSize = emitActualTypeSize(data); + bool is4 = (dataSize == EA_4BYTE); + if (m_compiler->opts.compSupportsISA.HasInstructionSet(InstructionSet_LAM_CAS)) + { + // amcas_db use the comparand as the target reg + emit->emitIns_R_R(INS_mov, dataSize, targetReg, comparandReg); + + // Catch case we destroyed data or address before use + noway_assert((addrReg != targetReg) || (targetReg == comparandReg)); + noway_assert((dataReg != targetReg) || (targetReg == comparandReg)); + + instruction ins = INS_none; + ins = is4 ? INS_amcas_db_w : INS_amcas_db_d; + if (varTypeIsByte(treeNode->TypeGet())) + { + ins = INS_amcas_db_b; + } + else if (varTypeIsShort(treeNode->TypeGet())) + { + ins = INS_amcas_db_h; + } + GetEmitter()->emitIns_R_R_R(ins, dataSize, targetReg, dataReg, addrReg); + } + else + { + assert(!varTypeIsSmall(treeNode->TypeGet())); + regNumber exResultReg = internalRegisters.Extract(treeNode, RBM_ALLINT); + // Register allocator should have extended the lifetimes of all input and internal registers + // They should all be different + noway_assert(addrReg != targetReg); + noway_assert(dataReg != targetReg); + noway_assert(comparandReg != targetReg); + noway_assert(addrReg != dataReg); + noway_assert(targetReg != REG_NA); + noway_assert(exResultReg != REG_NA); + noway_assert(exResultReg != targetReg); + + assert(addr->isUsedFromReg()); + assert(!comparand->isUsedFromMemory()); + + // Store exclusive unpredictable cases must be avoided + noway_assert(exResultReg != dataReg); + noway_assert(exResultReg != addrReg); + + // NOTE: `genConsumeAddress` marks consumed register as not a GC pointer, assuming the input + // registers die at the first generated instruction. However, here the input registers are reused, + // so mark the location register as a GC pointer until code generation for this node is finished. + gcInfo.gcMarkRegPtrVal(addrReg, addr->TypeGet()); + + BasicBlock* labelRetry = genCreateTempLabel(); + BasicBlock* fail = genCreateTempLabel(); + + if (is4) + { + // INS_bne is 64 bit comparison, high bits may be contaminated. + emit->emitIns_R_R_I(INS_slli_w, dataSize, comparandReg, comparandReg, 0x0); + } + instGen_MemoryBarrier(); + genDefineTempLabel(labelRetry); + emit->emitIns_R_R_I(is4 ? INS_ll_w : INS_ll_d, dataSize, targetReg, addrReg, 0); // load original value + emit->emitIns_J_cond_la(INS_bne, fail, targetReg, comparandReg); // fail if doesn’t match + emit->emitIns_R_R(INS_mov, dataSize, exResultReg, dataReg); + emit->emitIns_R_R_I(is4 ? INS_sc_w : INS_sc_d, dataSize, exResultReg, addrReg, 0); // try to update + emit->emitIns_J_cond_la(INS_beq, labelRetry, exResultReg, REG_R0); // retry if update failed + genDefineTempLabel(fail); + instGen_MemoryBarrier(); + gcInfo.gcMarkRegSetNpt(addr->gtGetRegMask()); + } + + if (varTypeIsSmall(treeNode->TypeGet()) && varTypeIsUnsigned(treeNode->TypeGet())) + { + int imm1 = varTypeIsByte(treeNode->TypeGet()) ? 7 : 15; + GetEmitter()->emitIns_R_R_I_I(INS_bstrpick_d, dataSize, targetReg, targetReg, imm1, 0); + } + + genProduceReg(treeNode); } static inline bool isImmed(GenTree* treeNode) @@ -4205,6 +4356,8 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) case GT_XCHG: case GT_XADD: + case GT_XORR: + case GT_XAND: genLockedInstructions(treeNode->AsOp()); break; diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index ca98cc9be25bad..dfd16201a3d3d0 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -10795,7 +10795,7 @@ class Compiler // on which the function is executed (except for CoreLib, where there are special rules) bool compExactlyDependsOn(CORINFO_InstructionSet isa) const { -#if defined(TARGET_XARCH) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) +#if defined(TARGET_XARCH) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) if ((opts.compSupportsISAReported.HasInstructionSet(isa)) == false) { if (notifyInstructionSetUsage(isa, (opts.compSupportsISA.HasInstructionSet(isa)))) diff --git a/src/coreclr/jit/emitloongarch64.cpp b/src/coreclr/jit/emitloongarch64.cpp index dedb4aaf034795..6193283a9f3acb 100644 --- a/src/coreclr/jit/emitloongarch64.cpp +++ b/src/coreclr/jit/emitloongarch64.cpp @@ -1557,17 +1557,33 @@ void emitter::emitIns_R_R_R( code |= (reg2 /*& 0x1f*/) << 5; // rj code |= (reg3 /*& 0x1f*/) << 10; // rk } - else if ((INS_amswap_w <= ins) && (ins <= INS_ammin_db_du)) + else if ((INS_amcas_b <= ins) && (ins <= INS_ammin_db_du)) { #ifdef DEBUG switch (ins) { + case INS_amcas_b: + case INS_amcas_h: + case INS_amcas_w: + case INS_amcas_d: + case INS_amcas_db_b: + case INS_amcas_db_h: + case INS_amcas_db_w: + case INS_amcas_db_d: + case INS_amswap_b: + case INS_amswap_h: case INS_amswap_w: case INS_amswap_d: + case INS_amswap_db_b: + case INS_amswap_db_h: case INS_amswap_db_w: case INS_amswap_db_d: + case INS_amadd_b: + case INS_amadd_h: case INS_amadd_w: case INS_amadd_d: + case INS_amadd_db_b: + case INS_amadd_db_h: case INS_amadd_db_w: case INS_amadd_db_d: case INS_amand_w: @@ -4239,6 +4255,11 @@ void emitter::emitDisInsName(code_t code, const BYTE* addr, instrDesc* id) printf("0x%x, %s, %s\n", regd, RegNames[regj], RegNames[(code >> 10) & 0x1f]); return; } + if ((INS_amcas_b <= ins) && (ins <= INS_ammin_db_du)) + { + printf("%s, %s, %s\n", RegNames[regd], RegNames[(code >> 10) & 0x1f], RegNames[regj]); + return; + } printf("%s, %s, %s\n", RegNames[regd], RegNames[regj], RegNames[(code >> 10) & 0x1f]); return; } diff --git a/src/coreclr/jit/hwintrinsic.cpp b/src/coreclr/jit/hwintrinsic.cpp index 0aa3dda05359a7..191c93cee8570d 100644 --- a/src/coreclr/jit/hwintrinsic.cpp +++ b/src/coreclr/jit/hwintrinsic.cpp @@ -1013,6 +1013,9 @@ static const HWIntrinsicIsaRange hwintrinsicIsaRangeArray[] = { { NI_Illegal, NI_Illegal }, // SveAes_Arm64 { NI_Illegal, NI_Illegal }, // SveSha3_Arm64 { NI_Illegal, NI_Illegal }, // SveSm4_Arm64 +#elif defined (TARGET_LOONGARCH64) + { FIRST_NI_LAM_BH, LAST_NI_LAM_BH }, // LAM_BH + { FIRST_NI_LAM_CAS, LAST_NI_LAM_CAS }, // LAM_CAS #elif defined(TARGET_WASM) { FIRST_NI_WasmBase, LAST_NI_WasmBase }, // WasmBase { FIRST_NI_PackedSimd, LAST_NI_PackedSimd }, // PackedSimd diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 3709097fc4be73..95ff808f48b61c 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -3635,6 +3635,12 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd, mustExpand |= sig->sigInst.methInstCount == 1; break; +#if defined(TARGET_LOONGARCH64) + case NI_System_Threading_Interlocked_CompareExchange: + case NI_System_Threading_Interlocked_Exchange: + betterToExpand = true; + break; +#endif default: break; } @@ -4362,7 +4368,7 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd, break; } -#if defined(TARGET_ARM64) || defined(TARGET_RISCV64) || defined(TARGET_XARCH) +#if defined(TARGET_ARM64) || defined(TARGET_RISCV64) || defined(TARGET_XARCH) || defined(TARGET_LOONGARCH64) case NI_System_Threading_Interlocked_Or: case NI_System_Threading_Interlocked_And: { @@ -4391,9 +4397,9 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd, } break; } -#endif // defined(TARGET_ARM64) || defined(TARGET_RISCV64) +#endif // defined(TARGET_ARM64) || defined(TARGET_RISCV64) || defined(TARGET_XARCH) || defined(TARGET_LOONGARCH64) -#if defined(TARGET_XARCH) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) +#if defined(TARGET_XARCH) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) // TODO-ARM-CQ: reenable treating InterlockedCmpXchg32 operation as intrinsic case NI_System_Threading_Interlocked_CompareExchange: { @@ -4404,10 +4410,29 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd, break; } #if !defined(TARGET_XARCH) && !defined(TARGET_ARM64) +#if defined(TARGET_LOONGARCH64) + if (!compOpportunisticallyDependsOn(InstructionSet_LAM_CAS)) + { + if (!opts.compSupportsISA.HasInstructionSet(InstructionSet_LAM_CAS) && varTypeIsSmall(retType)) + { + if (mustExpand) + return impUnsupportedNamedIntrinsic(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, method, sig, + mustExpand); + break; + } + else if (IsTargetAbi(CORINFO_NATIVEAOT_ABI) && + opts.compSupportsISA.HasInstructionSet(InstructionSet_LAM_CAS)) + { + if (!mustExpand) + break; + } + } +#else else if (genTypeSize(retType) < 4) { break; } +#endif // defined(TARGET_LOONGARCH64) #endif // !defined(TARGET_XARCH) && !defined(TARGET_ARM64) if ((retType == TYP_REF) && @@ -4448,9 +4473,28 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd, #if !defined(TARGET_XARCH) && !defined(TARGET_ARM64) else if (genTypeSize(retType) < 4) { +#if defined(TARGET_LOONGARCH64) + if (!compOpportunisticallyDependsOn(InstructionSet_LAM_BH)) + { + if (!opts.compSupportsISA.HasInstructionSet(InstructionSet_LAM_BH)) + { + if (mustExpand) + return impUnsupportedNamedIntrinsic(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, method, + sig, mustExpand); + break; + } + else if (IsTargetAbi(CORINFO_NATIVEAOT_ABI) && + opts.compSupportsISA.HasInstructionSet(InstructionSet_LAM_BH)) + { + if (!mustExpand) + break; + } + } +#else break; +#endif } -#endif // !defined(TARGET_XARCH) && !defined(TARGET_ARM64) +#endif // !defined(TARGET_XARCH) && !defined(TARGET_ARM64) && !defined(TARGET_LOONGARCH64) if ((retType == TYP_REF) && (impStackTop().val->IsIntegralConst(0) || impStackTop().val->IsIconHandle(GTF_ICON_OBJ_HDL))) @@ -4480,7 +4524,7 @@ GenTree* Compiler::impIntrinsic(CORINFO_CLASS_HANDLE clsHnd, callType, op1, op2); break; } -#endif // defined(TARGET_XARCH) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) +#endif // defined(TARGET_XARCH) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) case NI_System_Threading_Interlocked_MemoryBarrier: { @@ -11709,6 +11753,36 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) result = NI_IsSupported_Type; } +#if defined(TARGET_LOONGARCH64) + else if ((strncmp(className, "BH", 2) == 0) && + opts.compSupportsISA.HasInstructionSet(InstructionSet_LAM_BH)) + { + if (!IsTargetAbi(CORINFO_NATIVEAOT_ABI) || + compExactlyDependsOn(InstructionSet_LAM_BH)) + { + result = NI_IsSupported_True; + } + else + { + assert(IsTargetAbi(CORINFO_NATIVEAOT_ABI)); + result = NI_IsSupported_Dynamic; + } + } + else if ((strncmp(className, "CAS", 3) == 0) && + opts.compSupportsISA.HasInstructionSet(InstructionSet_LAM_CAS)) + { + if (!IsTargetAbi(CORINFO_NATIVEAOT_ABI) || + compExactlyDependsOn(InstructionSet_LAM_CAS)) + { + result = NI_IsSupported_True; + } + else + { + assert(IsTargetAbi(CORINFO_NATIVEAOT_ABI)); + result = NI_IsSupported_Dynamic; + } + } +#endif else { result = NI_IsSupported_False; @@ -11726,6 +11800,16 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) result = NI_Vector_GetCount; } +#if defined(TARGET_LOONGARCH64) + else if (strcmp(methodName, "CompareExchange") == 0) + { + result = NI_System_Threading_Interlocked_CompareExchange; + } + else if (strcmp(methodName, "Exchange") == 0) + { + result = NI_System_Threading_Interlocked_Exchange; + } +#endif else if (gtIsRecursiveCall(method, false)) { // For the framework itself, any recursive intrinsics will either be diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 5e3095e94dd87c..d350f6d47a47c5 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -457,6 +457,9 @@ RELEASE_CONFIG_INTEGER(EnableRiscV64Zba, "EnableRiscV64Zba", RELEASE_CONFIG_INTEGER(EnableRiscV64Zbb, "EnableRiscV64Zbb", 1) // Allows RiscV64 Zbb hardware intrinsics to be disabled RELEASE_CONFIG_INTEGER(EnableRiscV64Zbs, "EnableRiscV64Zbs", 1) // Allows RiscV64 Zbs hardware intrinsics to be disabled RELEASE_CONFIG_INTEGER(EnableRiscV64Zicond, "EnableRiscV64Zicond", 1) // Allows RiscV64 Zicond hardware intrinsics to be disabled +#elif defined(TARGET_LOONGARCH64) +RELEASE_CONFIG_INTEGER(EnableLoongArch64LAM_BH, "EnableLoongArch64LAM_BH", 1) // Allows LoongArch64 LAM_BH+ hardware intrinsics to be disabled +RELEASE_CONFIG_INTEGER(EnableLoongArch64LAM_CAS, "EnableLoongArch64LAM_CAS", 1) // Allows LoongArch64 LAM_CAS+ hardware intrinsics to be disabled #endif RELEASE_CONFIG_INTEGER(EnableEmbeddedBroadcast, "EnableEmbeddedBroadcast", 1) // Allows embedded broadcasts to be disabled diff --git a/src/coreclr/jit/lowerloongarch64.cpp b/src/coreclr/jit/lowerloongarch64.cpp index 9ecc26231fb9ac..7d81fb9c6cabeb 100644 --- a/src/coreclr/jit/lowerloongarch64.cpp +++ b/src/coreclr/jit/lowerloongarch64.cpp @@ -66,10 +66,11 @@ bool Lowering::IsContainableImmed(GenTree* parentNode, GenTree* childNode) const switch (parentNode->OperGet()) { case GT_CMPXCHG: - case GT_LOCKADD: + case GT_XORR: + case GT_XAND: case GT_XADD: - NYI_LOONGARCH64("GT_CMPXCHG,GT_LOCKADD,GT_XADD"); - break; + case GT_XCHG: + return (immVal == 0); case GT_ADD: case GT_EQ: diff --git a/src/coreclr/jit/lsraloongarch64.cpp b/src/coreclr/jit/lsraloongarch64.cpp index 5565c47033b8f0..a1d66bc1fde6c6 100644 --- a/src/coreclr/jit/lsraloongarch64.cpp +++ b/src/coreclr/jit/lsraloongarch64.cpp @@ -396,17 +396,64 @@ int LinearScan::BuildNode(GenTree* tree) case GT_CMPXCHG: { - NYI_LOONGARCH64("-----unimplemented on LOONGARCH64 yet----"); + GenTreeCmpXchg* cas = tree->AsCmpXchg(); + assert(dstCount == 1); + assert(!cas->Addr()->isContained() && !cas->Data()->isContained()); + srcCount = 2; + setDelayFree(BuildUse(cas->Addr())); + setDelayFree(BuildUse(cas->Data())); + + GenTree* comparand = cas->Comparand(); + if (!comparand->isContained()) + { + srcCount++; + setDelayFree(BuildUse(comparand)); + } + else + { + assert(comparand->IsIntegralConst(0)); + } + + if (!m_compiler->opts.compSupportsISA.HasInstructionSet(InstructionSet_LAM_CAS)) + { + buildInternalIntRegisterDefForNode(tree); // ISA1.1 temp reg for store conditional error + } + // Internals may not collide with target + setInternalRegsDelayFree = true; + buildInternalRegisterUses(); + BuildDef(tree); } break; case GT_LOCKADD: + { + NYI_LOONGARCH64("-----unimplemented on LOONGARCH64 yet----"); + } + break; + case GT_XORR: case GT_XAND: case GT_XADD: case GT_XCHG: { - NYI_LOONGARCH64("-----unimplemented on LOONGARCH64 yet----"); + assert(dstCount == (tree->TypeIs(TYP_VOID) ? 0 : 1)); + GenTree* addr = tree->gtGetOp1(); + GenTree* data = tree->gtGetOp2(); + assert(!addr->isContained()); + + setDelayFree(BuildUse(addr)); + srcCount = 1; + if (!data->isContained()) + { + srcCount++; + setDelayFree(BuildUse(data)); + } + else + { + assert(data->IsIntegralConst(0)); + } + + BuildDef(tree); } break; diff --git a/src/coreclr/jit/rangecheck.h b/src/coreclr/jit/rangecheck.h index 4880f7696fb070..74c4df51e2ea1c 100644 --- a/src/coreclr/jit/rangecheck.h +++ b/src/coreclr/jit/rangecheck.h @@ -328,10 +328,10 @@ struct RangeOps { if (unsignedAdd) { - bool r1StraddlesZero = r1.IsConstantRange() && (r1.LowerLimit().GetConstant() < 0) && - (r1.UpperLimit().GetConstant() >= 0); - bool r2StraddlesZero = r2.IsConstantRange() && (r2.LowerLimit().GetConstant() < 0) && - (r2.UpperLimit().GetConstant() >= 0); + bool r1StraddlesZero = + r1.IsConstantRange() && (r1.LowerLimit().GetConstant() < 0) && (r1.UpperLimit().GetConstant() >= 0); + bool r2StraddlesZero = + r2.IsConstantRange() && (r2.LowerLimit().GetConstant() < 0) && (r2.UpperLimit().GetConstant() >= 0); if (r1StraddlesZero || r2StraddlesZero) { // Signed intervals that straddle zero are not monotonic when interpreted as unsigned. @@ -355,7 +355,8 @@ struct RangeOps static_assert(CheckedOps::Unsigned == true); // For unsigned adds, require both unsigned and signed endpoint sums to not overflow. bool requestedAddOverflows = CheckedOps::AddOverflows(a.GetConstant(), b.GetConstant(), unsignedAdd); - bool signedEndpointOverflows = unsignedAdd && CheckedOps::AddOverflows(a.GetConstant(), b.GetConstant(), CheckedOps::Signed); + bool signedEndpointOverflows = + unsignedAdd && CheckedOps::AddOverflows(a.GetConstant(), b.GetConstant(), CheckedOps::Signed); if (!requestedAddOverflows && !signedEndpointOverflows) { if (a.IsConstant() && b.IsConstant()) diff --git a/src/coreclr/nativeaot/Runtime/startup.cpp b/src/coreclr/nativeaot/Runtime/startup.cpp index ba80907aeb646b..ce07b4ba469459 100644 --- a/src/coreclr/nativeaot/Runtime/startup.cpp +++ b/src/coreclr/nativeaot/Runtime/startup.cpp @@ -48,7 +48,7 @@ static bool DetectCPUFeatures(); extern RhConfig * g_pRhConfig; -#if defined(HOST_X86) || defined(HOST_AMD64) || defined(HOST_ARM64) +#if defined(HOST_X86) || defined(HOST_AMD64) || defined(HOST_ARM64) || defined(HOST_LOONGARCH64) // This field is inspected from the generated code to determine what intrinsics are available. EXTERN_C int g_cpuFeatures; int g_cpuFeatures = 0; @@ -167,7 +167,7 @@ static bool InitDLL(HANDLE hPalInstance) bool DetectCPUFeatures() { -#if defined(HOST_X86) || defined(HOST_AMD64) || defined(HOST_ARM64) +#if defined(HOST_X86) || defined(HOST_AMD64) || defined(HOST_ARM64) || defined(HOST_LOONGARCH64) int cpuFeatures = minipal_getcpufeatures(); if ((cpuFeatures & IntrinsicConstants_Invalid) != 0) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Interlocked.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Interlocked.cs index c8a9c739831fcc..9e3711813b9975 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Interlocked.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Interlocked.cs @@ -5,6 +5,9 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime; using System.Runtime.CompilerServices; +#if TARGET_LOONGARCH64 +using System.Runtime.Intrinsics.LoongArch; +#endif namespace System.Threading { @@ -18,6 +21,12 @@ public static int CompareExchange(ref int location1, int value, int comparand) #if TARGET_X86 || TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 return CompareExchange(ref location1, value, comparand); // Must expand intrinsic #else +#if TARGET_LOONGARCH64 + if (LAM.CAS.IsSupported) + { + return LAM.CAS.CompareExchange(ref location1, value, comparand); // Must expand intrinsic + } +#endif if (Unsafe.IsNullRef(ref location1)) ThrowHelper.ThrowNullReferenceException(); return RuntimeImports.InterlockedCompareExchange(ref location1, value, comparand); @@ -36,6 +45,12 @@ internal static unsafe int CompareExchange(int* location1, int value, int compar #if TARGET_X86 || TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 return CompareExchange(location1, value, comparand); // Must expand intrinsic #else +#if TARGET_LOONGARCH64 + if (LAM.CAS.IsSupported) + { + return LAM.CAS.CompareExchange(location1, value, comparand); // Must expand intrinsic + } +#endif Debug.Assert(location1 != null); return RuntimeImports.InterlockedCompareExchange(location1, value, comparand); #endif @@ -48,6 +63,12 @@ public static long CompareExchange(ref long location1, long value, long comparan #if TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 return CompareExchange(ref location1, value, comparand); // Must expand intrinsic #else +#if TARGET_LOONGARCH64 + if (LAM.CAS.IsSupported) + { + return LAM.CAS.CompareExchange(ref location1, value, comparand); // Must expand intrinsic + } +#endif if (Unsafe.IsNullRef(ref location1)) ThrowHelper.ThrowNullReferenceException(); return RuntimeImports.InterlockedCompareExchange(ref location1, value, comparand); @@ -71,7 +92,7 @@ public static long CompareExchange(ref long location1, long value, long comparan [Intrinsic] public static int Exchange(ref int location1, int value) { -#if TARGET_X86 || TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 +#if TARGET_X86 || TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 || TARGET_LOONGARCH64 return Exchange(ref location1, value); // Must expand intrinsic #else int oldValue; @@ -88,7 +109,7 @@ public static int Exchange(ref int location1, int value) [Intrinsic] public static long Exchange(ref long location1, long value) { -#if TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 +#if TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 || TARGET_LOONGARCH64 return Exchange(ref location1, value); // Must expand intrinsic #else long oldValue; diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/System/Threading/Interlocked.cs b/src/coreclr/nativeaot/Test.CoreLib/src/System/Threading/Interlocked.cs index 9ac6aa5110ad81..f8bd53aff88058 100644 --- a/src/coreclr/nativeaot/Test.CoreLib/src/System/Threading/Interlocked.cs +++ b/src/coreclr/nativeaot/Test.CoreLib/src/System/Threading/Interlocked.cs @@ -21,7 +21,7 @@ public static IntPtr CompareExchange(ref IntPtr location1, IntPtr value, IntPtr [Intrinsic] public static int CompareExchange(ref int location1, int value, int comparand) { -#if TARGET_X86 || TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 +#if TARGET_X86 || TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 || TARGET_LOONGARCH64 return CompareExchange(ref location1, value, comparand); #else return RuntimeImports.InterlockedCompareExchange(ref location1, value, comparand); @@ -31,7 +31,7 @@ public static int CompareExchange(ref int location1, int value, int comparand) [Intrinsic] public static long CompareExchange(ref long location1, long value, long comparand) { -#if TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 +#if TARGET_AMD64 || TARGET_ARM64 || TARGET_RISCV64 || TARGET_LOONGARCH64 return CompareExchange(ref location1, value, comparand); #else return RuntimeImports.InterlockedCompareExchange(ref location1, value, comparand); diff --git a/src/coreclr/tools/Common/Compiler/HardwareIntrinsicHelpers.cs b/src/coreclr/tools/Common/Compiler/HardwareIntrinsicHelpers.cs index d4b0be7fb725b3..ea057d497c73c6 100644 --- a/src/coreclr/tools/Common/Compiler/HardwareIntrinsicHelpers.cs +++ b/src/coreclr/tools/Common/Compiler/HardwareIntrinsicHelpers.cs @@ -29,6 +29,7 @@ public static bool IsHardwareIntrinsic(MethodDesc method) return method.Context.Target.Architecture switch { TargetArchitecture.ARM64 => ns == "System.Runtime.Intrinsics.Arm", + TargetArchitecture.LoongArch64 => ns == "System.Runtime.Intrinsics.LoongArch", TargetArchitecture.Wasm32 => ns == "System.Runtime.Intrinsics.Wasm", TargetArchitecture.X64 or TargetArchitecture.X86 => ns == "System.Runtime.Intrinsics.X86", _ => false, @@ -52,6 +53,9 @@ public static void AddRuntimeRequiredIsaFlagsToBuilder(InstructionSetSupportBuil case TargetArchitecture.RiscV64: RiscV64IntrinsicConstants.AddToBuilder(builder, flags); break; + case TargetArchitecture.LoongArch64: + LoongArch64IntrinsicConstants.AddToBuilder(builder, flags); + break; default: Debug.Fail("Probably unimplemented"); break; @@ -360,5 +364,29 @@ public static int FromInstructionSet(InstructionSet instructionSet) }; } } + + private static class LoongArch64IntrinsicConstants + { + public const int LAM_BH = (1 << 0); + public const int LAM_CAS = (1 << 1); + + public static void AddToBuilder(InstructionSetSupportBuilder builder, int flags) + { + if ((flags & LAM_BH) != 0) + builder.AddSupportedInstructionSet("lam_bh"); + if ((flags & LAM_CAS) != 0) + builder.AddSupportedInstructionSet("lam_cas"); + } + + public static int FromInstructionSet(InstructionSet instructionSet) + { + return instructionSet switch + { + InstructionSet.LoongArch64_LAM_BH => LAM_BH, + InstructionSet.LoongArch64_LAM_CAS => LAM_CAS, + _ => throw new NotSupportedException(((InstructionSet_LoongArch64)instructionSet).ToString()) + }; + } + } } } diff --git a/src/coreclr/tools/Common/Compiler/InstructionSetSupport.cs b/src/coreclr/tools/Common/Compiler/InstructionSetSupport.cs index 061d4e161cc3b0..1a91984a530291 100644 --- a/src/coreclr/tools/Common/Compiler/InstructionSetSupport.cs +++ b/src/coreclr/tools/Common/Compiler/InstructionSetSupport.cs @@ -89,6 +89,7 @@ public static string GetHardwareIntrinsicId(TargetArchitecture architecture, Typ } else if (architecture is TargetArchitecture.LoongArch64) { + if (potentialType.Namespace != "System.Runtime.Intrinsics.LoongArch"u8) return ""; } else if (architecture is TargetArchitecture.RiscV64) diff --git a/src/coreclr/tools/Common/InstructionSetHelpers.cs b/src/coreclr/tools/Common/InstructionSetHelpers.cs index f167a4328e94d2..54620143571388 100644 --- a/src/coreclr/tools/Common/InstructionSetHelpers.cs +++ b/src/coreclr/tools/Common/InstructionSetHelpers.cs @@ -309,6 +309,11 @@ public static InstructionSetSupport ConfigureInstructionSetSupport(string instru optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sha1"); optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("sha2"); } + else if (allowOptimistic && targetArchitecture is TargetArchitecture.LoongArch64) + { + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lam_bh"); + optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("lam_cas"); + } // Vector can always be part of the optimistic set, we only want to optionally exclude it from the supported set optimisticInstructionSetSupportBuilder.ComputeInstructionSetFlags(maxVectorTBitWidth, skipAddingVectorT: false, out var optimisticInstructionSet, out _, diff --git a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunInstructionSet.cs b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunInstructionSet.cs index 3a581d76aa81e9..9ceb71af8e64d9 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunInstructionSet.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunInstructionSet.cs @@ -105,5 +105,7 @@ public enum ReadyToRunInstructionSet PackedSimd = 92, Cssc = 93, Zicond = 94, + LAM_BH = 95, + LAM_CAS = 96, } } diff --git a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunInstructionSetHelper.cs b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunInstructionSetHelper.cs index c4e7bee775476b..e5b6c3139a10a6 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunInstructionSetHelper.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunInstructionSetHelper.cs @@ -66,6 +66,17 @@ public static class ReadyToRunInstructionSetHelper } } + case TargetArchitecture.LoongArch64: + { + switch (instructionSet) + { + case InstructionSet.LoongArch64_LAM_BH: return ReadyToRunInstructionSet.LAM_BH; + case InstructionSet.LoongArch64_LAM_CAS: return ReadyToRunInstructionSet.LAM_CAS; + + default: throw new Exception("Unknown instruction set"); + } + } + case TargetArchitecture.RiscV64: { switch (instructionSet) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs b/src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs index 05ce0e298aa6e4..b8d5ba652e0eda 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs @@ -56,6 +56,8 @@ public enum InstructionSet ARM64_SveAes_Arm64 = InstructionSet_ARM64.SveAes_Arm64, ARM64_SveSha3_Arm64 = InstructionSet_ARM64.SveSha3_Arm64, ARM64_SveSm4_Arm64 = InstructionSet_ARM64.SveSm4_Arm64, + LoongArch64_LAM_BH = InstructionSet_LoongArch64.LAM_BH, + LoongArch64_LAM_CAS = InstructionSet_LoongArch64.LAM_CAS, RiscV64_RiscV64Base = InstructionSet_RiscV64.RiscV64Base, RiscV64_Zba = InstructionSet_RiscV64.Zba, RiscV64_Zbb = InstructionSet_RiscV64.Zbb, @@ -202,6 +204,14 @@ public enum InstructionSet_ARM64 SveSm4_Arm64 = 39, } + public enum InstructionSet_LoongArch64 + { + ILLEGAL = InstructionSet.ILLEGAL, + NONE = InstructionSet.NONE, + LAM_BH = 1, + LAM_CAS = 2, + } + public enum InstructionSet_RiscV64 { ILLEGAL = InstructionSet.ILLEGAL, @@ -333,6 +343,8 @@ public unsafe struct InstructionSetFlags : IEnumerable private fixed ulong _flags[FlagsFieldCount]; public IEnumerable ARM64Flags => this.Select((x) => (InstructionSet_ARM64)x); + public IEnumerable LoongArch64Flags => this.Select((x) => (InstructionSet_LoongArch64)x); + public IEnumerable RiscV64Flags => this.Select((x) => (InstructionSet_RiscV64)x); public IEnumerable Wasm32Flags => this.Select((x) => (InstructionSet_Wasm32)x); @@ -596,6 +608,9 @@ public static InstructionSetFlags ExpandInstructionSetByImplicationHelper(Target resultflags.AddInstructionSet(InstructionSet.ARM64_Sm4); break; + case TargetArchitecture.LoongArch64: + break; + case TargetArchitecture.RiscV64: if (resultflags.HasInstructionSet(InstructionSet.RiscV64_Zbb)) resultflags.AddInstructionSet(InstructionSet.RiscV64_RiscV64Base); @@ -903,6 +918,9 @@ private static InstructionSetFlags ExpandInstructionSetByReverseImplicationHelpe resultflags.AddInstructionSet(InstructionSet.ARM64_SveSm4); break; + case TargetArchitecture.LoongArch64: + break; + case TargetArchitecture.RiscV64: if (resultflags.HasInstructionSet(InstructionSet.RiscV64_RiscV64Base)) resultflags.AddInstructionSet(InstructionSet.RiscV64_Zbb); @@ -1167,6 +1185,11 @@ public static IEnumerable ArchitectureToValidInstructionSets yield return new InstructionSetInfo("cssc", "", InstructionSet.ARM64_Cssc, true); break; + case TargetArchitecture.LoongArch64: + yield return new InstructionSetInfo("lam_bh", "LAM_BH", InstructionSet.LoongArch64_LAM_BH, true); + yield return new InstructionSetInfo("lam_cas", "LAM_CAS", InstructionSet.LoongArch64_LAM_CAS, true); + break; + case TargetArchitecture.RiscV64: yield return new InstructionSetInfo("base", "", InstructionSet.RiscV64_RiscV64Base, true); yield return new InstructionSetInfo("zba", "", InstructionSet.RiscV64_Zba, true); @@ -1369,6 +1392,9 @@ public void Set64BitInstructionSetVariants(TargetArchitecture architecture) AddInstructionSet(InstructionSet.ARM64_SveSm4_Arm64); break; + case TargetArchitecture.LoongArch64: + break; + case TargetArchitecture.RiscV64: break; @@ -1438,6 +1464,9 @@ public void Set64BitInstructionSetVariantsUnconditionally(TargetArchitecture arc AddInstructionSet(InstructionSet.ARM64_SveSm4_Arm64); break; + case TargetArchitecture.LoongArch64: + break; + case TargetArchitecture.RiscV64: break; @@ -1627,6 +1656,12 @@ public static InstructionSet LookupPlatformIntrinsicInstructionSet(TargetArchite else return InstructionSet.ARM64_SveSm4; + default: + return InstructionSet.ILLEGAL; + } + case TargetArchitecture.LoongArch64: + switch (typeName) + { default: return InstructionSet.ILLEGAL; } diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetDesc.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetDesc.txt index 87a52852ac7130..e142c726850527 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetDesc.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetDesc.txt @@ -26,7 +26,7 @@ ; DO NOT CHANGE R2R NUMERIC VALUES OF THE EXISTING SETS. Changing R2R numeric values definitions would be R2R format breaking change. ; The ISA definitions should also be mapped to `hwintrinsicIsaRangeArray` in hwintrinsic.cpp. -; NEXT_AVAILABLE_R2R_BIT = 94 +; NEXT_AVAILABLE_R2R_BIT = 97 ; Definition of X86 instruction sets definearch ,X86 ,32Bit ,X64, X64, X86 @@ -290,6 +290,12 @@ implication ,RiscV64 ,Zba ,RiscV64Base implication ,RiscV64 ,Zbs ,RiscV64Base implication ,RiscV64 ,Zicond ,RiscV64Base +; Definition of LoongArch64 instruction sets +definearch ,LoongArch64 ,64Bit ,LoongArch64 ,LoongArch64, + +instructionset ,LoongArch64 ,LAM_BH , ,95 ,LAM_BH ,lam_bh +instructionset ,LoongArch64 ,LAM_CAS , ,96 ,LAM_CAS ,lam_cas + ; ,name and aliases ,archs ,lower baselines included by implication instructionsetgroup ,x86-64-v2 ,X64 X86 ,base instructionsetgroup ,x86-64-v3 ,X64 X86 ,x86-64-v2 avx2 diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetGenerator.cs b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetGenerator.cs index d15da9ddbff34c..1cfb3adf942ecc 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetGenerator.cs +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetGenerator.cs @@ -119,6 +119,8 @@ private static string ArchToIfDefArch(string arch) { if (arch == "X64") return "AMD64"; + if (arch == "LoongArch64") + return "LOONGARCH64"; if (arch == "RiscV64") return "RISCV64"; if (arch == "Wasm32") diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExpectedIsaFeaturesRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExpectedIsaFeaturesRootProvider.cs index aef028d8c0aeee..95476d7e440911 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExpectedIsaFeaturesRootProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ExpectedIsaFeaturesRootProvider.cs @@ -21,6 +21,7 @@ void ICompilationRootProvider.AddCompilationRoots(IRootingServiceProvider rootPr { if (_isaSupport.Architecture == TargetArchitecture.X64 || _isaSupport.Architecture == TargetArchitecture.X86 + || _isaSupport.Architecture == TargetArchitecture.LoongArch64 || _isaSupport.Architecture == TargetArchitecture.ARM64) { int isaFlags = HardwareIntrinsicHelpers.GetRuntimeRequiredIsaFlags(_isaSupport); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/HardwareIntrinsicHelpers.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/HardwareIntrinsicHelpers.Aot.cs index 0a66f169fbc978..ef9bdec953e851 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/HardwareIntrinsicHelpers.Aot.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/HardwareIntrinsicHelpers.Aot.cs @@ -41,6 +41,10 @@ public static MethodIL EmitIsSupportedIL(MethodDesc method, FieldDesc isSupporte flag = Arm64IntrinsicConstants.FromInstructionSet(instructionSet); break; + case TargetArchitecture.LoongArch64: + flag = LoongArch64IntrinsicConstants.FromInstructionSet(instructionSet); + break; + default: Debug.Fail("Unsupported Architecture"); break; @@ -93,6 +97,11 @@ public static int GetRuntimeRequiredIsaFlags(InstructionSetSupport instructionSe result |= Arm64IntrinsicConstants.FromInstructionSet(instructionSet); break; + case TargetArchitecture.LoongArch64: + foreach (InstructionSet instructionSet in instructionSetSupport.SupportedFlags) + result |= LoongArch64IntrinsicConstants.FromInstructionSet(instructionSet); + break; + default: Debug.Fail("Unsupported Architecture"); break; diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index a9e7eb3c486ae9..9296bc39ce6b2f 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -1729,6 +1729,16 @@ void EEJitManager::SetCpuInfo() { CPUCompileFlags.Set(InstructionSet_Zicond); } +#elif defined(TARGET_LOONGARCH64) + if (((cpuFeatures & LOONGARCH64IntrinsicConstants_LAM_BH) != 0) && CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_EnableLoongArch64LAM_BH)) + { + CPUCompileFlags.Set(InstructionSet_LAM_BH); + } + + if (((cpuFeatures & LOONGARCH64IntrinsicConstants_LAM_CAS) != 0) && CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_EnableLoongArch64LAM_CAS)) + { + CPUCompileFlags.Set(InstructionSet_LAM_CAS); + } #endif // These calls are very important as it ensures the flags are consistent with any diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index c9984deef79a1b..2a8182d5b1c656 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -1537,7 +1537,7 @@ MethodTableBuilder::BuildMethodTableThrowing( } } -#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_WASM) +#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_WASM) || defined(TARGET_LOONGARCH64) if (bmtProp->fIsIntrinsicType && !bmtGenerics->HasInstantiation()) { LPCUTF8 nameSpace; @@ -1565,6 +1565,8 @@ MethodTableBuilder::BuildMethodTableThrowing( // All the functions in System.Runtime.Intrinsics. are hardware intrinsics. #if defined(TARGET_ARM64) if (hr == S_OK && strcmp(nameSpace, "System.Runtime.Intrinsics.Arm") == 0) +#elif defined(TARGET_LOONGARCH64) + if (hr == S_OK && strcmp(nameSpace, "System.Runtime.Intrinsics.LoongArch") == 0) #elif defined(TARGET_WASM) if (hr == S_OK && strcmp(nameSpace, "System.Runtime.Intrinsics.Wasm") == 0) #else diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 0b64b2d83de1b7..3eab2a6cb3431a 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -20,6 +20,7 @@ false true false + true .PlatformNotSupported $(MSBuildThisFileDirectory)ILLink\ true @@ -2899,6 +2900,9 @@ + + + diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/LoongArch/LAM.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/LoongArch/LAM.cs new file mode 100644 index 00000000000000..2308e6ae0e3a5d --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/LoongArch/LAM.cs @@ -0,0 +1,76 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +namespace System.Runtime.Intrinsics.LoongArch +{ + /// Provides access to the LoongArch64 LAM hardware instructions via intrinsics. + [Intrinsic] + internal abstract class LAM + { + internal LAM() { } + + [Intrinsic] + internal abstract class BH + { + internal BH() { } + /// Gets a value that indicates whether the APIs in this class are supported. + /// if the APIs are supported; otherwise, . + /// A value of indicates that the APIs will throw . + internal static bool IsSupported { get => IsSupported; } + + /// + /// AMSWAP[_DB].B rd, rk, rj + /// This is the newly added atomic instruction on ISA1.1 + /// + internal static byte Exchange(ref byte location1, byte value) => Exchange(ref location1, value); + + /// + /// AMSWAP[_DB].H rd, rk, rj + /// This is the newly added atomic instruction on ISA1.1 + /// + internal static ushort Exchange(ref ushort location1, ushort value) => Exchange(ref location1, value); + } + + [Intrinsic] + internal abstract class CAS + { + internal CAS() { } + /// Gets a value that indicates whether the APIs in this class are supported. + /// if the APIs are supported; otherwise, . + /// A value of indicates that the APIs will throw . + internal static bool IsSupported { get => IsSupported; } + + /// + /// AMCAS[_DB].B rd, rk, rj + /// This is the newly added atomic instruction on ISA1.1 + /// + internal static byte CompareExchange(ref byte location1, byte value, byte comparand) => CompareExchange(ref location1, value, comparand); + + /// + /// AMCAS[_DB].H rd, rk, rj + /// This is the newly added atomic instruction on ISA1.1 + /// + internal static ushort CompareExchange(ref ushort location1, ushort value, ushort comparand) => CompareExchange(ref location1, value, comparand); + + /// + /// AMCAS[_DB].W rd, rk, rj + /// This is the newly added atomic instruction on ISA1.1 + /// + internal static int CompareExchange(ref int location1, int value, int comparand) => CompareExchange(ref location1, value, comparand); + + /// + /// AMCAS[_DB].W rd, rk, rj + /// This is the newly added atomic instruction on ISA1.1 + /// + internal static unsafe int CompareExchange(int* location1, int value, int comparand) => CompareExchange(location1, value, comparand); + + /// + /// AMCAS[_DB].D rd, rk, rj + /// This is the newly added atomic instruction on ISA1.1 + /// + internal static long CompareExchange(ref long location1, long value, long comparand) => CompareExchange(ref location1, value, comparand); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Interlocked.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Interlocked.cs index dfa92e8b9381e1..4e76aa51c90d7e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Interlocked.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Interlocked.cs @@ -5,6 +5,9 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +#if TARGET_LOONGARCH64 +using System.Runtime.Intrinsics.LoongArch; +#endif namespace System.Threading { @@ -85,6 +88,12 @@ public static byte Exchange(ref byte location1, byte value) #if (MONO && (TARGET_AMD64 || TARGET_ARM64 || TARGET_WASM)) || (!MONO && (TARGET_X86 || TARGET_AMD64 || TARGET_ARM64)) return Exchange(ref location1, value); // Must expand intrinsic #else +#if TARGET_LOONGARCH64 + if (LAM.BH.IsSupported) + { + return LAM.BH.Exchange(ref location1, value); // Must expand intrinsic + } +#endif // this relies on GC keeping 4B alignment for refs and on subtracting to such alignment being in the same object nuint offset = Unsafe.OpportunisticMisalignment(ref location1, sizeof(uint)); ref uint alignedRef = ref Unsafe.As(ref Unsafe.SubtractByteOffset(ref location1, offset)); @@ -124,6 +133,12 @@ public static ushort Exchange(ref ushort location1, ushort value) #if ((MONO && (TARGET_AMD64 || TARGET_ARM64 || TARGET_WASM)) || !MONO && (TARGET_X86 || TARGET_AMD64 || TARGET_ARM64)) return Exchange(ref location1, value); // Must expand intrinsic #else +#if TARGET_LOONGARCH64 + if (LAM.BH.IsSupported) + { + return LAM.BH.Exchange(ref location1, value); // Must expand intrinsic + } +#endif // this relies on GC keeping 4B alignment for refs and on subtracting to such alignment being in the same object nuint offset = Unsafe.OpportunisticMisalignment(ref location1, sizeof(uint)); ref uint alignedRef = ref Unsafe.As(ref Unsafe.SubtractByteOffset(ref location1, offset)); @@ -321,6 +336,12 @@ public static byte CompareExchange(ref byte location1, byte value, byte comparan #if (MONO && (TARGET_ARM64 || TARGET_AMD64 || TARGET_WASM)) || (!MONO && (TARGET_X86 || TARGET_AMD64 || TARGET_ARM64)) return CompareExchange(ref location1, value, comparand); // Must expand intrinsic #else +#if TARGET_LOONGARCH64 + if (LAM.CAS.IsSupported) + { + return LAM.CAS.CompareExchange(ref location1, value, comparand); // Must expand intrinsic + } +#endif // this relies on GC keeping 4B alignment for refs and on subtracting to such alignment being in the same object nuint offset = Unsafe.OpportunisticMisalignment(ref location1, sizeof(uint)); ref uint alignedRef = ref Unsafe.As(ref Unsafe.SubtractByteOffset(ref location1, offset)); @@ -364,6 +385,12 @@ public static ushort CompareExchange(ref ushort location1, ushort value, ushort #if (MONO && (TARGET_ARM64 || TARGET_AMD64 || TARGET_WASM)) || (!MONO && (TARGET_X86 || TARGET_AMD64 || TARGET_ARM64)) return CompareExchange(ref location1, value, comparand); // Must expand intrinsic #else +#if TARGET_LOONGARCH64 + if (LAM.CAS.IsSupported) + { + return LAM.CAS.CompareExchange(ref location1, value, comparand); // Must expand intrinsic + } +#endif // this relies on GC keeping 4B alignment for refs and on subtracting to such alignment being in the same object nuint offset = Unsafe.OpportunisticMisalignment(ref location1, sizeof(uint)); ref uint alignedRef = ref Unsafe.As(ref Unsafe.SubtractByteOffset(ref location1, offset)); diff --git a/src/native/minipal/cpufeatures.c b/src/native/minipal/cpufeatures.c index 5e8ab7fa182e89..79537c194bb9c9 100644 --- a/src/native/minipal/cpufeatures.c +++ b/src/native/minipal/cpufeatures.c @@ -779,6 +779,24 @@ int minipal_getcpufeatures(void) #endif // HOST_RISCV64 +#if defined(HOST_LOONGARCH64) +#define LAM_BH 0x8000000 // LAM_BH +#define LAM_CAS 0x10000000 // LAMCAS + // CPUCFG..[27] + // CPUCFG..[28] + uint32_t cpucfg = 0; + uint32_t world = 0x2; + asm volatile("cpucfg %0, %1\n\t" + :"=r"(cpucfg) + :"r"(world) + ); + + if (cpucfg & LAM_BH) + result |= LOONGARCH64IntrinsicConstants_LAM_BH; + if (cpucfg & LAM_CAS) + result |= LOONGARCH64IntrinsicConstants_LAM_CAS; +#endif // HOST_LOONGARCH64 + return result; } diff --git a/src/native/minipal/cpufeatures.h b/src/native/minipal/cpufeatures.h index 08261ee262771c..4672373651841a 100644 --- a/src/native/minipal/cpufeatures.h +++ b/src/native/minipal/cpufeatures.h @@ -66,6 +66,11 @@ static_assert((1 << ARM64_ATOMICS_FEATURE_FLAG_BIT) == ARM64IntrinsicConstants_A #define RiscV64IntrinsicConstants_Zicond (1 << 3) #endif // HOST_RISCV64 +#if defined(HOST_LOONGARCH64) +#define LOONGARCH64IntrinsicConstants_LAM_BH (1 << 0) +#define LOONGARCH64IntrinsicConstants_LAM_CAS (1 << 1) +#endif // HOST_LOONGARCH64 + #ifdef __cplusplus extern "C" {