Skip to content

Commit

Permalink
[NativeAOT-LLVM] Add a native WASM object writer (#2643)
Browse files Browse the repository at this point in the history
* Instrumentation

* Add a WASM object writer

* Remove diagnostics

* Vacuum LLVM code
  • Loading branch information
SingleAccretion committed Aug 3, 2024
1 parent 53dc495 commit 3e66fa1
Show file tree
Hide file tree
Showing 11 changed files with 1,160 additions and 318 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ The .NET Foundation licenses this file to you under the MIT license.
<LibFileExt Condition="'$(_targetOS)' != 'win'">.a</LibFileExt>

<IlcOutputFileExt Condition="$(IlcOutputFileExt) == ''">$(NativeObjectExt)</IlcOutputFileExt>
<IlcOutputFileExt Condition="'$(NativeCodeGen)' == 'llvm'">$(LlvmObjectExt)</IlcOutputFileExt>

<IsNativeExecutable Condition="'$(OutputType)' == 'Exe' or '$(OutputType)' == 'WinExe'">true</IsNativeExecutable>

Expand Down Expand Up @@ -375,13 +374,14 @@ The .NET Foundation licenses this file to you under the MIT license.
AfterTargets="IlcCompile"
BeforeTargets="CompileWasmObjects;LinkNativeLlvm">
<ReadLinesFromFile File="$(NativeIntermediateOutputPath)$(TargetName).results.txt">
<Output TaskParameter="Lines" ItemName="LlvmObjects" />
<Output TaskParameter="Lines" ItemName="_IlcProducedFiles" />
</ReadLinesFromFile>

<ItemGroup>
<LlvmObjects>
<LlvmObjects Include="@(_IlcProducedFiles)" Condition="'%(Extension)' == '$(LlvmObjectExt)'">
<NativeObject>%(RelativeDir)%(Filename)$(NativeObjectExt)</NativeObject>
</LlvmObjects>
<NativeObjects Include="@(_IlcProducedFiles)" Condition="'%(Extension)' == '$(NativeObjectExt)'" />
<NativeObjects Include="@(LlvmObjects->'%(NativeObject)')" />
</ItemGroup>
</Target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ public void EmitReloc(ISymbolNode symbol, RelocType relocType, int delta = 0)
// And add space for the reloc
switch (relocType)
{
case RelocType.R_WASM_FUNCTION_OFFSET_I32:
case RelocType.IMAGE_REL_BASED_REL32:
case RelocType.IMAGE_REL_BASED_RELPTR32:
case RelocType.IMAGE_REL_BASED_ABSOLUTE:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ public enum RelocType
//
IMAGE_REL_SYMBOL_SIZE = 0x1000, // The size of data in the image represented by the target symbol node
IMAGE_REL_FILE_ABSOLUTE = 0x1001, // 32 bit offset from beginning of image

//
// WASM relocations.
//
R_WASM_FUNCTION_OFFSET_I32, // Offset of a function relative to the Code section.
}

public struct Relocation
Expand Down Expand Up @@ -499,6 +504,7 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v
{
switch (relocType)
{
case RelocType.R_WASM_FUNCTION_OFFSET_I32:
case RelocType.IMAGE_REL_BASED_ABSOLUTE:
case RelocType.IMAGE_REL_BASED_ADDR32NB:
case RelocType.IMAGE_REL_BASED_HIGHLOW:
Expand Down Expand Up @@ -569,6 +575,7 @@ public static unsafe long ReadValue(RelocType relocType, void* location)
{
switch (relocType)
{
case RelocType.R_WASM_FUNCTION_OFFSET_I32:
case RelocType.IMAGE_REL_BASED_ABSOLUTE:
case RelocType.IMAGE_REL_BASED_ADDR32NB:
case RelocType.IMAGE_REL_BASED_HIGHLOW:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ public void Add(T item)
}

#if NET
public readonly Span<T> AsSpan(int start) => _items.AsSpan(start);
public readonly Span<T> AsSpan() => _items.AsSpan(0, _count);

public readonly Span<T> AsSpan(int start) => _items.AsSpan(start, _count - start);

public Span<T> AppendSpan(int length)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,9 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
command |= StackTraceDataCommand.IsStackTraceHidden;
}

RelocType reloc = factory.Target.IsWasm ? RelocType.R_WASM_FUNCTION_OFFSET_I32 : RelocType.IMAGE_REL_BASED_RELPTR32;
objData.EmitByte(commandReservation, command);
objData.EmitReloc(factory.MethodEntrypoint(entry.Method), RelocType.IMAGE_REL_BASED_RELPTR32);
objData.EmitReloc(factory.MethodEntrypoint(entry.Method), reloc);
}

_size = objData.CountBytes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Numerics;

using ILCompiler.DependencyAnalysis;
using ILCompiler.ObjectWriter;

using Internal.JitInterface;
using Internal.JitInterface.LLVMInterop;
Expand Down Expand Up @@ -47,6 +48,116 @@ public override ISymbolNode GetExternalMethodAccessor(MethodDesc method, ReadOnl

public override CorInfoLlvmEHModel GetLlvmExceptionHandlingModel() => Options.ExceptionHandlingModel;

internal WasmFunctionType GetWasmFunctionTypeForMethod(MethodSignature signature, bool hasHiddenParam)
{
WasmValueType wasmPointerType = _nodeFactory.Target.PointerSize == 4 ? WasmValueType.I32 : WasmValueType.I64;

WasmValueType GetWasmTypeForTypeDesc(TypeDesc type)
{
switch (type.Category)
{
case TypeFlags.Boolean:
case TypeFlags.SByte:
case TypeFlags.Byte:
case TypeFlags.Int16:
case TypeFlags.UInt16:
case TypeFlags.Char:
case TypeFlags.Int32:
case TypeFlags.UInt32:
return WasmValueType.I32;

case TypeFlags.IntPtr:
case TypeFlags.UIntPtr:
case TypeFlags.Array:
case TypeFlags.SzArray:
case TypeFlags.ByRef:
case TypeFlags.Class:
case TypeFlags.Interface:
case TypeFlags.Pointer:
case TypeFlags.FunctionPointer:
return wasmPointerType;

case TypeFlags.Int64:
case TypeFlags.UInt64:
return WasmValueType.I64;

case TypeFlags.Single:
return WasmValueType.F32;

case TypeFlags.Double:
return WasmValueType.F64;

case TypeFlags.Enum:
return GetWasmTypeForTypeDesc(type.UnderlyingType);

case TypeFlags.Void:
return WasmValueType.Invalid;

default:
throw new UnreachableException(type.Category.ToString());
}
}

WasmValueType GetWasmReturnType(TypeDesc sigReturnType, out bool isPassedByRef)
{
TypeDesc returnType = sigReturnType;
if (IsStruct(sigReturnType))
{
returnType = GetPrimitiveTypeForTrivialWasmStruct(sigReturnType);
}

isPassedByRef = returnType == null;
return isPassedByRef ? WasmValueType.Invalid : GetWasmTypeForTypeDesc(returnType);
}

WasmValueType GetWasmArgTypeForArg(TypeDesc argSigType)
{
bool isPassedByRef = false;
TypeDesc argType = argSigType;
if (IsStruct(argSigType))
{
argType = GetPrimitiveTypeForTrivialWasmStruct(argSigType);
if (argType == null)
{
isPassedByRef = true;
}
}

return isPassedByRef ? wasmPointerType : GetWasmTypeForTypeDesc(argType);
}

WasmValueType wasmReturnType = GetWasmReturnType(signature.ReturnType, out bool isReturnByRef);

int maxWasmSigLength = signature.Length + 4;
Span<WasmValueType> signatureTypes =
maxWasmSigLength > 100 ? new WasmValueType[maxWasmSigLength] : stackalloc WasmValueType[maxWasmSigLength];

int index = 0;
signatureTypes[index++] = wasmPointerType;

if (!signature.IsStatic) // TODO-LLVM-Bug: doesn't handle explicit 'this'.
{
signatureTypes[index++] = wasmPointerType;
}

if (isReturnByRef)
{
signatureTypes[index++] = wasmPointerType;
}

if (hasHiddenParam)
{
signatureTypes[index++] = wasmPointerType;
}

foreach (TypeDesc type in signature)
{
signatureTypes[index++] = GetWasmArgTypeForArg(type);
}

return new WasmFunctionType(wasmReturnType, signatureTypes.Slice(0, index).ToArray());
}

internal LLVMTypeRef GetLLVMSignatureForMethod(MethodSignature signature, bool hasHiddenParam)
{
LLVMTypeRef llvmReturnType = GetLlvmReturnType(signature.ReturnType, out bool isReturnByRef);
Expand All @@ -58,7 +169,7 @@ internal LLVMTypeRef GetLLVMSignatureForMethod(MethodSignature signature, bool h
int index = 0;
signatureTypes[index++] = LLVMPtrType;

if (!signature.IsStatic) // Bug: doesn't handle explicit 'this'.
if (!signature.IsStatic) // TODO-LLVM-Bug: doesn't handle explicit 'this'.
{
signatureTypes[index++] = LLVMPtrType;
}
Expand Down
Loading

0 comments on commit 3e66fa1

Please sign in to comment.