Skip to content

Latest commit

 

History

History
140 lines (112 loc) · 6.53 KB

compiling.md

File metadata and controls

140 lines (112 loc) · 6.53 KB

Compiling with NativeAOT-LLVM

Compilation using NativeAOT-LLVM is currently only supported on Windows x64 and Linux x64. Contributions enabling the compiler on other platforms are welcome.

This document explains how to compile and publish your project using NativeAOT-LLVM toolchain. First, please ensure that pre-requisites are installed. If you are starting a new project, you may find the samples useful.

Add ILCompiler package reference

To use NativeAOT-LLVM with your project, you need to add a reference to the ILCompiler NuGet packages containing the Native AOT compiler and runtime. Make sure the nuget.config file for your project contains the following package sources under the <packageSources> element:

<add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
<add key="nuget" value="https://api.nuget.org/v3/index.json" />

If your project has no nuget.config file, it may be created by running

> dotnet new nugetconfig

from the project's root directory. New package sources must be added after the <clear /> element if you decide to keep it.

Once you have added the package sources, add references to the ILCompiler packages by adding the following elements to the project file:

<ItemGroup>
  <PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="10.0.0-*" />
  <PackageReference Include="runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM" Version="10.0.0-*" />
</ItemGroup>

Note that it is important to use the same version for both packages to avoid potential hard-to-debug issues (use the latest version from the dotnet-experimental feed).

Adjust the project configuration

NativeAOT-LLVM is not integrated into the SDK, so first remove

<PublishAot>true</PublishAot>

from any PropertyGroup tags if you have it. Instead, add

<PropertyGroup>
  <PublishTrimmed>true</PublishTrimmed>
  <SelfContained>true</SelfContained>
  <MSBuildEnableWorkloadResolver>false</MSBuildEnableWorkloadResolver>
</PropertyGroup>

Note that the wasm-tools workload is identified as a dependency even though its not used, and this confuses the toolchain, hence MSBuildEnableWorkloadResolver=false.

Compile and publish your app

Use the dotnet publish command to compile and publish your app:

> dotnet publish -r <RID> -c <Configuration>

where <Configuration> is your project configuration (such as Debug or Release) and <RID> is the runtime identifier reflecting your target (browser-wasm or wasi-wasm). For example, to publish the Release build of your app for browser-wasm, run the following command:

> dotnet publish -r browser-wasm -c Release

If the compilation succeeds, the native artifacts will be placed under the bin/<Configuration>/net9.0/<RID>/publish/ path relative to your project's root directory.

  • For browser-wasm, the output can be run via NodeJS: node <..>/publish/YourProject.js, or opened in a browser: emrun <..>/publish/YourProject.html.
  • For wasi-wasm, the output can be run via any WASM runtime that supports WASI, e. g. wasmtime: wasmtime publish/YourProject.wasm.

WebAssembly application configuration

By default, the build will produce a binary with debug information, which is usually quite large. If you do not need it, add /p:NativeDebugSymbols=false to the publish command line. Note that this will disable the generation of all debug info, including function names for stack traces.

Another large contributor to the size is globalization support (ICU data and code). You can opt out by setting the InvariantGlobalization MSBuild property to true.

Additionally, NativeAOT-LLVM supports the following properties:

WebAssembly native libraries

To compile a WebAssembly native library that exports a function Answer:

[System.Runtime.InteropServices.UnmanagedCallersOnly(EntryPoint = "Answer")]
public static int Answer()
{
    return 42;
}
<PropertyGroup>
  <OutputType>library</OutputType> <!-- In addition to the other properties. -->
</PropertyGroup>

<ItemGroup>
  <LinkerArg Include="-sEXPORTED_RUNTIME_METHODS=cwrap" />
  <LinkerArg Include="--post-js=invokeLibraryFunction.js" />
</ItemGroup>
> dotnet publish -r browser-wasm

Where invokeLibraryFunction.js is a Javascript file with the callback to call Answer, e.g.

Module['onRuntimeInitialized'] = function() { 
  // Call your function
  const answer = Module.cwrap('Answer', 'number', []);
  console.log(answer())
};

Note that assemblies other than the one being published (e. g. those from referenced projects) need to be explicitly specified in the project file if you want their methods to be exported:

<ItemGroup>
  <UnmanagedEntryPointsAssembly Include="DependencyAssembly" />
</ItemGroup>

See also the NativeLibrary sample.

WebAssembly module imports

Functions in other WebAssembly modules can be imported and invoked using DllImport and WasmImportLinkage e.g.

[WasmImportLinkage]
[DllImport("wasi_snapshot_preview1", EntryPoint = "random_get")]
static extern int GetRandom(byte* buf, uint size);

This will create an import from the wasi_snapshot_preview1 module with the function name random_get. The import in the WAT would look like this:

(import "wasi_snapshot_preview1" "random_get" (func $random_get (type 3)))

Note: WasmImportLinkageAttribute is currently only available in the nightly SDK. You can either build against the nightly SDK or you can define this attribute in your code:

namespace System.Runtime.InteropServices
{
    [AttributeUsage(AttributeTargets.Method, Inherited = false)]
    public sealed class WasmImportLinkageAttribute : Attribute
    {
        public WasmImportLinkageAttribute() { }
    }
}

This can be used to import WASI functions that are in other modules, either as the above, in WASI, wasi_snapshot_preview1, or in other WebAssembly modules that may be linked with WebAssembly module linking.