New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Compiler intrinsics #11475

Closed
tmat opened this Issue May 21, 2016 · 50 comments

Comments

Projects
None yet
@tmat
Copy link
Member

tmat commented May 21, 2016

Background

C# and VB languages aren't able to express certain IL instructions and patterns (e.g. ldtoken, calli, ldftn, etc.). That's ok, it's not the job of a language to express all possible patterns of the underlying VM. However, there are scenarios where these patterns are very useful. In these cases users are currently left with options that are painful to use, break debugging, break IDE experience (refactorings etc.), are hard to maintain. These include various forms of IL rewriting, writing code in plain IL and compiling it to a separate assembly, runtime code generation, etc.

A few examples of such scenarios:

  • the CLR's tool for generating native interop stubs (MCG) would greatly benefit from the ability to emit IL instructions like calli, ldftn, etc.
  • infoof feature could be implemented as a library method if it was possible to directly emit ldtoken instruction
  • @joeduffy's implementation of slices currently needs some IL helpers compiled into a separate library.

If only Roslyn compilers could provide a way to emit these commonly needed IL patterns without changing the language specification ...

Proposal

A compiler intrinsic is a static extern method declared in compilation source code that is marked with CompilerIntrinsicAttribute and its name is well-known to the compiler.

Each intrinsic provides a way to emit certain IL instruction or pattern that otherwise can’t be expressed in the C# language, using existing syntax and preserving standard evaluation stack behavior. These intrinsics can thus be used in the middle of ordinary statements and expressions with no adverse effect on debugging, EnC, IntelliSense, refactorings, etc. Intrinsics have well-known names that determine the IL instruction pattern to emit and imply a signature pattern. The specific signature declared by the user along with the call-site arguments provide the compiler all the information it needs to emit the requested IL.

The declaration of an intrinsic won't be emitted to metadata. It can't be, as the type loader would not be able to load the containing type due to missing implementation for the intrinsics.

Additional constraints might need to be enforced on intrinsic call-sites and corresponding errors reported during binding phase. One of the constraints is that some arguments passed to the call-site of an intrinsic method must evaluate to a compile-time constant. This constraint is needed when the generated IL depends on the value of such arguments (e.g. mappedData in AddressOfMappedData).

Currently proposed intrinsics

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Method)]
    internal class CompilerIntrinsicAttribute : Attribute { }
}

class Program
{
    [CompilerIntrinsic]
    static unsafe extern void* LoadFunctionPointer(DelegateType target);  // e.g. DelegateType = Func<int, int>

    [CompilerIntrinsic]
    static unsafe extern void* LoadVirtualFunctionPointer(DelegateType target);

    [CompilerIntrinsic]
    static unsafe extern ReturnType CallIndirect(Type1 p1, ..., TypeN pN, void* managedFunctionPointer);

    [CompilerIntrinsic]
    static unsafe extern ReturnType CallIndirectCDecl(Type1 p1, ..., TypeN pN, void* nativeFunctionPointer);

    [CompilerIntrinsic]
    static unsafe extern ReturnType CallIndirectStdCall(Type1 p1, ..., TypeN pN, void* nativeFunctionPointer);

    [CompilerIntrinsic]
    static unsafe extern ReturnType CallIndirectThisCall(Type1 p1, ..., TypeN pN, void* nativeFunctionPointer);

    [CompilerIntrinsic]
    static unsafe extern DataType* AddressOfMappedData(DataType[] mappedData); // adds entry to FieldRVA table
}

Generic Intrinsics

It might be useful to allow the declarations to be generic in order to reduce the amount of declarations required in the code, for example

class Program
{
    [CompilerIntrinsic]
    static unsafe extern void* LoadFunctionPointer<T1, ..., TN, TRet>(Func<T1, ..., Tn, TRet> target);

    [CompilerIntrinsic]
    static unsafe extern TRet CallIndirect<T1, ..., TN, TRet>(T1 p1, ..., TN pN, void* managedFunctionPointer);
}

