|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// Provides an abstraction over platform specific calling conventions (specifically, the calling convention
// utilized by the JIT on that platform). The caller enumerates each argument of a signature in turn, and is
// provided with information mapping that argument into registers and/or stack locations.
using System;
using System.Diagnostics;
using Internal.TypeSystem;
using Internal.CorConstants;
using Internal.JitInterface;
namespace ILCompiler.DependencyAnalysis.ReadyToRun
{
internal abstract class TransitionBlock
{
public static TransitionBlock FromTarget(TargetDetails target)
{
switch (target.Architecture)
{
case TargetArchitecture.X86:
return X86TransitionBlock.Instance;
case TargetArchitecture.X64:
return target.OperatingSystem == TargetOS.Windows ?
X64WindowsTransitionBlock.Instance :
X64UnixTransitionBlock.Instance;
case TargetArchitecture.ARM:
if (target.Abi == TargetAbi.NativeAotArmel)
{
return Arm32ElTransitionBlock.Instance;
}
else
{
return Arm32TransitionBlock.Instance;
}
case TargetArchitecture.ARM64:
return target.IsApplePlatform ?
AppleArm64TransitionBlock.Instance :
Arm64TransitionBlock.Instance;
case TargetArchitecture.LoongArch64:
return LoongArch64TransitionBlock.Instance;
case TargetArchitecture.RiscV64:
return RiscV64TransitionBlock.Instance;
case TargetArchitecture.Wasm32:
return Wasm32TransitionBlock.Instance;
default:
throw new NotImplementedException(target.Architecture.ToString());
}
}
public const int MaxArgSize = 0xFFFFFF;
// Unix AMD64 ABI: Special offset value to represent struct passed in registers. Such a struct can span both
// general purpose and floating point registers, so it can have two different offsets.
public const int StructInRegsOffset = -2;
public abstract TargetArchitecture Architecture { get; }
public bool IsX86 => Architecture == TargetArchitecture.X86;
public bool IsX64 => Architecture == TargetArchitecture.X64;
public bool IsARM => Architecture == TargetArchitecture.ARM;
public bool IsARM64 => Architecture == TargetArchitecture.ARM64;
public bool IsLoongArch64 => Architecture == TargetArchitecture.LoongArch64;
public bool IsRiscV64 => Architecture == TargetArchitecture.RiscV64;
public bool IsWasm32 => Architecture == TargetArchitecture.Wasm32;
/// <summary>
/// This property is only overridden in AMD64 Unix variant of the transition block.
/// </summary>
public virtual bool IsX64UnixABI => false;
public virtual bool IsArmelABI => false;
public virtual bool IsArmhfABI => false;
public virtual bool IsAppleArm64ABI => false;
public abstract int PointerSize { get; }
public abstract int FloatRegisterSize { get; }
public abstract int StackElemSize(int parmSize, bool isValueType = false, bool isFloatHfa = false);
public abstract int NumArgumentRegisters { get; }
public int SizeOfArgumentRegisters => NumArgumentRegisters * PointerSize;
public abstract int NumCalleeSavedRegisters { get; }
public int SizeOfCalleeSavedRegisters => NumCalleeSavedRegisters * PointerSize;
public abstract int SizeOfTransitionBlock { get; }
public abstract int OffsetOfArgumentRegisters { get; }
/// <summary>
/// The offset of the first slot in a GC ref map. Overridden on ARM64 to return the offset of the X8 register.
/// </summary>
public virtual int OffsetOfFirstGCRefMapSlot => OffsetOfArgumentRegisters;
public abstract int OffsetOfFloatArgumentRegisters { get; }
public bool IsFloatArgumentRegisterOffset(int offset) => offset < 0;
public abstract int EnregisteredParamTypeMaxSize { get; }
public abstract int EnregisteredReturnTypeIntegerMaxSize { get; }
public abstract int GetRetBuffArgOffset(bool hasThis);
/// <summary>
/// Only overridden on ARM64 to return false.
/// </summary>
public virtual bool IsRetBuffPassedAsFirstArg => true;
/// <summary>
/// Default implementation of ThisOffset; X86TransitionBlock provides a slightly different implementation.
/// </summary>
public virtual int ThisOffset { get { return OffsetOfArgumentRegisters; } }
/// <summary>
/// Recalculate pos in GC ref map to actual offset. This is the default implementation for all architectures
/// except for X86 where it's overridden to supply a more complex algorithm.
/// </summary>
public virtual int OffsetFromGCRefMapPos(int pos)
{
return OffsetOfFirstGCRefMapSlot + pos * PointerSize;
}
/// <summary>
/// The transition block should define everything pushed by callee. The code assumes in number of places that
/// end of the transition block is caller's stack pointer.
/// </summary>
public int OffsetOfArgs => SizeOfTransitionBlock;
public bool IsStackArgumentOffset(int offset)
{
int ofsArgRegs = OffsetOfArgumentRegisters;
return offset >= (int)(ofsArgRegs + SizeOfArgumentRegisters);
}
public bool IsArgumentRegisterOffset(int offset)
{
Debug.Assert(!IsX64UnixABI || offset != StructInRegsOffset);
int ofsArgRegs = OffsetOfArgumentRegisters;
return offset >= ofsArgRegs && offset < (int)(ofsArgRegs + SizeOfArgumentRegisters);
}
public int GetArgumentIndexFromOffset(int offset)
{
Debug.Assert(!IsX86);
offset -= OffsetOfArgumentRegisters;
Debug.Assert((offset % PointerSize) == 0);
return offset / PointerSize;
}
public int GetStackArgumentIndexFromOffset(int offset)
{
Debug.Assert(!IsX86);
return (offset - OffsetOfArgs) / PointerSize;
}
public int GetStackArgumentByteIndexFromOffset(int offset)
{
Debug.Assert(!IsX86);
return offset - OffsetOfArgs;
}
/// <summary>
/// X86: Indicates whether an argument is to be put in a register using the
/// default IL calling convention. This should be called on each parameter
/// in the order it appears in the call signature. For a non-static meethod,
/// this function should also be called once for the "this" argument, prior
/// to calling it for the "real" arguments. Pass in a typ of ELEMENT_TYPE_CLASS.
/// </summary>
/// <param name="pNumRegistersUsed">
/// keeps track of the number of argument registers assigned previously.
/// The caller should initialize this variable to 0 - then each call will update it.
/// </param>
/// <param name="typ">parameter type</param>
/// <param name="thArgType">Exact type info is used to check struct enregistration</param>
public bool IsArgumentInRegister(ref int pNumRegistersUsed, CorElementType typ, TypeHandle thArgType)
{
Debug.Assert(IsX86);
// LIMITED_METHOD_CONTRACT;
if (pNumRegistersUsed < NumArgumentRegisters)
{
switch (typ)
{
case CorElementType.ELEMENT_TYPE_BOOLEAN:
case CorElementType.ELEMENT_TYPE_CHAR:
case CorElementType.ELEMENT_TYPE_I1:
case CorElementType.ELEMENT_TYPE_U1:
case CorElementType.ELEMENT_TYPE_I2:
case CorElementType.ELEMENT_TYPE_U2:
case CorElementType.ELEMENT_TYPE_I4:
case CorElementType.ELEMENT_TYPE_U4:
case CorElementType.ELEMENT_TYPE_STRING:
case CorElementType.ELEMENT_TYPE_PTR:
case CorElementType.ELEMENT_TYPE_BYREF:
case CorElementType.ELEMENT_TYPE_CLASS:
case CorElementType.ELEMENT_TYPE_ARRAY:
case CorElementType.ELEMENT_TYPE_I:
case CorElementType.ELEMENT_TYPE_U:
case CorElementType.ELEMENT_TYPE_FNPTR:
case CorElementType.ELEMENT_TYPE_OBJECT:
case CorElementType.ELEMENT_TYPE_SZARRAY:
pNumRegistersUsed++;
return true;
case CorElementType.ELEMENT_TYPE_VALUETYPE:
if (IsTrivialPointerSizedStruct(thArgType))
{
pNumRegistersUsed++;
return true;
}
break;
}
}
return false;
}
private bool IsTrivialPointerSizedStruct(TypeHandle thArgType)
{
Debug.Assert(IsX86);
Debug.Assert(thArgType.IsValueType());
if (thArgType.GetSize() != 4)
{
// Type does not have trivial layout or has the wrong size.
return false;
}
TypeDesc typeOfEmbeddedField = null;
foreach (var field in thArgType.GetRuntimeTypeHandle().GetFields())
{
if (field.IsStatic)
continue;
if (typeOfEmbeddedField != null)
{
// Type has more than one instance field
return false;
}
typeOfEmbeddedField = field.FieldType;
}
if ((typeOfEmbeddedField != null) && ((typeOfEmbeddedField.IsValueType) || (typeOfEmbeddedField.IsPointer)))
{
switch (typeOfEmbeddedField.UnderlyingType.Category)
{
case TypeFlags.IntPtr:
case TypeFlags.UIntPtr:
case TypeFlags.Int32:
case TypeFlags.UInt32:
case TypeFlags.Pointer:
return true;
case TypeFlags.ValueType:
return IsTrivialPointerSizedStruct(new TypeHandle(typeOfEmbeddedField));
}
}
return false;
}
/// <summary>
/// This overload should only be used in AMD64-specific code only.
/// </summary>
/// <param name="size"></param>
/// <returns></returns>
public bool IsArgPassedByRef(int size)
{
Debug.Assert(IsX64);
// LIMITED_METHOD_CONTRACT;
// If the size is bigger than ENREGISTERED_PARAM_TYPE_MAXSIZE, or if the size is NOT a power of 2, then
// the argument is passed by reference.
return (size > EnregisteredParamTypeMaxSize) || ((size & (size - 1)) != 0);
}
/// <summary>
/// Check whether an arg is automatically switched to passing by reference.
/// Note that this overload does not handle varargs. This method only works for
/// valuetypes - true value types, primitives, enums and TypedReference.
/// The method is only overridden to do something meaningful on X64, ARM64 and WASM.
/// </summary>
/// <param name="th">Type to analyze</param>
public virtual bool IsArgPassedByRef(TypeHandle th)
{
throw new NotImplementedException(Architecture.ToString());
}
/// <summary>
/// This overload should be used for varargs only. The default implementation
/// is only overridden on X64.
/// </summary>
/// <param name="size">Byte size of the argument</param>
public virtual bool IsVarArgPassedByRef(int size)
{
return size > EnregisteredParamTypeMaxSize;
}
public void ComputeReturnValueTreatment(CorElementType type, TypeHandle thRetType, bool isVarArgMethod, out bool usesRetBuffer, out uint fpReturnSize, out uint returnedFpFieldOffset1st, out uint returnedFpFieldOffset2nd)
{
usesRetBuffer = false;
fpReturnSize = 0;
returnedFpFieldOffset1st = 0;
returnedFpFieldOffset2nd = 0;
switch (type)
{
case CorElementType.ELEMENT_TYPE_TYPEDBYREF:
throw new NotSupportedException();
case CorElementType.ELEMENT_TYPE_R4:
if (IsRiscV64 || IsLoongArch64)
{
fpReturnSize = (uint)FpStruct.OnlyOne | (2 << (int)FpStruct.PosSizeShift1st);
}
else if (!IsArmelABI)
{
fpReturnSize = sizeof(float);
}
break;
case CorElementType.ELEMENT_TYPE_R8:
if (IsRiscV64 || IsLoongArch64)
{
fpReturnSize = (uint)FpStruct.OnlyOne | (3 << (int)FpStruct.PosSizeShift1st);
}
else if (!IsArmelABI)
{
fpReturnSize = sizeof(double);
}
break;
case CorElementType.ELEMENT_TYPE_VALUETYPE:
{
Debug.Assert(!thRetType.IsNull() && thRetType.IsValueType());
if ((Architecture == TargetArchitecture.X64) && IsX64UnixABI)
{
SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR descriptor;
SystemVStructClassificator.GetSystemVAmd64PassStructInRegisterDescriptor(thRetType.GetRuntimeTypeHandle(), out descriptor);
if (descriptor.passedInRegisters)
{
if (descriptor.eightByteCount == 1)
{
if (descriptor.eightByteClassifications0 == SystemVClassificationType.SystemVClassificationTypeSSE)
{
// Structs occupying just one eightbyte are treated as int / double
fpReturnSize = sizeof(double);
}
}
else
{
// Size of the struct is 16 bytes
fpReturnSize = 16;
// The lowest two bits of the size encode the order of the int and SSE fields
if (descriptor.eightByteClassifications0 == SystemVClassificationType.SystemVClassificationTypeSSE)
{
fpReturnSize += 1;
}
if (descriptor.eightByteClassifications0 == SystemVClassificationType.SystemVClassificationTypeSSE)
{
fpReturnSize += 2;
}
}
break;
}
}
else
{
if (thRetType.IsHomogeneousAggregate() && !isVarArgMethod)
{
int haElementSize = thRetType.GetHomogeneousAggregateElementSize();
fpReturnSize = 4 * (uint)haElementSize;
break;
}
uint size = (uint)thRetType.GetSize();
if (IsX86 || IsX64)
{
// Return value types of size which are not powers of 2 using a RetBuffArg
if ((size & (size - 1)) != 0)
{
usesRetBuffer = true;
break;
}
}
if (size <= EnregisteredReturnTypeIntegerMaxSize)
{
if (IsLoongArch64 || IsRiscV64)
{
FpStructInRegistersInfo info = RiscVLoongArch64FpStruct.GetFpStructInRegistersInfo(
thRetType.GetRuntimeTypeHandle(), Architecture);
fpReturnSize = (uint)info.flags;
returnedFpFieldOffset1st = info.offset1st;
returnedFpFieldOffset2nd = info.offset2nd;
}
break;
}
}
}
// Value types are returned using return buffer by default
usesRetBuffer = true;
break;
default:
break;
}
}
public static int ALIGN_UP(int input, int align_to)
{
return (input + (align_to - 1)) & ~(align_to - 1);
}
public const int InvalidOffset = -1;
public sealed class X86Constants
{
public const int OffsetOfEcx = 1 * sizeof(int);
public const int OffsetOfEdx = 0 * sizeof(int);
}
private sealed class X86TransitionBlock : TransitionBlock
{
public static TransitionBlock Instance = new X86TransitionBlock();
public override TargetArchitecture Architecture => TargetArchitecture.X86;
public override int PointerSize => 4;
public override int FloatRegisterSize => throw new NotImplementedException();
public override int NumArgumentRegisters => 2;
public override int NumCalleeSavedRegisters => 4;
// Argument registers, callee-save registers, return address
public override int SizeOfTransitionBlock => SizeOfArgumentRegisters + SizeOfCalleeSavedRegisters + PointerSize;
public override int OffsetOfArgumentRegisters => 0;
// CALLDESCR_FPARGREGS is not set for X86
public override int OffsetOfFloatArgumentRegisters => 0;
// offsetof(ArgumentRegisters.ECX)
public override int ThisOffset => X86Constants.OffsetOfEcx;
public override int EnregisteredParamTypeMaxSize => 0;
public override int EnregisteredReturnTypeIntegerMaxSize => 4;
public override int OffsetFromGCRefMapPos(int pos)
{
if (pos < NumArgumentRegisters)
{
return OffsetOfArgumentRegisters + SizeOfArgumentRegisters - (pos + 1) * PointerSize;
}
else
{
return OffsetOfArgs + (pos - NumArgumentRegisters) * PointerSize;
}
}
public override bool IsArgPassedByRef(TypeHandle th) => false;
/// <summary>
/// x86 is special as always
/// </summary>
public override int GetRetBuffArgOffset(bool hasThis)
{
return hasThis ? X86Constants.OffsetOfEdx : X86Constants.OffsetOfEcx;
}
public override int StackElemSize(int parmSize, bool isValueType = false, bool isFloatHfa = false)
{
int stackSlotSize = 4;
return ALIGN_UP(parmSize, stackSlotSize);
}
}
public const int SizeOfM128A = 16;
/// <summary>
/// X64 properties common to Windows and Unix ABI.
/// </summary>
internal abstract class X64TransitionBlock : TransitionBlock
{
public override TargetArchitecture Architecture => TargetArchitecture.X64;
public override int PointerSize => 8;
public override int FloatRegisterSize => 16;
public override bool IsArgPassedByRef(TypeHandle th)
{
Debug.Assert(!th.IsNull());
Debug.Assert(th.IsValueType());
return IsArgPassedByRef((int)th.GetSize());
}
public override bool IsVarArgPassedByRef(int size)
{
return IsArgPassedByRef(size);
}
public override int GetRetBuffArgOffset(bool hasThis) => OffsetOfArgumentRegisters + (hasThis ? PointerSize : 0);
public sealed override int StackElemSize(int parmSize, bool isValueType = false, bool isFloatHfa = false)
{
int stackSlotSize = 8;
return ALIGN_UP(parmSize, stackSlotSize);
}
}
private sealed class X64WindowsTransitionBlock : X64TransitionBlock
{
public static TransitionBlock Instance = new X64WindowsTransitionBlock();
// RCX, RDX, R8, R9
public override int NumArgumentRegisters => 4;
// RDI, RSI, RBX, RBP, R12, R13, R14, R15
public override int NumCalleeSavedRegisters => 8;
// Callee-saved registers, return address
public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + PointerSize;
public override int OffsetOfArgumentRegisters => SizeOfTransitionBlock;
// CALLDESCR_FPARGREGS is not set for Amd64 on
public override int OffsetOfFloatArgumentRegisters => 0;
public override int EnregisteredParamTypeMaxSize => 8;
public override int EnregisteredReturnTypeIntegerMaxSize => 8;
}
internal sealed class X64UnixTransitionBlock : X64TransitionBlock
{
public static readonly TransitionBlock Instance = new X64UnixTransitionBlock();
public override bool IsX64UnixABI => true;
public const int NUM_FLOAT_ARGUMENT_REGISTERS = 8;
// RDI, RSI, RDX, RCX, R8, R9
public override int NumArgumentRegisters => 6;
// R12, R13, R14, R15, RBX, RBP
public override int NumCalleeSavedRegisters => 6;
// Argument registers, callee-saved registers, return address
public override int SizeOfTransitionBlock => SizeOfArgumentRegisters + SizeOfCalleeSavedRegisters + PointerSize;
public override int OffsetOfArgumentRegisters => 0;
public override int OffsetOfFloatArgumentRegisters => SizeOfM128A * NUM_FLOAT_ARGUMENT_REGISTERS;
public override int EnregisteredParamTypeMaxSize => 16;
public override int EnregisteredReturnTypeIntegerMaxSize => 16;
public override bool IsArgPassedByRef(TypeHandle th) => false;
}
private class Arm32TransitionBlock : TransitionBlock
{
public static TransitionBlock Instance = new Arm32TransitionBlock();
public sealed override TargetArchitecture Architecture => TargetArchitecture.ARM;
public sealed override int PointerSize => 4;
public override int FloatRegisterSize => 4;
// R0, R1, R2, R3
public sealed override int NumArgumentRegisters => 4;
// R4, R5, R6, R7, R8, R9, R10, R11, R14
public sealed override int NumCalleeSavedRegisters => 9;
// Callee-saves, argument registers
public sealed override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + SizeOfArgumentRegisters;
public sealed override int OffsetOfArgumentRegisters => SizeOfCalleeSavedRegisters;
// D0..D7
public sealed override int OffsetOfFloatArgumentRegisters => 8 * sizeof(double) + PointerSize;
public sealed override int EnregisteredParamTypeMaxSize => 0;
public sealed override int EnregisteredReturnTypeIntegerMaxSize => 4;
public override bool IsArmhfABI => true;
public sealed override bool IsArgPassedByRef(TypeHandle th) => false;
public sealed override int GetRetBuffArgOffset(bool hasThis) => OffsetOfArgumentRegisters + (hasThis ? PointerSize : 0);
public sealed override int StackElemSize(int parmSize, bool isValueType = false, bool isFloatHfa = false)
{
int stackSlotSize = 4;
return ALIGN_UP(parmSize, stackSlotSize);
}
}
private class Arm32ElTransitionBlock : Arm32TransitionBlock
{
public new static TransitionBlock Instance = new Arm32ElTransitionBlock();
public override bool IsArmhfABI => false;
public override bool IsArmelABI => true;
}
private class Arm64TransitionBlock : TransitionBlock
{
public static TransitionBlock Instance = new Arm64TransitionBlock();
public override TargetArchitecture Architecture => TargetArchitecture.ARM64;
public override int PointerSize => 8;
public override int FloatRegisterSize => 16;
// X0 .. X7
public override int NumArgumentRegisters => 8;
// X29, X30, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28
public override int NumCalleeSavedRegisters => 12;
// Callee-saves, padding, m_x8RetBuffReg, argument registers
public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + 2 * PointerSize + SizeOfArgumentRegisters;
public override int OffsetOfArgumentRegisters => SizeOfCalleeSavedRegisters + 2 * PointerSize;
private int OffsetOfX8Register => OffsetOfArgumentRegisters - PointerSize;
public override int OffsetOfFirstGCRefMapSlot => OffsetOfX8Register;
// D0..D7
public override int OffsetOfFloatArgumentRegisters => 8 * sizeof(double) + PointerSize;
public override int EnregisteredParamTypeMaxSize => 16;
public override int EnregisteredReturnTypeIntegerMaxSize => 16;
public override bool IsArgPassedByRef(TypeHandle th)
{
Debug.Assert(!th.IsNull());
Debug.Assert(th.IsValueType());
// Composites greater than 16 bytes are passed by reference
return (th.GetSize() > EnregisteredParamTypeMaxSize) && !th.IsHomogeneousAggregate();
}
public override int GetRetBuffArgOffset(bool hasThis) => OffsetOfX8Register;
public override bool IsRetBuffPassedAsFirstArg => false;
public override int StackElemSize(int parmSize, bool isValueType = false, bool isFloatHfa = false)
{
int stackSlotSize = 8;
return ALIGN_UP(parmSize, stackSlotSize);
}
}
private sealed class AppleArm64TransitionBlock : Arm64TransitionBlock
{
public new static TransitionBlock Instance = new AppleArm64TransitionBlock();
public override bool IsAppleArm64ABI => true;
public sealed override int StackElemSize(int parmSize, bool isValueType = false, bool isFloatHfa = false)
{
if (!isValueType)
{
// The primitive types' sizes are expected to be powers of 2.
Debug.Assert((parmSize & (parmSize - 1)) == 0);
// No padding/alignment for primitive types.
return parmSize;
}
if (isFloatHfa)
{
Debug.Assert((parmSize % 4) == 0);
// float hfa is not considered a struct type and passed with 4-byte alignment.
return parmSize;
}
return base.StackElemSize(parmSize, isValueType, isFloatHfa);
}
}
private class LoongArch64TransitionBlock : TransitionBlock
{
public static TransitionBlock Instance = new LoongArch64TransitionBlock();
public override TargetArchitecture Architecture => TargetArchitecture.LoongArch64;
public override int PointerSize => 8;
public override int FloatRegisterSize => 8; // TODO: for SIMD.
// R4(=A0) .. R11(=A7)
public override int NumArgumentRegisters => 8;
// fp=R22,ra=R1,s0-s8(R23-R31),tp=R2
public override int NumCalleeSavedRegisters => 12;
// Callee-saves, argument registers
public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + SizeOfArgumentRegisters;
public override int OffsetOfFirstGCRefMapSlot => SizeOfCalleeSavedRegisters;
public override int OffsetOfArgumentRegisters => OffsetOfFirstGCRefMapSlot;
// F0..F7
public override int OffsetOfFloatArgumentRegisters => 8 * sizeof(double);
public override int EnregisteredParamTypeMaxSize => 16;
public override int EnregisteredReturnTypeIntegerMaxSize => 16;
public override bool IsArgPassedByRef(TypeHandle th)
{
Debug.Assert(!th.IsNull());
Debug.Assert(th.IsValueType());
// Composites greater than 16 bytes are passed by reference
if (th.GetSize() > EnregisteredParamTypeMaxSize)
{
return true;
}
else
{
return false;
}
}
public sealed override int GetRetBuffArgOffset(bool hasThis) => OffsetOfFirstGCRefMapSlot + (hasThis ? 8 : 0);
public override int StackElemSize(int parmSize, bool isValueType = false, bool isFloatHfa = false)
{
int stackSlotSize = 8;
return ALIGN_UP(parmSize, stackSlotSize);
}
}
private class RiscV64TransitionBlock : TransitionBlock
{
public static TransitionBlock Instance = new RiscV64TransitionBlock();
public override TargetArchitecture Architecture => TargetArchitecture.RiscV64;
public override int PointerSize => 8;
public override int FloatRegisterSize => 8;
// a0 .. a7
public override int NumArgumentRegisters => 8;
// fp=x8, ra=x1, s1-s11(R9,R18-R27), tp=x3, gp=x4
public override int NumCalleeSavedRegisters => 15;
// Callee-saves, argument registers
public override int SizeOfTransitionBlock => SizeOfCalleeSavedRegisters + PointerSize + SizeOfArgumentRegisters;
public override int OffsetOfFirstGCRefMapSlot => SizeOfCalleeSavedRegisters + PointerSize;
public override int OffsetOfArgumentRegisters => OffsetOfFirstGCRefMapSlot;
public override int OffsetOfFloatArgumentRegisters => 8 * sizeof(double);
public override int EnregisteredParamTypeMaxSize => 16;
public override int EnregisteredReturnTypeIntegerMaxSize => 16;
public override bool IsArgPassedByRef(TypeHandle th)
{
Debug.Assert(!th.IsNull());
Debug.Assert(th.IsValueType());
// Composites greater than 16 bytes are passed by reference
return th.GetSize() > EnregisteredParamTypeMaxSize;
}
public sealed override int GetRetBuffArgOffset(bool hasThis) => OffsetOfFirstGCRefMapSlot + (hasThis ? 8 : 0);
public override int StackElemSize(int parmSize, bool isValueType = false, bool isFloatHfa = false)
{
int stackSlotSize = 8;
return ALIGN_UP(parmSize, stackSlotSize);
}
}
private class Wasm32TransitionBlock : TransitionBlock
{
public static TransitionBlock Instance = new Wasm32TransitionBlock();
public override TargetArchitecture Architecture => TargetArchitecture.Wasm32;
public override int PointerSize => 4;
public override int FloatRegisterSize => 0;
public override int NumArgumentRegisters => 0;
public override int NumCalleeSavedRegisters => 0;
public override int SizeOfTransitionBlock => 8;
public override int OffsetOfArgumentRegisters => 8;
public override int OffsetOfFloatArgumentRegisters => 0;
public override int EnregisteredParamTypeMaxSize => 0;
public override int EnregisteredReturnTypeIntegerMaxSize => 0;
public override int GetRetBuffArgOffset(bool hasThis) => OffsetOfArgumentRegisters + (hasThis ? StackElemSize(PointerSize, false, false) : 0);
public override bool IsArgPassedByRef(TypeHandle th)
{
return WasmLowering.LowerToAbiType(th.GetRuntimeTypeHandle()) == null;
}
public override int StackElemSize(int parmSize, bool isValueType, bool isFloatHfa)
{
int stackSlotSize = 8;
return ALIGN_UP(parmSize, stackSlotSize);
}
}
}
}
|