Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

[CPAOT] Generating code for hardware intrinsic #26772

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 24 additions & 18 deletions src/tools/crossgen2/Common/JitInterface/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -710,11 +710,6 @@ private uint getMethodAttribsInternal(MethodDesc method)
return (uint)result;
}

private uint getMethodAttribs(CORINFO_METHOD_STRUCT_* ftn)
{
return getMethodAttribsInternal(HandleToObject(ftn));
}

private void setMethodAttribs(CORINFO_METHOD_STRUCT_* ftn, CorInfoMethodRuntimeFlags attribs)
{
// TODO: Inlining
Expand Down Expand Up @@ -892,6 +887,7 @@ private bool isInSIMDModule(CORINFO_CLASS_STRUCT_* classHnd)
getJitFlags(ref flags, (uint)sizeof(CORJIT_FLAGS));

Debug.Assert(!_simdHelper.IsVectorOfT(type)
|| ((DefType)type).InstanceFieldSize.IsIndeterminate /* This would happen in the ReadyToRun case */
|| ((DefType)type).InstanceFieldSize.AsInt == GetMaxIntrinsicSIMDVectorLength(_jit, &flags));
#endif

Expand Down Expand Up @@ -2489,33 +2485,45 @@ private static byte[] StringToUTF8(string s)
return (byte*)GetPin(StringToUTF8(method.Name));
}

private byte* getMethodNameFromMetadata(CORINFO_METHOD_STRUCT_* ftn, byte** className, byte** namespaceName, byte** enclosingClassName)
private String getMethodNameFromMetadataImpl(MethodDesc method, out string className, out string namespaceName, out string enclosingClassName)
{
MethodDesc method = HandleToObject(ftn);

string result = null;
string classResult = null;
string namespaceResult = null;
string enclosingResult = null;
className = null;
namespaceName = null;
enclosingClassName = null;

result = method.Name;

MetadataType owningType = method.OwningType as MetadataType;
if (owningType != null)
{
classResult = owningType.Name;
namespaceResult = owningType.Namespace;
className = owningType.Name;
namespaceName = owningType.Namespace;

// Query enclosingClassName when the method is in a nested class
// and get the namespace of enclosing classes (nested class's namespace is empty)
var containingType = owningType.ContainingType;
if (containingType != null)
{
enclosingResult = containingType.Name;
namespaceResult = containingType.Namespace;
enclosingClassName = containingType.Name;
namespaceName = containingType.Namespace;
}
}


return result;
}

