-
Notifications
You must be signed in to change notification settings - Fork 5.3k
[RyuJIT Wasm] Add Object Writer Imports Section + Constant Exprs to Encode R2R Payload Placement #123739
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
base: main
Are you sure you want to change the base?
[RyuJIT Wasm] Add Object Writer Imports Section + Constant Exprs to Encode R2R Payload Placement #123739
Changes from all commits
bd0cac5
da9edbb
6232eef
25a7ff5
4684843
1d3f7fc
5790ae4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System; | ||
| using System.Diagnostics; | ||
|
|
||
| // This namespace implements encodings for certain Wasm expressions (instructions) | ||
| // which are used in the object writer. | ||
| // For now, these instructions are only used for constructing constant expressions | ||
| // to calculate placements for data segments based on imported globals. | ||
| namespace ILCompiler.ObjectWriter.WasmInstructions | ||
| { | ||
| public enum WasmExprKind | ||
| { | ||
| I32Const = 0x41, | ||
| 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; | ||
| } | ||
| } | ||
|
|
||
| // Represents a group of Wasm instructions (expressions) which | ||
| // form a complete expression ending with the 'end' opcode. | ||
| 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; | ||
| } | ||
| } | ||
|
|
||
| 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; | ||
| } | ||
| } | ||
|
|
||
| // Represents a constant expression (e.g., (i32.const <value>)) | ||
| class WasmConstExpr : WasmExpr | ||
| { | ||
| long ConstValue; | ||
|
|
||
| public WasmConstExpr(WasmExprKind kind, long value) : base(kind) | ||
| { | ||
| if (kind == WasmExprKind.I32Const) | ||
| { | ||
| ArgumentOutOfRangeException.ThrowIfGreaterThan(value, int.MaxValue); | ||
| ArgumentOutOfRangeException.ThrowIfLessThan(value, int.MinValue); | ||
| } | ||
|
|
||
| ConstValue = value; | ||
| } | ||
|
|
||
| public override int EncodeSize() | ||
| { | ||
| uint valSize = DwarfHelper.SizeOfSLEB128(ConstValue); | ||
| return base.EncodeSize() + (int)valSize; | ||
| } | ||
|
|
||
| public override int Encode(Span<byte> buffer) | ||
| { | ||
| int pos = base.Encode(buffer); | ||
| pos += DwarfHelper.WriteSLEB128(buffer.Slice(pos), ConstValue); | ||
|
|
||
| return pos; | ||
| } | ||
| } | ||
|
|
||
| // Represents a global variable expression (e.g., (global.get <index)) | ||
| 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 | ||
| } | ||
|
|
||
| // ************************************************ | ||
| // Simple DSL wrapper for creating Wasm expressions | ||
| // ************************************************ | ||
| static class Global | ||
| { | ||
| 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); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -8,6 +8,12 @@ | |||||
|
|
||||||
| namespace ILCompiler.ObjectWriter | ||||||
| { | ||||||
| public interface IWasmEncodable | ||||||
| { | ||||||
| int EncodeSize(); | ||||||
| int Encode(Span<byte> buffer); | ||||||
| } | ||||||
|
|
||||||
| public enum WasmSectionType | ||||||
| { | ||||||
| Custom = 0, | ||||||
|
|
@@ -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) | ||||||
|
|
@@ -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; | ||||||
|
|
@@ -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) | ||||||
|
|
@@ -182,45 +194,110 @@ public override string ToString() | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| // Represents a WebAssembly expression used in simple contexts for address calculation | ||||||
| enum WasmExprKind | ||||||
| public abstract class WasmImportType : IWasmEncodable | ||||||
| { | ||||||
| I32Const = 0x41, | ||||||
| I64Const = 0x42 | ||||||
| public abstract int Encode(Span<byte> buffer); | ||||||
| public abstract int EncodeSize(); | ||||||
| } | ||||||
|
|
||||||
| class WasmConstExpr | ||||||
| public enum WasmExternalKind : byte | ||||||
| { | ||||||
| WasmExprKind _kind; | ||||||
| long ConstValue; | ||||||
| Function = 0x00, | ||||||
| Table = 0x01, | ||||||
| Memory = 0x02, | ||||||
| Global = 0x03, | ||||||
| Tag = 0x04, | ||||||
| Count = 0x05 // Not actually part of the spec; used for counting kinds | ||||||
| } | ||||||
|
|
||||||
| public WasmConstExpr(WasmExprKind kind, long value) | ||||||
| public class WasmGlobalType : WasmImportType | ||||||
| { | ||||||
| WasmValueType ValueType; | ||||||
| WasmMutabilityType Mutability; | ||||||
|
|
||||||
|
Comment on lines
+215
to
+217
|
||||||
| public WasmGlobalType(WasmValueType valueType, WasmMutabilityType mutability) | ||||||
| { | ||||||
| if (kind == WasmExprKind.I32Const) | ||||||
| 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; | ||||||
|
|
||||||
|
Comment on lines
+242
to
+245
|
||||||
| public WasmMemoryType(WasmLimitType limitType, uint min, uint? max = null) | ||||||
| { | ||||||
| if (limitType == WasmLimitType.HasMinAndMax && !max.HasValue) | ||||||
| { | ||||||
| ArgumentOutOfRangeException.ThrowIfGreaterThan(value, int.MaxValue); | ||||||
| ArgumentOutOfRangeException.ThrowIfLessThan(value, int.MinValue); | ||||||
| throw new ArgumentException("Max must be provided when LimitType is HasMinAndMax"); | ||||||
| } | ||||||
|
|
||||||
| _kind = kind; | ||||||
| ConstValue = value; | ||||||
| LimitType = limitType; | ||||||
| Min = min; | ||||||
| Max = max; | ||||||
| } | ||||||
|
|
||||||
| public int EncodeSize() | ||||||
| public override int Encode(Span<byte> buffer) | ||||||
| { | ||||||
| uint valSize = DwarfHelper.SizeOfSLEB128(ConstValue); | ||||||
| return 1 + (int)valSize + 1; // opcode + value + end opcode | ||||||
| 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 int Encode(Span<byte> buffer) | ||||||
| public override int EncodeSize() | ||||||
| { | ||||||
| int pos = 0; | ||||||
| buffer[pos++] = (byte)_kind; // the kind is the opcode, either i32.const or i64.const | ||||||
| uint size = 1 + DwarfHelper.SizeOfULEB128(Min); | ||||||
| if (LimitType == WasmLimitType.HasMinAndMax) | ||||||
| { | ||||||
| size += DwarfHelper.SizeOfULEB128(Max!.Value); | ||||||
| } | ||||||
| return (int)size; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| pos += DwarfHelper.WriteSLEB128(buffer.Slice(pos), ConstValue); | ||||||
| public class WasmImport : IWasmEncodable | ||||||
| { | ||||||
| public readonly string Module; | ||||||
| public readonly string Name; | ||||||
| public readonly WasmExternalKind Kind; | ||||||
| public readonly int? Index; | ||||||
| public readonly WasmImportType Import; | ||||||
|
|
||||||
| buffer[pos++] = 0x0B; // end opcode | ||||||
| return pos; | ||||||
| public WasmImport(string module, string name, WasmExternalKind kind, WasmImportType import, int? index = null) | ||||||
| { | ||||||
| Module = module; | ||||||
| Name = name; | ||||||
| Kind = kind; | ||||||
| Import = import; | ||||||
| Index = index; | ||||||
| } | ||||||
|
|
||||||
| public int Encode(Span<byte> buffer) => Import.Encode(buffer); | ||||||
| public int EncodeSize() => Import.EncodeSize(); | ||||||
|
|
||||||
| #nullable disable | ||||||
|
Comment on lines
+300
to
+301
|
||||||
| #nullable disable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There appears to be trailing whitespace in the comment lines here (e.g., after the period). The repo enforces
trim_trailing_whitespace = true(.editorconfig:13), so please remove trailing spaces to avoid formatting-only diffs and potential CI/style checks.