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
177 changes: 83 additions & 94 deletions .github/skills/add-new-jit-ee-api/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,129 +86,116 @@ Implement the API if asked, leave the UNREACHABLE() otherwise.

6. Now implement the most complex part - SuperPMI. SuperPMI acts as a (de)serializer for JIT-VM queries in order
to then replay them without the actual VM to speed up jit-diffs and other scenarios. All parameters and return
values recorded/restored using special primitve types and helpers. We need to update the following files:
values recorded/restored using special primitve types and helpers. It is written in C#; we need to update the following
files in `<repo_root>/src/coreclr/tools/superpmi/managed/SuperPmi.Shared`:

* `<repo_root>/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h`:
* `<repo_root>/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h`:
* `<repo_root>/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h`:
* `<repo_root>/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp`:
* `Agnostic.cs`
* `PacketId.cs`
* `MethodContext.Maps.cs`
* `MethodContext.Record.cs`
* `MethodContext.Dump.cs`
* `MethodContext.Replay.cs`

Go through each of them one by one.

* `<repo_root>/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h`:
* `Agnostic.cs`:
Define two `Agnostic_*` types for input arguments and another one for output parameters (return value, output arguments).
Do not create them if one of the generics ones can be re-used such as `DLD`, `DD`, `DLDL`, etc. Use `DWORD*`
like types for integers. Inspect the whole file to see how other APIs are defined.
Do not create them if one of the generics ones can be re-used such as `DLD`, `DD`, `DLDL`, etc. Use `uint`/`ulong`
for integers and handles. Inspect the whole file to see how other APIs are defined.

* `<repo_root>/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h`:
Add a new entry to the `LWM` list. Example:
* `PacketId.cs`:
Add a new packet ID at the end of `enum PacketId`. Example:

```diff
+LWM(GetUnboxedEntry, DWORDLONG, DLD);
+ GetUnboxedEntry = <last value + 1>,
```

NOTE: Use upper-case for the first letter of the API name here.
Add the new record after the very last LWM one.

* `<repo_root>/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h`:
Define 3 methods in this header file inside `class MethodContext` class (at the end of its definition).
* `MethodContext.Maps.cs`:
Add the new map at the end of `s_maps`, with the next index, and a property and a dump for it at the end of the other ones. Example:

The methods are prefixed with `rec*` (record), `dmp*` (dump to console) and `rep*` (replay). Example
```diff
+ new MapInfo(<last index + 1>, "GetUnboxedEntry", PacketId.GetUnboxedEntry, static () => new LightWeightMap<ulong, DLD>()),
```

```diff
+ void recGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg, CORINFO_METHOD_HANDLE result);
+ void dmpGetUnboxedEntry(DWORDLONG key, DLD value);
+ CORINFO_METHOD_HANDLE repGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg);
+ public LightWeightMap<ulong, DLD> GetUnboxedEntry { get => (LightWeightMap<ulong, DLD>)_maps[<index>]; set => _maps[<index>] = value; }
```
Now add a new element to `enum mcPackets` enum in the same file. Example:

```diff
+ Packet_GetUnboxedEntry = <last value + 1>,
+ MapDumper.Dump(nameof(GetUnboxedEntry), GetUnboxedEntry, (k, v) => dmpGetUnboxedEntry(k, v));
```

* `<repo_root>/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp`:
Add the implementation of the 3 methods to `methodcontext.cpp` at the end of it.
Consider other similar methods in the file for reference. Do not change implementations of other methods in the file. Example:
NOTE: Use upper-case for the first letter of the API name here.

* `MethodContext.Record.cs`, `MethodContext.Dump.cs` and `MethodContext.Replay.cs`:
Add 3 methods, prefixed with `rec*` (record), `dmp*` (dump to console) and `rep*` (replay), one to each file at its end.
Consider other similar methods in the files for reference. Do not change implementations of other methods. Example:

```diff
+void MethodContext::recGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn,
+ bool* requiresInstMethodTableArg,
+ CORINFO_METHOD_HANDLE result)
+{
+ // Initialize the "input - output" map if it is not already initialized
+ if (GetUnboxedEntry == nullptr)
+ public void recGetUnboxedEntry(CORINFO_METHOD_STRUCT_* ftn, bool* requiresInstMethodTableArg, CORINFO_METHOD_STRUCT_* result)
+ {
+ GetUnboxedEntry = new LightWeightMap<DWORDLONG, DLD>();
+ }
+ // Initialize the "input - output" map if it is not already initialized
+ GetUnboxedEntry ??= new();
+
+ // Create a key out of the input arguments
+ DWORDLONG key = CastHandle(ftn);
+ DLD value;
+ value.A = CastHandle(result);
+ // Create a key out of the input arguments
+ ulong key = CastHandle(ftn);
+
+ // Create a value out of the return value and out parameters
+ if (requiresInstMethodTableArg != nullptr)
+ {
+ value.B = (DWORD)*requiresInstMethodTableArg ? 1 : 0;
+ // Create a value out of the return value and out parameters
+ DLD value = default;
+ value.A = CastHandle(result);
+ value.B = requiresInstMethodTableArg != null && *requiresInstMethodTableArg ? 1u : 0u;
+
+ // Save it to the map
+ GetUnboxedEntry.Add(key, value);
+ }
+ else
```