private byte* getMethodNameFromMetadata(CORINFO_METHOD_STRUCT_* ftn, byte** className, byte** namespaceName, byte** enclosingClassName)
{
MethodDesc method = HandleToObject(ftn);

string result;
string classResult;
string namespaceResult;
string enclosingResult;

result = getMethodNameFromMetadataImpl(method, out classResult, out namespaceResult, out enclosingResult);

if (className != null)
*className = classResult != null ? (byte*)GetPin(StringToUTF8(classResult)) : null;
if (namespaceName != null)
Expand Down Expand Up @@ -3015,7 +3023,6 @@ private uint getJitFlags(ref CORJIT_FLAGS flags, uint sizeInBytes)
flags.Set(CorJitFlag.CORJIT_FLAG_PREJIT);
flags.Set(CorJitFlag.CORJIT_FLAG_USE_PINVOKE_HELPERS);

#if !READYTORUN
if (_compilation.TypeSystemContext.Target.Architecture == TargetArchitecture.X86
|| _compilation.TypeSystemContext.Target.Architecture == TargetArchitecture.X64)
{
Expand All @@ -3027,7 +3034,6 @@ private uint getJitFlags(ref CORJIT_FLAGS flags, uint sizeInBytes)
flags.Set(CorJitFlag.CORJIT_FLAG_USE_SSSE3);
flags.Set(CorJitFlag.CORJIT_FLAG_USE_LZCNT);
}
#endif

if (this.MethodBeingCompiled.IsNativeCallable)
flags.Set(CorJitFlag.CORJIT_FLAG_REVERSE_PINVOKE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ public override ICompilation ToCompilation()
if (_ibcTuning)
corJitFlags.Add(CorJitFlag.CORJIT_FLAG_BBINSTR);

corJitFlags.Add(CorJitFlag.CORJIT_FLAG_FEATURE_SIMD);
var jitConfig = new JitConfigProvider(corJitFlags, _ryujitOptions);

return new ReadyToRunCodegenCompilation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,16 @@ private static mdToken FindGenericMethodArgTypeSpec(EcmaModule module)

private bool ShouldSkipCompilation(IMethodNode methodCodeNodeNeedingCode)
{
return methodCodeNodeNeedingCode.Method.IsAggressiveOptimization;
MethodDesc method = methodCodeNodeNeedingCode.Method;
if (method.IsAggressiveOptimization)
{
return true;
}
if (HardwareIntrinsicHelpers.IsHardwareIntrinsic(method))
{
return true;
}
return false;
}

public void CompileMethod(IReadyToRunMethodCodeNode methodCodeNodeNeedingCode)
Expand Down Expand Up @@ -1143,6 +1152,109 @@ private void ceeInfoGetCallInfo(
Get_CORINFO_SIG_INFO(methodToDescribe, &pResult->sig, useInstantiatingStub);
}

private uint getMethodAttribs(CORINFO_METHOD_STRUCT_* ftn)
{
MethodDesc method = HandleToObject(ftn);
uint attribs = getMethodAttribsInternal(method);
attribs = FilterNamedIntrinsicMethodAttribs(attribs, method);
return attribs;
}

private uint FilterNamedIntrinsicMethodAttribs(uint attribs, MethodDesc method)
{
bool _TARGET_X86_ = _compilation.TypeSystemContext.Target.Architecture == TargetArchitecture.X86;
bool _TARGET_AMD64_ = _compilation.TypeSystemContext.Target.Architecture == TargetArchitecture.X64;
bool _TARGET_ARM64_ = _compilation.TypeSystemContext.Target.Architecture == TargetArchitecture.ARM64;

if ((attribs & (uint)CorInfoFlag.CORINFO_FLG_JIT_INTRINSIC) != 0)
{
// Figure out which intrinsic we are dealing with.
string namespaceName;
string className;
string enclosingClassName;
string methodName = this.getMethodNameFromMetadataImpl(method, out className, out namespaceName, out enclosingClassName);

// Is this the get_IsSupported method that checks whether intrinsic is supported?
bool fIsGetIsSupportedMethod = string.Equals(methodName, "get_IsSupported");
cshung marked this conversation as resolved.
Show resolved Hide resolved
bool fIsPlatformHWIntrinsic = false;
bool fIsHWIntrinsic = false;
bool fTreatAsRegularMethodCall = false;

if (_TARGET_X86_ || _TARGET_AMD64_)
{
fIsPlatformHWIntrinsic = (namespaceName == "System.Runtime.Intrinsics.X86");
}
else if (_TARGET_ARM64_)
{
fIsPlatformHWIntrinsic = (namespaceName == "System.Runtime.Intrinsics.Arm.Arm64");
}

fIsHWIntrinsic = fIsPlatformHWIntrinsic || (namespaceName == "System.Runtime.Intrinsics");

// By default, we want to treat the get_IsSupported method for platform specific HWIntrinsic ISAs as
// method calls. This will be modified as needed below based on what ISAs are considered baseline.
//
// We also want to treat the non-platform specific hardware intrinsics as regular method calls. This
// is because they often change the code they emit based on what ISAs are supported by the compiler,
// but we don't know what the target machine will support.
//
// Additionally, we make sure none of the hardware intrinsic method bodies get pregenerated in crossgen
cshung marked this conversation as resolved.
Show resolved Hide resolved
// (see ShouldSkipCompilation) but get JITted instead. The JITted method will have the correct
// answer for the CPU the code is running on.
fTreatAsRegularMethodCall = (fIsGetIsSupportedMethod && fIsPlatformHWIntrinsic) || (!fIsPlatformHWIntrinsic && fIsHWIntrinsic);

if (_TARGET_X86_ || _TARGET_AMD64_)
{
if (fIsPlatformHWIntrinsic)
{
// Simplify the comparison logic by grabbing the name of the ISA
string isaName = (enclosingClassName == null) ? className : enclosingClassName;
if ((isaName == "Sse") || (isaName == "Sse2"))
{
if ((enclosingClassName == null) || (className == "X64"))
{
// If it's anything related to Sse/Sse2, we can expand unconditionally since this is
// a baseline requirement of CoreCLR.
fTreatAsRegularMethodCall = false;
}
}
else if ((className == "Avx") || (className == "Fma") || (className == "Avx2") || (className == "Bmi1") || (className == "Bmi2"))
{
if ((enclosingClassName == null) || (string.Equals(className, "X64")))
{
// If it is the get_IsSupported method for an ISA which requires the VEX
// encoding we want to expand unconditionally. This will force those code
// paths to be treated as dead code and dropped from the compilation.
//
// For all of the other intrinsics in an ISA which requires the VEX encoding
// we need to treat them as regular method calls. This is done because RyuJIT
// doesn't currently support emitting both VEX and non-VEX encoded instructions
// for a single method.
fTreatAsRegularMethodCall = !fIsGetIsSupportedMethod;
}
}
}
else if (namespaceName == "System")
{
if ((className == "Math") || (className == "MathF"))
{
// These are normally handled via the SSE4.1 instructions ROUNDSS/ROUNDSD.
// However, we don't know the ISAs the target machine supports so we should
// fallback to the method call implementation instead.
fTreatAsRegularMethodCall = (methodName == "Round");
}
}
}

if (fTreatAsRegularMethodCall)
{
// Treat as a regular method call (into a JITted method).
attribs = (attribs & ~(uint)CorInfoFlag.CORINFO_FLG_JIT_INTRINSIC) | (uint)CorInfoFlag.CORINFO_FLG_DONT_INLINE;
}
}
return attribs;
}

private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, CORINFO_METHOD_STRUCT_* callerHandle, CORINFO_CALLINFO_FLAGS flags, CORINFO_CALL_INFO* pResult)
{
MethodDesc methodToCall;
Expand All @@ -1168,6 +1280,8 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO
out callerModule,
out useInstantiatingStub);

pResult->methodFlags = FilterNamedIntrinsicMethodAttribs(pResult->methodFlags, methodToCall);

if (pResult->thisTransform == CORINFO_THIS_TRANSFORM.CORINFO_BOX_THIS)
{
// READYTORUN: FUTURE: Optionally create boxing stub at runtime
Expand Down