diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs index a65791887ce865..1c215c908b2e44 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs @@ -57,6 +57,45 @@ static void Validate(ReadyToRunReader reader) } } + [Fact] + public void WasmWebcilModule() + { + var wasmWebcilModule = new CompiledAssembly + { + AssemblyName = nameof(WasmWebcilModule), + SourceResourceNames = ["Webcil/WasmWebcilModule.cs"], + }; + + new R2RTestRunner(_output).Run(new R2RTestCase( + nameof(WasmWebcilModule), + [ + new(nameof(WasmWebcilModule), [new CrossgenAssembly(wasmWebcilModule)]) + { + OutputFileExtension = ".wasm", + AdditionalArgs = + { + "--targetarch", + "wasm", + "--targetos", + "browser", + }, + Validate = Validate, + }, + ])); + + static void Validate(ReadyToRunReader reader) + { + Assert.Equal(WasmMachine.Wasm32, reader.Machine); + var webcilReader = Assert.IsType(reader.CompositeReader); + Assert.Equal(WasmMachine.Wasm32, webcilReader.Machine); + Assert.True(webcilReader.IsWasmWrapped); + + var metadataReader = reader.GetGlobalMetadata().MetadataReader; + Assert.Contains(metadataReader.MethodDefinitions, methodHandle => + metadataReader.GetString(metadataReader.GetMethodDefinition(methodHandle).Name) == "AddIntegers"); + } + } + [Fact] public void TransitiveReferences() { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs new file mode 100644 index 00000000000000..209a61a56155ed --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Webcil; + +public static class WasmWebcilModule +{ + public static int AddIntegers(int left, int right) + { + return left + right; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RDriver.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RDriver.cs index 6a28f9118c7478..708c5d35be3be5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RDriver.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RDriver.cs @@ -30,11 +30,8 @@ internal enum Crossgen2Option { Composite, InputBubble, - ObjectFormat, HotColdSplitting, Optimize, - TargetArch, - TargetOS, } internal static class Crossgen2OptionsExtensions @@ -58,11 +55,8 @@ internal static class Crossgen2OptionsExtensions { Crossgen2Option.Composite => $"--composite", Crossgen2Option.InputBubble => $"--input-bubble", - Crossgen2Option.ObjectFormat => $"--object-format", Crossgen2Option.HotColdSplitting => $"--hot-cold-splitting", Crossgen2Option.Optimize => $"--optimize", - Crossgen2Option.TargetArch => $"--target-arch", - Crossgen2Option.TargetOS => $"--target-os", _ => throw new ArgumentOutOfRangeException(nameof(kind)), }; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs index 980edfe8f75e72..c6a2e26649671e 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RTestRunner.cs @@ -105,9 +105,14 @@ internal sealed class CrossgenCompilation(string name, List as /// to avoid colliding with component stubs that crossgen2 creates alongside the composite image. /// public string FilePath => _outputDir != null - ? Path.Combine(_outputDir, "CG2", Name + (IsComposite ? "-composite" : "") + ".dll") + ? Path.Combine(_outputDir, "CG2", Name + (IsComposite ? "-composite" : "") + OutputFileExtension) : throw new InvalidOperationException("Output directory not set"); + /// + /// File extension for the crossgen2 output image. + /// + public string OutputFileExtension { get; init; } = ".dll"; + public void SetOutputDir(string outputDir) { _outputDir = outputDir; diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcSlotTable.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcSlotTable.cs index bd633fbbc9e406..78e9c762b456ab 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcSlotTable.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcSlotTable.cs @@ -74,6 +74,8 @@ private static string GetRegisterName(int registerNumber, Machine machine) case Machine.RiscV64: return ((RiscV64.Registers)registerNumber).ToString(); + case WasmMachine.Wasm32: + throw new NotImplementedException("No implementation for machine type Wasm32."); default: throw new NotImplementedException(machine.ToString()); } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcTransition.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcTransition.cs index 749f7e48f22e2f..69d12790f9d951 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcTransition.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/Amd64/GcTransition.cs @@ -75,6 +75,9 @@ public string GetSlotState(GcSlotTable slotTable, Machine machine) regType = typeof(RiscV64.Registers); break; + case WasmMachine.Wasm32: + throw new NotImplementedException($"No implementation for machine type Wasm32."); + default: throw new NotImplementedException(); } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs index d2c0e7af16732f..ba486661064e2a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs @@ -86,6 +86,8 @@ public static string GetPlatformSpecificRegister(Machine machine, int regnum) return ((LoongArch64.Registers)regnum).ToString(); case Machine.RiscV64: return ((RiscV64.Registers)regnum).ToString(); + case WasmMachine.Wasm32: + throw new NotImplementedException($"No implementation for machine type {machine}."); default: throw new NotImplementedException($"No implementation for machine type {machine}."); } diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/GCInfoTypes.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/GCInfoTypes.cs index 40bb78977e71df..b7985983f807fb 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/GCInfoTypes.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/GCInfoTypes.cs @@ -149,6 +149,34 @@ internal GcInfoTypes(Machine machine, bool denormalizeCodeOffsets) STACK_BASE_REGISTER_ENCBASE = 2; NUM_REGISTERS_ENCBASE = 3; break; + case WasmMachine.Wasm32: + PSP_SYM_STACK_SLOT_ENCBASE = 6; + GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE = 6; + SECURITY_OBJECT_STACK_SLOT_ENCBASE = 6; + GS_COOKIE_STACK_SLOT_ENCBASE = 6; + CODE_LENGTH_ENCBASE = 6; + STACK_BASE_REGISTER_ENCBASE = 3; + SIZE_OF_STACK_AREA_ENCBASE = 6; + SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE = 3; + REVERSE_PINVOKE_FRAME_ENCBASE = 6; + NUM_REGISTERS_ENCBASE = 3; + NUM_STACK_SLOTS_ENCBASE = 5; + NUM_UNTRACKED_SLOTS_ENCBASE = 5; + NORM_PROLOG_SIZE_ENCBASE = 4; + NORM_EPILOG_SIZE_ENCBASE = 3; + INTERRUPTIBLE_RANGE_DELTA1_ENCBASE = 5; + INTERRUPTIBLE_RANGE_DELTA2_ENCBASE = 5; + REGISTER_ENCBASE = 3; + REGISTER_DELTA_ENCBASE = REGISTER_ENCBASE; + STACK_SLOT_ENCBASE = 6; + STACK_SLOT_DELTA_ENCBASE = 4; + NUM_SAFE_POINTS_ENCBASE = 4; + NUM_INTERRUPTIBLE_RANGES_ENCBASE = 1; + POINTER_SIZE_ENCBASE = 3; + LIVESTATE_RLE_RUN_ENCBASE = 2; + LIVESTATE_RLE_SKIP_ENCBASE = 4; + break; + case Machine.I386: CODE_LENGTH_ENCBASE = 6; NORM_PROLOG_SIZE_ENCBASE = 4; diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index 8939b4f3eb4cf9..eefcd036f899ef 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -33,6 +33,11 @@ public enum OperatingSystem Unknown = -1 } + public static class WasmMachine + { + public const Machine Wasm32 = (Machine)0xFFFE; + } + public struct InstanceMethod { public byte Bucket; @@ -683,6 +688,7 @@ private unsafe void EnsureHeader() case Machine.Arm: case Machine.Thumb: case Machine.ArmThumb2: + case WasmMachine.Wasm32: _pointerSize = 4; break; @@ -1532,6 +1538,7 @@ private void EnsureImportSectionsImpl() { case Machine.I386: case Machine.ArmThumb2: + case WasmMachine.Wasm32: entrySize = 4; break; diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/TransitionBlock.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/TransitionBlock.cs index 85d6bda61dbbf2..043b54c8206dce 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/TransitionBlock.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/TransitionBlock.cs @@ -37,6 +37,9 @@ public static TransitionBlock FromReader(ReadyToRunReader reader) case Machine.RiscV64: return RiscV64TransitionBlock.Instance; + case WasmMachine.Wasm32: + return Wasm32TransitionBlock.Instance; + default: throw new NotImplementedException(); } @@ -100,6 +103,23 @@ public override int OffsetFromGCRefMapPos(int pos) } } + private sealed class Wasm32TransitionBlock : TransitionBlock + { + public static readonly TransitionBlock Instance = new Wasm32TransitionBlock(); + + public override int PointerSize => 4; + public override int NumArgumentRegisters => 0; + public override int NumCalleeSavedRegisters => 0; + // Argument registers, callee-save registers, return address + public override int SizeOfTransitionBlock => 8; + public override int OffsetOfArgumentRegisters => 0; + + public override int OffsetFromGCRefMapPos(int pos) + { + return SizeOfTransitionBlock + pos * PointerSize; + } + } + private sealed class X64WindowsTransitionBlock : TransitionBlock { public static readonly TransitionBlock Instance = new X64WindowsTransitionBlock(); diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/WebcilImageReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/WebcilImageReader.cs index 7ced312c9239a5..6c4aea1392d52d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/WebcilImageReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/WebcilImageReader.cs @@ -28,7 +28,7 @@ public class WebcilImageReader : IBinaryImageReader private readonly CorFlags _corFlags; private readonly DirectoryEntry _managedNativeHeaderDirectory; - public Machine Machine => Machine.I386; // Webcil doesn't encode machine type; wasm targets use a placeholder + public Machine Machine => WasmMachine.Wasm32; // Webcil doesn't encode machine type; wasm targets use a placeholder public OperatingSystem OperatingSystem => OperatingSystem.Unknown; public ulong ImageBase => 0; diff --git a/src/coreclr/tools/r2rdump/CoreDisTools.cs b/src/coreclr/tools/r2rdump/CoreDisTools.cs index 5f3e1ed5780b7c..0dd3195faa0f5a 100644 --- a/src/coreclr/tools/r2rdump/CoreDisTools.cs +++ b/src/coreclr/tools/r2rdump/CoreDisTools.cs @@ -78,6 +78,8 @@ public static IntPtr GetDisasm(Machine machine) case Machine.RiscV64: target = TargetArch.Target_RiscV64; break; + case WasmMachine.Wasm32: + return IntPtr.Zero; default: Program.WriteWarning($"{machine} not supported on CoreDisTools"); return IntPtr.Zero; @@ -187,6 +189,7 @@ private void SetIndentations() // to 7 * 3 characters; see https://github.com/dotnet/llilc/blob/master/lib/CoreDisTools/coredistools.cpp. Machine.I386 => 7 * 3, Machine.Amd64 => 7 * 3, + WasmMachine.Wasm32 => 7 * 3, // Instructions are either 2 or 4 bytes long Machine.ArmThumb2 => 4 * 3, diff --git a/src/coreclr/tools/r2rdump/Program.cs b/src/coreclr/tools/r2rdump/Program.cs index deb9dd6c82ebe6..d2ed2cdb89aca4 100644 --- a/src/coreclr/tools/r2rdump/Program.cs +++ b/src/coreclr/tools/r2rdump/Program.cs @@ -212,6 +212,7 @@ public void Dump(ReadyToRunReader r2r) Machine.Arm64 => TargetArchitecture.ARM64, Machine.LoongArch64 => TargetArchitecture.LoongArch64, Machine.RiscV64 => TargetArchitecture.RiscV64, + WasmMachine.Wasm32 => TargetArchitecture.Wasm32, _ => throw new NotImplementedException(r2r.Machine.ToString()), }; TargetOS os = r2r.OperatingSystem switch @@ -466,7 +467,7 @@ public int Run() // parse the ReadyToRun image ReadyToRunReader r2r = new(model, filename); r2r.ValidateDebugInfo = Get(_command.ValidateDebugInfo); - if (disasm && !(r2r.CompositeReader is WebcilImageReader)) + if (disasm) { disassembler = new Disassembler(r2r, model); }