Local Intrinsics

Provided that C# 7 allows local functions to be declared as extern and custom attributes applied to them it would be possible to declare intrinsics "inline" (see #11731), for example:

unsafe void IL()
{
    [CompilerIntrinsic]
    extern int CallIndirectCDecl(int arg0, void* nativeFunctionPointer);

    Console.WriteLine(CallIndirectCDecl(123, GetAddressOfNativeFunction()));
}

Possible future intrinsics

The compiler can define any number of intrinsics and add more as needed. The following example demonstrates what would be possible. The exact set of intrinsics to introduce is up for a debate.

Note that the following code compiles and works in the current version of C# compiler and IDE services. It just doesn't compile to the desired IL.

class Program
{
    [CompilerIntrinsic]
    static unsafe extern int CallIndirect(string arg0, bool arg1, void* functionPointer);

    [CompilerIntrinsic]
    static unsafe extern int CallIndirectCDecl(int arg0, void* functionPointer);

    [CompilerIntrinsic]
    static unsafe extern int TailCallIndirect(string arg0, bool arg1, void* functionPointer);

    [CompilerIntrinsic]
    static unsafe extern void TailCallIndirectHasThis(void* functionPointer);

    [CompilerIntrinsic]
    static unsafe extern void* LoadFunctionPointer(Func<string, bool, int> target);

    [CompilerIntrinsic]
    static unsafe extern void* LoadFunctionPointer(Action target);

    [CompilerIntrinsic]
    static unsafe extern void* LoadVirtualFunctionPointer(Func<int, int> target);

    [CompilerIntrinsic]
    static extern RuntimeTypeHandle LoadTypeToken<T>();

    [CompilerIntrinsic]
    static extern RuntimeMethodHandle LoadMethodToken(Func<int, int> target);

    [CompilerIntrinsic]
    static extern RuntimeMethodHandle LoadGetterToken<T>(T targetProperty);

    [CompilerIntrinsic]
    static extern RuntimeMethodHandle LoadSetterToken<T>(T targetProperty);

    [CompilerIntrinsic]
    static extern RuntimeFieldHandle LoadFieldToken<T>(T targetField);

    [CompilerIntrinsic]
    static extern int LoadTypeTokenInt32<T>();

    [CompilerIntrinsic]
    static extern int LoadMethodTokenInt32(Func<int, int> target);

    [CompilerIntrinsic]
    static extern int LoadFieldTokenInt32<T>(T targetField);

    [CompilerIntrinsic]
    static extern int LoadGetterTokenInt32<T>(T targetProperty);

    [CompilerIntrinsic]
    static extern int LoadSetterTokenInt32<T>(T targetProperty);

    [CompilerIntrinsic]
    static extern string LoadMvidString();


    abstract class C<T>
    {
        public C(int a) { }
        public abstract int F(int a);
    }

    public void F() { }
    public static T G<T>(T a) => default(T);
    public static int H(string s, bool b) => 1;

    public static string fld;

    public static int P { get; set; }

    private unsafe void* GetAddressOfNativeFunction() => null;

