Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
[RyuJit/WASM] Reach the NYI in "genFnProlog"
  • Loading branch information
SingleAccretion committed Nov 21, 2025
commit 95576a4fc3aa767c68dcdeff11ea987b3dfde110
10 changes: 9 additions & 1 deletion src/coreclr/jit/codegencommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6949,6 +6949,7 @@ void CodeGen::genLongReturn(GenTree* treeNode)
inst_Mov(targetType, REG_LNGRET_HI, hiRetVal->GetRegNum(), /* canSkip */ true, emitActualTypeSize(TYP_INT));
}
#endif // TARGET_X86 || TARGET_ARM
#endif // !TARGET_WASM

//------------------------------------------------------------------------
// genReturn: Generates code for return statement.
Expand Down Expand Up @@ -6995,7 +6996,7 @@ void CodeGen::genReturn(GenTree* treeNode)
else if (targetType != TYP_VOID)
{
assert(op1 != nullptr);
noway_assert(op1->GetRegNum() != REG_NA);
noway_assert(!HAS_FIXED_REGISTER_SET || (op1->GetRegNum() != REG_NA));

// !! NOTE !! genConsumeReg will clear op1 as GC ref after it has
// consumed a reg for the operand. This is because the variable
Expand All @@ -7005,6 +7006,7 @@ void CodeGen::genReturn(GenTree* treeNode)
// instructions until emitted then should not rely on the outdated GC info.
genConsumeReg(op1);

#if HAS_FIXED_REGISTER_SET
#if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
genSimpleReturn(treeNode);
#else // !TARGET_ARM64 || !TARGET_LOONGARCH64 || !TARGET_RISCV64
Expand Down Expand Up @@ -7047,6 +7049,7 @@ void CodeGen::genReturn(GenTree* treeNode)
inst_Mov_Extend(targetType, /* srcInReg */ true, retReg, op1->GetRegNum(), /* canSkip */ true);
}
#endif // !TARGET_ARM64 || !TARGET_LOONGARCH64 || !TARGET_RISCV64
#endif // HAS_FIXED_REGISTER_SET
}
}

Expand All @@ -7055,10 +7058,12 @@ void CodeGen::genReturn(GenTree* treeNode)
instGen_Set_Reg_To_Zero(EA_PTRSIZE, REG_ASYNC_CONTINUATION_RET);
}

#if EMIT_GENERATE_GCINFO
if (treeNode->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET))
{
genMarkReturnGCInfo();
}
#endif // EMIT_GENERATE_GCINFO

#ifdef PROFILING_SUPPORTED

Expand Down Expand Up @@ -7103,6 +7108,7 @@ void CodeGen::genReturn(GenTree* treeNode)
#endif // defined(DEBUG) && defined(TARGET_XARCH)
}

#ifndef TARGET_WASM
#ifdef SWIFT_SUPPORT
//------------------------------------------------------------------------
// genSwiftErrorReturn: Generates code for returning the normal return value,
Expand Down Expand Up @@ -7200,6 +7206,7 @@ void CodeGen::genCodeForAsyncContinuation(GenTree* tree)

genProduceReg(tree);
}
#endif // !TARGET_WASM

//------------------------------------------------------------------------
// isStructReturn: Returns whether the 'treeNode' is returning a struct.
Expand Down Expand Up @@ -7234,6 +7241,7 @@ bool CodeGen::isStructReturn(GenTree* treeNode)
#endif
}

#ifndef TARGET_WASM
//------------------------------------------------------------------------
// genStructReturn: Generates code for returning a struct.
//
Expand Down
10 changes: 9 additions & 1 deletion src/coreclr/jit/codegenlinear.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,7 @@ void CodeGenInterface::genUpdateVarReg(LclVarDsc* varDsc, GenTree* tree, int reg
assert(tree->IsMultiRegLclVar() || tree->OperIs(GT_COPY));
varDsc->SetRegNum(tree->GetRegByIndex(regIndex));
}
#endif // !TARGET_WASM

//------------------------------------------------------------------------
// genUpdateVarReg: Update the current register location for a lclVar
Expand All @@ -1078,6 +1079,7 @@ void CodeGenInterface::genUpdateVarReg(LclVarDsc* varDsc, GenTree* tree)
varDsc->SetRegNum(tree->GetRegNum());
}

#ifndef TARGET_WASM
//------------------------------------------------------------------------
// genUnspillLocal: Reload a register candidate local into a register, if needed.
//
Expand Down Expand Up @@ -1610,7 +1612,6 @@ regNumber CodeGen::genConsumeReg(GenTree* tree)
return tree->GetRegNum();
}

