From 7e9a325dd4c973a503db71866673672906979e7f Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 15 Mar 2021 11:25:55 -0700 Subject: [PATCH 1/3] Make the JIT built for crossgen2 be dynamically configurable between armhf and armel calling conventions Crossgen2 managed code changes to support Armel abi - Adjust handling for armel target architecture command line handling to capture that armel is the abi - Tweak field layout to disable computation of hfa status for valuetypes - Inform JIT that armel abi is to be used - Add armel abi value to TargetAbi enum Checkin requested changes Fix build on Unix platforms Make the JIT built for crossgen2 be dynamically configurable between armhf and armel calling conventions Crossgen2 managed code changes to support Armel abi - Adjust handling for armel target architecture command line handling to capture that armel is the abi - Tweak field layout to disable computation of hfa status for valuetypes - Inform JIT that armel abi is to be used - Add armel abi value to TargetAbi enum Checkin requested changes Fix build on Unix platforms Reformat the code Fix build break Fix Windows build Code review - Use static variable, but protect from misuse with NO_WAY - Pass info to jit via jitflag, not via jit config tweak variable type Address jit format issue Fix build break --- src/coreclr/inc/corjitflags.h | 5 + src/coreclr/inc/switches.h | 2 +- src/coreclr/jit/CMakeLists.txt | 1 + src/coreclr/jit/codegencommon.cpp | 99 +++---- src/coreclr/jit/compiler.cpp | 132 ++++----- src/coreclr/jit/compiler.h | 258 +++++++++++------- src/coreclr/jit/gentree.cpp | 21 +- src/coreclr/jit/importer.cpp | 2 +- src/coreclr/jit/jit.h | 23 +- src/coreclr/jit/jitconfigvalues.h | 4 + src/coreclr/jit/jitee.h | 12 + src/coreclr/jit/lclvars.cpp | 53 ++-- src/coreclr/jit/lower.cpp | 74 ++--- src/coreclr/jit/morph.cpp | 57 ++-- src/coreclr/jit/vartype.h | 21 +- .../tools/Common/JitInterface/CorInfoImpl.cs | 7 + .../tools/Common/JitInterface/CorInfoTypes.cs | 1 + .../Common/MetadataFieldLayoutAlgorithm.cs | 3 + .../Common/TypeSystem/Common/TargetDetails.cs | 4 + .../ReadyToRun/ArgIterator.cs | 2 +- .../ReadyToRun/TransitionBlock.cs | 56 +++- .../ReadyToRunCodegenCompilationBuilder.cs | 5 + src/coreclr/tools/aot/crossgen2/Program.cs | 6 +- src/coreclr/vm/jitinterface.cpp | 4 + src/coreclr/zap/zapinfo.cpp | 4 + 25 files changed, 507 insertions(+), 349 deletions(-) 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..e1cb4952d5aed1 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) - { - useType = hfaType; - } - else + 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)) { - 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,28 @@ 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)); +#else + // Not ARM, or normal armhf abi compilation + 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..383b6708ca414f 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 Arm32TransitionBlock.Instance; + } + else + { + return Arm32ElTransitionBlock.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; } From e92b859724a5ce09c16991828af2ef462d52b7a5 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 15 Mar 2021 11:31:49 -0700 Subject: [PATCH 2/3] Fix build break --- src/coreclr/jit/compiler.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index e1cb4952d5aed1..99dd9b83ad5e8f 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -3244,9 +3244,6 @@ void Compiler::compInitOptions(JitFlags* jitFlags) #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)); -#else - // Not ARM, or normal armhf abi compilation - assert(!jitFlags->IsSet(JitFlags::JIT_FLAG_SOFTFP_ABI)); #endif // CONFIGURABLE_ARM_ABI opts.compScopeInfo = opts.compDbgInfo; From 011ab02aeb7f7337416019ffa3086c97ec0d59da Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 15 Mar 2021 17:46:32 -0700 Subject: [PATCH 3/3] Use correct transition block --- src/coreclr/jit/compiler.cpp | 2 ++ .../Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 99dd9b83ad5e8f..156054a7b8f117 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -3244,6 +3244,8 @@ void Compiler::compInitOptions(JitFlags* jitFlags) #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; 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 383b6708ca414f..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 @@ -31,11 +31,11 @@ public static TransitionBlock FromTarget(TargetDetails target) case TargetArchitecture.ARM: if (target.Abi == TargetAbi.CoreRTArmel) { - return Arm32TransitionBlock.Instance; + return Arm32ElTransitionBlock.Instance; } else { - return Arm32ElTransitionBlock.Instance; + return Arm32TransitionBlock.Instance; } case TargetArchitecture.ARM64: