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

Commit

Permalink
[CPAOT] Generating code for hardware intrinsic (#26772)
Browse files Browse the repository at this point in the history
  • Loading branch information
cshung authored Oct 1, 2019
1 parent 87e4971 commit 6ec7898
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 19 deletions.
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");
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
// (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

0 comments on commit 6ec7898

Please sign in to comment.