#ifndef TARGET_WASM
// Do liveness update for an address tree: one of GT_LEA, GT_LCL_VAR, or GT_CNS_INT (for call indirect).
void CodeGen::genConsumeAddress(GenTree* addr)
{
Expand Down Expand Up @@ -1763,6 +1764,7 @@ void CodeGen::genConsumeOperands(GenTreeOp* tree)
}
}

#ifndef TARGET_WASM
#if defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)
//------------------------------------------------------------------------
// genConsumeOperands: Do liveness update for the operands of a multi-operand node,
Expand Down Expand Up @@ -2087,6 +2089,7 @@ void CodeGen::genSpillLocal(unsigned varNum, var_types type, GenTreeLclVar* lclN
varNum, 0);
}
}
#endif // !TARGET_WASM

//-------------------------------------------------------------------------
// genProduceReg: do liveness update for register produced by the current
Expand All @@ -2105,6 +2108,7 @@ void CodeGen::genProduceReg(GenTree* tree)
tree->gtDebugFlags |= GTF_DEBUG_NODE_CG_PRODUCED;
#endif

#if HAS_FIXED_REGISTER_SET
if (tree->gtFlags & GTF_SPILL)
{
// Code for GT_COPY node gets generated as part of consuming regs by its parent.
Expand Down Expand Up @@ -2174,10 +2178,12 @@ void CodeGen::genProduceReg(GenTree* tree)
return;
}
}
#endif // HAS_FIXED_REGISTER_SET

// Updating variable liveness after instruction was emitted
genUpdateLife(tree);

#if EMIT_GENERATE_GCINFO
// If we've produced a register, mark it as a pointer, as needed.
if (tree->gtHasReg(compiler))
{
Expand Down Expand Up @@ -2254,8 +2260,10 @@ void CodeGen::genProduceReg(GenTree* tree)
}
}
}
#endif // EMIT_GENERATE_GCINFO
}

#ifndef TARGET_WASM
// transfer gc/byref status of src reg to dst reg
void CodeGen::genTransferRegGCState(regNumber dst, regNumber src)
{
Expand Down
120 changes: 113 additions & 7 deletions src/coreclr/jit/codegenwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,110 @@ void CodeGen::genFuncletEpilog()
NYI_WASM("genFuncletEpilog");
}

void CodeGen::genCodeForTreeNode(GenTree* node)
void CodeGen::genCodeForTreeNode(GenTree* treeNode)
{
NYI_WASM("genCodeForTreeNode");
#ifdef DEBUG
lastConsumedNode = nullptr;
if (compiler->verbose)
{
compiler->gtDispLIRNode(treeNode, "Generating: ");
}
#endif // DEBUG

assert(!treeNode->IsReuseRegVal()); // TODO-WASM-CQ: enable.

// Contained nodes are part of the parent for codegen purposes.
if (treeNode->isContained())
{
return;
}

switch (treeNode->OperGet())
{
case GT_ADD:
genCodeForBinary(treeNode->AsOp());
break;
Comment on lines +82 to +84
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the future is it possible for this sort of thing to be table-driven, by looking up the AsOp() value in a table optimistically? Or do you think we'll need a big switch?


case GT_LCL_VAR:
genCodeForLclVar(treeNode->AsLclVar());
break;

case GT_RETURN:
genReturn(treeNode);
break;

case GT_IL_OFFSET:
// Do nothing; this node is a marker for debug info.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we eventually want to do something with this node once we start generating DWARF debug info?

break;

default:
#ifdef DEBUG
NYIRAW(GenTree::OpName(treeNode->OperGet()));
#else
NYI_WASM("Opcode not implemented");
#endif
break;
}
}

//------------------------------------------------------------------------
// genCodeForBinary: Generate code for a binary arithmetic operator
//
// Arguments:
// treeNode - The binary operation for which we are generating code.
//
void CodeGen::genCodeForBinary(GenTreeOp* treeNode)
{
genConsumeOperands(treeNode);

instruction ins;
switch (treeNode->OperGet())
{
case GT_ADD:
if (!treeNode->TypeIs(TYP_INT))
{
NYI_WASM("genCodeForBinary: non-INT GT_ADD");
}
ins = INS_i32_add;
break;
default:
ins = INS_none;
NYI_WASM("genCodeForBinary");
break;
}

GetEmitter()->emitIns(ins);
genProduceReg(treeNode);
}

