Skip to content

Commit c7f9549

Browse files
Replace extern method accessors with cells (#3120)
Before: <push args> call get.method call_indirect After: <push args> i32.const 0 i32.load 2 <cell.method> call_indirect This is a marginally larger sequence for a marginally smaller payload of the cell itself (4 bytes vs 8 for an accessor). It has worse "best-case" characteristics (WASM compilers won't be able to understand the indirect call target and potentially inline the call), but better "average case" ones (e. g. wasmtime currently does not do any WASM inlining at all). However, the main motivation for this change is better compatibility with the -fPIC case, where the static table index relocation makes it impossible to use the ILC-produced code as-is.
1 parent 7076842 commit c7f9549

File tree

12 files changed

+89
-96
lines changed

12 files changed

+89
-96
lines changed

src/coreclr/jit/llvm.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -777,11 +777,10 @@ const char* Llvm::GetAlternativeFunctionName()
777777
return CallEEApi<EEAI_GetAlternativeFunctionName, const char*>(m_pEECorInfo);
778778
}
779779

780-
CORINFO_GENERIC_HANDLE Llvm::GetExternalMethodAccessor(
781-
CORINFO_METHOD_HANDLE methodHandle, const TargetAbiType* sig, int sigLength)
780+
void Llvm::GetExternalMethodAddress(
781+
CORINFO_METHOD_HANDLE methodHandle, const TargetAbiType* sig, int sigLength, CORINFO_CONST_LOOKUP* pLookup)
782782
{
783-
return CallEEApi<EEAI_GetExternalMethodAccessor, CORINFO_GENERIC_HANDLE>(
784-
m_pEECorInfo, methodHandle, sig, sigLength);
783+
return CallEEApi<EEAI_GetExternalMethodAddress, void>(m_pEECorInfo, methodHandle, sig, sigLength, pLookup);
785784
}
786785

787786
void Llvm::GetDebugInfoForCurrentMethod(CORINFO_LLVM_METHOD_DEBUG_INFO* pInfo)

src/coreclr/jit/llvm.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -423,8 +423,8 @@ class Llvm
423423
CorInfoType GetPrimitiveTypeForTrivialWasmStruct(CORINFO_CLASS_HANDLE structHandle);
424424
void GetTypeDescriptor(CORINFO_CLASS_HANDLE typeHandle, TypeDescriptor* pTypeDescriptor);
425425
const char* GetAlternativeFunctionName();
426-
CORINFO_GENERIC_HANDLE GetExternalMethodAccessor(
427-
CORINFO_METHOD_HANDLE methodHandle, const TargetAbiType* callSiteSig, int sigLength);
426+
void GetExternalMethodAddress(
427+
CORINFO_METHOD_HANDLE methodHandle, const TargetAbiType* callSiteSig, int sigLength, CORINFO_CONST_LOOKUP* pLookup);
428428
void GetDebugInfoForCurrentMethod(CORINFO_LLVM_METHOD_DEBUG_INFO* pInfo);
429429
SingleThreadedCompilationContext* GetSingleThreadedCompilationContext();
430430
CorInfoLlvmEHModel GetExceptionHandlingModel();

src/coreclr/jit/llvmcodegen.cpp

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2719,42 +2719,45 @@ FunctionType* Llvm::createFunctionType()
27192719

27202720
llvm::FunctionCallee Llvm::consumeCallTarget(GenTreeCall* call)
27212721
{
2722-
llvm::FunctionCallee callee;
2722+
Value* calleeValue;
27232723
if (call->IsVirtualVtable() || call->IsDelegateInvoke() || (call->gtCallType == CT_INDIRECT))
27242724
{
2725-
FunctionType* calleeFuncType = createFunctionTypeForCall(call);
27262725
GenTree* calleeNode = (call->gtCallType == CT_INDIRECT) ? call->gtCallAddr : call->gtControlExpr;
2727-
Value* calleeValue = consumeValue(calleeNode, getPtrLlvmType());
2728-
2729-
callee = {calleeFuncType, calleeValue};
2726+
calleeValue = consumeValue(calleeNode, getPtrLlvmType());
27302727
}
27312728
else
27322729
{
2733-
CORINFO_GENERIC_HANDLE handle = call->gtEntryPoint.handle;
2734-
CorInfoHelpFunc helperFunc = _compiler->eeGetHelperNum(call->gtCallMethHnd);
2735-
if (handle == nullptr)
2730+
CORINFO_CONST_LOOKUP lookup = call->gtEntryPoint;
2731+
CorInfoHelpFunc helperFunc = call->GetHelperNum();
2732+
if (lookup.handle == nullptr)
27362733
{
2737-
handle = getSymbolHandleForHelperFunc(helperFunc);
2734+
lookup.accessType = IAT_VALUE;
2735+
lookup.handle = getSymbolHandleForHelperFunc(helperFunc);
27382736
}
2739-
else
2737+
2738+
StringRef symbolName = GetMangledSymbolName(lookup.handle);
2739+
AddCodeReloc(lookup.handle); // Replacement for _info.compCompHnd->recordRelocation.
2740+
2741+
if (lookup.accessType == IAT_VALUE)
27402742
{
2741-
assert(call->gtEntryPoint.accessType == IAT_VALUE);
2743+
Function* llvmFunc = getOrCreateKnownLlvmFunction(symbolName, [this, call]() -> FunctionType* {
2744+
return createFunctionTypeForCall(call);
2745+
}, [this, helperFunc](Function* llvmFunc) {
2746+
if (helperFunc != CORINFO_HELP_UNDEF)
2747+
{
2748+
annotateHelperFunction(helperFunc, llvmFunc);
2749+
}
2750+
});
2751+
return llvmFunc;
27422752
}
27432753

2744-
const char* symbolName = GetMangledSymbolName(handle);
2745-
AddCodeReloc(handle); // Replacement for _info.compCompHnd->recordRelocation.
2746-
2747-
callee = getOrCreateKnownLlvmFunction(symbolName, [this, call]() -> FunctionType* {
2748-
return createFunctionTypeForCall(call);
2749-
}, [this, helperFunc](Function* llvmFunc) {
2750-
if (helperFunc != CORINFO_HELP_UNDEF)
2751-
{
2752-
annotateHelperFunction(helperFunc, llvmFunc);
2753-
}
2754-
});
2754+
assert(lookup.accessType == IAT_PVALUE);
2755+
calleeValue = getOrCreateSymbol(lookup.handle);
2756+
calleeValue = _builder.CreateLoad(getPtrLlvmType(), calleeValue);
27552757
}
27562758

2757-
return callee;
2759+
FunctionType* calleeFuncType = createFunctionTypeForCall(call);
2760+
return {calleeFuncType, calleeValue};
27582761
}
27592762

27602763
FunctionType* Llvm::createFunctionTypeForSignature(CORINFO_SIG_INFO* pSig)

src/coreclr/jit/llvmlower.cpp

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -850,18 +850,10 @@ void Llvm::lowerUnmanagedCall(GenTreeCall* callNode)
850850
// given module, we can only have one function declaration, thus, one callee type. And we cannot know whether
851851
// this type will be the right one until, in general, runtime (this is the case for WASM imports provided by
852852
// the host environment). Thus, to achieve the experience of runtime erros on signature mismatches, we "hide"
853-
// the target behind an external function from another module, turning this call into an indirect one.
854-
GenTreeCall* getTargetCall =
855-
_compiler->gtNewHelperCallNode(CORINFO_HELP_LLVM_GET_EXTERNAL_CALL_TARGET, TYP_I_IMPL);
856-
getTargetCall->gtEntryPoint.handle =
857-
GetExternalMethodAccessor(callNode->gtCallMethHnd, &sig.BottomRef(), sig.Height());
858-
getTargetCall->gtEntryPoint.accessType = IAT_VALUE;
859-
860-
callNode->gtCallType = CT_INDIRECT;
861-
callNode->gtCallAddr = getTargetCall;
862-
callNode->gtCallCookie = nullptr;
863-
CurrentRange().InsertBefore(callNode, getTargetCall);
864-
lowerNode(getTargetCall);
853+
// the target behind an indirection, turning this call into an indirect one.
854+
// TODO-LLVM-Cleanup: switch to using the standard "getAddressOfPInvokeTarget" Jit-EE call, we no longer need
855+
// the signature on the EE side.
856+
GetExternalMethodAddress(callNode->gtCallMethHnd, &sig.BottomRef(), sig.Height(), &callNode->gtEntryPoint);
865857
}
866858
}
867859