    void IL()
    {
        // ldvirtftn C<string>.F
        // pop
        unsafe { LoadVirtualFunctionPointer(default(C<string>).F); }

        // ldstr "str"
        // ldc.i4.1
        // ldftn H
        // calli int(string, bool)
        // box
        // call Console.WriteLine(object)
        unsafe 
        {
            Console.WriteLine(CallIndirect("str", true, LoadFunctionPointer(H))); 
         }

        // ldc.i4 123
        // call GetAddressOfNativeFunction()
        // calli int(int)
        // box
        // call Console.WriteLine(object)
        unsafe 
        {
            Console.WriteLine(CallIndirectCDecl(123, GetAddressOfNativeFunction())); 
        }

        LoadTypeToken<Program>();                 // ldtoken Program
        LoadMethodToken(default(C<bool>).F);      // ldtoken C<bool>.F
        LoadMethodToken(G<int>);                  // ldtoken G<int>
        LoadFieldToken(fld);                      // ldtoken fld
        LoadGetterToken(P);                       // ldtoken get_P
        LoadSetterToken(P);                       // ldtoken set_P

        LoadTypeTokenInt32<Program>();            // ldc.i4 <token of Program>
        LoadMethodTokenInt32(default(C<bool>).F); // ldc.i4 <token of C<bool>.F>
        LoadFieldTokenInt32(fld);                 // ldc.i4 <token of fld>
        LoadGetterTokenInt32(P);                  // ldc.i4 <token of get_P>
        LoadSetterTokenInt32(P);                  // ldc.i4 <token of set_P>
        LoadMvidString();                         // ldstr "<containing module mvid>"

        // ldarg.0
        // ldftn F
        // tail.calli void()
        unsafe
        {
           TailCallIndirectHasThis(LoadFunctionPointer(F)); 
         }
    }
}
@tmat

This comment has been minimized.

@mjsabby

This comment has been minimized.

Copy link
Member

mjsabby commented May 21, 2016

Looks exactly how I had imagined, @jaredpar and I were talking about this afternoon, so I'm super happy to see this!

@mjsabby

This comment has been minimized.

Copy link
Member

mjsabby commented May 21, 2016

How do we feel about not taking a dependency on CoreFX defining the CompilerIntrinsic attribute, instead allowing the compiler to make a decision about which attribute to look for via an argument, or (and this may be heavy handed) making a keyword out of this.

I'd hate for this feature to be gated on your need to upgrade the compiler AND your standard library.

@tmat

This comment has been minimized.

Copy link
Member

tmat commented May 21, 2016

There is no dependency on CoreFX defining the attribute. The attribute can be defined in source as it is demonstrated in the example above (notice the internal visibility). The compiler doesn't require any attribute to be defined in a specific assembly. It just looks for its namespace qualified name and particular signature of the attribute constructor.

In other words you can just define the attribute anywhere in your project.

@mjsabby

This comment has been minimized.

Copy link
Member

mjsabby commented May 21, 2016

Gotcha, thanks.

@0xd4d

This comment has been minimized.

Copy link

0xd4d commented May 21, 2016

Event adders/removers seem to be missing from the example.

@svick

This comment has been minimized.

Copy link
Contributor

svick commented May 21, 2016

How would LoadMethodToken work with overload resolution? For example, if I wanted the tokens of both Console.WriteLine(object) and Console.WriteLine(string), would the following work?

class Program
{
    [CompilerIntrinsic]
    static unsafe extern void* LoadFunctionPointer(Action<object> target);

    [CompilerIntrinsic]
    static unsafe extern void* LoadFunctionPointer(Action<string> target);

    unsafe static void Main()
    {
        var writeLineObject = LoadFunctionPointer((Action<object>)Console.WriteLine);
        var writeLineString = LoadFunctionPointer((Action<string>)Console.WriteLine);
    }
}
@tmat

This comment has been minimized.

Copy link
Member

tmat commented May 21, 2016

@svick Yes, exactly.

@miloush

This comment has been minimized.

Copy link

miloush commented May 21, 2016

What are the reasons to prefer this over supporting inline assembly directly?

@tmat

This comment has been minimized.

Copy link
Member

tmat commented May 21, 2016

@miloush Because this approach doesn't need to change the language syntax and semantics.

@miloush

This comment has been minimized.

Copy link

miloush commented May 21, 2016

@tmat Feels a little bit like hack to avoid changes in the language. I agree it's much easier to ship though than designing IL support if it had to offer comparable features. Do you expect all IL instructions to be available via such methods or only the "impossible" ones?

@tmat

This comment has been minimized.

Copy link
Member

tmat commented May 22, 2016

@miloush Only a few special purpose patterns as needed. The intrinsics are designed to be used in very rare cases. Most C# programmers won't ever see one. Embedding full IL sublanguage into C# would be a lot of effort and would make the language much more complex for something that should be used very rarely. That's not a good trade off to make.

@alrz

This comment has been minimized.

Copy link
Contributor

alrz commented May 22, 2016

What if we could define them as generic functions,

[CompilerIntrinsic]
static unsafe extern void* LoadFunctionPointer<T>(Action<T> target);

var writeLineObject = LoadFunctionPointer<object>(Console.WriteLine);
var writeLineString = LoadFunctionPointer<string>(Console.WriteLine);

Or with a different name.

[CompilerIntrinsic("LoadFunctionPointer")]
static unsafe extern void* LoadFunctionPointerStr(Action<string> target);
[CompilerIntrinsic("LoadFunctionPointer")]
static unsafe extern void* LoadFunctionPointerObj(Action<object> target);

Or a general one,

[CompilerIntrinsic]
static unsafe extern void* LoadFunctionPointer(Delegate target);

Because you will need to cast the argument anyways.

@xoofx

This comment has been minimized.

Copy link
Member

xoofx commented May 23, 2016

+1 I like the general idea as it would obviate almost the need for IL patching in SharpDX interop codegen (similar to MCG), though, ideally, I would prefer a generic solution like (in between the proposal and @miloush concerns):

[CompilerIntrinsic] static extern void ilemit(string emit);

...

ilemit("ldtoken Program");
ilemit("ldtoken fld");

The underlying ilemit would have to be decoded by Roslyn and understand a simplified ILDASM syntax. Imho, much easier to author and roundtrip with an ILDASM output.

In addition to calli I also needed:

  • sizeof T
  • pin blittable struct T or T[] argument to pass it to a native function param.
@tmat

This comment has been minimized.

Copy link
Member

tmat commented May 23, 2016

@xoofx I don't understand how adding quotes around intrinsics adds any value. As I already mentioned we'd only support very small subset of IL patterns, which is expressed by the set of supported intrinsics. So the amount of IL you could express would be the same.

Imho, much easier to author and roundtrip with an ILDASM output.

Have you tried to prototype such approach? I believe the opposite is true. It'd be much more complicated.

@miloush

This comment has been minimized.

Copy link

miloush commented May 23, 2016

@xoofx that seems to me as complicated as supporting inline assembly

The proposal of @tmat has at least some type safety, which is nice.

@xoofx

This comment has been minimized.

Copy link
Member

xoofx commented May 23, 2016

@xoofx I don't understand how adding quotes around intrinsics adds any value. As I already mentioned we'd only support very small subset of IL patterns, which is expressed by the set of supported intrinsics. So the amount of IL you could express would be the same.

The benefits are:

  • You need to declare just one intrinsic
  • You can use an IL ASM syntax with which we are much more familiar. It allows to almost copy/paste directly from ILDASM, which is very handy. The syntax is familiar, we have some documentation for it, we don't have to look for another syntax declaration.
  • It would be compatible with all .NET languages (e.g unsafe is not supported by VB, so the original proposal would only work for C#)

Have you tried to prototype such approach? I believe the opposite is true. It'd be much more complicated.

I have almost always started to develop my IL in IL ASM, compile an assembly from it and linked to it. Then I had to go to the Mono.Cecil route to avoid having another assembly (ILMerge has not been always safe to use on some assemblies). Exactly as you can see it in PtrUtils.il by @joeduffy

It has been somewhat prototyped in "Tool to allow inline IL in C# / VB.Net", although it was of course not done at compilation time but as a ILDASM post-processing injection trick.

@xoofx that seems to me as complicated as supporting inline assembly

Not sure what you both mean by "complicated". Do you mean complicated to implement in Roslyn? (in that case, knowing a bit of Roslyn, I know for sure that it is quite easy to hook a prototype into it for this kind of things) Or do you mean complicated to read for someone that doesn't know IL ASM?

So yes, I prefer a well known syntax like on the right side, rather than the left side which is a bit more cumbersome to use and author:

        LoadTypeTokenInt32<Program>();            =>  ilemit("ldc.i4 Program");
        LoadMethodTokenInt32(default(C<bool>).F); =>  ilemit("ldc.i4 C<bool>.F");
        LoadFieldTokenInt32(fld);                 =>  ilemit("ldc.i4 fld");
        LoadGetterTokenInt32(P);                  =>  ilemit("ldc.i4 get_P");
        LoadSetterTokenInt32(P);                  =>  ilemit("ldc.i4 set_P");

The proposal of @tmat has at least some type safety, which is nice.

My proposal has type safety as well. Roslyn would parse the asm string in exactly the same way it would do it if it was part of the language. It means that a reference inside the string would be evaluated (you could navigate to it), you would have a SyntaxToken for it, and it would be part of the SyntaxTree. It is just that by escaping the IL ASM in a string, you don't introduce any breaking changes to the language(s) and it is compatible typically with any .NET language.

@tmat

This comment has been minimized.

Copy link
Member

tmat commented May 23, 2016

@xoofx

Do you mean complicated to implement in Roslyn?

Yes.

Writing code in IL is an explicit non-goal of this feature. We want our users to write code in C#.

@xoofx

This comment has been minimized.

Copy link
Member

xoofx commented May 23, 2016

Writing code in IL is an explicit non-goal of this feature. We want our users to write code in C#.

Fair enough if it is a requirement.

@pebezo

This comment has been minimized.

Copy link

pebezo commented May 23, 2016

Too bad, we could've had an ASM (maybe il { }) for C# http://wiki.freepascal.org/Lazarus_Inline_Assembler

@tmat

This comment has been minimized.

Copy link
Member

tmat commented May 23, 2016

@pebezo There are million things we could potentially have in C#. Adding all of them won't make the language better. https://i.kinja-img.com/gawker-media/image/upload/s--BdEb-Dl5--/c_scale,fl_progressive,q_80,w_800/185xbqdcr7fgmjpg.jpg

@xoofx

This comment has been minimized.

Copy link
Member

xoofx commented May 25, 2016

After a few hours of work, here we go: http://xoofx.com/blog/2016/05/25/inline-il-asm-in-csharp-with-roslyn/ 🎉

@tmat

This comment has been minimized.

Copy link
Member

tmat commented May 25, 2016

@xoofx Nice! Certainly better than strings ;) However a few issues you might want to think about:

a) The IL validation you mention in the blog post (balanced stack, etc.), especially when combined with all other C# language features -- like using il(...) in the middle of expression, etc. Perhaps you could do the final validation once everything is on IL level, but then you won't be able to report diagnostics to the user as they are typing.

b) Debugging -- il(...) looks like a statement but it is not a statement. How are you gonna place sequence points? Is EnC gonna work in a method that contains il() calls?

c) How many IDE features (e.g. refactorings, rename, etc.) need to understand il(...) calls in order to not produce incorrect results?

Complexity is hidden in the detail...

@xoofx

This comment has been minimized.

Copy link
Member

xoofx commented May 25, 2016

@tmat The points you mention are of course things that would have to be handled/crafted carefully for a non-prototype code. That would require certainly more work than the original proposal. I never claimed the opposite. but...

Complexity is hidden in the detail...

first, I'm making a large difference in nature (and not in levels) between the word "complexity" and "complicated"...
So I don't think that neither the problem nor my solution are complex. Regarding the "complicated" part of the implem, considering that I only worked a few hours on this, with a prototype that is already able to validate many things (while being able to cover almost the whole IL instruction set), it gives already a good tendency about the problems ahead.

But I definitely agree that a clean work would require more work for sure! Though, nothing complicated here, just laborious work. 😉

