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
22 changes: 8 additions & 14 deletions src/coreclr/inc/clrnt.h
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ RtlVirtualUnwind(
#define UNW_FLAG_EHANDLER 0x1 /* filter handler */
#define UNW_FLAG_UHANDLER 0x2 /* unwind handler */

inline PEXCEPTION_ROUTINE
PEXCEPTION_ROUTINE
RtlVirtualUnwind (
_In_ DWORD HandlerType,
_In_ DWORD ImageBase,
Expand All @@ -516,25 +516,19 @@ RtlVirtualUnwind (
_Out_ PVOID *HandlerData,
_Out_ PDWORD EstablisherFrame,
__inout_opt PT_KNONVOLATILE_CONTEXT_POINTERS ContextPointers
)
{
PORTABILITY_ASSERT("The function RtlVirtualUnwind is not implemented on wasm");
return nullptr;
}
);

UINT32 DecodeULEB128AsU32(PTR_BYTE* ppData);

FORCEINLINE
ULONG
RtlpGetFunctionEndAddress (
_In_ PT_RUNTIME_FUNCTION FunctionEntry,
_In_ TADDR ImageBase
)
{
PORTABILITY_ASSERT("The function RtlpGetFunctionEndAddress is not implemented on wasm");
return 0;
}
);

#define RUNTIME_FUNCTION__BeginAddress(FunctionEntry) ((FunctionEntry)->BeginAddress)
#define RUNTIME_FUNCTION__SetBeginAddress(FunctionEntry,address) ((FunctionEntry)->BeginAddress = (address))
#define RUNTIME_FUNCTION__IsFunclet(FunctionEntry) ((FunctionEntry)->BeginAddress & 0x80000000)
#define RUNTIME_FUNCTION__BeginAddress(FunctionEntry) ((FunctionEntry)->BeginAddress & 0x7FFFFFFF)
#define RUNTIME_FUNCTION__SetBeginAddress(FunctionEntry,address) ((FunctionEntry)->BeginAddress = address)

#define RUNTIME_FUNCTION__EndAddress(FunctionEntry, ImageBase) (RtlpGetFunctionEndAddress(FunctionEntry, (ULONG64)(ImageBase)))

Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -1775,6 +1775,7 @@ struct FuncInfoDsc

