What does the [Intrinsic] attribute in C# do?
Here's what I've managed to find after a very limited search through dotnet/corefx repository on github.
[Intrinsic]
marks methods, properties and fields that can be potentially replaced/optimized by JIT. Source code comments say something similar (IntrinsicAttribute.cs
):
Calls to methods or references to fields marked with this attribute may be replaced at some call sites with jit intrinsic expansions. Types marked with this attribute may be specially treated by the runtime/compiler.
Purpose
For core developers, [Intrinsic]
serves at least two purposes:
- it notifies the developer that the code of the marked field, method or property can be replaced by VM. So, if the code changes, the change should probably be introduced in both places;
- it is used as a flag for JIT-optimizer to quickly identify methods that can potentially be optimized.
To give a rough example: JIT-optimizer can replace Enum.HasFlag
with a simple bitwise comparison in some cases and not in the others. To do this it needs to identify the method as Enum.HasFlag
, check some conditions and replace it with a more optimal implementation. The optimizer can identify the method by name, but, for performance reasons, it's better to filter out methods by a simple flag before performing string comparisons.
Usage
The attribute is only relevant to core developers. You should only use it in an internal class and only in the case when you want to propose very specific JIT-level optimizations for it. [Intrinsic]
is pretty much restricted to a small set of widely used .Net classes, that, for some reason, can't be optimized by other means.
from the comments: I'm planning to propose a Color struct for .NET Core which needs to behave similarly to other built-in types for consistency.
You should probably not use [Intrinsic]
in your initial proposal. After it passes, you can think about optimization, and if you have a valid scenario when Color
will benefit from low level optimizations, you can suggest using [Intrinsic]
on some of its methods or properties.
How It Works
Here's how [Intrinsic]
is currently used in core:
it is defined as a well-known attribute (
wellknownattributes.h
):case WellKnownAttribute::Intrinsic: return "System.Runtime.CompilerServices.IntrinsicAttribute";
VM parses it and sets the
IsJitIntrinsic
flag to true for a method (methodtablebuilder.cpp
):if (bmtProp->fIsHardwareIntrinsic || (S_OK == GetCustomAttribute(pMethod->GetMethodSignature().GetToken(), WellKnownAttribute::Intrinsic, NULL, NULL))) { pNewMD->SetIsJitIntrinsic(); }
this flag is used to set another flag in method attributes (
jitinterface.cpp
):if (pMD->IsJitIntrinsic()) result |= CORINFO_FLG_JIT_INTRINSIC;
this flag is later used to filter out methods which are obviously not intrinsic (
importer.cpp
):if ((mflags & (CORINFO_FLG_INTRINSIC | CORINFO_FLG_JIT_INTRINSIC)) != 0) { const bool isTail = canTailCall && (tailCall != 0); call = impIntrinsic(newobjThis, clsHnd, methHnd, sig, mflags, pResolvedToken->token, readonlyCall, isTail, pConstrainedResolvedToken, callInfo->thisTransform, &intrinsicID, &isSpecialIntrinsic);
impIntrinsic
then callslookupNamedIntrinsic
to identify (mostly by name) methods that really (not just potentially) should be optimized;after all of that
importer
can perform optimizations based on method. For example, optimization forEnum.HasFlag
(importer.cpp
):case NI_System_Enum_HasFlag: { GenTree* thisOp = impStackTop(1).val; GenTree* flagOp = impStackTop(0).val; GenTree* optTree = gtOptimizeEnumHasFlag(thisOp, flagOp); if (optTree != nullptr) { // Optimization successful. Pop the stack for real. impPopStack(); impPopStack(); retNode = optTree; } else { // Retry optimizing this during morph. isSpecial = true; } break; }
DISCLAIMER: as far as I can tell, the attribute's behaviour is not properly documented anywhere and, thus, is subject for change. The description above is only relevant to code currently in master, this part of core is actively developed and the whole process can be changed in the future.
History
Here's a short timeline of [Intrinsic]
based on github repository history:
At some time before 2014
[JitIntrisic]
attribute was introduced as a part ofSystem.Numerics
with a goal to support new processor instructions (see How does JitIntrinsicAttribute affect code generation?).On June 6, 2016, Chris McKinsey opened an issue #5626. "Optimize enum1.HasFlag(enum2) into inline bittest without boxing allocations when types are the same". At the time,
Enum.HasFlag
had a well-known performance issues (see What is it that makes Enum.HasFlag so slow?).While working on the issue Andy Ayers suggested to introduce a universal mechanism to introduce JIT intrinsics (Issue #13813: Add more flexible method for specifying jit instrinsics)
This led to two pull requests: New jit intrinsic support introduced the general mechanics for
[Intrinsic]
and JIT: optimize Enum.HasFlag implemented it forEnum.HasFlag
. I suggest going through both of them as they are extremely illustrative on the changes that come with[Intrinsic]
.Later, during the discussion about moving
Vector
classes to the CoreLib it was suggested that[JitIntrinsic]
isn't used anywhere and should be replaced/removed:
@jkotas: We should not need the JitIntrinsicAttribute. As far as I know, this attribute was future proofing, never used for anything real. We should delete it, and use the IntrinsicAttribute from CoreLib instead.
- Promptly,
[JitIntrinsic]
was removed and replace by[Intrinsic]
(Replace JitIntrinsicAttribute with IntrinsicAttribute). That's how this attribute came to be inVector2
.