Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ public readonly void WriteUtf8StringNoNull(string value)
bufferWriter.Advance(size);
}

public readonly void WriteUtf8WithLength(string value)
{
WriteULEB128((ulong)Encoding.UTF8.GetByteCount(value));
WriteUtf8StringNoNull(value);
}

public readonly void WritePadding(int size) => _sectionData.AppendPadding(size);

public readonly long Position => _sectionData.Length;
Expand Down
255 changes: 239 additions & 16 deletions src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@

namespace ILCompiler.ObjectWriter
{
public interface IWasmEncodable
{
int EncodeSize();
int Encode(Span<byte> buffer);
}

public enum WasmSectionType
{
Custom = 0,
Expand Down Expand Up @@ -51,6 +57,12 @@ public enum WasmValueType : byte
F64 = 0x7C
}

public enum WasmMutabilityType : byte
{
Const = 0x00,
Mut = 0x01
}

public static class WasmValueTypeExtensions
{
public static string ToTypeString(this WasmValueType valueType)
Expand All @@ -66,7 +78,7 @@ public static string ToTypeString(this WasmValueType valueType)
}
}

#nullable enable
#nullable enable
public readonly struct WasmResultType : IEquatable<WasmResultType>
{
private readonly WasmValueType[] _types;
Expand Down Expand Up @@ -150,15 +162,15 @@ public readonly int Encode(Span<byte> buffer)
buffer[0] = 0x60; // function type indicator

int paramSize = _params.Encode(buffer.Slice(1));
int returnSize = _returns.Encode(buffer.Slice(1+paramSize));
int returnSize = _returns.Encode(buffer.Slice(1 + paramSize));
Debug.Assert(totalSize == 1 + paramSize + returnSize);

return totalSize;
}

public bool Equals(WasmFuncType other)
{
return _params.Equals(other._params) && _returns.Equals(other._returns);
return _params.Equals(other._params) && _returns.Equals(other._returns);
}

public override bool Equals(object? obj)
Expand All @@ -183,44 +195,255 @@ public override string ToString()
}

// Represents a WebAssembly expression used in simple contexts for address calculation
enum WasmExprKind
public enum WasmExprKind
{
I32Const = 0x41,
I64Const = 0x42
I64Const = 0x42,
GlobalGet = 0x23,
I32Add = 0x6A,
}

public static class WasmExprKindExtensions
{
public static bool IsConstExpr(this WasmExprKind kind)
{
return kind == WasmExprKind.I32Const || kind == WasmExprKind.I64Const;
}

public static bool IsBinaryExpr(this WasmExprKind kind)
{
return kind == WasmExprKind.I32Add;
}

public static bool IsGlobalVarExpr(this WasmExprKind kind)
{
return kind == WasmExprKind.GlobalGet;
}
}

class WasmInstructionGroup : IWasmEncodable
{
readonly WasmExpr[] WasmExprs;
public WasmInstructionGroup(WasmExpr[] wasmExprs)
{
WasmExprs = wasmExprs;
}

public int Encode(Span<byte> buffer)
{
int pos = 0;
foreach (var expr in WasmExprs)
{
pos += expr.Encode(buffer.Slice(pos));
}
buffer[pos++] = 0x0B; // end opcode
return pos;
}

public int EncodeSize()
{
int size = 0;
foreach (var expr in WasmExprs)
{
size += expr.EncodeSize();
}
// plus one for the end opcode
return size + 1;
}
}

class WasmConstExpr
public abstract class WasmExpr : IWasmEncodable
{
WasmExprKind _kind;
public WasmExpr(WasmExprKind kind)
{
_kind = kind;
}

public virtual int EncodeSize() => 1;
public virtual int Encode(Span<byte> buffer)
{
buffer[0] = (byte)_kind;
return 1;
}
}

