diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
index 6d8853ac86f244..287806fa9e8999 100644
--- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
+++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
@@ -2316,7 +2316,7 @@ public static int GetClassAlignmentRequirementStatic(DefType type)
}
}
- if (type.Context.Target.Architecture == TargetArchitecture.ARM &&
+ if (type.Context.Target.SupportsAlign8 &&
alignment < 8 && type.RequiresAlign8())
{
// If the structure contains 64-bit primitive fields and the platform requires 8-byte alignment for
diff --git a/src/coreclr/tools/Common/JitInterface/WasmLowering.cs b/src/coreclr/tools/Common/JitInterface/WasmLowering.cs
index ab9857664c6116..3049de33e02e9b 100644
--- a/src/coreclr/tools/Common/JitInterface/WasmLowering.cs
+++ b/src/coreclr/tools/Common/JitInterface/WasmLowering.cs
@@ -12,7 +12,7 @@
namespace Internal.JitInterface
{
- public static class WasmLowering
+ public static partial class WasmLowering
{
// The Wasm "basic C ABI" passes structs that contain one
// primitive field as that primitive field.
diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs
index fc541a267aaa7c..885db56adb4b84 100644
--- a/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs
+++ b/src/coreclr/tools/Common/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs
@@ -858,7 +858,7 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type,
// pointer (the Crossgen2 way) to ensure 8-alignment for longs and doubles as required by the ARM32 ISA. Please note
// that for 16-alignment used by Vector128 this logic actually ensures that the fields are 16-misaligned
// (they are 16-aligned after the 4-byte or 8-byte method table pointer).
- if (!type.IsValueType && cumulativeInstanceFieldPos != LayoutInt.Zero && type.Context.Target.Architecture != TargetArchitecture.ARM)
+ if (!type.IsValueType && cumulativeInstanceFieldPos != LayoutInt.Zero && !type.Context.Target.SupportsAlign8)
{
offsetBias = type.Context.Target.LayoutPointerSize;
cumulativeInstanceFieldPos -= offsetBias;
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs
index c6b25756045c50..de2f97852565ea 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs
@@ -1076,49 +1076,15 @@ public int GetNextOffset()
case TargetArchitecture.Wasm32:
{
bool isValueType = (argType == CorElementType.ELEMENT_TYPE_VALUETYPE);
- WasmValueType actualWasmAbiType = default(WasmValueType);
-
- switch (argType)
+ int cbArg = ALIGN_UP(argSize, 8);
+ int align;
+ if (isValueType)
{
- case CorElementType.ELEMENT_TYPE_VALUETYPE:
- actualWasmAbiType = WasmLowering.LowerType(_argTypeHandle.GetRuntimeTypeHandle());
- break;
-
- case CorElementType.ELEMENT_TYPE_I8:
- case CorElementType.ELEMENT_TYPE_U8:
- actualWasmAbiType = WasmValueType.I64;
- break;
- case CorElementType.ELEMENT_TYPE_R8:
- actualWasmAbiType = WasmValueType.F64;
- break;
- case CorElementType.ELEMENT_TYPE_R4:
- actualWasmAbiType = WasmValueType.F32;
- break;
- default:
- actualWasmAbiType = WasmValueType.I32;
- break;
+ align = Math.Clamp(((DefType)_argTypeHandle.GetRuntimeTypeHandle()).InstanceFieldAlignment.AsInt, 8, 16);
}
-
- int cbArg;
- int align;
- switch (actualWasmAbiType)
+ else
{
- case WasmValueType.I64:
- case WasmValueType.F64:
- cbArg = 8;
- align = 8;
- break;
- case WasmValueType.I32:
- case WasmValueType.F32:
- cbArg = 8;
align = 8;
- break;
- case WasmValueType.V128:
- cbArg = 16;
- align = 16;
- break;
- default:
- throw new Exception(); // These are the only WasmValueTypes defined in our transition block handling
}
_wasmOfsStack = ALIGN_UP(_wasmOfsStack, align);
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs
index b85e3fa2163b60..4ece51700d2cf3 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs
@@ -388,6 +388,12 @@ public void ComputeReturnValueTreatment(CorElementType type, TypeHandle thRetTyp
break;
}
+ if (IsWasm32)
+ {
+ // Wasm doesn't use a ret buffer for returning structs in the interpreter calling convention, which is the TransitionBlock convention on Wasm platforms.
+ break;
+ }
+
uint size = (uint)thRetType.GetSize();
if (IsX86 || IsX64)
@@ -774,7 +780,7 @@ private class Wasm32TransitionBlock : TransitionBlock
public override bool IsArgPassedByRef(TypeHandle th)
{
- return WasmLowering.LowerToAbiType(th.GetRuntimeTypeHandle()) == null;
+ return false;
}
public override int StackElemSize(int parmSize, bool isValueType, bool isFloatHfa)
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmImportThunk.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmImportThunk.cs
index 27b2d792466a6f..48f9f8f5f41cc3 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmImportThunk.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmImportThunk.cs
@@ -135,7 +135,7 @@ protected override void EmitCode(NodeFactory factory, ref Wasm.WasmEmitter instr
while ((argOffset = argit.GetNextOffset()) != TransitionBlock.InvalidOffset)
{
offsets[argIndex] = argOffset;
- isIndirectStructArg[argIndex] = argit.IsArgPassedByRef() && argit.IsValueType();
+ isIndirectStructArg[argIndex] = WasmLowering.CurrentArgLowersValueTypeToPassAsByref(argit);
argIndex++;
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmInterpreterToR2RThunkNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmInterpreterToR2RThunkNode.cs
index bf6ba702683af0..c391b4aec8b7c2 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmInterpreterToR2RThunkNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmInterpreterToR2RThunkNode.cs
@@ -102,7 +102,7 @@ protected override void EmitCode(NodeFactory factory, ref Wasm.WasmEmitter instr
while ((argOffset = argit.GetNextOffset()) != TransitionBlock.InvalidOffset)
{
interpOffsets[argIndex] = argOffset - sizeOfTransitionBlock;
- isIndirectStructArg[argIndex] = argit.IsArgPassedByRef() && argit.IsValueType();
+ isIndirectStructArg[argIndex] = WasmLowering.CurrentArgLowersValueTypeToPassAsByref(argit);
argIndex++;
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmR2RToInterpreterThunkNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmR2RToInterpreterThunkNode.cs
index a8bf4482c3e26a..a519b0ea445c4f 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmR2RToInterpreterThunkNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/WasmR2RToInterpreterThunkNode.cs
@@ -107,7 +107,7 @@ protected override void EmitCode(NodeFactory factory, ref Wasm.WasmEmitter instr
while ((argOffset = argit.GetNextOffset()) != TransitionBlock.InvalidOffset)
{
offsets[argIndex] = argOffset;
- isIndirectStructArg[argIndex] = argit.IsArgPassedByRef() && argit.IsValueType();
+ isIndirectStructArg[argIndex] = WasmLowering.CurrentArgLowersValueTypeToPassAsByref(argit);
argIndex++;
}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
index e12093001fe525..0edb1fb02b5b74 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
@@ -351,6 +351,7 @@
+
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/WasmLowering.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/WasmLowering.ReadyToRun.cs
new file mode 100644
index 00000000000000..d0d82721d001e3
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/WasmLowering.ReadyToRun.cs
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+using ILCompiler;
+using ILCompiler.DependencyAnalysis.Wasm;
+using ILCompiler.DependencyAnalysis.ReadyToRun;
+
+using Internal.TypeSystem;
+
+namespace Internal.JitInterface
+{
+ public static partial class WasmLowering
+ {
+ internal static bool CurrentArgLowersValueTypeToPassAsByref(ArgIterator argit)
+ {
+ if (argit.IsValueType())
+ {
+ // Check to see if this argument lowers to a byref on the wasm side
+ TypeHandle typeHandle;
+ argit.GetArgType(out typeHandle);
+ if (WasmLowering.LowerToAbiType(typeHandle.GetRuntimeTypeHandle()) == null)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/src/coreclr/vm/callhelpers.h b/src/coreclr/vm/callhelpers.h
index 74d4453bbfb5b8..a91f65078c271c 100644
--- a/src/coreclr/vm/callhelpers.h
+++ b/src/coreclr/vm/callhelpers.h
@@ -34,6 +34,7 @@ struct CallDescrData
size_t nArgsSize;
bool hasThis;
bool hasRetBuff;
+ void* pRetBuffArg;
#endif // TARGET_WASM
#ifdef CALLDESCR_RETBUFFARGREG
diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h
index 746464ca77f2c0..aee40963e09128 100644
--- a/src/coreclr/vm/callingconvention.h
+++ b/src/coreclr/vm/callingconvention.h
@@ -1919,15 +1919,9 @@ int ArgIteratorTemplate::GetNextOffset()
align = INTERP_STACK_SLOT_SIZE;
}
- if (HasRetBuffArg())
- {
- // the slot for retbuf arg will be removed before the actual call
- m_ofsStack = ALIGN_UP(m_ofsStack - INTERP_STACK_SLOT_SIZE, align) + INTERP_STACK_SLOT_SIZE;
- }
- else
- {
- m_ofsStack = ALIGN_UP(m_ofsStack, align);
- }
+ _ASSERTE(!HasRetBuffArg());
+
+ m_ofsStack = ALIGN_UP(m_ofsStack, align);
int cbArg = ALIGN_UP(argSize, INTERP_STACK_SLOT_SIZE);
int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack;
@@ -2075,6 +2069,11 @@ void ArgIteratorTemplate::ComputeReturnFlags()
break;
}
+#ifdef TARGET_WASM
+ // WebAssembly ArgIterator follows the Interpreter calling convention which does not use a return buffer arg in the normal argument stream
+ flags &= ~RETURN_HAS_RET_BUFFER;
+#endif
+
m_dwFlags |= flags;
}
diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp
index 37343b4fcb9cc2..e48b733634a60d 100644
--- a/src/coreclr/vm/reflectioninvocation.cpp
+++ b/src/coreclr/vm/reflectioninvocation.cpp
@@ -208,11 +208,13 @@ class ArgIteratorBaseForMethodInvoke
SIGNATURENATIVEREF * m_ppNativeSig;
bool m_fHasThis;
+public:
FORCEINLINE CorElementType GetReturnType(TypeHandle * pthValueType)
{
WRAPPER_NO_CONTRACT;
return (*pthValueType = (*m_ppNativeSig)->GetReturnTypeHandle()).GetInternalCorElementType();
}
+protected:
FORCEINLINE CorElementType GetNextArgumentType(DWORD iArg, TypeHandle * pthValueType)
{
@@ -423,7 +425,20 @@ extern "C" void QCALLTYPE RuntimeMethodHandle_InvokeMethod(
// WASM-TODO: this is now called from the interpreter, so the arguments layout is OK. reconsider with codegen
callDescrData.nArgsSize = nStackBytes;
callDescrData.hasThis = argit.HasThis();
- callDescrData.hasRetBuff = argit.HasRetBuffArg();
+
+ TypeHandle thValueType;
+ CorElementType type = argit.GetReturnType(&thValueType);
+ DWORD retSize = 0;
+ if (type == ELEMENT_TYPE_TYPEDBYREF)
+ {
+ retSize = sizeof(TypedByRef);
+ }
+ else if (type == ELEMENT_TYPE_VALUETYPE)
+ {
+ retSize = thValueType.GetSize();
+ }
+
+ callDescrData.hasRetBuff = retSize > sizeof(callDescrData.returnValue);
#endif // TARGET_WASM
// This is duplicated logic from MethodDesc::GetCallTarget
@@ -453,7 +468,11 @@ extern "C" void QCALLTYPE RuntimeMethodHandle_InvokeMethod(
TypeHandle retTH = gc.pSig->GetReturnTypeHandle();
TypeHandle refReturnTargetTH; // Valid only if retType == ELEMENT_TYPE_BYREF. Caches the TypeHandle of the byref target.
+#ifdef TARGET_WASM
+ BOOL fHasRetBuffArg = callDescrData.hasRetBuff;
+#else
BOOL fHasRetBuffArg = argit.HasRetBuffArg();
+#endif
CorElementType retType = retTH.GetSignatureCorElementType();
BOOL hasValueTypeReturn = retTH.IsValueType() && retType != ELEMENT_TYPE_VOID;
_ASSERTE(hasValueTypeReturn || !fHasRetBuffArg); // only valuetypes are returned via a return buffer.
@@ -538,7 +557,11 @@ extern "C" void QCALLTYPE RuntimeMethodHandle_InvokeMethod(
// buffer which will be copied to gc.retVal later.
pLocalRetBuf = _alloca(localRetBufSize);
ZeroMemory(pLocalRetBuf, localRetBufSize);
+#ifdef TARGET_WASM
+ callDescrData.pRetBuffArg = reinterpret_cast(pLocalRetBuf);
+#else
*((LPVOID*) (pTransitionBlock + argit.GetRetBuffArgOffset())) = pLocalRetBuf;
+#endif
if (pMT->ContainsGCPointers())
{
pValueClasses = new (_alloca(sizeof(ValueClassInfo))) ValueClassInfo(pLocalRetBuf, pMT, pValueClasses);
diff --git a/src/coreclr/vm/wasm/calldescrworkerwasm.cpp b/src/coreclr/vm/wasm/calldescrworkerwasm.cpp
index 5c64790f55b8c2..ad7581265efb1a 100644
--- a/src/coreclr/vm/wasm/calldescrworkerwasm.cpp
+++ b/src/coreclr/vm/wasm/calldescrworkerwasm.cpp
@@ -7,8 +7,6 @@
// Forward declaration
void ExecuteInterpretedMethodWithArgs(TADDR targetIp, int8_t* args, size_t argSize, void* retBuff, PCODE callerIp);
-#define SPECIAL_ARG_ADDR(pos) (void**)(((int8_t*)pCallDescrData->pSrc) + ((pos)*INTERP_STACK_SLOT_SIZE))
-
extern "C" void STDCALL CallDescrWorkerInternal(CallDescrData* pCallDescrData)
{
_ASSERTE(pCallDescrData != NULL);
@@ -30,25 +28,14 @@ extern "C" void STDCALL CallDescrWorkerInternal(CallDescrData* pCallDescrData)
size_t argsSize = pCallDescrData->nArgsSize;
void* retBuff;
- int8_t* args;
+ int8_t* args = (int8_t*)pCallDescrData->pSrc;
if (pCallDescrData->hasRetBuff)
{
- argsSize -= INTERP_STACK_SLOT_SIZE;
- if (pCallDescrData->hasThis)
- {
- retBuff = *SPECIAL_ARG_ADDR(1);
- *SPECIAL_ARG_ADDR(1) = *SPECIAL_ARG_ADDR(0);
- }
- else
- {
- retBuff = *SPECIAL_ARG_ADDR(0);
- }
- args = (int8_t*)SPECIAL_ARG_ADDR(1);
+ retBuff = pCallDescrData->pRetBuffArg;
}
else
{
- args = (int8_t*)pCallDescrData->pSrc;
- retBuff = pCallDescrData->returnValue;
+ retBuff = &pCallDescrData->returnValue;
}
ExecuteInterpretedMethodWithArgs((TADDR)targetIp, args, argsSize, retBuff, (PCODE)&CallDescrWorkerInternal);