diff --git a/src/coreclr/inc/corjitflags.h b/src/coreclr/inc/corjitflags.h index e8c330e3e41b39..c749e876d2e0fa 100644 --- a/src/coreclr/inc/corjitflags.h +++ b/src/coreclr/inc/corjitflags.h @@ -97,7 +97,12 @@ class CORJIT_FLAGS CORJIT_FLAG_NO_INLINING = 42, // JIT should not inline any called method into this method +#if defined(TARGET_ARM) + CORJIT_FLAG_SOFTFP_ABI = 43, // JIT should generate PC-relative address computations instead of EE relocation records +#else // !defined(TARGET_ARM) CORJIT_FLAG_UNUSED16 = 43, +#endif // !defined(TARGET_ARM) + CORJIT_FLAG_UNUSED17 = 44, CORJIT_FLAG_UNUSED18 = 45, CORJIT_FLAG_UNUSED19 = 46, diff --git a/src/coreclr/inc/switches.h b/src/coreclr/inc/switches.h index 8fb65335116b8b..322a71ea11f440 100644 --- a/src/coreclr/inc/switches.h +++ b/src/coreclr/inc/switches.h @@ -150,7 +150,7 @@ // do not work reliably with conservative GC. #define FEATURE_CONSERVATIVE_GC 1 -#if (defined(TARGET_ARM) && !defined(ARM_SOFTFP)) || defined(TARGET_ARM64) +#if (defined(TARGET_ARM) && (!defined(ARM_SOFTFP) || defined(CONFIGURABLE_ARM_ABI))) || defined(TARGET_ARM64) #define FEATURE_HFA #endif diff --git a/src/coreclr/jit/CMakeLists.txt b/src/coreclr/jit/CMakeLists.txt index f5c5429a77c19a..570a2e99469297 100644 --- a/src/coreclr/jit/CMakeLists.txt +++ b/src/coreclr/jit/CMakeLists.txt @@ -490,6 +490,7 @@ if (CLR_CMAKE_BUILD_SUBSET_ALLJITS AND NOT CLR_CROSS_COMPONENTS_BUILD) create_standalone_jit(TARGET clrjit_unix_armel_${ARCH_HOST_NAME} OS unix ARCH armel) create_standalone_jit(TARGET clrjit_unix_arm_${ARCH_HOST_NAME} OS unix ARCH arm) + target_compile_definitions(clrjit_unix_arm_${ARCH_HOST_NAME} PRIVATE ARM_SOFTFP CONFIGURABLE_ARM_ABI) create_standalone_jit(TARGET clrjit_win_arm_${ARCH_HOST_NAME} OS win ARCH arm) create_standalone_jit(TARGET clrjit_win_x86_${ARCH_HOST_NAME} OS win ARCH x86) else() diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 75f038612b3c2c..da78cb3c944d61 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -4004,40 +4004,38 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere if (doingFloat) { -#if defined(FEATURE_HFA) || defined(UNIX_AMD64_ABI) - insCopy = ins_Copy(TYP_DOUBLE); - // Compute xtraReg here when we have a float argument - assert(xtraReg == REG_NA); +#ifndef UNIX_AMD64_ABI + if (GlobalJitOptions::compFeatureHfa) +#endif // !UNIX_AMD64_ABI + { + insCopy = ins_Copy(TYP_DOUBLE); + // Compute xtraReg here when we have a float argument + assert(xtraReg == REG_NA); - regMaskTP fpAvailMask; + regMaskTP fpAvailMask; - fpAvailMask = RBM_FLT_CALLEE_TRASH & ~regArgMaskLive; -#if defined(FEATURE_HFA) - fpAvailMask &= RBM_ALLDOUBLE; -#else -#if !defined(UNIX_AMD64_ABI) -#error Error. Wrong architecture. -#endif // !defined(UNIX_AMD64_ABI) -#endif // defined(FEATURE_HFA) + fpAvailMask = RBM_FLT_CALLEE_TRASH & ~regArgMaskLive; + if (GlobalJitOptions::compFeatureHfa) + { + fpAvailMask &= RBM_ALLDOUBLE; + } - if (fpAvailMask == RBM_NONE) - { - fpAvailMask = RBM_ALLFLOAT & ~regArgMaskLive; -#if defined(FEATURE_HFA) - fpAvailMask &= RBM_ALLDOUBLE; -#else -#if !defined(UNIX_AMD64_ABI) -#error Error. Wrong architecture. -#endif // !defined(UNIX_AMD64_ABI) -#endif // defined(FEATURE_HFA) - } + if (fpAvailMask == RBM_NONE) + { + fpAvailMask = RBM_ALLFLOAT & ~regArgMaskLive; + if (GlobalJitOptions::compFeatureHfa) + { + fpAvailMask &= RBM_ALLDOUBLE; + } + } - assert(fpAvailMask != RBM_NONE); + assert(fpAvailMask != RBM_NONE); - // We pick the lowest avail register number - regMaskTP tempMask = genFindLowestBit(fpAvailMask); - xtraReg = genRegNumFromMask(tempMask); -#elif defined(TARGET_X86) + // We pick the lowest avail register number + regMaskTP tempMask = genFindLowestBit(fpAvailMask); + xtraReg = genRegNumFromMask(tempMask); + } +#if defined(TARGET_X86) // This case shouldn't occur on x86 since NYI gets converted to an assert NYI("Homing circular FP registers via xtraReg"); #endif @@ -9562,20 +9560,26 @@ bool Compiler::IsHfa(CORINFO_CLASS_HANDLE hClass) bool Compiler::IsHfa(GenTree* tree) { -#ifdef FEATURE_HFA - return IsHfa(gtGetStructHandleIfPresent(tree)); -#else - return false; -#endif + if (GlobalJitOptions::compFeatureHfa) + { + return IsHfa(gtGetStructHandleIfPresent(tree)); + } + else + { + return false; + } } var_types Compiler::GetHfaType(GenTree* tree) { -#ifdef FEATURE_HFA - return GetHfaType(gtGetStructHandleIfPresent(tree)); -#else - return TYP_UNDEF; -#endif + if (GlobalJitOptions::compFeatureHfa) + { + return GetHfaType(gtGetStructHandleIfPresent(tree)); + } + else + { + return TYP_UNDEF; + } } unsigned Compiler::GetHfaCount(GenTree* tree) @@ -9585,18 +9589,19 @@ unsigned Compiler::GetHfaCount(GenTree* tree) var_types Compiler::GetHfaType(CORINFO_CLASS_HANDLE hClass) { -#ifdef FEATURE_HFA - if (hClass != NO_CLASS_HANDLE) + if (GlobalJitOptions::compFeatureHfa) { - CorInfoHFAElemType elemKind = info.compCompHnd->getHFAType(hClass); - if (elemKind != CORINFO_HFA_ELEM_NONE) + if (hClass != NO_CLASS_HANDLE) { - // This type may not appear elsewhere, but it will occupy a floating point register. - compFloatingPointUsed = true; + CorInfoHFAElemType elemKind = info.compCompHnd->getHFAType(hClass); + if (elemKind != CORINFO_HFA_ELEM_NONE) + { + // This type may not appear elsewhere, but it will occupy a floating point register. + compFloatingPointUsed = true; + } + return HfaTypeFromElemKind(elemKind); } - return HfaTypeFromElemKind(elemKind); } -#endif // FEATURE_HFA return TYP_UNDEF; } diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 7a8fb916abfc18..156054a7b8f117 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -54,6 +54,12 @@ bool Compiler::s_pJitFunctionFileInitialized = false; MethodSet* Compiler::s_pJitMethodSet = nullptr; #endif // DEBUG +#ifdef CONFIGURABLE_ARM_ABI +// static +bool GlobalJitOptions::compFeatureHfa = false; +LONG GlobalJitOptions::compUseSoftFPConfigured = 0; +#endif // CONFIGURABLE_ARM_ABI + /***************************************************************************** * * Little helpers to grab the current cycle counter value; this is done @@ -468,49 +474,6 @@ var_types Compiler::getJitGCType(BYTE gcType) return result; } -#ifdef ARM_SOFTFP -//--------------------------------------------------------------------------- -// IsSingleFloat32Struct: -// Check if the given struct type contains only one float32 value type -// -// Arguments: -// clsHnd - the handle for the struct type -// -// Return Value: -// true if the given struct type contains only one float32 value type, -// false otherwise. -// - -bool Compiler::isSingleFloat32Struct(CORINFO_CLASS_HANDLE clsHnd) -{ - for (;;) - { - // all of class chain must be of value type and must have only one field - if (!info.compCompHnd->isValueClass(clsHnd) || info.compCompHnd->getClassNumInstanceFields(clsHnd) != 1) - { - return false; - } - - CORINFO_CLASS_HANDLE* pClsHnd = &clsHnd; - CORINFO_FIELD_HANDLE fldHnd = info.compCompHnd->getFieldInClass(clsHnd, 0); - CorInfoType fieldType = info.compCompHnd->getFieldType(fldHnd, pClsHnd); - - switch (fieldType) - { - case CORINFO_TYPE_VALUECLASS: - clsHnd = *pClsHnd; - break; - - case CORINFO_TYPE_FLOAT: - return true; - - default: - return false; - } - } -} -#endif // ARM_SOFTFP - #ifdef TARGET_X86 //--------------------------------------------------------------------------- // isTrivialPointerSizedStruct: @@ -623,53 +586,44 @@ var_types Compiler::getPrimitiveTypeForStruct(unsigned structSize, CORINFO_CLASS var_types useType = TYP_UNKNOWN; -// Start by determining if we have an HFA/HVA with a single element. -#ifdef FEATURE_HFA + // Start by determining if we have an HFA/HVA with a single element. + if (GlobalJitOptions::compFeatureHfa) + { #if defined(TARGET_WINDOWS) && defined(TARGET_ARM64) - // Arm64 Windows VarArg methods arguments will not classify HFA types, they will need to be treated - // as if they are not HFA types. - if (!isVarArg) + // Arm64 Windows VarArg methods arguments will not classify HFA types, they will need to be treated + // as if they are not HFA types. + if (!isVarArg) #endif // defined(TARGET_WINDOWS) && defined(TARGET_ARM64) - { - switch (structSize) { - case 4: - case 8: + switch (structSize) + { + case 4: + case 8: #ifdef TARGET_ARM64 - case 16: + case 16: #endif // TARGET_ARM64 - { - var_types hfaType; -#ifdef ARM_SOFTFP - // For ARM_SOFTFP, HFA is unsupported so we need to check in another way. - // This matters only for size-4 struct because bigger structs would be processed with RetBuf. - if (isSingleFloat32Struct(clsHnd)) - { - hfaType = TYP_FLOAT; - } -#else // !ARM_SOFTFP - hfaType = GetHfaType(clsHnd); -#endif // ARM_SOFTFP - // We're only interested in the case where the struct size is equal to the size of the hfaType. - if (varTypeIsValidHfaType(hfaType)) { - if (genTypeSize(hfaType) == structSize) + var_types hfaType = GetHfaType(clsHnd); + // We're only interested in the case where the struct size is equal to the size of the hfaType. + if (varTypeIsValidHfaType(hfaType)) { - useType = hfaType; - } - else - { - return TYP_UNKNOWN; + if (genTypeSize(hfaType) == structSize) + { + useType = hfaType; + } + else + { + return TYP_UNKNOWN; + } } } } - } - if (useType != TYP_UNKNOWN) - { - return useType; + if (useType != TYP_UNKNOWN) + { + return useType; + } } } -#endif // FEATURE_HFA // Now deal with non-HFA/HVA structs. switch (structSize) @@ -3273,6 +3227,27 @@ void Compiler::compInitOptions(JitFlags* jitFlags) } #endif // FEATURE_FASTTAILCALL +#ifdef CONFIGURABLE_ARM_ABI + opts.compUseSoftFP = jitFlags->IsSet(JitFlags::JIT_FLAG_SOFTFP_ABI); + unsigned int softFPConfig = opts.compUseSoftFP ? 2 : 1; + unsigned int oldSoftFPConfig = + InterlockedCompareExchange(&GlobalJitOptions::compUseSoftFPConfigured, softFPConfig, 0); + if (oldSoftFPConfig != softFPConfig && oldSoftFPConfig != 0) + { + // There are no current scenarios where the abi can change during the lifetime of a process + // that uses the JIT. If such a change occurs, either compFeatureHfa will need to change to a TLS static + // or we will need to have some means to reset the flag safely. + NO_WAY("SoftFP ABI setting changed during lifetime of process"); + } + + GlobalJitOptions::compFeatureHfa = !opts.compUseSoftFP; +#elif defined(ARM_SOFTFP) + // Armel is unconditionally enabled in the JIT. Verify that the VM side agrees. + assert(jitFlags->IsSet(JitFlags::JIT_FLAG_SOFTFP_ABI)); +#elif TARGET_ARM + assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_SOFTFP_ABI)); +#endif // CONFIGURABLE_ARM_ABI + opts.compScopeInfo = opts.compDbgInfo; #ifdef LATE_DISASM diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 64635e15cb2960..660b94d40133dd 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -139,7 +139,6 @@ const int BAD_STK_OFFS = 0xBAADF00D; // for LclVarDsc::lvStkOffs //------------------------------------------------------------------------ // HFA info shared by LclVarDsc and fgArgTabEntry //------------------------------------------------------------------------ -#ifdef FEATURE_HFA inline bool IsHfa(CorInfoHFAElemType kind) { return kind != CORINFO_HFA_ELEM_NONE; @@ -186,7 +185,6 @@ inline CorInfoHFAElemType HfaElemKindFromType(var_types type) return CORINFO_HFA_ELEM_NONE; } } -#endif // FEATURE_HFA // The following holds the Local var info (scope information) typedef const char* VarName; // Actual ASCII string @@ -489,9 +487,9 @@ class LclVarDsc unsigned char lvIsMultiRegArg : 1; // true if this is a multireg LclVar struct used in an argument context unsigned char lvIsMultiRegRet : 1; // true if this is a multireg LclVar struct assigned from a multireg call -#ifdef FEATURE_HFA +#ifdef FEATURE_HFA_FIELDS_PRESENT CorInfoHFAElemType _lvHfaElemKind : 3; // What kind of an HFA this is (CORINFO_HFA_ELEM_NONE if it is not an HFA). -#endif // FEATURE_HFA +#endif // FEATURE_HFA_FIELDS_PRESENT #ifdef DEBUG // TODO-Cleanup: See the note on lvSize() - this flag is only in use by asserts that are checking for struct @@ -561,22 +559,47 @@ class LclVarDsc } #endif // FEATURE_MULTIREG_ARGS - bool lvIsHfa() const + CorInfoHFAElemType GetLvHfaElemKind() const { -#ifdef FEATURE_HFA - return IsHfa(_lvHfaElemKind); +#ifdef FEATURE_HFA_FIELDS_PRESENT + return _lvHfaElemKind; #else - return false; -#endif + NOWAY_MSG("GetLvHfaElemKind"); + return CORINFO_HFA_ELEM_NONE; +#endif // FEATURE_HFA_FIELDS_PRESENT } - bool lvIsHfaRegArg() const + void SetLvHfaElemKind(CorInfoHFAElemType elemKind) { -#ifdef FEATURE_HFA - return lvIsRegArg && lvIsHfa(); +#ifdef FEATURE_HFA_FIELDS_PRESENT + _lvHfaElemKind = elemKind; #else - return false; -#endif + NOWAY_MSG("SetLvHfaElemKind"); +#endif // FEATURE_HFA_FIELDS_PRESENT + } + + bool lvIsHfa() const + { + if (GlobalJitOptions::compFeatureHfa) + { + return IsHfa(GetLvHfaElemKind()); + } + else + { + return false; + } + } + + bool lvIsHfaRegArg() const + { + if (GlobalJitOptions::compFeatureHfa) + { + return lvIsRegArg && lvIsHfa(); + } + else + { + return false; + } } //------------------------------------------------------------------------------ @@ -595,7 +618,7 @@ class LclVarDsc slots = lvExactSize / sizeof(float); assert(slots <= 8); #elif defined(TARGET_ARM64) - switch (_lvHfaElemKind) + switch (GetLvHfaElemKind()) { case CORINFO_HFA_ELEM_NONE: assert(!"lvHfaSlots called for non-HFA"); @@ -921,22 +944,26 @@ class LclVarDsc var_types GetHfaType() const { -#ifdef FEATURE_HFA - assert(lvIsHfa()); - return HfaTypeFromElemKind(_lvHfaElemKind); -#else - return TYP_UNDEF; -#endif // FEATURE_HFA + if (GlobalJitOptions::compFeatureHfa) + { + assert(lvIsHfa()); + return HfaTypeFromElemKind(GetLvHfaElemKind()); + } + else + { + return TYP_UNDEF; + } } void SetHfaType(var_types type) { -#ifdef FEATURE_HFA - CorInfoHFAElemType elemKind = HfaElemKindFromType(type); - _lvHfaElemKind = elemKind; - // Ensure we've allocated enough bits. - assert(_lvHfaElemKind == elemKind); -#endif // FEATURE_HFA + if (GlobalJitOptions::compFeatureHfa) + { + CorInfoHFAElemType elemKind = HfaElemKindFromType(type); + SetLvHfaElemKind(elemKind); + // Ensure we've allocated enough bits. + assert(GetLvHfaElemKind() == elemKind); + } } var_types lvaArgType(); @@ -1481,9 +1508,27 @@ struct fgArgTabEntry #ifdef FEATURE_ARG_SPLIT bool _isSplit : 1; // True when this argument is split between the registers and OutArg area #endif // FEATURE_ARG_SPLIT -#ifdef FEATURE_HFA +#ifdef FEATURE_HFA_FIELDS_PRESENT CorInfoHFAElemType _hfaElemKind : 3; // What kind of an HFA this is (CORINFO_HFA_ELEM_NONE if it is not an HFA). #endif + CorInfoHFAElemType GetHfaElemKind() const + { +#ifdef FEATURE_HFA_FIELDS_PRESENT + return _hfaElemKind; +#else + NOWAY_MSG("GetHfaElemKind"); + return CORINFO_HFA_ELEM_NONE; +#endif + } + + void SetHfaElemKind(CorInfoHFAElemType elemKind) + { +#ifdef FEATURE_HFA_FIELDS_PRESENT + _hfaElemKind = elemKind; +#else + NOWAY_MSG("SetHfaElemKind"); +#endif + } bool isLateArg() const { @@ -1557,20 +1602,26 @@ struct fgArgTabEntry bool IsHfaArg() const { -#ifdef FEATURE_HFA - return IsHfa(_hfaElemKind); -#else - return false; -#endif + if (GlobalJitOptions::compFeatureHfa) + { + return IsHfa(GetHfaElemKind()); + } + else + { + return false; + } } bool IsHfaRegArg() const { -#ifdef FEATURE_HFA - return IsHfa(_hfaElemKind) && isPassedInRegisters(); -#else - return false; -#endif + if (GlobalJitOptions::compFeatureHfa) + { + return IsHfa(GetHfaElemKind()) && isPassedInRegisters(); + } + else + { + return false; + } } unsigned intRegCount() const @@ -1626,57 +1677,61 @@ struct fgArgTabEntry var_types GetHfaType() const { -#ifdef FEATURE_HFA - return HfaTypeFromElemKind(_hfaElemKind); -#else - return TYP_UNDEF; -#endif // FEATURE_HFA + if (GlobalJitOptions::compFeatureHfa) + { + return HfaTypeFromElemKind(GetHfaElemKind()); + } + else + { + return TYP_UNDEF; + } } void SetHfaType(var_types type, unsigned hfaSlots) { -#ifdef FEATURE_HFA - if (type != TYP_UNDEF) + if (GlobalJitOptions::compFeatureHfa) { - // We must already have set the passing mode. - assert(numRegs != 0 || GetStackByteSize() != 0); - // We originally set numRegs according to the size of the struct, but if the size of the - // hfaType is not the same as the pointer size, we need to correct it. - // Note that hfaSlots is the number of registers we will use. For ARM, that is twice - // the number of "double registers". - unsigned numHfaRegs = hfaSlots; -#ifdef TARGET_ARM - if (type == TYP_DOUBLE) + if (type != TYP_UNDEF) { - // Must be an even number of registers. - assert((numRegs & 1) == 0); - numHfaRegs = hfaSlots / 2; - } + // We must already have set the passing mode. + assert(numRegs != 0 || GetStackByteSize() != 0); + // We originally set numRegs according to the size of the struct, but if the size of the + // hfaType is not the same as the pointer size, we need to correct it. + // Note that hfaSlots is the number of registers we will use. For ARM, that is twice + // the number of "double registers". + unsigned numHfaRegs = hfaSlots; +#ifdef TARGET_ARM + if (type == TYP_DOUBLE) + { + // Must be an even number of registers. + assert((numRegs & 1) == 0); + numHfaRegs = hfaSlots / 2; + } #endif // TARGET_ARM - if (!IsHfaArg()) - { - // We haven't previously set this; do so now. - CorInfoHFAElemType elemKind = HfaElemKindFromType(type); - _hfaElemKind = elemKind; - // Ensure we've allocated enough bits. - assert(_hfaElemKind == elemKind); - if (isPassedInRegisters()) + if (!IsHfaArg()) { - numRegs = numHfaRegs; + // We haven't previously set this; do so now. + CorInfoHFAElemType elemKind = HfaElemKindFromType(type); + SetHfaElemKind(elemKind); + // Ensure we've allocated enough bits. + assert(GetHfaElemKind() == elemKind); + if (isPassedInRegisters()) + { + numRegs = numHfaRegs; + } } - } - else - { - // We've already set this; ensure that it's consistent. - if (isPassedInRegisters()) + else { - assert(numRegs == numHfaRegs); + // We've already set this; ensure that it's consistent. + if (isPassedInRegisters()) + { + assert(numRegs == numHfaRegs); + } + assert(type == HfaTypeFromElemKind(GetHfaElemKind())); } - assert(type == HfaTypeFromElemKind(_hfaElemKind)); } } -#endif // FEATURE_HFA } #ifdef TARGET_ARM @@ -1749,33 +1804,34 @@ struct fgArgTabEntry unsigned getSize() const { unsigned size = getSlotCount(); -#ifdef FEATURE_HFA - if (IsHfaRegArg()) + if (GlobalJitOptions::compFeatureHfa) { -#ifdef TARGET_ARM - // We counted the number of regs, but if they are DOUBLE hfa regs we have to double the size. - if (GetHfaType() == TYP_DOUBLE) + if (IsHfaRegArg()) { - assert(!IsSplit()); - size <<= 1; - } +#ifdef TARGET_ARM + // We counted the number of regs, but if they are DOUBLE hfa regs we have to double the size. + if (GetHfaType() == TYP_DOUBLE) + { + assert(!IsSplit()); + size <<= 1; + } #elif defined(TARGET_ARM64) - // We counted the number of regs, but if they are FLOAT hfa regs we have to halve the size, - // or if they are SIMD16 vector hfa regs we have to double the size. - if (GetHfaType() == TYP_FLOAT) - { - // Round up in case of odd HFA count. - size = (size + 1) >> 1; - } + // We counted the number of regs, but if they are FLOAT hfa regs we have to halve the size, + // or if they are SIMD16 vector hfa regs we have to double the size. + if (GetHfaType() == TYP_FLOAT) + { + // Round up in case of odd HFA count. + size = (size + 1) >> 1; + } #ifdef FEATURE_SIMD - else if (GetHfaType() == TYP_SIMD16) - { - size <<= 1; - } + else if (GetHfaType() == TYP_SIMD16) + { + size <<= 1; + } #endif // FEATURE_SIMD #endif // TARGET_ARM64 + } } -#endif // FEATURE_HFA return size; } @@ -2273,10 +2329,6 @@ class Compiler GenTree* impAssignSmallStructTypeToVar(GenTree* op, CORINFO_CLASS_HANDLE hClass); -#ifdef ARM_SOFTFP - bool isSingleFloat32Struct(CORINFO_CLASS_HANDLE hClass); -#endif // ARM_SOFTFP - #ifdef TARGET_X86 bool isTrivialPointerSizedStruct(CORINFO_CLASS_HANDLE clsHnd) const; #endif // TARGET_X86 @@ -9169,11 +9221,15 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX int compJitSaveFpLrWithCalleeSavedRegisters; #endif // defined(TARGET_ARM64) +#ifdef CONFIGURABLE_ARM_ABI + bool compUseSoftFP = false; +#else #ifdef ARM_SOFTFP static const bool compUseSoftFP = true; -#else // !ARM_SOFTFP +#else // !ARM_SOFTFP static const bool compUseSoftFP = false; -#endif +#endif // ARM_SOFTFP +#endif // CONFIGURABLE_ARM_ABI } opts; static bool s_pAltJitExcludeAssembliesListInitialized; diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index ec5352b1338f16..98cc8e0d055862 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -230,14 +230,19 @@ void GenTree::InitNodeSize() // Now set all of the appropriate entries to 'large' CLANG_FORMAT_COMMENT_ANCHOR; -// clang-format off -#if defined(FEATURE_HFA) || defined(UNIX_AMD64_ABI) - // On ARM32, ARM64 and System V for struct returning - // there is code that does GT_ASG-tree.CopyObj call. - // CopyObj is a large node and the GT_ASG is small, which triggers an exception. - GenTree::s_gtNodeSizes[GT_ASG] = TREE_NODE_SZ_LARGE; - GenTree::s_gtNodeSizes[GT_RETURN] = TREE_NODE_SZ_LARGE; -#endif // defined(FEATURE_HFA) || defined(UNIX_AMD64_ABI) + // clang-format off + if (GlobalJitOptions::compFeatureHfa +#if defined(UNIX_AMD64_ABI) + || true +#endif // defined(UNIX_AMD64_ABI) + ) + { + // On ARM32, ARM64 and System V for struct returning + // there is code that does GT_ASG-tree.CopyObj call. + // CopyObj is a large node and the GT_ASG is small, which triggers an exception. + GenTree::s_gtNodeSizes[GT_ASG] = TREE_NODE_SZ_LARGE; + GenTree::s_gtNodeSizes[GT_RETURN] = TREE_NODE_SZ_LARGE; + } GenTree::s_gtNodeSizes[GT_CALL] = TREE_NODE_SZ_LARGE; GenTree::s_gtNodeSizes[GT_CAST] = TREE_NODE_SZ_LARGE; diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 0b083746808c43..026e333d2ef64c 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -9719,7 +9719,7 @@ GenTree* Compiler::impFixupStructReturnType(GenTree* op, return impAssignMultiRegTypeToVar(op, retClsHnd DEBUGARG(unmgdCallConv)); } -#endif // FEATURE_MULTIREG_RET && FEATURE_HFA +#endif // FEATURE_MULTIREG_RET && TARGET_ARM64 if (!compDoOldStructRetyping() && (!op->IsCall() || !op->AsCall()->TreatAsHasRetBufArg(this))) { diff --git a/src/coreclr/jit/jit.h b/src/coreclr/jit/jit.h index 4c0a5bf63487d8..562eaccbba0731 100644 --- a/src/coreclr/jit/jit.h +++ b/src/coreclr/jit/jit.h @@ -386,6 +386,27 @@ typedef ptrdiff_t ssize_t; #define FLD_GLOBAL_DS ((CORINFO_FIELD_HANDLE)-4) #define FLD_GLOBAL_FS ((CORINFO_FIELD_HANDLE)-8) +class GlobalJitOptions +{ +public: +#ifdef FEATURE_HFA +#define FEATURE_HFA_FIELDS_PRESENT +#ifdef CONFIGURABLE_ARM_ABI + // These are safe to have globals as they cannot change once initialized within the process. + static LONG compUseSoftFPConfigured; + static bool compFeatureHfa; +#else // !CONFIGURABLE_ARM_ABI + static const bool compFeatureHfa = true; +#endif // CONFIGURABLE_ARM_ABI +#else // !FEATURE_HFA + static const bool compFeatureHfa = false; +#endif // FEATURE_HFA + +#ifdef FEATURE_HFA +#undef FEATURE_HFA +#endif +}; + /*****************************************************************************/ #include "vartype.h" @@ -508,7 +529,7 @@ typedef ptrdiff_t ssize_t; #if DUMP_GC_TABLES #pragma message("NOTE: this non-debug build has GC ptr table dumping always enabled!") -const bool dspGCtbls = true; +const bool dspGCtbls = true; #endif /*****************************************************************************/ diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 478aa4d8d0bd87..c892abe0a4b9cd 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -306,6 +306,10 @@ CONFIG_INTEGER(EnableArm64Sm4, W("EnableArm64Sm4"), 1) CONFIG_INTEGER(EnableArm64Sve, W("EnableArm64Sve"), 1) #endif // defined(TARGET_ARM64) +#if defined(CONFIGURABLE_ARM_ABI) +CONFIG_INTEGER(JitSoftFP, W("JitSoftFP"), 0) +#endif // defined(CONFIGURABLE_ARM_ABI) + // clang-format on #ifdef FEATURE_SIMD diff --git a/src/coreclr/jit/jitee.h b/src/coreclr/jit/jitee.h index 496da9edfb1cb8..27149c824348aa 100644 --- a/src/coreclr/jit/jitee.h +++ b/src/coreclr/jit/jitee.h @@ -81,7 +81,12 @@ class JitFlags JIT_FLAG_NO_INLINING = 42, // JIT should not inline any called method into this method +#if defined(TARGET_ARM) + JIT_FLAG_SOFTFP_ABI = 43, // On ARM should enable armel calling convention +#else // !defined(TARGET_ARM) JIT_FLAG_UNUSED16 = 43, +#endif // !defined(TARGET_ARM) + JIT_FLAG_UNUSED17 = 44, JIT_FLAG_UNUSED18 = 45, JIT_FLAG_UNUSED19 = 46, @@ -215,6 +220,13 @@ class JitFlags #endif // TARGET_ARM FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_NO_INLINING, JIT_FLAG_NO_INLINING); + +#if defined(TARGET_ARM) + + FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_SOFTFP_ABI, JIT_FLAG_SOFTFP_ABI); + +#endif // TARGET_ARM + #undef FLAGS_EQUAL } diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index ca6e34a083e1ca..c671d015b79767 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -1400,11 +1400,12 @@ void Compiler::lvaInitVarDsc(LclVarDsc* varDsc, varDsc->lvIsImplicitByRef = 0; #endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) -// Set the lvType (before this point it is TYP_UNDEF). + // Set the lvType (before this point it is TYP_UNDEF). -#ifdef FEATURE_HFA - varDsc->SetHfaType(TYP_UNDEF); -#endif + if (GlobalJitOptions::compFeatureHfa) + { + varDsc->SetHfaType(TYP_UNDEF); + } if ((varTypeIsStruct(type))) { lvaSetStruct(varNum, typeHnd, typeHnd != nullptr, !tiVerificationNeeded); @@ -2804,25 +2805,26 @@ void Compiler::lvaSetStruct(unsigned varNum, CORINFO_CLASS_HANDLE typeHnd, bool varDsc->lvBaseType = simdBaseType; } #endif // FEATURE_SIMD -#ifdef FEATURE_HFA - // For structs that are small enough, we check and set HFA element type - if (varDsc->lvExactSize <= MAX_PASS_MULTIREG_BYTES) + if (GlobalJitOptions::compFeatureHfa) { - // hfaType is set to float, double or SIMD type if it is an HFA, otherwise TYP_UNDEF - var_types hfaType = GetHfaType(typeHnd); - if (varTypeIsValidHfaType(hfaType)) + // For structs that are small enough, we check and set HFA element type + if (varDsc->lvExactSize <= MAX_PASS_MULTIREG_BYTES) { - varDsc->SetHfaType(hfaType); - - // hfa variables can never contain GC pointers - assert(!layout->HasGCPtr()); - // The size of this struct should be evenly divisible by 4 or 8 - assert((varDsc->lvExactSize % genTypeSize(hfaType)) == 0); - // The number of elements in the HFA should fit into our MAX_ARG_REG_COUNT limit - assert((varDsc->lvExactSize / genTypeSize(hfaType)) <= MAX_ARG_REG_COUNT); + // hfaType is set to float, double or SIMD type if it is an HFA, otherwise TYP_UNDEF + var_types hfaType = GetHfaType(typeHnd); + if (varTypeIsValidHfaType(hfaType)) + { + varDsc->SetHfaType(hfaType); + + // hfa variables can never contain GC pointers + assert(!layout->HasGCPtr()); + // The size of this struct should be evenly divisible by 4 or 8 + assert((varDsc->lvExactSize % genTypeSize(hfaType)) == 0); + // The number of elements in the HFA should fit into our MAX_ARG_REG_COUNT limit + assert((varDsc->lvExactSize / genTypeSize(hfaType)) <= MAX_ARG_REG_COUNT); + } } } -#endif // FEATURE_HFA } } else @@ -2936,14 +2938,15 @@ void Compiler::makeExtraStructQueries(CORINFO_CLASS_HANDLE structHandle, int lev void Compiler::lvaSetStructUsedAsVarArg(unsigned varNum) { -#ifdef FEATURE_HFA + if (GlobalJitOptions::compFeatureHfa) + { #if defined(TARGET_WINDOWS) && defined(TARGET_ARM64) - LclVarDsc* varDsc = &lvaTable[varNum]; - // For varargs methods incoming and outgoing arguments should not be treated - // as HFA. - varDsc->SetHfaType(TYP_UNDEF); + LclVarDsc* varDsc = &lvaTable[varNum]; + // For varargs methods incoming and outgoing arguments should not be treated + // as HFA. + varDsc->SetHfaType(TYP_UNDEF); #endif // defined(TARGET_WINDOWS) && defined(TARGET_ARM64) -#endif // FEATURE_HFA + } } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 031685696b76c2..65170c586f1582 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -3243,38 +3243,41 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) // void Lowering::LowerRetStruct(GenTreeUnOp* ret) { -#if defined(FEATURE_HFA) && defined(TARGET_ARM64) - if (varTypeIsSIMD(ret)) +#ifdef TARGET_ARM64 + if (GlobalJitOptions::compFeatureHfa) { - if (comp->info.compRetNativeType == TYP_STRUCT) + if (varTypeIsSIMD(ret)) { - assert(varTypeIsSIMD(ret->gtGetOp1())); - assert(comp->compMethodReturnsMultiRegRegTypeAlternate()); - if (!comp->compDoOldStructRetyping()) + if (comp->info.compRetNativeType == TYP_STRUCT) { - ret->ChangeType(comp->info.compRetNativeType); + assert(varTypeIsSIMD(ret->gtGetOp1())); + assert(comp->compMethodReturnsMultiRegRegTypeAlternate()); + if (!comp->compDoOldStructRetyping()) + { + ret->ChangeType(comp->info.compRetNativeType); + } + else + { + // With old struct retyping a value that is returned as HFA + // could have both SIMD* or STRUCT types, keep it as it. + return; + } } else { - // With old struct retyping a value that is returned as HFA - // could have both SIMD* or STRUCT types, keep it as it. + assert(comp->info.compRetNativeType == ret->TypeGet()); + GenTree* retVal = ret->gtGetOp1(); + if (retVal->TypeGet() != ret->TypeGet()) + { + assert(retVal->OperIs(GT_LCL_VAR)); + assert(!comp->compDoOldStructRetyping()); + LowerRetSingleRegStructLclVar(ret); + } return; } } - else - { - assert(comp->info.compRetNativeType == ret->TypeGet()); - GenTree* retVal = ret->gtGetOp1(); - if (retVal->TypeGet() != ret->TypeGet()) - { - assert(retVal->OperIs(GT_LCL_VAR)); - assert(!comp->compDoOldStructRetyping()); - LowerRetSingleRegStructLclVar(ret); - } - return; - } } -#endif +#endif // TARGET_ARM64 if (comp->compMethodReturnsMultiRegRegTypeAlternate()) { @@ -3461,25 +3464,26 @@ void Lowering::LowerCallStruct(GenTreeCall* call) return; } -#if defined(FEATURE_HFA) - if (comp->IsHfa(call)) + if (GlobalJitOptions::compFeatureHfa) { + if (comp->IsHfa(call)) + { #if defined(TARGET_ARM64) - assert(comp->GetHfaCount(call) == 1); + assert(comp->GetHfaCount(call) == 1); #elif defined(TARGET_ARM) - // ARM returns double in 2 float registers, but - // `call->HasMultiRegRetVal()` count double registers. - assert(comp->GetHfaCount(call) <= 2); -#elif // !TARGET_ARM64 && !TARGET_ARM - unreached(); + // ARM returns double in 2 float registers, but + // `call->HasMultiRegRetVal()` count double registers. + assert(comp->GetHfaCount(call) <= 2); +#else // !TARGET_ARM64 && !TARGET_ARM + NYI("Unknown architecture"); #endif // !TARGET_ARM64 && !TARGET_ARM - var_types hfaType = comp->GetHfaType(call); - if (call->TypeIs(hfaType)) - { - return; + var_types hfaType = comp->GetHfaType(call); + if (call->TypeIs(hfaType)) + { + return; + } } } -#endif // FEATURE_HFA assert(!comp->compDoOldStructRetyping()); CORINFO_CLASS_HANDLE retClsHnd = call->gtRetClsHnd; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index c2ee204766075e..c85cd432ba31dd 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -1056,9 +1056,10 @@ fgArgTabEntry* fgArgInfo::AddRegArg(unsigned argNum, curArgTabEntry->needTmp = false; curArgTabEntry->needPlace = false; curArgTabEntry->processed = false; -#ifdef FEATURE_HFA - curArgTabEntry->_hfaElemKind = CORINFO_HFA_ELEM_NONE; -#endif + if (GlobalJitOptions::compFeatureHfa) + { + curArgTabEntry->SetHfaElemKind(CORINFO_HFA_ELEM_NONE); + } curArgTabEntry->isBackFilled = false; curArgTabEntry->isNonStandard = false; curArgTabEntry->isStruct = isStruct; @@ -1153,9 +1154,10 @@ fgArgTabEntry* fgArgInfo::AddStkArg(unsigned argNum, curArgTabEntry->needTmp = false; curArgTabEntry->needPlace = false; curArgTabEntry->processed = false; -#ifdef FEATURE_HFA - curArgTabEntry->_hfaElemKind = CORINFO_HFA_ELEM_NONE; -#endif + if (GlobalJitOptions::compFeatureHfa) + { + curArgTabEntry->SetHfaElemKind(CORINFO_HFA_ELEM_NONE); + } curArgTabEntry->isBackFilled = false; curArgTabEntry->isNonStandard = false; curArgTabEntry->isStruct = isStruct; @@ -3015,28 +3017,29 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) bool isNonStandard = false; regNumber nonStdRegNum = REG_NA; -#ifdef FEATURE_HFA - hfaType = GetHfaType(argx); - isHfaArg = varTypeIsValidHfaType(hfaType); + if (GlobalJitOptions::compFeatureHfa) + { + hfaType = GetHfaType(argx); + isHfaArg = varTypeIsValidHfaType(hfaType); #if defined(TARGET_WINDOWS) && defined(TARGET_ARM64) - // Make sure for vararg methods isHfaArg is not true. - isHfaArg = callIsVararg ? false : isHfaArg; + // Make sure for vararg methods isHfaArg is not true. + isHfaArg = callIsVararg ? false : isHfaArg; #endif // defined(TARGET_WINDOWS) && defined(TARGET_ARM64) - if (isHfaArg) - { - isHfaArg = true; - hfaSlots = GetHfaCount(argx); + if (isHfaArg) + { + isHfaArg = true; + hfaSlots = GetHfaCount(argx); - // If we have a HFA struct it's possible we transition from a method that originally - // only had integer types to now start having FP types. We have to communicate this - // through this flag since LSRA later on will use this flag to determine whether - // or not to track the FP register set. - // - compFloatingPointUsed = true; + // If we have a HFA struct it's possible we transition from a method that originally + // only had integer types to now start having FP types. We have to communicate this + // through this flag since LSRA later on will use this flag to determine whether + // or not to track the FP register set. + // + compFloatingPointUsed = true; + } } -#endif // FEATURE_HFA const bool isFloatHfa = (hfaType == TYP_FLOAT); @@ -3586,12 +3589,14 @@ void Compiler::fgInitArgInfo(GenTreeCall* call) #endif } -#ifdef FEATURE_HFA - if (isHfaArg) + if (GlobalJitOptions::compFeatureHfa) { - newArgEntry->SetHfaType(hfaType, hfaSlots); + if (isHfaArg) + { + newArgEntry->SetHfaType(hfaType, hfaSlots); + } } -#endif // FEATURE_HFA + newArgEntry->SetMultiRegNums(); noway_assert(newArgEntry != nullptr); diff --git a/src/coreclr/jit/vartype.h b/src/coreclr/jit/vartype.h index d7a42c9e9cdd5e..d6cca019197237 100644 --- a/src/coreclr/jit/vartype.h +++ b/src/coreclr/jit/vartype.h @@ -335,20 +335,23 @@ inline bool varTypeUsesFloatArgReg(T vt) template inline bool varTypeIsValidHfaType(T vt) { -#ifdef FEATURE_HFA - bool isValid = (TypeGet(vt) != TYP_UNDEF); - if (isValid) + if (GlobalJitOptions::compFeatureHfa) { + bool isValid = (TypeGet(vt) != TYP_UNDEF); + if (isValid) + { #ifdef TARGET_ARM64 - assert(varTypeUsesFloatReg(vt)); + assert(varTypeUsesFloatReg(vt)); #else // !TARGET_ARM64 - assert(varTypeIsFloating(vt)); + assert(varTypeIsFloating(vt)); #endif // !TARGET_ARM64 + } + return isValid; + } + else + { + return false; } - return isValid; -#else // !FEATURE_HFA - return false; -#endif // !FEATURE_HFA } /*****************************************************************************/ diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index fcaaef55030db0..3c9c3b9c271c97 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3318,7 +3318,14 @@ private uint getJitFlags(ref CORJIT_FLAGS flags, uint sizeInBytes) } if (this.MethodBeingCompiled.IsNoOptimization) + { flags.Set(CorJitFlag.CORJIT_FLAG_MIN_OPT); + } + + if (this.MethodBeingCompiled.Context.Target.Abi == TargetAbi.CoreRTArmel) + { + flags.Set(CorJitFlag.CORJIT_FLAG_SOFTFP_ABI); + } return (uint)sizeof(CORJIT_FLAGS); } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 2b406909f27321..76763e22c71d6c 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1334,6 +1334,7 @@ public enum CorJitFlag : uint CORJIT_FLAG_TIER1 = 40, // This is the final tier (for now) for tiered compilation which should generate high quality code CORJIT_FLAG_RELATIVE_CODE_RELOCS = 41, // JIT should generate PC-relative address computations instead of EE relocation records CORJIT_FLAG_NO_INLINING = 42, // JIT should not inline any called method into this method + CORJIT_FLAG_SOFTFP_ABI = 43, // On ARM should enable armel calling convention } public struct CORJIT_FLAGS diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs index 2e23688d1a062d..1f57b5f1a8c4b2 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs @@ -915,6 +915,9 @@ private ValueTypeShapeCharacteristics ComputeHomogeneousAggregateCharacteristic( if ((targetArch != TargetArchitecture.ARM) && (targetArch != TargetArchitecture.ARM64)) return NotHA; + if (type.Context.Target.Abi == TargetAbi.CoreRTArmel) + return NotHA; + MetadataType metadataType = (MetadataType)type; // No HAs with explicit layout. There may be cases where explicit layout may be still diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TargetDetails.cs b/src/coreclr/tools/Common/TypeSystem/Common/TargetDetails.cs index 8bb69209cb7316..21ea040857855e 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/TargetDetails.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/TargetDetails.cs @@ -29,6 +29,10 @@ public enum TargetAbi /// CoreRT, /// + /// model for armel execution model + /// + CoreRTArmel, + /// /// Jit runtime ABI /// Jit, diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs index de5cb85bfad428..079ef257595a70 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs @@ -1085,7 +1085,7 @@ public int GetNextOffset() // Ignore floating point argument placement in registers if we're dealing with a vararg function (the ABI // specifies this so that vararg processing on the callee side is simplified). - if (fFloatingPoint && !IsVarArg) + if (fFloatingPoint && _transitionBlock.IsArmhfABI && !IsVarArg) { // Handle floating point (primitive) arguments. diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs index bd89459beeca96..667bae1742abf6 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs @@ -29,7 +29,14 @@ public static TransitionBlock FromTarget(TargetDetails target) X64UnixTransitionBlock.Instance; case TargetArchitecture.ARM: - return Arm32TransitionBlock.Instance; + if (target.Abi == TargetAbi.CoreRTArmel) + { + return Arm32ElTransitionBlock.Instance; + } + else + { + return Arm32TransitionBlock.Instance; + } case TargetArchitecture.ARM64: return Arm64TransitionBlock.Instance; @@ -57,6 +64,9 @@ public static TransitionBlock FromTarget(TargetDetails target) /// public virtual bool IsX64UnixABI => false; + public virtual bool IsArmelABI => false; + public virtual bool IsArmhfABI => false; + public abstract int PointerSize { get; } public int StackElemSize() => PointerSize; @@ -301,11 +311,17 @@ public void ComputeReturnValueTreatment(CorElementType type, TypeHandle thRetTyp throw new NotSupportedException(); case CorElementType.ELEMENT_TYPE_R4: - fpReturnSize = sizeof(float); + if (!IsArmelABI) + { + fpReturnSize = sizeof(float); + } break; case CorElementType.ELEMENT_TYPE_R8: - fpReturnSize = sizeof(double); + if (!IsArmelABI) + { + fpReturnSize = sizeof(double); + } break; case CorElementType.ELEMENT_TYPE_VALUETYPE: @@ -499,27 +515,37 @@ internal sealed class X64UnixTransitionBlock : X64TransitionBlock public override bool IsArgPassedByRef(TypeHandle th) => false; } - private sealed class Arm32TransitionBlock : TransitionBlock + private class Arm32TransitionBlock : TransitionBlock { public static TransitionBlock Instance = new Arm32TransitionBlock(); - public override TargetArchitecture Architecture => TargetArchitecture.ARM; - public override int PointerSize => 4; + public sealed override TargetArchitecture Architecture => TargetArchitecture.ARM; + public sealed override int PointerSize => 4; // R0, R1, R2, R3 - public override int NumArgumentRegisters => 4; + public sealed override int NumArgumentRegisters => 4; // R4, R5, R6, R7, R8, R9, R10, R11, R14 - public override int NumCalleeSavedRegisters => 9; + public sealed override int NumCalleeSavedRegisters => 9; // Callee-saves, argument registers - public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + SizeOfArgumentRegisters; - public override int OffsetOfArgumentRegisters => SizeOfCalleeSavedRegisters; + public sealed override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + SizeOfArgumentRegisters; + public sealed override int OffsetOfArgumentRegisters => SizeOfCalleeSavedRegisters; // D0..D7 - public override int OffsetOfFloatArgumentRegisters => 8 * sizeof(double) + PointerSize; - public override int EnregisteredParamTypeMaxSize => 0; - public override int EnregisteredReturnTypeIntegerMaxSize => 4; + public sealed override int OffsetOfFloatArgumentRegisters => 8 * sizeof(double) + PointerSize; + public sealed override int EnregisteredParamTypeMaxSize => 0; + public sealed override int EnregisteredReturnTypeIntegerMaxSize => 4; - public override bool IsArgPassedByRef(TypeHandle th) => false; + public override bool IsArmhfABI => true; - public override int GetRetBuffArgOffset(bool hasThis) => OffsetOfArgumentRegisters + (hasThis ? PointerSize : 0); + public sealed override bool IsArgPassedByRef(TypeHandle th) => false; + + public sealed override int GetRetBuffArgOffset(bool hasThis) => OffsetOfArgumentRegisters + (hasThis ? PointerSize : 0); + } + + private class Arm32ElTransitionBlock : Arm32TransitionBlock + { + public new static TransitionBlock Instance = new Arm32ElTransitionBlock(); + + public override bool IsArmhfABI => false; + public override bool IsArmelABI => true; } private sealed class Arm64TransitionBlock : TransitionBlock diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs index ac8dfcb2d963ed..910dffe43b2ae5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs @@ -82,6 +82,11 @@ public override CompilationBuilder UseBackendOptions(IEnumerable options builder.Add(new KeyValuePair(name, value)); } + if (_context.Target.Abi == TargetAbi.CoreRTArmel) + { + builder.Add(new KeyValuePair("JitSoftFP", "1")); + } + _ryujitOptions = builder.ToArray(); return this; diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 64f75116d65106..cd906d2799c1ef 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -23,6 +23,7 @@ internal class Program private CommandLineOptions _commandLineOptions; public TargetOS _targetOS; public TargetArchitecture _targetArchitecture; + private bool _armelAbi = false; public OptimizationMode _optimizationMode; private Dictionary _inputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); private Dictionary _unrootedInputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -167,7 +168,10 @@ private void ConfigureTarget() else if (_commandLineOptions.TargetArch.Equals("arm", StringComparison.OrdinalIgnoreCase)) _targetArchitecture = TargetArchitecture.ARM; else if (_commandLineOptions.TargetArch.Equals("armel", StringComparison.OrdinalIgnoreCase)) + { _targetArchitecture = TargetArchitecture.ARM; + _armelAbi = true; + } else if (_commandLineOptions.TargetArch.Equals("arm64", StringComparison.OrdinalIgnoreCase)) _targetArchitecture = TargetArchitecture.ARM64; else @@ -316,7 +320,7 @@ private int Run(string[] args) SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes; - var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, TargetAbi.CoreRT, instructionSetSupport.GetVectorTSimdVector()); + var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, _armelAbi ? TargetAbi.CoreRTArmel : TargetAbi.CoreRT, instructionSetSupport.GetVectorTSimdVector()); bool versionBubbleIncludesCoreLib = false; if (_commandLineOptions.InputBubble) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 4c1ba0afb5c107..2c91f19442fad0 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -12910,6 +12910,10 @@ CORJIT_FLAGS GetCompileFlags(MethodDesc * ftn, CORJIT_FLAGS flags, CORINFO_METHO flags.Clear(CORJIT_FLAGS::CORJIT_FLAG_DEBUG_INFO); } +#ifdef ARM_SOFTFP + flags.Set(CORJIT_FLAGS::CORJIT_FLAG_SOFTFP_ABI); +#endif // ARM_SOFTFP + #ifdef FEATURE_PGO // Instrument, if diff --git a/src/coreclr/zap/zapinfo.cpp b/src/coreclr/zap/zapinfo.cpp index 73a162497fcaa5..12fe2ab074ce55 100644 --- a/src/coreclr/zap/zapinfo.cpp +++ b/src/coreclr/zap/zapinfo.cpp @@ -205,6 +205,10 @@ CORJIT_FLAGS ZapInfo::ComputeJitFlags(CORINFO_METHOD_HANDLE handle) } #endif // FEATURE_READYTORUN_COMPILER +#ifdef ARM_SOFTFP + jitFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_SOFTFP_ABI); +#endif + return jitFlags; }