```diff
+ public void dmpGetUnboxedEntry(ulong key, DLD value)
+ => CFormat.Printf(Out, "GetUnboxedEntry ftn-%016llX, result-%016llX, requires-inst-%u", key, value.A, value.B);
```

```diff
+ public CORINFO_METHOD_STRUCT_* repGetUnboxedEntry(CORINFO_METHOD_STRUCT_* ftn, bool* requiresInstMethodTableArg)
+ {
+ value.B = 0;
+ }
+
+ // Save it to the map
+ GetUnboxedEntry->Add(key, value);
+ DEBUG_REC(dmpGetUnboxedEntry(key, value));
+}
+void MethodContext::dmpGetUnboxedEntry(DWORDLONG key, DLD value)
+{
+ // Dump key and value to the console for debug purposes.
+ printf("GetUnboxedEntry ftn-%016" PRIX64 ", result-%016" PRIX64 ", requires-inst-%u", key, value.A, value.B);
+}
+CORINFO_METHOD_HANDLE MethodContext::repGetUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg)
+{
+ // Create a key out of the input arguments
+ DWORDLONG key = CastHandle(ftn);
+ // Create a key out of the input arguments
+ ulong key = CastHandle(ftn);
+
+ // Perform the lookup to obtain the value (output arguments and return value)
+ DLD value = LookupByKeyOrMiss(GetUnboxedEntry, key, ": key %016" PRIX64 "", key);
+ DEBUG_REP(dmpGetUnboxedEntry(key, value));
+ // Perform the lookup to obtain the value (output arguments and return value)
+ DLD value = LookupByKeyOrMiss(GetUnboxedEntry, key, ": key %016llX", key);
+
+ // propagate result to output arguments and return value (if exists)
+ if (requiresInstMethodTableArg != nullptr)
+ {
+ *requiresInstMethodTableArg = (value.B == 1);
+ }
+ return (CORINFO_METHOD_HANDLE)(value.A);
+}
+ // propagate result to output arguments and return value (if exists)
+ if (requiresInstMethodTableArg != null)
+ *requiresInstMethodTableArg = value.B == 1;
+ return (CORINFO_METHOD_STRUCT_*)value.A;
+ }
```

7. Add a new function to `<repo_root>/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp` that calls the `rep*` method. Example:
7. Add a new method to `<repo_root>/src/coreclr/tools/superpmi/managed/superpmi/CorInfoImpl.SuperPmi.cs` that calls the `rep*` method.
It has the signature of the API in `CorInfoImpl_generated.cs`. Example:

```diff
+CORINFO_METHOD_HANDLE MyICJI::getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg)
+{
+ jitInstance->mc->cr->AddCall("getUnboxedEntry");
+ CORINFO_METHOD_HANDLE result = jitInstance->mc->repGetUnboxedEntry(ftn, requiresInstMethodTableArg);
+ return result;
+}
+ private CORINFO_METHOD_STRUCT_* getUnboxedEntry(CORINFO_METHOD_STRUCT_* ftn, ref bool requiresInstMethodTableArg)
+ {
+ AddCall("getUnboxedEntry");
+ return mc.repGetUnboxedEntry(ftn, P(ref requiresInstMethodTableArg));
+ }
```

8. Add a new function to `<repo_root>/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp` that calls the `rec*` method. Example:
8. Add a new method to `<repo_root>/src/coreclr/tools/superpmi/managed/superpmi-shim-collector/CorInfoImpl.Collector.cs` that calls the
VM (`_original`) and the `rec*` method. Example:

