Skip to content

Commit 41b8bbf

Browse files
radekdoulikCopilot
andcommitted
[cDAC][wasm] Register platform GC-info decoding
Co-authored-by: Copilot App <[email protected]>
1 parent b9bafb9 commit 41b8bbf

4 files changed

Lines changed: 97 additions & 0 deletions

File tree

‎docs/design/datacontracts/GCInfo.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ This contract is for fetching information related to GCInfo associated with nati
44

55
The GCInfo contract has platform specific implementations as GCInfo differs per architecture. With the exception of x86, all platforms have a common encoding scheme with different encoding lengths and normalization functions for data. x86 uses an entirely different scheme which is partially supported by this contract.
66

7+
WebAssembly uses the common decoder with `Wasm32GcInfoEncoding` traits from
8+
`src/coreclr/inc/gcinfotypes.h`: code lengths/offsets and stack offsets are
9+
unscaled, the code-length encoding base is 6, and there is no fixed stack
10+
parameter scratch area. The platform encoding has no interruptible-range
11+
count in its fat header (`HAS_INTERRUPTIBLE_RANGES` is false).
12+
`DecodeInterpreterGCInfo` uses the separate interpreter traits even on
13+
WebAssembly, including its code-length encoding base of 8 and its
14+
interruptible-range count field.
15+
716
## APIs of contract
817

