What does the [Intrinsic] attribute in C# do?
Asked Answered
N

2

34

A quick Google search for "instrinsic attribute c#" only returns articles about other attributes, such as [Serializable]. Apparently these are called "intrinsic attributes".

However, there is also an attribute in C# that is itself called [Intrinsic] and I'm trying to figure out what exactly it is and how it works. It doesn't exist on the common attributes page of the .NET Documentation, or anywhere else in the documentation as far as I can see.

This attribute is used inside of .NET Core in several places, for example, in the System.Numerics.Vectors folder, such as Vector2_Intrinsics.cs. Code snippet:

[Intrinsic]
public Vector2(float x, float y)
{
    X = x;
    Y = y;
}
Norvol answered 31/5, 2019 at 4:27 Comment(4)
Source code for this attribute is available here: github.com/dotnet/coreclr/blob/master/src/… which includes some comments.Norword
JitIntrinsicAttribute appears to be possibly related: #26904433Norword
here is the proposal github.com/dotnet/corefx/issues/22940 you can learn all about it in awesome gory detail and follow the push to integrationSpeedboat
It is just annotation that you can use when you use MethodInfo in your code. Dragons live there, you wouldn't want to Invoke() it. Not that this is a very practical concern, but Microsoft Support can easily get you off the phone by pointing out that you can add the test. There are lots of other intrinsics in .NET, Math.Sqrt() for example. A processor has a dedicated machine code instruction for it so no need to generate a method call, very efficient. Invoking it with reflection is however okay, mscorlib.dll still has a method for it.Supranational
K
68

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 calls lookupNamedIntrinsic 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 for Enum.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:

@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.

Kyl answered 31/5, 2019 at 9:59 Comment(3)
Agree on the fact that we should not use or care about undocumented features. If it is not document, MS don't want developers to use it and can change at any moment.Marduk
Wow, this is one of the most impressive SO answers I've seen to a question before. Did you already know the history and background information on this or did you go track it down and research it? Either way, I tip my hat to you, sir!Matins
@AaronCarter Thanks! I didn't know all of this, but at the time I was trying to build the framework from sources, so I was already digging into the repository and I had some free time :)Kyl
M
0

Explanation:

Special types are indicated to the compiler using the IntrinsicAttribute custom attribute. If a type is annotated with the IntrinsicAttribute attribute, the compiler knows not that the implementation for the given type will be present at runtime. Methods for types marked as Intrinsic can declare methods to be extern, in which case the implementation is assumed to be available at runtime.

Source: MSIL to JavaScript Compiler, section 4.4.1.1

Link: http://tenpow.com/Academics/MSIL2JS/MSIL2JS.pdf

In general, I would suggest not to care about it, nor use it for your own classes.

Marduk answered 31/5, 2019 at 4:43 Comment(4)
All though intristics is the right topic and captures the essence of the question, i find it dubious linking to a document for MSIL to JavaScript Compiler, anyway just my opinionSpeedboat
@Speedboat The document explains several internals of MSIL and CTS (common type system). The programming language is irrelevant.Marduk
"I would suggest not to care about it" I want to understand how this attribute works in System.Numerics.Vectors and why it exists, as I'm planning to propose a Color struct for .NET Core which needs to behave similarly to other built-in types for consistency.Norvol
I just can say this attribute is used internally for the benefit of the implementation of the .net runtime library. I haven't found a single user project using it. A Color class or struct is not different than any other.Marduk

© 2022 - 2024 — McMap. All rights reserved.