src/coreclr/tools/Common/JitInterface/JitEEApi.Shared.cspp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ enum EEApiId
3030
EEAI_GetPrimitiveTypeForTrivialWasmStruct,
3131
EEAI_GetTypeDescriptor,
3232
EEAI_GetAlternativeFunctionName,
33-
EEAI_GetExternalMethodAccessor,
33+
EEAI_GetExternalMethodAddress,
3434
EEAI_GetDebugInfoForCurrentMethod,
3535
EEAI_GetSingleThreadedCompilationContext,
3636
EEAI_GetExceptionHandlingModel,

src/coreclr/tools/aot/ILCompiler.LLVM/CodeGen/WasmObjectWriter.EmitObject.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ public static void EmitObject(string objectFilePath, IEnumerable<DependencyNode>
5656
}
5757

5858
WasmObjectWriter writer;
59-
if (node is ExternMethodAccessorNode accessor)
59+
if (node is ExternMethodCellNode methodCell)
6060
{
61-
accessor.EmitWarnings(compilation);
61+
methodCell.EmitWarnings(compilation);
6262
writer = externalObjectWriter;
6363
}
6464
else

src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/ExternMethodAccessorNode.cs renamed to src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/ExternMethodCellNode.cs

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
namespace ILCompiler.DependencyAnalysis
2222
{
23-
internal sealed class ExternMethodAccessorNode(string externMethodName) : AssemblyStubNode, IWasmFunctionNode
23+
internal sealed class ExternMethodCellNode(string externMethodName) : ObjectNode, ISymbolDefinitionNode
2424
{
2525
private readonly Utf8String _externMethodName = externMethodName;
2626
private TargetAbiType[] _signature;
@@ -29,7 +29,10 @@ internal sealed class ExternMethodAccessorNode(string externMethodName) : Assemb
2929
public Utf8String ExternMethodName => _externMethodName;
3030
public ref TargetAbiType[] Signature => ref _signature;
3131

32-
public override int ClassCode => 935251149;
32+
public int Offset => 0;
33+
public override bool RepresentsIndirectionCell => true;
34+
public override bool IsShareable => false;
35+
public override bool StaticDependenciesAreComputed => true;
3336

3437
public void AddMethod(MethodDesc method)
3538
{
@@ -87,20 +90,6 @@ public MethodDesc GetSingleMethod(NodeFactory factory)
8790
}
8891
}
8992

90-
public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
91-
{
92-
sb.Append("get.");
93-
sb.Append(ExternMethodName);
94-
}
95-
96-
public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
97-
{
98-
return ExternMethodName.CompareTo(((ExternMethodAccessorNode)other).ExternMethodName);
99-
}
100-
101-
public WasmFunctionType GetWasmFunctionType(NodeFactory factory) =>
102-
new WasmFunctionType(WasmAbi.GetNaturalIntType(factory.Target), []);
103-
10493
public void EmitWarnings(Compilation compilation)
10594
{
10695
if (HasSignatureMismatch(compilation.NodeFactory))
@@ -117,32 +106,40 @@ public void EmitWarnings(Compilation compilation)
117106
}
118107
}
119108

