-
Notifications
You must be signed in to change notification settings - Fork 5.4k
[Wasm RyuJit] Call Codegen Fixes for R2R #126778
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
0b80545
9c88d47
7141714
1878b53
7ca6386
5e4a83f
26af840
b42c336
f2b96b6
83015d2
df103cf
d6e2cbe
b2f6e14
0cdc541
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2515,6 +2515,9 @@ void CodeGen::genCallInstruction(GenTreeCall* call) | |
|
|
||
| params.wasmSignature = m_compiler->info.compCompHnd->getWasmTypeSymbol(typeStack.Data(), typeStack.Height()); | ||
|
|
||
| // A non-null target expression always indicates an indirect call on Wasm, | ||
| // as currently the only possible result of the target expression would be a | ||
| // table index which must be used via call_indirect | ||
| if (target != nullptr) | ||
| { | ||
| // Codegen should have already evaluated our target node (last) and pushed it onto the stack, | ||
|
|
@@ -2526,52 +2529,28 @@ void CodeGen::genCallInstruction(GenTreeCall* call) | |
| } | ||
| else | ||
| { | ||
| // If we have no target and this is a call with indirection cell then | ||
| // we do an optimization where we load the call address directly from | ||
| // the indirection cell instead of duplicating the tree. In BuildCall | ||
| // we ensure that get an extra register for the purpose. Note that for | ||
| // CFG the call might have changed to | ||
| // CORINFO_HELP_DISPATCH_INDIRECT_CALL in which case we still have the | ||
| // indirection cell but we should not try to optimize. | ||
| WellKnownArg indirectionCellArgKind = WellKnownArg::None; | ||
| if (!call->IsHelperCall(CORINFO_HELP_DISPATCH_INDIRECT_CALL)) | ||
| { | ||
| indirectionCellArgKind = call->GetIndirectionCellArgKind(); | ||
| } | ||
| // Generate a direct call to a non-virtual user defined or helper method | ||
| assert(call->IsHelperCall() || (call->gtCallType == CT_USER_FUNC)); | ||
|
|
||
| if (indirectionCellArgKind != WellKnownArg::None) | ||
| { | ||
| assert(call->IsR2ROrVirtualStubRelativeIndir()); | ||
| assert(call->gtEntryPoint.addr == NULL); | ||
|
|
||
| params.callType = EC_INDIR_R; | ||
| // params.ireg = targetAddrReg; | ||
| genEmitCallWithCurrentGC(params); | ||
| if (call->IsHelperCall()) | ||
| { | ||
| assert(!call->IsFastTailCall()); | ||
| CorInfoHelpFunc helperNum = m_compiler->eeGetHelperNum(params.methHnd); | ||
| noway_assert(helperNum != CORINFO_HELP_UNDEF); | ||
| CORINFO_CONST_LOOKUP helperLookup = m_compiler->compGetHelperFtn(helperNum); | ||
| assert(helperLookup.accessType == IAT_VALUE); | ||
| params.addr = helperLookup.addr; | ||
| } | ||
| else | ||
| { | ||
| // Generate a direct call to a non-virtual user defined or helper method | ||
| assert(call->IsHelperCall() || (call->gtCallType == CT_USER_FUNC)); | ||
|
|
||
| assert(call->gtEntryPoint.addr == NULL); | ||
|
|
||
| if (call->IsHelperCall()) | ||
| { | ||
| assert(!call->IsFastTailCall()); | ||
| CorInfoHelpFunc helperNum = m_compiler->eeGetHelperNum(params.methHnd); | ||
| noway_assert(helperNum != CORINFO_HELP_UNDEF); | ||
| CORINFO_CONST_LOOKUP helperLookup = m_compiler->compGetHelperFtn(helperNum); | ||
| assert(helperLookup.accessType == IAT_VALUE); | ||
| params.addr = helperLookup.addr; | ||
| } | ||
| else | ||
| { | ||
| // Direct call to a non-virtual user function. | ||
| params.addr = call->gtDirectCallAddress; | ||
| } | ||
|
|
||
| params.callType = EC_FUNC_TOKEN; | ||
| genEmitCallWithCurrentGC(params); | ||
| // Direct call to a non-virtual user function. | ||
| params.addr = call->gtDirectCallAddress; | ||
| } | ||
|
|
||
| params.callType = EC_FUNC_TOKEN; | ||
| genEmitCallWithCurrentGC(params); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -2676,16 +2655,21 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, | |
| if (helperIsManaged) | ||
| { | ||
| // Push PEP onto the stack because we are calling a managed helper that expects it as the last parameter. | ||
| // The helper function address is the address of an indirection cell, so we load from the cell to get the PEP | ||
| // address to push. | ||
| assert(helperFunction.accessType == IAT_PVALUE); | ||
| GetEmitter()->emitAddressConstant(helperFunction.addr); | ||
| GetEmitter()->emitIns_I(INS_i32_load, EA_PTRSIZE, 0); | ||
| } | ||
|
|
||
| if (params.callType == EC_INDIR_R) | ||
| { | ||
| // Push the call target onto the wasm evaluation stack by dereferencing the PEP. | ||
| // Push the call target onto the wasm evaluation stack by dereferencing the indirection cell | ||
| // and then the PEP pointed to by the indirection cell. | ||
| assert(helperFunction.accessType == IAT_PVALUE); | ||
| GetEmitter()->emitAddressConstant(helperFunction.addr); | ||
| GetEmitter()->emitIns_I(INS_i32_load, EA_PTRSIZE, 0); | ||
| GetEmitter()->emitIns_I(INS_i32_load, EA_PTRSIZE, 0); | ||
|
Comment on lines
+2658
to
+2672
|
||
| } | ||
|
Comment on lines
2657
to
2673
|
||
|
|
||
| genEmitCallWithCurrentGC(params); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3022,6 +3022,15 @@ GenTree* Lowering::LowerCall(GenTree* node) | |
| } | ||
| } | ||
|
|
||
| #ifdef TARGET_WASM | ||
| // For any type of managed call, if we have portable entry points enabled, we need to lower | ||
| // the call according to the portable entrypoint abi | ||
| if (!call->IsUnmanaged() && m_compiler->opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PORTABLE_ENTRY_POINTS)) | ||
| { | ||
| LowerPEPCall(call); | ||
| } | ||
| #endif // TARGET_WASM | ||
|
|
||
| if (varTypeIsStruct(call)) | ||
| { | ||
| LowerCallStruct(call); | ||
|
|
@@ -3638,6 +3647,73 @@ GenTree* Lowering::LowerTailCallViaJitHelper(GenTreeCall* call, GenTree* callTar | |
| return result; | ||
| } | ||
|
|
||
| #ifdef TARGET_WASM | ||
| //--------------------------------------------------------------------------------------------- | ||
| // LowerPEPCall: Lower a call node dispatched through a PortableEntryPoint (PEP) | ||
| // | ||
| // Given a call node with gtControlExpr representing a call target which is the address of a portable entrypoint, | ||
| // this function lowers the call to appropriately dispatch through the portable entrypoint using the Portable | ||
| // entrypoint calling convention. | ||
| // To do this, it: | ||
| // 1. Introduces a new local variable to hold the PEP address | ||
| // 2. Adds a new well-known argument to the call passing this local | ||
| // 3. Rewrites the control expression to indirect through the new local, since for PEP's, the actual call target | ||
| // must be loaded from the portable entry point address. | ||
| // | ||
| // Arguments: | ||
| // call - The call node to lower. It is expected that the call node has gtControlExpr set to the original | ||
| // call target and that the call does not have a PEP arg already. | ||
| // | ||
| // Return Value: | ||
| // None. The call node is modified in place. | ||
| // | ||
| void Lowering::LowerPEPCall(GenTreeCall* call) | ||
| { | ||
| JITDUMP("Begin lowering PEP call\n"); | ||
| DISPTREERANGE(BlockRange(), call); | ||
|
|
||
| // PEP call must always have a control expression | ||
| assert(call->gtControlExpr != nullptr); | ||
| LIR::Use callTargetUse(BlockRange(), &call->gtControlExpr, call); | ||
|
|
||
| JITDUMP("Creating new local variable for PEP"); | ||
| unsigned int callTargetLclNum = callTargetUse.ReplaceWithLclVar(m_compiler); | ||
| GenTreeLclVar* callTargetLclForArg = m_compiler->gtNewLclvNode(callTargetLclNum, TYP_I_IMPL); | ||
| DISPTREE(call) | ||
|
|
||
| JITDUMP("Add new arg to call arg list corresponding to PEP target"); | ||
| NewCallArg pepTargetArg = | ||
| NewCallArg::Primitive(callTargetLclForArg).WellKnown(WellKnownArg::WasmPortableEntryPoint); | ||
| CallArg* pepArg = call->gtArgs.PushBack(m_compiler, pepTargetArg); | ||
|
|
||
| pepArg->SetEarlyNode(nullptr); | ||
| pepArg->SetLateNode(callTargetLclForArg); | ||
| call->gtArgs.PushLateBack(pepArg); | ||
|
|
||
| // Set up ABI information for this arg; PEP's should be passed as the last param to a wasm function | ||
| unsigned pepIndex = call->gtArgs.CountArgs() - 1; | ||
| regNumber pepReg = MakeWasmReg(pepIndex, WasmValueType::I); | ||
| pepArg->AbiInfo = | ||
| ABIPassingInformation::FromSegmentByValue(m_compiler, | ||
| ABIPassingSegment::InRegister(pepReg, 0, TARGET_POINTER_SIZE)); | ||
| BlockRange().InsertBefore(call, callTargetLclForArg); | ||
|
|
||
| // Lower the new PEP arg now that the call abi info is updated and lcl var is inserted | ||
| LowerArg(call, pepArg); | ||
| DISPTREE(call); | ||
|
|
||
| JITDUMP("Rewrite PEP call's control expression to indirect through the new local variable\n"); | ||
| // Rewrite the call's control expression to have an additional load from the PEP local | ||
| GenTree* controlExpr = call->gtControlExpr; | ||
| GenTree* target = Ind(controlExpr); | ||
| BlockRange().InsertAfter(controlExpr, target); | ||
| call->gtControlExpr = target; | ||
|
|
||
|
Comment on lines
+3684
to
+3711
|
||
| JITDUMP("Finished lowering PEP call\n"); | ||
| DISPTREERANGE(BlockRange(), call); | ||
|
adamperlin marked this conversation as resolved.
|
||
| } | ||
| #endif // TARGET_WASM | ||
|
|
||
| //------------------------------------------------------------------------ | ||
| // LowerCFGCall: Potentially lower a call to use control-flow guard. This | ||
| // expands indirect calls into either a validate+call sequence or to a dispatch | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See if it's possible to use the new ADDR_REL reloc type for this to avoid the
i32.const <address>; i32.loadpattern.