Skip to content
Merged
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
18 changes: 18 additions & 0 deletions src/coreclr/jit/codegenwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2876,6 +2876,24 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
{
m_compiler->eeGetMethodSig(params.methHnd, &sigInfoLocal);
sigInfoCall = &sigInfoLocal;
if (callRetType == TYP_REF && sigInfoCall->retType == CORINFO_TYPE_VOID &&
Comment thread
davidwrighton marked this conversation as resolved.
sigInfoCall->callConv == CORINFO_CALLCONV_HASTHIS)
{
// String ctors are special. The JIT recognizes them, and rewrites the call to call the function as
// a static function which returns the string instead of calling them with the normal
Comment thread
davidwrighton marked this conversation as resolved.
// allocation/initialization pattern. The signature of the static function is different from the
// instance method, so we need to adjust the signature here to match what the WASM runtime expects.
const unsigned methodFlags = m_compiler->info.compCompHnd->getMethodAttribs(params.methHnd);
Comment thread
davidwrighton marked this conversation as resolved.
CORINFO_CLASS_HANDLE stringClass = m_compiler->info.compCompHnd->getBuiltinClass(CLASSID_STRING);

if ((methodFlags & CORINFO_FLG_CONSTRUCTOR) != 0 && (stringClass != NO_CLASS_HANDLE) &&
(m_compiler->info.compCompHnd->getMethodClass(params.methHnd) == stringClass))
{
// Adjust sigInfoCall for string ctor case
sigInfoCall->retType = CORINFO_TYPE_CLASS;
sigInfoCall->callConv = CORINFO_CALLCONV_DEFAULT;
}
}
}
}

Expand Down
15 changes: 15 additions & 0 deletions src/coreclr/tools/Common/JitInterface/WasmLowering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@ namespace Internal.JitInterface
{
public static partial class WasmLowering
{
public static MethodSignature GetStringCtorActualSignature(MethodSignature signature)
{
Debug.Assert(signature.Context.GetWellKnownType(WellKnownType.String).GetMethod(".ctor"u8, signature) != null);
Debug.Assert(signature.GenericParameterCount == 0);
Comment thread
davidwrighton marked this conversation as resolved.
Debug.Assert(signature.Flags == 0);

TypeDesc[] arguments = new TypeDesc[signature.Length];
for (int i = 0; i < signature.Length; i++)
{
arguments[i] = signature[i];
}

return new MethodSignature(MethodSignatureFlags.Static, 0, signature.Context.GetWellKnownType(WellKnownType.String), arguments);
}

// The Wasm "basic C ABI" passes structs that contain one
// primitive field as that primitive field.
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,18 @@ public override ObjectData GetData(NodeFactory factory, System.Boolean relocsOnl
else
{
MethodDesc method = ((MethodFixupSignature)(_import.Signature)).Method;
MethodSignature signature = method.Signature;

if (method.IsInternalCall && method.OwningType.IsWellKnownType(WellKnownType.String) && method.IsConstructor)
{
// Special case for string ctors, which are internal calls but have a managed signature.
signature = WasmLowering.GetStringCtorActualSignature(signature);
}
// The import thunk always uses managed calling convention ($sp + PE entrypoint)
// even if the underlying method is UnmanagedCallersOnly, because the thunk is
// called from R2R-generated managed code.
WasmLowering.LoweringFlags flags = WasmLowering.GetLoweringFlags(method) & ~WasmLowering.LoweringFlags.IsUnmanagedCallersOnly;
wasmSignature = WasmLowering.GetSignature(method.Signature, flags);
wasmSignature = WasmLowering.GetSignature(signature, flags);
}
builder.EmitReloc(factory.WasmImportThunk(wasmSignature, HelperId, _import.Table, UseVirtualCall, UseJumpableStub), tableIndexPointerRelocType);
builder.EmitReloc(_import, RelocType.IMAGE_REL_BASED_ADDR32NB);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3604,6 +3604,18 @@ private void recordWasmManagedCallSig(CORINFO_SIG_INFO* callSig)
{
var sig = HandleToObject(callSig->methodSignature);

if (callSig->callConv == CorInfoCallConv.CORINFO_CALLCONV_DEFAULT &&
callSig->retType == CorInfoType.CORINFO_TYPE_CLASS &&
!sig.IsStatic &&
sig.ReturnType == sig.Context.GetWellKnownType(WellKnownType.Void))
Comment thread
davidwrighton marked this conversation as resolved.
{
// Detect special case for string ctors
if (sig.Context.GetWellKnownType(WellKnownType.String).GetMethod(".ctor"u8, sig) is not null)
{
sig = WasmLowering.GetStringCtorActualSignature(sig);
}
}

WasmLowering.LoweringFlags flags = 0;
if (callSig->hasTypeArg())
{
Expand Down
40 changes: 39 additions & 1 deletion src/coreclr/vm/wasm/helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1416,7 +1416,45 @@ void* GetPortableEntryPointToInterpreterThunk(MethodDesc *pMD)
}

MetaSig sig(pMD);
void* thunk = ComputePortableEntryPointToInterpreterThunk(sig);
void* thunk;

if (pMD->IsCtor() && pMD->GetMethodTable()->IsString())
{
const char *thunkKey = nullptr;

// String constructors are special-cased in the interpreter and have special thunks that don't match the normal signature.
if (sig.NumFixedArgs() == 1 && sig.NextArg() == ELEMENT_TYPE_VALUETYPE)
{
thunkKey = "IiS8p"; // String constructor with a single argument of type System.ReadOnlySpan<char>
}
Comment thread
davidwrighton marked this conversation as resolved.
else
{
switch (sig.NumFixedArgs())
{
case 1:
thunkKey = "Iiip";
break;
case 2:
thunkKey = "Iiiip";
break;
case 3:
thunkKey = "Iiiiip";
break;
case 4:
thunkKey = "Iiiiiip";
break;
default:
PORTABILITY_ASSERT("GetPortableEntryPointToInterpreterThunk: unknown thunk for string constructor");
return nullptr;
}
}

thunk = LookupPortableEntryPointThunk(thunkKey);
}
else
{
thunk = ComputePortableEntryPointToInterpreterThunk(sig);
}

return thunk;
}
Expand Down
Loading