120-
protected override void EmitCode(NodeFactory factory, ref WasmEmitter encoder, bool relocsOnly)
109+
public override ObjectData GetData(NodeFactory factory, bool relocsOnly)
121110
{
122-
encoder.DefineLocals();
123-
encoder.EmitNaturalConst(((LLVMCodegenNodeFactory)factory).ExternWasmMethod(this));
124-
encoder.EmitEnd();
111+
ObjectDataBuilder builder = new(factory, relocsOnly);
112+
builder.AddSymbol(this);
113+
builder.EmitPointerReloc(((LLVMCodegenNodeFactory)factory).ExternWasmMethod(this));
114+
115+
return builder.ToObjectData();
125116
}
126117

127-
protected override void EmitCode(NodeFactory factory, ref X64Emitter instructionEncoder, bool relocsOnly) => throw new NotImplementedException();
128-
protected override void EmitCode(NodeFactory factory, ref X86Emitter instructionEncoder, bool relocsOnly) => throw new NotImplementedException();
129-
protected override void EmitCode(NodeFactory factory, ref ARMEmitter instructionEncoder, bool relocsOnly) => throw new NotImplementedException();
130-
protected override void EmitCode(NodeFactory factory, ref ARM64Emitter instructionEncoder, bool relocsOnly) => throw new NotImplementedException();
131-
protected override void EmitCode(NodeFactory factory, ref LoongArch64Emitter instructionEncoder, bool relocsOnly) => throw new NotImplementedException();
132-
protected override void EmitCode(NodeFactory factory, ref RiscV64Emitter instructionEncoder, bool relocsOnly) => throw new NotImplementedException();
118+
public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.ReadOnlyDataSection;
133119

134-
protected override string GetName(NodeFactory context) => $"ExternMethodAccessor {ExternMethodName}";
120+
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
121+
{
122+
sb.Append("cell.");
123+
sb.Append(ExternMethodName);
124+
}
125+
126+
public override int ClassCode => 935251149;
127+
128+
public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
129+
{
130+
return ExternMethodName.CompareTo(((ExternMethodCellNode)other).ExternMethodName);
131+
}
132+
133+
protected override string GetName(NodeFactory context) => $"{nameof(ExternMethodCellNode)} {ExternMethodName}";
135134
}
136135

137-
internal sealed class ExternWasmMethodNode(ExternMethodAccessorNode accessor) : ExternSymbolNode(accessor.ExternMethodName), IWasmFunctionNode
136+
internal sealed class ExternWasmMethodNode(ExternMethodCellNode methodCell) : ExternSymbolNode(methodCell.ExternMethodName), IWasmFunctionNode
138137
{
139-
private readonly ExternMethodAccessorNode _accessor = accessor;
140-
141-
public override int ClassCode => 1890343813;
138+
private readonly ExternMethodCellNode _methodCell = methodCell;
142139

143140
public WasmFunctionType GetWasmFunctionType(NodeFactory factory)
144141
{
145-
if (_accessor.HasSignatureMismatch(factory))
142+
if (_methodCell.HasSignatureMismatch(factory))
146143
{
147144
return new WasmFunctionType(WasmValueType.Invalid, []);
148145
}
@@ -162,7 +159,7 @@ public WasmFunctionType GetWasmFunctionType(NodeFactory factory)
162159
_ => throw new NotImplementedException()
163160
};
164161

165-
ReadOnlySpan<TargetAbiType> jitSig = _accessor.Signature;
162+
ReadOnlySpan<TargetAbiType> jitSig = _methodCell.Signature;
166163
WasmValueType wasmReturnType = ToWasmType(jitSig[0]);
167164
WasmValueType[] wasmParamTypes = new WasmValueType[jitSig.Length - 1];
168165
for (int i = 0; i < wasmParamTypes.Length; i++)
@@ -176,16 +173,18 @@ public WasmFunctionType GetWasmFunctionType(NodeFactory factory)
176173
public bool GetImportModuleAndName(Compilation compilation, out string module, out string name)
177174
{
178175
NodeFactory factory = compilation.NodeFactory;
179-
if (_accessor.HasSignatureMismatch(factory))
176+
if (_methodCell.HasSignatureMismatch(factory))
180177
{
181178
(module, name) = (null, null);
182179
return false;
183180
}
184181

185-
MethodDesc method = _accessor.GetSingleMethod(factory);
182+
MethodDesc method = _methodCell.GetSingleMethod(factory);
186183
return compilation.PInvokeILProvider.GetWasmImportCallInfo(method, out name, out module);
187184
}
188185

186+
public override int ClassCode => 1890343813;
187+
189188
protected override string GetName(NodeFactory context) => $"WasmImportFunctionNode {Utf8Name}";
190189
}
191190
}

