Feb 5
Inline Assembly in F#! How does it work?
This is assembly language in F#. No, this is not emulated. It is not IL. It is x86 assembly language. The one that runs on your CPU natively. … in F#.
So, let’s see how it works under the hood!
Let’s encode a few instructions from C#!
So, let me tell you about Iced. It’s an absolutely awesome library which allows you to encode and decode assembly for x86 (basically, most desktop CPUs).
We can just create a builder of assembly like this, and add a few instructions:
But this doesn’t do anything. It just saves them somewhere and is made for our convenience to work with them. It doesn’t make anything executable.
Now, we’re saving assembly to executable memory
What’s executable memory? It’s like regular memory, but the OS “allows” to run the bytes from there as CPU instructions.
We’re going to use Windows API to allocate memory. Once allocated, we will change its protection to executable. Here’s what we’re doing:
Ok so… what does it mean now that we did it? It means that if we write to that memory, we can make a function pointer which would point to the first byte, and run it natively!
Copy our assembly to the memory
Basically, using Iced API we just save the bytes to a stream, then we allocate the memory for them and copy to it.
Compiling to delegate
So far we stay in C# as you’ve noticed. The reason for that is because F# doesn’t like function pointers yet. We have to make something that can be executed by F#.
Remember — we got a pointer to the first byte of our assembly memory block. So all we need is to invoke it somehow as a function. For that purpose I made an extension method which casts the pointer into function pointer:
Then I need to make Func<TOut> for it. This part I’ll omit, because it’s too evil. But if you’re that curious, here’s the source for it. It gets Func<TOut> out of the function pointer.
Neat syntax for F#
To get the syntax I’ve shown in the screenshot we’re going to use computation expressions (CE)— very rich feature and fairly unique to F#.
You’ve probably already seen CEs in use, for example, task { }, async { }, seq {}, like here:
We’re going to declare our own. Moreover, we’re going to use custom operations for it.
First, let’s define Yield, which in case of custom operators can be used as the “initializer”:
Now, let’s create a few methods. Iced’s Assembler has hundreds if not thousands methods, and one can actually generate a wrapper for them all using reflection, but here for simplicity I’ll present the methods I used for the sample:
We gave them names via the first argument of the attribute.
Finally, we declare method Run, which “finalizes” the expression:
So in the end it returns a function, which takes in no arguments but returns an int64.
Done! We declared the AsmBuilder. We can now instantiate an instance of it (we actually need to create it just once, since we never mutate it):
Usage
Now we can use our asm computation expression, by using there the custom operations we defined, and passing there constants or registers from Iced.
Here, two examples of functions made with this inline asm syntax:
Conclusion
So, this is about it, I didn’t get into detail for any topic, I think it’s just a fun use case for Iced/F#’s CE and things.
For “real” use there’s a bunch of limitations.
First, F# doesn’t support function pointers, and it’s impossible to create a delegate from a generic function pointer. That means that we’d need to create a whole number of overloads for arguments of different sizes and ABI (rememer, floats on Windows x86 are passed in xmm registers, for instance). In this article I only consider a function which returns, but doesn’t take any arguments.
Second, I haven’t done anything to support calls and labels (which are essential for anything non-basic, obviously). So it’s a Proof of Concept, not a real thing.
But anyway, I hope it was fun! The source is here. Thanks for your attention!