@agocke

This comment has been minimized.

Copy link
Contributor

agocke commented May 25, 2016

@xoofx Cool prototype! Looking at it, though, I think I like @tmat's proposal more. In order to incorporate your changes we would basically have to create an entire IL sub-language inside C# and validate it all in the compiler. I would prefer to separate concerns here and let an IL compiler handle IL and the C# compiler handle C#.

@tmat

This comment has been minimized.

Copy link
Member

tmat commented May 25, 2016

@xoofx I understand you haven't spent too much time on your prototype so it doesn't cover everything.
However, the points I raised are important to consider and explain how they would be addressed. It's not just extra laborious work. There is additional design work to be made for all of them. These are the reasons why I said that support for arbitrary IL would be complicated.

The original proposal is designed to avoid these problems and thus the complete implementation of it would be a simple local change in the compiler. Your proposal adds more features but they come with a price. The features are inherently non-local, they don't only affect the compiler but all language services need to deal with them.

@mjsabby

This comment has been minimized.

Copy link
Member

mjsabby commented Nov 22, 2016

@tmat We should update the proposal to modify the parameter passing of the calling convention to instead be name suffixed.

[CompilerIntrinsic]
static unsafe extern int CallIndirect(arg0, void* managedMethodPointer); // managed cc

[CompilerIntrinsic]
static unsafe extern int CallIndirectCDecl(arg0, void* functionPointer); // unmanaged

[CompilerIntrinsic]
static unsafe extern int CallIndirectStdCall(arg0, void* functionPointer); // unmanaged

[CompilerIntrinsic]
static unsafe extern int TailCallIndirectFastCall(arg0, void* functionPointer);  // unmanaged

I have some of these intrinsics implemented here (namely CallIndirect and friends): https://github.com/mjsabby/roslyn/tree/intrinsic_backup

The implementation is a prototype and the mechanics of how it generates the code are likely to change, but there is correctness. I intend to keep my fork around until we can get this proposal landed. There is also a nuget package of this that is much like the Microsoft.Net.Compilers package in that installing it in your VS project will make the project build against this version of the compiler with intrinsics support (https://www.nuget.org/packages/CSCWithCompilerIntrinsics).

@bartdesmet

This comment has been minimized.

Copy link

bartdesmet commented Nov 22, 2016

dotnet/corefx#13561 has another case where "infoof" functionality will come in handy for the System.Linq.Queryable factory methods.

@tmat

This comment has been minimized.

Copy link
Member

tmat commented Nov 28, 2016

@mjsabby Updated the proposal.

@jkotas

This comment has been minimized.

Copy link
Member

jkotas commented Nov 28, 2016

static unsafe extern PointerType AddressOfMappedData(byte[] mappedData)

The return type should match the argument type, e.g. byte* for byte[] mappedData.

@tmat

This comment has been minimized.

Copy link
Member

tmat commented Nov 28, 2016

@jkotas Fixed

@MichalStrehovsky

This comment has been minimized.

Copy link
Member

MichalStrehovsky commented Nov 28, 2016

static unsafe extern DataType* AddressOfMappedData(DataType[] mappedData)

I assume there will be restrictions on how much data flow analysis Roslyn can do to get the mapped data array - can I do:

var myArray = new byte[] { 1, 2, 3 };
var data = AddressOfMappedData(myArray);
UseData(data, myArray.Length /* constprop optimized to 3 at compile time */);

Or do I have to do:

var data = AddressOfMappedData(new byte[] { 1, 2, 3 });
UseData(data, 3);

If it's the latter, any chance we can make the intrinsic return a Span<DataType> so that the user can safely get the length?

@ltrzesniewski

This comment has been minimized.

Copy link

ltrzesniewski commented Mar 25, 2018

Here's another approach for those interested in embedded IL code support:

I also wanted to be able to write IL directly in my C# code, so I wrote a Fody addin which lets you do just that: InlineIL.Fody. Just add the NuGet package and now you have a compile-time ILGenerator.

For those who don't know what Fody is, it's an extensible weaver which adds a build step that modifies assemblies generated by Roslyn.

@mjsabby

This comment has been minimized.

Copy link
Member

mjsabby commented Jun 18, 2018

I've implemented most of the Compiler Intrinsics according to this proposal and put them in a nuget package CSCWithCompilerIntrinsics. Adding this nuget package overrides the C# Compiler and gives you the C# 2.8.2 (as of this writing) compiler + the intrinsics. (https://github.com/mjsabby/roslyn/tree/compilerintrinsics_2_8_2)

dotnet run should produce the following output: Hello World from Compiler Intrinsics. 42.

intrinsictest.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="CSCWithCompilerIntrinsics" Version="2.8.2" />
  </ItemGroup>

</Project>

Program.cs

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Method)]
    internal class CompilerIntrinsicAttribute : Attribute { }
}