class WasmConstExpr : WasmExpr
{
long ConstValue;

public WasmConstExpr(WasmExprKind kind, long value)
public WasmConstExpr(WasmExprKind kind, long value) : base(kind)
{
if (kind == WasmExprKind.I32Const)
{
ArgumentOutOfRangeException.ThrowIfGreaterThan(value, int.MaxValue);
ArgumentOutOfRangeException.ThrowIfLessThan(value, int.MinValue);
}

_kind = kind;
ConstValue = value;
}

public int EncodeSize()
public override int EncodeSize()
{
uint valSize = DwarfHelper.SizeOfSLEB128(ConstValue);
return 1 + (int)valSize + 1; // opcode + value + end opcode
uint valSize = DwarfHelper.SizeOfSLEB128(ConstValue);
return base.EncodeSize() + (int)valSize;
}

public int Encode(Span<byte> buffer)
public override int Encode(Span<byte> buffer)
{
int pos = 0;
buffer[pos++] = (byte)_kind; // the kind is the opcode, either i32.const or i64.const

int pos = base.Encode(buffer);
pos += DwarfHelper.WriteSLEB128(buffer.Slice(pos), ConstValue);

buffer[pos++] = 0x0B; // end opcode
return pos;
}
}

// Simple DSL wrapper for creating Wasm expressions
static class Global
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please put the DSL in a namespace of its own

{
public static WasmExpr Get(int index)
{
return new WasmGlobalVarExpr(WasmExprKind.GlobalGet, index);
}
}

static class I32
{
public static WasmExpr Const(long value)
{
return new WasmConstExpr(WasmExprKind.I32Const, value);
}

public static WasmExpr Add => new WasmBinaryExpr(WasmExprKind.I32Add);
}

class WasmGlobalVarExpr : WasmExpr
{
public readonly int GlobalIndex;
public WasmGlobalVarExpr(WasmExprKind kind, int globalIndex) : base(kind)
{
Debug.Assert(globalIndex >= 0);
Debug.Assert(kind.IsGlobalVarExpr());
GlobalIndex = globalIndex;
}

public override int Encode(Span<byte> buffer)
{
int pos = base.Encode(buffer);
pos += DwarfHelper.WriteULEB128(buffer.Slice(pos), (uint)GlobalIndex);
return pos;
}

public override int EncodeSize()
{
return base.EncodeSize() + (int)DwarfHelper.SizeOfULEB128((uint)GlobalIndex);
}
}

// Represents a binary expression (e.g., i32.add)
class WasmBinaryExpr : WasmExpr
{
public WasmBinaryExpr(WasmExprKind kind) : base(kind)
{
Debug.Assert(kind.IsBinaryExpr());
}

// base class defaults are sufficient as the base class encodes just the opcode
}
public abstract class WasmImportType : IWasmEncodable
{
public abstract int Encode(Span<byte> buffer);
public abstract int EncodeSize();
}


public enum WasmExternalKind : byte
{
Function = 0x00,
Table = 0x01,
Memory = 0x02,
Global = 0x03,
Tag = 0x04
}

public class WasmGlobalType : WasmImportType
{
WasmValueType ValueType;
WasmMutabilityType Mutability;

public WasmGlobalType(WasmValueType valueType, WasmMutabilityType mutability)
{
ValueType = valueType;
Mutability = mutability;
}

public override int Encode(Span<byte> buffer)
{
buffer[0] = (byte)ValueType;
buffer[1] = (byte)Mutability;
return 2;
}

public override int EncodeSize() => 2;
}

public enum WasmLimitType : byte
{
HasMin = 0x00,
HasMinAndMax = 0x01
}

public class WasmMemoryType : WasmImportType
{
WasmLimitType LimitType;
uint Min;
uint? Max;

public WasmMemoryType(WasmLimitType limitType, uint min, uint? max = null)
{
if (LimitType == WasmLimitType.HasMinAndMax && !Max.HasValue)
{
throw new ArgumentException("Max must be provided when LimitType is HasMinAndMax");
}

LimitType = limitType;
Min = min;
Max = max;
}

public override int Encode(Span<byte> buffer)
{
int pos = 0;
buffer[pos++] = (byte)LimitType;
pos += DwarfHelper.WriteULEB128(buffer.Slice(pos), Min);
if (LimitType == WasmLimitType.HasMinAndMax)
{
pos += DwarfHelper.WriteULEB128(buffer.Slice(pos), Max!.Value);
}
return pos;
}

public override int EncodeSize() => 2;
}

public class WasmImport : IWasmEncodable
{
public string Module;
public string Name;
public WasmExternalKind Kind;
WasmImportType Import;

public WasmImport(string module, string name, WasmExternalKind kind, WasmImportType import)
{
Module = module;
Name = name;
Kind = kind;
Import = import;
}

public int Encode(Span<byte> buffer) => Import.Encode(buffer);
public int EncodeSize() => Import.EncodeSize();

#nullable disable
}
}
Loading
Loading