jitstd::vector<WasmLocalsDecl>* funWasmLocalDecls;
unsigned funWasmFrameSize;
unsigned funWasmVirtualIPCount;
bool needsUnwindableFrame;
emitLocation* startLoc;
emitLocation* endLoc;
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3154,6 +3154,7 @@ PhaseStatus Compiler::fgCreateFunclets()
#endif
#ifdef TARGET_WASM
funcInfo[i].funWasmLocalDecls = nullptr;
funcInfo[i].funWasmVirtualIPCount = 10;
#endif
}
#endif
Expand Down
11 changes: 7 additions & 4 deletions src/coreclr/jit/unwindwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ void Compiler::unwindReserveFunc(FuncInfoDsc* func)
{
bool isFunclet = func->IsFunclet();
bool isColdCode = false;
ULONG encodedSize = emitter::SizeOfULEB128(func->funWasmFrameSize);
ULONG encodedSize = emitter::SizeOfULEB128(func->funWasmFrameSize)
+ emitter::SizeOfULEB128(func->funWasmVirtualIPCount);

eeReserveUnwindInfo(isFunclet, isColdCode, encodedSize);
}
Expand Down Expand Up @@ -104,11 +105,13 @@ void Compiler::unwindEmitFunc(FuncInfoDsc* func, void* pHotCode, void* pColdCode
//
pColdCode = nullptr;

// Unwind info is just the frame size.
// Record frame size with ULEB128 compression.
// Unwind info is the frame size followed by the virtual IP count,
// both encoded as ULEB128.
//
uint8_t buffer[5];
uint8_t buffer[10];
ULONG encodedSize = (ULONG)GetEmitter()->emitOutputULEB128(buffer, func->funWasmFrameSize);
encodedSize += (ULONG)GetEmitter()->emitOutputULEB128(buffer + encodedSize, func->funWasmVirtualIPCount);
// WASM-TODO, sum total of virtual IP size should be recorded into the GC info for the method as well.
assert(encodedSize <= sizeof(buffer));

eeAllocUnwindInfo((BYTE*)pHotCode, (BYTE*)pColdCode, startOffset, endOffset, encodedSize, (BYTE*)&buffer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@ or IMAGE_REL_BASED_THUMB_BRANCH24 or IMAGE_REL_BASED_THUMB_MOV32_PCREL &&
{
fixed (byte* pData = data)
{
Relocation.WriteValue(relocType, (void*)pData, definedSymbol.Size);
long adjustedAddend = addend + Relocation.ReadValue(relocType, (void*)pData);
Relocation.WriteValue(relocType, (void*)pData, definedSymbol.Size + adjustedAddend);
}
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -895,31 +895,27 @@ private unsafe void ResolveRelocations(int sectionIndex, MemoryStream sectionStr
break;
}

// TODO-Wasm: None of the IMAGE_REL type relocs should occur in Wasm
// code, and we should add asserts for this once we've updated the necessary
// dependency nodes to emit the proper reloc type on Wasm.
case RelocType.IMAGE_REL_BASED_ABSOLUTE:
// No action required
break;

case RelocType.IMAGE_REL_BASED_DIR64:
case RelocType.IMAGE_REL_BASED_HIGHLOW:
// Debug.Assert(betweenWebcilSections);
// This is an ImageBase-relative value in PE, but our image base
// for Webcil is virtual address 0
Debug.Assert(symbolWebcilSection != null);
Relocation.WriteValue(reloc.Type, pData, virtualSymbolImageOffset + 0 + addend);
break;
case RelocType.IMAGE_REL_BASED_ADDR32NB:
// Debug.Assert(betweenWebcilSections);
Debug.Assert(symbolWebcilSection != null);
Relocation.WriteValue(reloc.Type, pData, virtualSymbolImageOffset + addend);
break;
case RelocType.IMAGE_REL_BASED_REL32:
case RelocType.IMAGE_REL_BASED_RELPTR32:
// Debug.Assert(betweenWebcilSections);
Debug.Assert(symbolWebcilSection != null);
Relocation.WriteValue(reloc.Type, pData, virtualSymbolImageOffset - (virtualRelocOffset + relocLength) + addend);
break;
case RelocType.IMAGE_REL_FILE_ABSOLUTE:
// Debug.Assert(betweenWebcilSections && symbolWebcilSection != null);
Debug.Assert(symbolWebcilSection != null);
long fileOffset = symbolWebcilSection.Header.PointerToRawData + definedSymbol.Value;
Relocation.WriteValue(reloc.Type, pData, fileOffset + addend);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,20 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
// Add the symbol representing this object node
exceptionInfoLookupBuilder.AddSymbol(this);

bool isWasm = factory.Target.Architecture == TargetArchitecture.Wasm32;

// First, emit the actual EH records in sequence and store map from methods to the EH record symbols
for (int index = 0; index < _methodNodes.Count; index++)
{
exceptionInfoLookupBuilder.EmitReloc(_methodNodes[index], RelocType.IMAGE_REL_BASED_ADDR32NB);
if (isWasm)
{
// For WASM, we use a virtual IP in the table here instead of a method pointer, so that the runtime can perform a binary search based on the virtual IP. The virtual IP is emitted as a plain u32 (not a reloc).
exceptionInfoLookupBuilder.EmitUInt(factory.RuntimeFunctionsTable.GetWasmVirtualIP(_methodNodes[index], 0));
}
else
{
exceptionInfoLookupBuilder.EmitReloc(_methodNodes[index], RelocType.IMAGE_REL_BASED_ADDR32NB);
}
exceptionInfoLookupBuilder.EmitReloc(_ehInfoNode, RelocType.IMAGE_REL_BASED_ADDR32NB, _ehInfoOffsets[index]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,8 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
builder.EmitReloc(item.StartSymbol, RelocType.IMAGE_REL_BASED_ADDR32NB);

// The header entry for the runtime functions table should not include the 4 byte 0xffffffff sentinel
// value in the covered range.
int delta = item.Id == ReadyToRunSectionType.RuntimeFunctions ? RuntimeFunctionsTableNode.SentinelSizeAdjustment : 0;
// value in the covered range. For WASM, also excludes the trailing min table index.
int delta = item.Id == ReadyToRunSectionType.RuntimeFunctions ? factory.RuntimeFunctionsTable.SentinelSizeAdjustment : 0;
builder.EmitReloc(item.StartSymbol, RelocType.IMAGE_REL_SYMBOL_SIZE, delta);

count++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class RuntimeFunctionsTableNode : HeaderTableNode
{
private List<MethodWithGCInfo> _methodNodes;
private Dictionary<MethodWithGCInfo, int> _insertedMethodNodes;
private Dictionary<ValueTuple<MethodWithGCInfo, int>, uint> _methodToVirtualIP;
private readonly NodeFactory _nodeFactory;
private int _tableSize = -1;

Expand Down Expand Up @@ -61,6 +62,80 @@ private void LayoutRuntimeFunctions()
}
}

public uint GetWasmVirtualIP(MethodWithGCInfo lookupMethod, int lookupFrameIndex)
{
bool isWasm = _nodeFactory.Target.Architecture == TargetArchitecture.Wasm32;

if (!isWasm)
{
// For non-WASM targets, virtual IPs are not used, so throw
throw new InvalidOperationException("Virtual IPs are not used on non-WASM targets.");
}

if (_methodToVirtualIP != null)
{
return _methodToVirtualIP[(lookupMethod, lookupFrameIndex)];
}

if (_methodNodes == null)
{
LayoutRuntimeFunctions();
}

Dictionary<ValueTuple<MethodWithGCInfo, int>, uint> methodToVirtualIP = new ();

// For WASM, track virtual IP index for each RUNTIME_FUNCTION entry
uint currentVirtualIP = 0;

foreach (MethodWithGCInfo method in _methodNodes)
{
int[] funcletOffsets = method.GCInfoNode.CalculateFuncletOffsets(_nodeFactory);

for (int frameIndex = 0; frameIndex < method.FrameInfos.Length; frameIndex++)
{
FrameInfo frameInfo = method.FrameInfos[frameIndex];
methodToVirtualIP.Add((method, frameIndex), currentVirtualIP);

// Advance the virtual IP by the number of virtual IPs for this frame
uint virtualIPCount = GetVirtualIPCountFromUnwindBlob(frameInfo.BlobData);
currentVirtualIP += virtualIPCount;
Comment on lines +90 to +101
}
}

_methodToVirtualIP = methodToVirtualIP;
return _methodToVirtualIP[(lookupMethod, lookupFrameIndex)];
}

/// <summary>
/// Decode a ULEB128 value from a byte array starting at the given offset.
/// Returns the decoded value and advances the offset past the encoded bytes.
/// </summary>
private static uint DecodeULEB128(byte[] data, ref int offset)
{
uint result = 0;
int shift = 0;
while (offset < data.Length)
{
byte b = data[offset++];
result |= (uint)(b & 0x7F) << shift;
if ((b & 0x80) == 0)
break;
shift += 7;
}
return result;
}

/// <summary>
/// Read the virtual IP count from a WASM unwind blob.
/// The blob format is: ULEB128(frameSize) followed by ULEB128(virtualIPCount).
/// </summary>
private static uint GetVirtualIPCountFromUnwindBlob(byte[] blobData)
{
int offset = 0;
DecodeULEB128(blobData, ref offset); // skip frame size
return DecodeULEB128(blobData, ref offset) * 2; // Multiply by 2 to force all virtual IPs to be an even number.
Comment on lines +131 to +136
}

public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
if (relocsOnly)
Expand All @@ -78,6 +153,8 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
uint runtimeFunctionIndex = 0;
List<uint> mapping = new List<uint>();

bool isWasm = _nodeFactory.Target.Architecture == TargetArchitecture.Wasm32;

for (int cold = 0; cold < 2; cold++)
{
foreach (MethodWithGCInfo method in _methodNodes)
Expand Down Expand Up @@ -124,9 +201,11 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
symbol = method;
}

if (_nodeFactory.Target.Architecture == TargetArchitecture.Wasm32)
if (isWasm)
{
runtimeFunctionsBuilder.EmitReloc(symbol, RelocType.WASM_TABLE_INDEX_I32, frameIndex);
// Emit the virtual IP index as a plain u32 (not a reloc)
// Set high bit to indicate that the RUNTIME_FUNCTION is a funclet
runtimeFunctionsBuilder.EmitUInt(GetWasmVirtualIP(method, frameIndex) | (frameIndex != 0 ? 0x80000000 : 0));
}
else
{
Expand Down Expand Up @@ -157,14 +236,28 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
// Emit sentinel entry
runtimeFunctionsBuilder.EmitUInt(~0u);

if (isWasm)
{
// After sentinel, emit a WASM_TABLE_INDEX_I32 reloc pointing to the first
// compiled method. This records the min function table index in the file.
if (_methodNodes.Count > 0)
{
runtimeFunctionsBuilder.EmitReloc(_methodNodes[0], RelocType.WASM_TABLE_INDEX_I32, delta: 0);
}
else
{
runtimeFunctionsBuilder.EmitUInt(0);
}
}

_tableSize = runtimeFunctionsBuilder.CountBytes;
return runtimeFunctionsBuilder.ToObjectData();
}

/// <summary>
/// Returns the runtime functions table size and excludes the 4 byte sentinel entry at the end (used by
/// the runtime in NativeUnwindInfoLookupTable::LookupUnwindInfoForMethod) so that it's not treated as
/// part of the table itself.
/// part of the table itself. For WASM, also excludes the trailing 4-byte min table index.
/// </summary>
public int TableSizeExcludingSentinel
{
Expand All @@ -179,6 +272,7 @@ public int TableSizeExcludingSentinel

public override int ClassCode => (int)ObjectNodeOrder.RuntimeFunctionsTableNode;

internal const int SentinelSizeAdjustment = -4;
internal int SentinelSizeAdjustment =>
_nodeFactory.Target.Architecture == TargetArchitecture.Wasm32 ? -8 : -4;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -870,9 +870,12 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase<NodeFactory> graph, I
RuntimeFunctionsGCInfo = new RuntimeFunctionsGCInfoNode();
graph.AddRoot(RuntimeFunctionsGCInfo, "GC info is always generated");

DelayLoadMethodCallThunks = new SymbolNodeRange("DelayLoadMethodCallThunkNodeRange");
graph.AddRoot(DelayLoadMethodCallThunks, "DelayLoadMethodCallThunks header entry is always generated");
Header.Add(Internal.Runtime.ReadyToRunSectionType.DelayLoadMethodCallThunks, DelayLoadMethodCallThunks);
if (!Target.IsWasm)
{
DelayLoadMethodCallThunks = new SymbolNodeRange("DelayLoadMethodCallThunkNodeRange");
graph.AddRoot(DelayLoadMethodCallThunks, "DelayLoadMethodCallThunks header entry is always generated");
Header.Add(Internal.Runtime.ReadyToRunSectionType.DelayLoadMethodCallThunks, DelayLoadMethodCallThunks);
}

ExceptionInfoLookupTableNode exceptionInfoLookupTableNode = new ExceptionInfoLookupTableNode(this);
Header.Add(Internal.Runtime.ReadyToRunSectionType.ExceptionInfo, exceptionInfoLookupTableNode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
return "NYI";
default:
throw new NotImplementedException($"No implementation for machine type {machine}.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,38 @@ 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;
// SIZE_OF_RETURN_KIND_IN_SLIM_HEADER = 2;
// SIZE_OF_RETURN_KIND_IN_FAT_HEADER = 2;
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;
// NORM_CODE_OFFSET_DELTA_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;
// NUM_EH_CLAUSES_ENCBASE = 2;
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;
Expand Down
Loading
Loading