```diff
+CORINFO_METHOD_HANDLE interceptor_ICJI::getUnboxedEntry(CORINFO_METHOD_HANDLE ftn, bool* requiresInstMethodTableArg)
+{
+ mc->cr->AddCall("getUnboxedEntry");
+ bool localRequiresInstMethodTableArg = false;
+ CORINFO_METHOD_HANDLE result = original_ICorJitInfo->getUnboxedEntry(ftn, &localRequiresInstMethodTableArg);
+ mc->recGetUnboxedEntry(ftn, &localRequiresInstMethodTableArg, result);
+ if (requiresInstMethodTableArg != nullptr)
+ {
+ *requiresInstMethodTableArg = localRequiresInstMethodTableArg;
+ }
+ return result;
+}
+ private CORINFO_METHOD_STRUCT_* getUnboxedEntry(CORINFO_METHOD_STRUCT_* ftn, ref bool requiresInstMethodTableArg)
+ {
+ AddCall("getUnboxedEntry");
+ bool localRequiresInstMethodTableArg = false;
+ CORINFO_METHOD_STRUCT_* result = _original.getUnboxedEntry(ftn, ref localRequiresInstMethodTableArg);
+ mc.recGetUnboxedEntry(ftn, &localRequiresInstMethodTableArg, result);
+ if (!Unsafe.IsNullRef(ref requiresInstMethodTableArg))
+ requiresInstMethodTableArg = localRequiresInstMethodTableArg;
+ return result;
+ }
```

#### 4 — Definition of Done (self-check list)
Expand All @@ -219,10 +206,12 @@ Consider other similar methods in the file for reference. Do not change implemen
* `<repo_root>/src/coreclr/inc/corinfo.h`
* `<repo_root>/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs`
* `<repo_root>/src/coreclr/vm/jitinterface.cpp`
* `<repo_root>/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h` [optional - only if new types are needed]
* `<repo_root>/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h`
* `<repo_root>/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h`
* `<repo_root>/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp`
* `<repo_root>/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp`
* `<repo_root>/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp`
* `<repo_root>/src/coreclr/tools/superpmi/managed/SuperPmi.Shared/Agnostic.cs` [optional - only if new types are needed]
* `<repo_root>/src/coreclr/tools/superpmi/managed/SuperPmi.Shared/PacketId.cs`
* `<repo_root>/src/coreclr/tools/superpmi/managed/SuperPmi.Shared/MethodContext.Maps.cs`
* `<repo_root>/src/coreclr/tools/superpmi/managed/SuperPmi.Shared/MethodContext.Record.cs`
* `<repo_root>/src/coreclr/tools/superpmi/managed/SuperPmi.Shared/MethodContext.Dump.cs`
* `<repo_root>/src/coreclr/tools/superpmi/managed/SuperPmi.Shared/MethodContext.Replay.cs`
* `<repo_root>/src/coreclr/tools/superpmi/managed/superpmi/CorInfoImpl.SuperPmi.cs`
* `<repo_root>/src/coreclr/tools/superpmi/managed/superpmi-shim-collector/CorInfoImpl.Collector.cs`
* [ ] All TODO/UNREACHABLE markers remain for future functional implementation.
7 changes: 3 additions & 4 deletions docs/project/updating-jitinterface.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@ It's a good idea to choose an existing API that is similar to the one you want t
3) Open `src/coreclr/inc/corinfo.h` and add the new API in `ICorStaticInfo`
4) Open `src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs` and add the new API in `CorInfoImpl` class. If the implementation is not shared for NativeAOT and R2R, use `CorInfoImpl.RyuJit.cs` and `CorInfoImpl.ReadyToRun.cs` to implement the API.
5) Open `src/coreclr/vm/jitinterface.cpp` and add the CoreCLR-specific implementation
6) Open `lwmlist.h` and add a definition of "input-args" - "output-args" map. Either use the generic `DLD`-like structs or create new ones in `agnostic.h`
7) Open `src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h` and add the necessary recording, dumping, and replaying methods for the new API and then implement them in `methodcontext.cpp`
8) Update `enum mcPackets` in `src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h` to include an entry for the new API and bump the max value of the enum
9) Use the `rec*` and `rep*` methods in `src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp` and `src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp` accordingly
6) Open `src/coreclr/tools/superpmi/managed/SuperPmi.Shared/MethodContext.Maps.cs` and add a map of "input-args" to "output-args" for the new API (its entry in `s_maps`, its property, and its dump), with a new packet ID in `PacketId.cs`. Either use the generic `DLD`-like records or create new ones in `Agnostic.cs`
7) Add the recording, dumping, and replaying methods for the new API to `MethodContext.Record.cs`, `MethodContext.Dump.cs`, and `MethodContext.Replay.cs` in the same directory
8) Use the `rec*` and `rep*` methods in `src/coreclr/tools/superpmi/managed/superpmi-shim-collector/CorInfoImpl.Collector.cs` and `src/coreclr/tools/superpmi/managed/superpmi/CorInfoImpl.SuperPmi.cs` accordingly

## Adding a new JIT-VM API through an agent

Expand Down
4 changes: 4 additions & 0 deletions eng/Subsets.props
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,10 @@
<ProjectToBuild Include="$(CoreClrProjectRoot)tools\cdac-build-tool\cdac-build-tool.csproj" Category="clr" />
</ItemGroup>

<ItemGroup Condition="($(_subset.Contains('+clr.spmi+')) or $(_subset.Contains('+clr.native+'))) and '$(SdkToolsSupported)' == 'true' and '$(UseNativeAotForComponents)' == 'true'">
<ProjectToBuild Include="$(CoreClrProjectRoot)tools\superpmi\managed\superpmi-tools.proj" Category="clr" />
</ItemGroup>

<ItemGroup Condition="$(_subset.Contains('+clr.toolstests+'))">
<ProjectToBuild Include="$(CoreClrProjectRoot)tools\aot\ILCompiler.DependencyAnalysisFramework.Tests\ILCompiler.DependencyAnalysisFramework.Tests.csproj"
Test="true" Category="clr" Condition="'$(DotNetBuildSourceOnly)' != 'true'"/>
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

// Dummy change to excercise in CI

/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Comment thread
JulieLeeMSFT marked this conversation as resolved.
Resolved
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XX XX
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices;

using Internal.TypeSystem;

namespace System.CommandLine;

internal sealed class CommandLineException : Exception
{
public CommandLineException(string message) : base(message) { }
}

// The target architecture options, apart from the rest so that the tools that don't use System.CommandLine
// (the SuperPMI ones) can share them.
internal static partial class Helpers
{
public static TargetArchitecture GetTargetArchitecture(string token)
{
if (string.IsNullOrEmpty(token))
{
return RuntimeInformation.ProcessArchitecture switch
{
Architecture.X86 => TargetArchitecture.X86,
Architecture.X64 => TargetArchitecture.X64,
Architecture.Arm => TargetArchitecture.ARM,
Architecture.Arm64 => TargetArchitecture.ARM64,
Architecture.Wasm => TargetArchitecture.Wasm32,
Architecture.LoongArch64 => TargetArchitecture.LoongArch64,
Architecture.RiscV64 => TargetArchitecture.RiscV64,
_ => throw new NotImplementedException()
};
}

return TryGetTargetArchitecture(token) ?? throw new CommandLineException($"Target architecture '{token}' is not supported");
}

public static TargetArchitecture? TryGetTargetArchitecture(string token) => token.ToLowerInvariant() switch
{
"x86" => TargetArchitecture.X86,
"x64" or "amd64" => TargetArchitecture.X64,
"arm" or "armel" or "arm32" => TargetArchitecture.ARM,
"arm64" => TargetArchitecture.ARM64,
"wasm" or "wasm32" => TargetArchitecture.Wasm32,
"loongarch64" => TargetArchitecture.LoongArch64,
"riscv64" => TargetArchitecture.RiscV64,
_ => null
};
}
37 changes: 0 additions & 37 deletions src/coreclr/tools/Common/CommandLineHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@

namespace System.CommandLine
{
internal sealed class CommandLineException : Exception
{
public CommandLineException(string message) : base(message) { }
}

//
// Helpers for command line processing
//
Expand Down Expand Up @@ -93,38 +88,6 @@ public static TargetOS GetTargetOS(string token)
};
}

public static TargetArchitecture GetTargetArchitecture(string token)
{
if (string.IsNullOrEmpty(token))
{
return RuntimeInformation.ProcessArchitecture switch
{
Architecture.X86 => TargetArchitecture.X86,
Architecture.X64 => TargetArchitecture.X64,
Architecture.Arm => TargetArchitecture.ARM,
Architecture.Arm64 => TargetArchitecture.ARM64,
Architecture.Wasm => TargetArchitecture.Wasm32,
Architecture.LoongArch64 => TargetArchitecture.LoongArch64,
Architecture.RiscV64 => TargetArchitecture.RiscV64,
_ => throw new NotImplementedException()
};
}
else
{
return token.ToLowerInvariant() switch
{
"x86" => TargetArchitecture.X86,
"x64" => TargetArchitecture.X64,
"arm" or "armel" => TargetArchitecture.ARM,
"arm64" => TargetArchitecture.ARM64,
"wasm" => TargetArchitecture.Wasm32,
"loongarch64" => TargetArchitecture.LoongArch64,
"riscv64" => TargetArchitecture.RiscV64,
_ => throw new CommandLineException($"Target architecture '{token}' is not supported")
};
}
}

public static (TargetArchitecture, TargetOS, TargetAbi) GetTargetSpec(string targetArchitectureToken, string targetOSToken)
{
targetArchitectureToken = targetArchitectureToken?.ToLowerInvariant();
Expand Down
Loading
Loading