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);