918
```csharp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.Diagnostics.DataContractReader.Contracts.GCInfoHelpers;
5+
6+
// Mirrors Wasm32GcInfoEncoding in src/coreclr/inc/gcinfotypes.h.
7+
internal sealed class WasmGCInfoTraits : IGCInfoTraits
8+
{
9+
public static uint DenormalizeStackBaseRegister(uint reg) => reg;
10+
public static uint DenormalizeCodeLength(uint len) => len;
11+
public static uint NormalizeCodeLength(uint len) => len;
12+
public static uint DenormalizeCodeOffset(uint offset) => offset;
13+
public static uint NormalizeCodeOffset(uint offset) => offset;
14+
public static int DenormalizeStackSlot(int value) => value;
15+
public static uint DenormalizeSizeOfStackArea(uint size) => size;
16+
17+
public static int GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE => 6;
18+
public static int GS_COOKIE_STACK_SLOT_ENCBASE => 6;
19+
public static int CODE_LENGTH_ENCBASE => 6;
20+
public static int STACK_BASE_REGISTER_ENCBASE => 3;
21+
public static int SIZE_OF_STACK_AREA_ENCBASE => 6;
22+
public static int SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE => 3;
23+
public static int SIZE_OF_EDIT_AND_CONTINUE_FIXED_STACK_FRAME_ENCBASE => 0;
24+
public static int REVERSE_PINVOKE_FRAME_ENCBASE => 6;
25+
public static int NUM_REGISTERS_ENCBASE => 3;
26+
public static int NUM_STACK_SLOTS_ENCBASE => 5;
27+
public static int NUM_UNTRACKED_SLOTS_ENCBASE => 5;
28+
public static int NORM_PROLOG_SIZE_ENCBASE => 4;
29+
public static int NORM_EPILOG_SIZE_ENCBASE => 3;
30+
public static int INTERRUPTIBLE_RANGE_DELTA1_ENCBASE => 5;
31+
public static int INTERRUPTIBLE_RANGE_DELTA2_ENCBASE => 5;
32+
public static int REGISTER_ENCBASE => 3;
33+
public static int REGISTER_DELTA_ENCBASE => 3;
34+
public static int STACK_SLOT_ENCBASE => 6;
35+
public static int STACK_SLOT_DELTA_ENCBASE => 4;
36+
public static int NUM_SAFE_POINTS_ENCBASE => 4;
37+
public static int NUM_INTERRUPTIBLE_RANGES_ENCBASE => 1;
38+
public static bool HAS_FIXED_STACK_PARAMETER_SCRATCH_AREA => false;
39+
public static bool HAS_INTERRUPTIBLE_RANGES => false;
40+
public static bool IsScratchRegister(uint regNum) => false;
41+
}

‎src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/CoreCLRContracts.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public static void Register(ContractRegistry registry)
6868
RuntimeInfoArchitecture.Arm => new GCInfo_1<ARMGCInfoTraits>(t),
6969
RuntimeInfoArchitecture.LoongArch64 => new GCInfo_1<LoongArch64GCInfoTraits>(t),
7070
RuntimeInfoArchitecture.RiscV64 => new GCInfo_1<RISCV64GCInfoTraits>(t),
71+
RuntimeInfoArchitecture.Wasm => new GCInfo_1<WasmGCInfoTraits>(t),
7172
_ => default(GCInfo),
7273
};
7374
});

‎src/native/managed/cdac/tests/UnitTests/StackWalkTests.cs‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,52 @@ namespace Microsoft.Diagnostics.DataContractReader.Tests;
1515

1616
public unsafe class StackWalkTests
1717
{
18+
[Theory]
19+
[InlineData(false, new byte[] { 0x04, 0x05, 0x21, 0x02 }, uint.MaxValue)]
20+
[InlineData(false, new byte[] { 0x81, 0x08, 0x0a, 0x30, 0x42, 0x04 }, 8u)]
21+
[InlineData(true, new byte[] { 0x04, 0x4a, 0x04 }, uint.MaxValue)]
22+
[InlineData(true, new byte[] { 0x81, 0x08, 0x84, 0x11, 0x22, 0x00 }, 8u)]
23+
public void GCInfo_Wasm_DecodesPlatformAndInterpreterFormats(
24+
bool interpreter, byte[] encoded, uint stackBaseRegister)
25+
{
26+
MockTarget.Architecture arch = new() { IsLittleEndian = true, Is64Bit = false };
27+
TestPlaceholderTarget.Builder builder = new(arch);
28+
// Length 129 and safe point 17 distinguish the platform's six-bit encoding
29+
// from the interpreter's eight-bit encoding and exercise header alignment.
30+
byte[] gcInfo = new byte[32];
31+
encoded.CopyTo(gcInfo, 0);
32+
builder.MemoryBuilder.AddHeapFragment(new MockMemorySpace.HeapFragment
33+
{
34+
Address = 0x1000,
35+
Data = gcInfo,
36+
Name = "Wasm GC info"
37+
});
38+
Mock<IRuntimeInfo> runtimeInfo = new();
39+
runtimeInfo.Setup(r => r.GetTargetArchitecture()).Returns(RuntimeInfoArchitecture.Wasm);
40+
IGCInfo contract = builder
41+
.AddMockContract(runtimeInfo)
42+
.AddContract<IGCInfo>(version: "c1")
43+
.Build().Contracts.GCInfo;
44+
IGCInfoHandle handle = interpreter
45+
? contract.DecodeInterpreterGCInfo(new TargetPointer(0x1000), 4)
46+
: contract.DecodePlatformSpecificGCInfo(new TargetPointer(0x1000), 4);
47+
48+
GCInfoHeader header = contract.GetHeader(handle);
49+
Assert.Equal(129u, contract.GetCodeLength(handle));
50+
Assert.Equal(129u, header.CodeSize);
51+
Assert.Equal(stackBaseRegister, header.StackBaseRegister);
52+
Assert.Equal(0u, header.SizeOfStackParameterArea);
53+
Assert.Equal(new uint[] { 17 }, contract.GetSafePoints(handle));
54+
Assert.Empty(contract.GetInterruptibleRanges(handle));
55+
Assert.Equal(interpreter, contract.TryGetGenericContextStorage(
56+
handle, GenericContextLoc.ThisPtr, 0, out GenericContextStorage storage));
57+
if (interpreter)
58+
{
59+
Assert.Equal(GenericContextStorageKind.InterpreterArgumentRelative, storage.Kind);
60+
Assert.Equal(0, storage.Offset);
61+
}
62+
}
63+
1864
[Fact]
1965
public void LoongArch64Unwind_EpilogReturn_DoesNotRepeatStackAdjustment()
2066
{

0 commit comments

Comments
 (0)