namespace CompilerIntrinsicsTest
{
    using System;
    using System.Runtime.CompilerServices;

    interface IFoo
    {
        void Bar<T>(T j);
    }

    class Foo : IFoo
    {
        public void Bar<T>(T j)
        {
            Console.WriteLine($"{j}.");
        }
    }

    class Program
    {
        [CompilerIntrinsic]
        private static unsafe extern void* LoadFunctionPointer(Action<string> target);

        [CompilerIntrinsic]
        private static unsafe extern void* LoadVirtualFunctionPointer(Action<int> bar);

        [CompilerIntrinsic]
        static unsafe extern void CallIndirect(IFoo arg0, int arg1, void* functionPointer);

        [CompilerIntrinsic]
        static unsafe extern void CallIndirect(string arg0, void* functionPointer);

        static void Main(string[] args)
        {
            unsafe
            {
                CallIndirect("Hello World from Compiler Intrinsics.", LoadFunctionPointer(PrintMessage));
                var foo = new Foo();
                CallIndirect(foo, 42, LoadVirtualFunctionPointer(foo.Bar<int>));
            }
        }

        private static void PrintMessage(string message)
        {
            Console.WriteLine(message);
        }
    }
}
@mjsabby

This comment has been minimized.

Copy link
Member

mjsabby commented Jun 28, 2018

@jkotas @MichalStrehovsky are you sufficiently satisfied with #24621 for AddressOfMappedData. I think it's better than what was proposed and I'm using it for my projects. If yes @tmat we can probably remove that from this proposal.

Now only the CallIndirect intrinsic has metadata impact.

@jkotas

This comment has been minimized.

Copy link
Member

jkotas commented Jun 28, 2018

are you sufficiently satisfied with #24621 for AddressOfMappedData

Yes.

jaredpar added a commit to jaredpar/csharplang that referenced this issue Jul 2, 2018

Compiler intrinsics
This is an alternate proposal for the compiler intrinsicts feature:

dotnet#191
dotnet/roslyn#11475

This alternate design proposal comes after reviewing a prototype implementation
of the original proposal by @msjabby as well as the use throughout a significant
code base.

This design was done with significant input from @msjabby, @tmat and @jkotas.

jaredpar added a commit to jaredpar/csharplang that referenced this issue Jul 2, 2018

Compiler intrinsics
This is an alternate proposal for the compiler intrinsicts feature:

dotnet#191
dotnet/roslyn#11475

This alternate design proposal comes after reviewing a prototype implementation
of the original proposal by @mjsabby as well as the use throughout a significant
code base.

This design was done with significant input from @mjsabby, @tmat and @jkotas.
@agocke

This comment has been minimized.

Copy link
Contributor

agocke commented Aug 14, 2018

Closing because LDM has decided to not take this design and instead adopt a more scoped approach.

https://github.com/dotnet/csharplang/blob/master/proposals/intrinsics.md

@agocke agocke closed this Aug 14, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment