Skip to content

Commit 029e9b2

Browse files
kgCopilotSingleAccretion
authored andcommitted
[Wasm RyuJIT] Initial scaffolding for calls and helper calls (dotnet#123044)
Starting to put together all the code we need for helper calls and calls in general. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com>
1 parent ce0cab5 commit 029e9b2

File tree

8 files changed

+335
-71
lines changed

8 files changed

+335
-71
lines changed

src/coreclr/jit/codegencommon.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1555,7 +1555,6 @@ bool CodeGen::genCreateAddrMode(GenTree* addr,
15551555
return true;
15561556
}
15571557

1558-
#ifndef TARGET_WASM
15591558
//------------------------------------------------------------------------
15601559
// genEmitCallWithCurrentGC:
15611560
// Emit a call with GC information captured from current GC information.
@@ -1570,7 +1569,6 @@ void CodeGen::genEmitCallWithCurrentGC(EmitCallParams& params)
15701569
params.byrefRegs = gcInfo.gcRegByrefSetCur;
15711570
GetEmitter()->emitIns_Call(params);
15721571
}
1573-
#endif // !TARGET_WASM
15741572

15751573
/*****************************************************************************
15761574
*
@@ -5737,6 +5735,8 @@ CORINFO_FIELD_HANDLE CodeGen::genEmitAsyncResumeInfo(unsigned stateNum)
57375735
return compiler->eeFindJitDataOffs(baseOffs + stateNum * sizeof(CORINFO_AsyncResumeInfo));
57385736
}
57395737

5738+
#endif // !TARGET_WASM
5739+
57405740
//------------------------------------------------------------------------
57415741
// getCallTarget - Get the node that evaluates to the call target
57425742
//
@@ -5814,6 +5814,8 @@ regNumber CodeGen::getCallIndirectionCellReg(GenTreeCall* call)
58145814
return result;
58155815
}
58165816

5817+
#if !defined(TARGET_WASM)
5818+
58175819
//------------------------------------------------------------------------
58185820
// genDefinePendingLabel - If necessary, define the pending call label after a
58195821
// call instruction was emitted.

src/coreclr/jit/codegenwasm.cpp

Lines changed: 203 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
511511
genCodeForStoreInd(treeNode->AsStoreInd());
512512
break;
513513

514+
case GT_CALL:
515+
genCall(treeNode->AsCall());
516+
break;
517+
514518
default:
515519
#ifdef DEBUG
516520
NYIRAW(GenTree::OpName(treeNode->OperGet()));
@@ -1016,45 +1020,61 @@ void CodeGen::genCodeForDivMod(GenTreeOp* treeNode)
10161020
//
10171021
void CodeGen::genCodeForConstant(GenTree* treeNode)
10181022
{
1019-
instruction ins;
1020-
cnsval_ssize_t bits;
1023+
instruction ins = INS_none;
1024+
cnsval_ssize_t bits = 0;
10211025
var_types type = treeNode->TypeIs(TYP_REF, TYP_BYREF) ? TYP_I_IMPL : treeNode->TypeGet();
10221026
static_assert(sizeof(cnsval_ssize_t) >= sizeof(double));
10231027

1024-
switch (type)
1028+
GenTreeIntConCommon* icon = nullptr;
1029+
if ((type == TYP_INT) || (type == TYP_LONG))
10251030
{
1026-
case TYP_INT:
1027-
{
1028-
ins = INS_i32_const;
1029-
GenTreeIntConCommon* con = treeNode->AsIntConCommon();
1030-
bits = con->IntegralValue();
1031-
break;
1032-
}
1033-
case TYP_LONG:
1031+
icon = treeNode->AsIntConCommon();
1032+
if (icon->ImmedValNeedsReloc(compiler))
10341033
{
1035-
ins = INS_i64_const;
1036-
GenTreeIntConCommon* con = treeNode->AsIntConCommon();
1037-
bits = con->IntegralValue();
1038-
break;
1034+
// WASM-TODO: Generate reloc for this handle
1035+
ins = INS_I_const;
1036+
bits = 0;
10391037
}
1040-
case TYP_FLOAT:
1038+
else
10411039
{
1042-
ins = INS_f32_const;
1043-
GenTreeDblCon* con = treeNode->AsDblCon();
1044-
double value = con->DconValue();
1045-
memcpy(&bits, &value, sizeof(double));
1046-
break;
1040+
bits = icon->IntegralValue();
10471041
}
1048-
case TYP_DOUBLE:
1042+
}
1043+
1044+
if (ins == INS_none)
1045+
{
1046+
switch (type)
10491047
{
1050-
ins = INS_f64_const;
1051-
GenTreeDblCon* con = treeNode->AsDblCon();
1052-
double value = con->DconValue();
1053-
memcpy(&bits, &value, sizeof(double));
1054-
break;
1048+
case TYP_INT:
1049+
{
1050+
ins = INS_i32_const;
1051+
assert(FitsIn<INT32>(bits));
1052+
break;
1053+
}
1054+
case TYP_LONG:
1055+
{
1056+
ins = INS_i64_const;
1057+
break;
1058+
}
1059+
case TYP_FLOAT:
1060+
{
1061+
ins = INS_f32_const;
1062+
GenTreeDblCon* con = treeNode->AsDblCon();
1063+
double value = con->DconValue();
1064+
memcpy(&bits, &value, sizeof(double));
1065+
break;
1066+
}
1067+
case TYP_DOUBLE:
1068+
{
1069+
ins = INS_f64_const;
1070+
GenTreeDblCon* con = treeNode->AsDblCon();
1071+
double value = con->DconValue();
1072+
memcpy(&bits, &value, sizeof(double));
1073+
break;
1074+
}
1075+
default:
1076+
unreached();
10551077
}
1056-
default:
1057-
unreached();
10581078
}
10591079

10601080
// The IF_ for the selected instruction, i.e. IF_F64, determines how these bits are emitted
@@ -1373,6 +1393,160 @@ void CodeGen::genCodeForStoreInd(GenTreeStoreInd* tree)
13731393
genUpdateLife(tree);
13741394
}
13751395

1396+
//------------------------------------------------------------------------
1397+
// genCall: Produce code for a GT_CALL node
1398+
//
1399+
void CodeGen::genCall(GenTreeCall* call)
1400+
{
1401+
if (call->NeedsNullCheck())
1402+
{
1403+
NYI_WASM("Insert nullchecks for calls that need it in lowering");
1404+
}
1405+
1406+
assert(!call->IsTailCall());
1407+
1408+
for (CallArg& arg : call->gtArgs.EarlyArgs())
1409+
{
1410+
genConsumeReg(arg.GetEarlyNode());
1411+
}
1412+
1413+
for (CallArg& arg : call->gtArgs.LateArgs())
1414+
{
1415+
genConsumeReg(arg.GetLateNode());
1416+
}
1417+
1418+
genCallInstruction(call);
1419+
genProduceReg(call);
1420+
}
1421+
1422+
//------------------------------------------------------------------------
1423+
// genCallInstruction - Generate instructions necessary to transfer control to the call.
1424+
//
1425+
// Arguments:
1426+
// call - the GT_CALL node
1427+
//
1428+
void CodeGen::genCallInstruction(GenTreeCall* call)
1429+
{
1430+
EmitCallParams params;
1431+
params.isJump = call->IsFastTailCall();
1432+
params.hasAsyncRet = call->IsAsync();
1433+
1434+
// We need to propagate the debug information to the call instruction, so we can emit
1435+
// an IL to native mapping record for the call, to support managed return value debugging.
1436+
// We don't want tail call helper calls that were converted from normal calls to get a record,
1437+
// so we skip this hash table lookup logic in that case.
1438+
if (compiler->opts.compDbgInfo && compiler->genCallSite2DebugInfoMap != nullptr && !call->IsTailCall())
1439+
{
1440+
DebugInfo di;
1441+
(void)compiler->genCallSite2DebugInfoMap->Lookup(call, &di);
1442+
params.debugInfo = di;
1443+
}
1444+
1445+
#ifdef DEBUG
1446+
// Pass the call signature information down into the emitter so the emitter can associate
1447+
// native call sites with the signatures they were generated from.
1448+
if (!call->IsHelperCall())
1449+
{
1450+
params.sigInfo = call->callSig;
1451+
}
1452+
#endif // DEBUG
1453+
GenTree* target = getCallTarget(call, &params.methHnd);
1454+
1455+
if (target != nullptr)
1456+
{
1457+
// Codegen should have already evaluated our target node (last) and pushed it onto the stack,
1458+
// ready for call_indirect. Consume it.
1459+
genConsumeReg(target);
1460+
1461+
params.callType = EC_INDIR_R;
1462+
genEmitCallWithCurrentGC(params);
1463+
}
1464+
else
1465+
{
1466+
// If we have no target and this is a call with indirection cell then
1467+
// we do an optimization where we load the call address directly from
1468+
// the indirection cell instead of duplicating the tree. In BuildCall
1469+
// we ensure that get an extra register for the purpose. Note that for
1470+
// CFG the call might have changed to
1471+
// CORINFO_HELP_DISPATCH_INDIRECT_CALL in which case we still have the
1472+
// indirection cell but we should not try to optimize.
1473+
WellKnownArg indirectionCellArgKind = WellKnownArg::None;
1474+
if (!call->IsHelperCall(compiler, CORINFO_HELP_DISPATCH_INDIRECT_CALL))
1475+
{
1476+
indirectionCellArgKind = call->GetIndirectionCellArgKind();
1477+
}
1478+
1479+
if (indirectionCellArgKind != WellKnownArg::None)
1480+
{
1481+
assert(call->IsR2ROrVirtualStubRelativeIndir());
1482+
1483+
params.callType = EC_INDIR_R;
1484+
// params.ireg = targetAddrReg;
1485+
genEmitCallWithCurrentGC(params);
1486+
}
1487+
else
1488+
{
1489+
// Generate a direct call to a non-virtual user defined or helper method
1490+
assert(call->IsHelperCall() || (call->gtCallType == CT_USER_FUNC));
1491+
1492+
assert(call->gtEntryPoint.addr == NULL);
1493+
1494+
if (call->IsHelperCall())
1495+
{
1496+
NYI_WASM("Call helper statically without indirection cell");
1497+
CorInfoHelpFunc helperNum = compiler->eeGetHelperNum(params.methHnd);
1498+
noway_assert(helperNum != CORINFO_HELP_UNDEF);
1499+
1500+
CORINFO_CONST_LOOKUP helperLookup = compiler->compGetHelperFtn(helperNum);
1501+
params.addr = helperLookup.addr;
1502+
assert(helperLookup.accessType == IAT_VALUE);
1503+
}
1504+
else
1505+
{
1506+
// Direct call to a non-virtual user function.
1507+
params.addr = call->gtDirectCallAddress;
1508+
}
1509+
1510+
params.callType = EC_FUNC_TOKEN;
1511+
genEmitCallWithCurrentGC(params);
1512+
}
1513+
}
1514+
}
1515+
1516+
/*****************************************************************************
1517+
* Emit a call to a helper function.
1518+
*/
1519+
void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, regNumber callTargetReg /*= REG_NA */)
1520+
{
1521+
EmitCallParams params;
1522+
1523+
CORINFO_CONST_LOOKUP helperFunction = compiler->compGetHelperFtn((CorInfoHelpFunc)helper);
1524+
params.ireg = callTargetReg;
1525+
1526+
if (helperFunction.accessType == IAT_VALUE)
1527+
{
1528+
params.callType = EC_FUNC_TOKEN;
1529+
params.addr = helperFunction.addr;
1530+
}
1531+
else
1532+
{
1533+
params.addr = nullptr;
1534+
assert(helperFunction.accessType == IAT_PVALUE);
1535+
void* pAddr = helperFunction.addr;
1536+
1537+
// Push indirection cell address onto stack for genEmitCall to dereference
1538+
GetEmitter()->emitIns_I(INS_i32_const, emitActualTypeSize(TYP_I_IMPL), (cnsval_ssize_t)pAddr);
1539+
1540+
params.callType = EC_INDIR_R;
1541+
}
1542+
1543+
params.methHnd = compiler->eeFindHelper(helper);
1544+
params.argSize = argSize;
1545+
params.retSize = retSize;
1546+
1547+
genEmitCallWithCurrentGC(params);
1548+
}
1549+
13761550
//------------------------------------------------------------------------
13771551
// genCodeForCompare: Produce code for a GT_EQ/GT_NE/GT_LT/GT_LE/GT_GE/GT_GT node.
13781552
//

src/coreclr/jit/emit.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3566,6 +3566,8 @@ void emitter::emitSetSecondRetRegGCType(instrDescCGCA* id, emitAttr secondRetSiz
35663566
}
35673567
#endif // MULTIREG_HAS_SECOND_GC_RET
35683568

3569+
#ifndef TARGET_WASM
3570+
35693571
/*****************************************************************************
35703572
*
35713573
* Allocate an instruction descriptor for an indirect call.
@@ -3731,6 +3733,8 @@ emitter::instrDesc* emitter::emitNewInstrCallDir(int argCnt,
37313733
}
37323734
}
37333735

3736+
#endif // TARGET_WASM
3737+
37343738
/*****************************************************************************
37353739
*
37363740
* Be very careful, some instruction descriptors are allocated as "tiny" and

src/coreclr/jit/emitfmtswasm.h

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,17 @@ enum ID_OPS
2626
// (unused)
2727
//////////////////////////////////////////////////////////////////////////////
2828

29-
IF_DEF(NONE, IS_NONE, NONE)
30-
IF_DEF(OPCODE, IS_NONE, NONE) // <opcode>
31-
IF_DEF(BLOCK, IS_NONE, NONE) // <opcode> <0x40>
32-
IF_DEF(RAW_ULEB128, IS_NONE, NONE) // <ULEB128 immediate>
33-
IF_DEF(ULEB128, IS_NONE, NONE) // <opcode> <ULEB128 immediate>
34-
IF_DEF(SLEB128, IS_NONE, NONE) // <opcode> <LEB128 immediate (signed)>
35-
IF_DEF(F32, IS_NONE, NONE) // <opcode> <f32 immediate (stored as 64-bit integer constant)>
36-
IF_DEF(F64, IS_NONE, NONE) // <opcode> <f64 immediate (stored as 64-bit integer constant)>
37-
IF_DEF(MEMARG, IS_NONE, NONE) // <opcode> <memarg> (<align> <offset>)
38-
IF_DEF(LOCAL_DECL, IS_NONE, NONE) // <ULEB128 immediate> <byte>
29+
IF_DEF(NONE, IS_NONE, NONE)
30+
IF_DEF(OPCODE, IS_NONE, NONE) // <opcode>
31+
IF_DEF(BLOCK, IS_NONE, NONE) // <opcode> <0x40>
32+
IF_DEF(RAW_ULEB128, IS_NONE, NONE) // <ULEB128 immediate>
33+
IF_DEF(ULEB128, IS_NONE, NONE) // <opcode> <ULEB128 immediate>
34+
IF_DEF(SLEB128, IS_NONE, NONE) // <opcode> <LEB128 immediate (signed)>
35+
IF_DEF(F32, IS_NONE, NONE) // <opcode> <f32 immediate (stored as 64-bit integer constant)>
36+
IF_DEF(F64, IS_NONE, NONE) // <opcode> <f64 immediate (stored as 64-bit integer constant)>
37+
IF_DEF(MEMARG, IS_NONE, NONE) // <opcode> <memarg> (<align> <offset>)
38+
IF_DEF(LOCAL_DECL, IS_NONE, NONE) // <ULEB128 immediate> <byte>
39+
IF_DEF(CALL_INDIRECT, IS_NONE, NONE) // <opcode> <ULEB128 immediate> <ULEB128 immediate>
3940

4041
#undef IF_DEF
4142
#endif // !DEFINE_ID_OPS

0 commit comments

Comments
 (0)