Repository: dotnet/runtime
File: src/coreclr/jit/codegenwasm.cpp (C++)

Code before:
void CodeGen::genEnregisterOSRArgsAndLocals()
{
    unreached(); // OSR not supported on WASM.
}

//------------------------------------------------------------------------
// genHomeRegisterParams: place register arguments into their RA-assigned locations.
//
// For the WASM RA, we have a much simplified (compared to LSRA) contract of:
// - If an argument is live on entry in a set of registers, then the RA will
//   assign those registers to that argument on entry.
// This means we never need to do any copying or cycle resolution here.
//
// The main motivation for this (along with the obvious CQ implications) is
// obviating the need to adapt the general "RegGraph"-based algorithm to
// !HAS_FIXED_REGISTER_SET constraints (no reg masks).
//
// Arguments:
//    initReg            - Unused
//    initRegStillZeroed - Unused
//
void CodeGen::genHomeRegisterParams(regNumber initReg, bool* initRegStillZeroed)
{
    JITDUMP("*************** In genHomeRegisterParams()\n");

    auto spillParam = [this](unsigned lclNum, unsigned offset, unsigned paramLclNum, const ABIPassingSegment& segment) {
        assert(segment.IsPassedInRegister());

        LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
        if (varDsc->lvTracked && !VarSetOps::IsMember(compiler, compiler->fgFirstBB->bbLiveIn, varDsc->lvVarIndex))
        {
            return;
        }

        if (varDsc->lvOnFrame && (!varDsc->lvIsInReg() || varDsc->lvLiveInOutOfHndlr))
        {
            LclVarDsc* paramVarDsc = compiler->lvaGetDesc(paramLclNum);
            var_types  storeType   = genParamStackType(paramVarDsc, segment);
            if (!varDsc->TypeIs(TYP_STRUCT) && (genTypeSize(genActualType(varDsc)) < genTypeSize(storeType)))
            {
                // Can happen for struct fields due to padding.
                storeType = genActualType(varDsc);
            }

            GetEmitter()->emitIns_I(INS_local_get, EA_PTRSIZE, WasmRegToIndex(GetFramePointerReg()));
            GetEmitter()->emitIns_I(INS_local_get, emitActualTypeSize(storeType),
                                    WasmRegToIndex(segment.GetRegister()));
            GetEmitter()->emitIns_S(ins_Store(storeType), emitActualTypeSize(storeType), lclNum, offset);
        }

        if (varDsc->lvIsInReg())

Review comment (bug):
I think this is a situation where the RA is wrong; with implicitly referenced variables we have to assume there may be some references to them outside of IR. So something like below:
```diff
diff --git a/src/coreclr/jit/regallocwasm.cpp b/src/coreclr/jit/regallocwasm.cpp
index 48bcd978abd..8c001fa77e7 100644
--- a/src/coreclr/jit/regallocwasm.cpp
+++ b/src/coreclr/jit/regallocwasm.cpp
@@ -100,6 +100,10 @@ void WasmRegAlloc::IdentifyCandidates()
         {
             JITDUMP("RA candidate: V%02u", lclNum);
         }
+        else if ((m_fpReg == REG_NA) && (varDsc->lvRefCnt() != 0))
+        {
+            m_fpReg = AllocateFreeRegister(TYP_I_IMPL);
+        }
     }
 }
 
@@ -147,11 +151,6 @@ void WasmRegAlloc::AllocateAndResolveNode(GenTree* node)
             {
                 RewriteLocalStackStore(node->AsLclVarCommon());
             }
-
-            if (m_fpReg == REG_NA)
-            {
-                m_fpReg = AllocateFreeRegister(TYP_I_IMPL);
-            }
         }
     }
     else if (node->OperIs(GT_LCLHEAP))
diff --git a/src/coreclr/jit/registeropswasm.cpp b/src/coreclr/jit/registeropswasm.cpp
index 0386d477d1b..b8f96cc35a7 100644
--- a/src/coreclr/jit/registeropswasm.cpp
+++ b/src/coreclr/jit/registeropswasm.cpp
@@ -109,7 +109,10 @@ unsigned UnpackWasmReg(regNumber reg, WasmValueType* pType)
 //
 unsigned WasmRegToIndex(regNumber reg)
 {
-    return UnpackWasmReg(reg);
+    WasmValueType wasmType;
+    unsigned index = UnpackWasmReg(reg, &wasmType);
+    assert((WasmValueType::Invalid < wasmType) && (wasmType < WasmValueType::Count));
+    return index;
 }
 
 bool genIsValidReg(regNumber reg)
```

Code after:
void CodeGen::genEnregisterOSRArgsAndLocals()
{
    unreached(); // OSR not supported on WASM.
}

//------------------------------------------------------------------------
// genHomeRegisterParams: place register arguments into their RA-assigned locations.
//
// For the WASM RA, we have a much simplified (compared to LSRA) contract of:
// - If an argument is live on entry in a set of registers, then the RA will
//   assign those registers to that argument on entry.
// This means we never need to do any copying or cycle resolution here.
//
// The main motivation for this (along with the obvious CQ implications) is
// obviating the need to adapt the general "RegGraph"-based algorithm to
// !HAS_FIXED_REGISTER_SET constraints (no reg masks).
//
// Arguments:
//    initReg            - Unused
//    initRegStillZeroed - Unused
//
void CodeGen::genHomeRegisterParams(regNumber initReg, bool* initRegStillZeroed)
{
    JITDUMP("*************** In genHomeRegisterParams()\n");

    if (GetStackPointerReg() == REG_NA)
    {
        // We didn't see any local or argument references, so there's nothing to spill.
        // TODO-WASM: debug codegen would likely spill all the user args anyways.
        //
        JITDUMP("  No local references -- skipping parameter homing\n");
        assert(!isFramePointerUsed());
        return;
    }

    auto spillParam = [this](unsigned lclNum, unsigned offset, unsigned paramLclNum, const ABIPassingSegment& segment) {
        assert(segment.IsPassedInRegister());

        LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
        if (varDsc->lvTracked && !VarSetOps::IsMember(compiler, compiler->fgFirstBB->bbLiveIn, varDsc->lvVarIndex))
        {
            return;
        }

        if (varDsc->lvOnFrame && (!varDsc->lvIsInReg() || varDsc->lvLiveInOutOfHndlr))
        {
            LclVarDsc* paramVarDsc = compiler->lvaGetDesc(paramLclNum);
            var_types  storeType   = genParamStackType(paramVarDsc, segment);
            if (!varDsc->TypeIs(TYP_STRUCT) && (genTypeSize(genActualType(varDsc)) < genTypeSize(storeType)))
            {
                // Can happen for struct fields due to padding.