src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/DependencyAnalysis/LLVMCodegenNodeFactory.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ internal sealed class LLVMCodegenNodeFactory : NodeFactory
1616
{
1717
private readonly CorInfoLlvmEHModel _ehModel;
1818

19-
private readonly Dictionary<string, ExternMethodAccessorNode> _externMethodAccessors = new();
20-
private readonly NodeCache<ExternMethodAccessorNode, ExternWasmMethodNode> _externWasmMethods =
21-
new(accessor => new ExternWasmMethodNode(accessor));
19+
private readonly Dictionary<string, ExternMethodCellNode> _externMethodAccessors = new();
20+
private readonly NodeCache<ExternMethodCellNode, ExternWasmMethodNode> _externWasmMethods =
21+
new(methodCell => new ExternWasmMethodNode(methodCell));
2222

2323
public LLVMCodegenNodeFactory(
2424
LLVMCodegenConfigProvider options,
@@ -52,19 +52,19 @@ public LLVMCodegenNodeFactory(
5252

5353
public override bool TargetsEmulatedEH() => _ehModel is CorInfoLlvmEHModel.Emulated;
5454

55-
internal ExternMethodAccessorNode ExternMethodAccessor(string name, MethodDesc method, ReadOnlySpan<TargetAbiType> sig)
55+
internal ExternMethodCellNode ExternMethodCell(string name, MethodDesc method, ReadOnlySpan<TargetAbiType> sig)
5656
{
57-
Dictionary<string, ExternMethodAccessorNode> map = _externMethodAccessors;
57+
Dictionary<string, ExternMethodCellNode> map = _externMethodAccessors;
5858

5959
// Not lockless since we mutate the node. Contention on this path is not expected.
6060
//
6161
lock (map)
6262
{
63-
ref ExternMethodAccessorNode node = ref CollectionsMarshal.GetValueRefOrAddDefault(map, name, out bool exists);
63+
ref ExternMethodCellNode node = ref CollectionsMarshal.GetValueRefOrAddDefault(map, name, out bool exists);
6464

6565
if (!exists)
6666
{
67-
node = new ExternMethodAccessorNode(name);
67+
node = new ExternMethodCellNode(name);
6868
node.Signature = sig.ToArray();
6969
}
7070
else if (!node.Signature.AsSpan().SequenceEqual(sig))
@@ -78,7 +78,7 @@ internal ExternMethodAccessorNode ExternMethodAccessor(string name, MethodDesc m
7878
}
7979
}
8080

81-
internal ExternWasmMethodNode ExternWasmMethod(ExternMethodAccessorNode accessor) => _externWasmMethods.GetOrAdd(accessor);
81+
internal ExternWasmMethodNode ExternWasmMethod(ExternMethodCellNode accessor) => _externWasmMethods.GetOrAdd(accessor);
8282

8383
protected override IMethodNode CreateMethodEntrypointNode(MethodDesc method)
8484
{

src/coreclr/tools/aot/ILCompiler.LLVM/Compiler/LLVMCodegenCompilation.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,12 +247,12 @@ private CorInfoImpl CreateModuleCompilationContext(string kind, CorInfoLlvmSingl
247247
return corInfo;
248248
}
249249

250-
public override ISymbolNode GetExternalMethodAccessor(MethodDesc method, ReadOnlySpan<TargetAbiType> sig)
250+
public override ISymbolNode GetExternalMethodCell(MethodDesc method, ReadOnlySpan<TargetAbiType> sig)
251251
{
252252
Debug.Assert(!sig.IsEmpty);
253253
string name = PInvokeILProvider.GetDirectCallExternName(method);
254254

255-
return NodeFactory.ExternMethodAccessor(name, method, sig);
255+
return NodeFactory.ExternMethodCell(name, method, sig);
256256
}
257257

258258
public override CorInfoLlvmEHModel GetLlvmExceptionHandlingModel() => Options.ExceptionHandlingModel;

src/coreclr/tools/aot/ILCompiler.LLVM/ILCompiler.LLVM.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
<Compile Include="CodeGen\WasmObjectWriter.cs" />
2525
<Compile Include="CodeGen\WasmObjectWriter.EmitObject.cs" />
2626
<Compile Include="CodeGen\WasmNative.cs" />
27-
<Compile Include="Compiler\DependencyAnalysis\ExternMethodAccessorNode.cs" />
27+
<Compile Include="Compiler\DependencyAnalysis\ExternMethodCellNode.cs" />
2828
<Compile Include="Compiler\DependencyAnalysis\LLVMCodegenNodeFactory.cs" />
2929
<Compile Include="Compiler\DependencyAnalysis\LLVMMethodCodeNode.cs" />
3030
<Compile Include="Compiler\LLVMCompilationResults.cs" />

0 commit comments

Comments
 (0)