//------------------------------------------------------------------------
// genCodeForLclVar: Produce code for a GT_LCL_VAR node.
//
// Arguments:
// tree - the GT_LCL_VAR node
//
void CodeGen::genCodeForLclVar(GenTreeLclVar* tree)
{
assert(tree->OperIs(GT_LCL_VAR) && !tree->IsMultiReg());
LclVarDsc* varDsc = compiler->lvaGetDesc(tree);

// Unlike other targets, we can't "reload at the point of use", since that would require inserting instructions
// into the middle of an already-emitted instruction group. Instead, we order the nodes in a way that obeys the
// value stack constraints of WASM precisely. However, the liveness tracking is done in the same way as for other
// targets, hence "genProduceReg" is only called for non-candidates.
if (!varDsc->lvIsRegCandidate())
{
var_types type = varDsc->GetRegisterType(tree);
// TODO-WASM: actually local.get the frame base local here.
GetEmitter()->emitIns_S(ins_Load(type), emitTypeSize(tree), tree->GetLclNum(), 0);
genProduceReg(tree);
Comment on lines +157 to +158
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have expected to see an emit with a memarg here, am I misunderstanding what this does?

}
else
{
assert(genIsValidReg(varDsc->GetRegNum()));
unsigned wasmLclIndex = UnpackWasmReg(varDsc->GetRegNum());
GetEmitter()->emitIns_I(INS_local_get, emitTypeSize(tree), wasmLclIndex);
}
}

BasicBlock* CodeGen::genCallFinally(BasicBlock* block)
Expand All @@ -76,13 +177,23 @@ void CodeGen::genEHCatchRet(BasicBlock* block)
NYI_WASM("genEHCatchRet");
}

void CodeGen::genStructReturn(GenTree* treeNode)
{
NYI_WASM("genStructReturn");
}

void CodeGen::genEmitGSCookieCheck(bool tailCall)
{
// TODO-WASM: GS cookie checks have limited utility on WASM since they can only help
// with detecting linear memory stack corruption. Decide if we want them anyway.
NYI_WASM("genEmitGSCookieCheck");
}

void CodeGen::genProfilingLeaveCallback(unsigned helper)
{
NYI_WASM("genProfilingLeaveCallback");
}

void CodeGen::genSpillVar(GenTree* tree)
{
NYI_WASM("Put all spillng to memory under '#if HAS_FIXED_REGISTER_SET'");
Expand Down Expand Up @@ -159,11 +270,6 @@ void CodeGenInterface::genUpdateVarReg(LclVarDsc* varDsc, GenTree* tree, int reg
NYI_WASM("Move genUpdateVarReg from codegenlinear.cpp to codegencommon.cpp shared code");
}

void CodeGenInterface::genUpdateVarReg(LclVarDsc* varDsc, GenTree* tree)
{
NYI_WASM("Move genUpdateVarReg from codegenlinear.cpp to codegencommon.cpp shared code");
}

void RegSet::verifyRegUsed(regNumber reg)
{
}
2 changes: 1 addition & 1 deletion src/coreclr/jit/emit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10098,7 +10098,7 @@ void emitter::emitRemoveLastInstruction()
* emitGetInsSC: Get the instruction's constant value.
*/

cnsval_ssize_t emitter::emitGetInsSC(const instrDesc* id) const
cnsval_ssize_t emitter::emitGetInsSC(const instrDesc* id)
{
#ifdef TARGET_ARM // should it be TARGET_ARMARCH? Why do we need this? Note that on ARM64 we store scaled immediates
// for some formats
Expand Down
11 changes: 3 additions & 8 deletions src/coreclr/jit/emit.h
Original file line number Diff line number Diff line change
Expand Up @@ -1264,12 +1264,7 @@ class emitter
_idCodeSize = sz;
}
#elif defined(TARGET_WASM)
unsigned idCodeSize() const
{
// TODO-WASM: return an accurate number here based on instruction format/opcode like ARM64 above.
NYI_WASM("isCodeSize");
return 0;
}
unsigned idCodeSize() const;
#endif

emitAttr idOpSize() const
Expand Down Expand Up @@ -2460,8 +2455,8 @@ class emitter
static bool HasApxPpx(instruction ins);
#endif // TARGET_XARCH

cnsval_ssize_t emitGetInsSC(const instrDesc* id) const;
unsigned emitInsCount;
NOT_ARM(static) cnsval_ssize_t emitGetInsSC(const instrDesc* id);
unsigned emitInsCount;

/************************************************************************/
/* A few routines used for debug display purposes */
Expand Down
5 changes: 4 additions & 1 deletion src/coreclr/jit/emitfmtswasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ enum ID_OPS
// (unused)
//////////////////////////////////////////////////////////////////////////////

IF_DEF(NONE, IS_NONE, NONE)
IF_DEF(NONE, IS_NONE, NONE)
IF_DEF(OPCODE, IS_NONE, NONE) // <opcode>
IF_DEF(ULEB128, IS_NONE, NONE) // <opcode> <ULEB128 immediate>
IF_DEF(MEMARG, IS_NONE, NONE) // <opcode> <memarg> (<align> <offset>)

#undef IF_DEF
#endif // !DEFINE_ID_OPS
Expand Down
Loading
Loading