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

Code before:
//
// Arguments:
//    codeKind -- kind of throw helper call needed
//
// Notes:
//    On entry the predicate for the throw helper is the only item on the Wasm stack.
//    An exception is thrown if the predicate is true.
//
void CodeGen::genJumpToThrowHlpBlk(SpecialCodeKind codeKind)
{
    if (m_compiler->fgUseThrowHelperBlocks())
    {
        Compiler::AddCodeDsc* const add = m_compiler->fgGetExcptnTarget(codeKind, m_compiler->compCurBB);
        assert(add != nullptr);
        assert(add->acdUsed);
        inst_JMP(EJ_jmpif, add->acdDstBlk);
    }
    else
    {
        GetEmitter()->emitIns(INS_if);
        genEmitHelperCall(m_compiler->acdHelper(codeKind), 0, EA_UNKNOWN);
        GetEmitter()->emitIns(INS_end);
    }
}

//---------------------------------------------------------------------
// genCodeForNullCheck - generate code for a GT_NULLCHECK node
//
// Arguments:
//    tree - the GT_NULLCHECK node
//
void CodeGen::genCodeForNullCheck(GenTreeIndir* tree)
{
    genConsumeAddress(tree->Addr());
    GetEmitter()->emitIns_I(INS_I_const, EA_PTRSIZE, m_compiler->compMaxUncheckedOffsetForNullObject);
    GetEmitter()->emitIns(INS_I_le_u);
    genJumpToThrowHlpBlk(SCK_NULL_CHECK);
}

//------------------------------------------------------------------------
// genRangeCheck - generate code for a GT_BOUNDS_CHECK node
//
// Arguments:
//    tree - the GT_BOUNDS_CHECK node
//
// Notes:
//    Incoming stack args are index; length (tos).
//
void CodeGen::genRangeCheck(GenTree* tree)
{
    assert(tree->OperIs(GT_BOUNDS_CHECK));

Review comment (bug):
In the inline-throw path (!fgUseThrowHelperBlocks), genJumpToThrowHlpBlk emits a helper call via genEmitHelperCall without supplying the WasmShadowStackPointer argument that morph inserts for managed calls on WASM. This can break the WASM calling convention for throw helpers (stack underflow/wrong params) unless throw helper blocks are always enabled. Consider either (1) ensuring throw helper blocks are always used on WASM, or (2) explicitly providing the shadow stack pointer argument when emitting the helper call here.

Code after:
//
// Arguments:
//    codeKind -- kind of throw helper call needed
//
// Notes:
//    On entry the predicate for the throw helper is the only item on the Wasm stack.
//    An exception is thrown if the predicate is true.
//
void CodeGen::genJumpToThrowHlpBlk(SpecialCodeKind codeKind)
{
    if (m_compiler->fgUseThrowHelperBlocks())
    {
        Compiler::AddCodeDsc* const add = m_compiler->fgGetExcptnTarget(codeKind, m_compiler->compCurBB);
        assert(add != nullptr);
        assert(add->acdUsed);
        inst_JMP(EJ_jmpif, add->acdDstBlk);
    }
    else
    {
        GetEmitter()->emitIns(INS_if);
        // Throw helper arity is (i (sp)) -> (void).
        // Push SP here as the arg for the call.
        GetEmitter()->emitIns_I(INS_local_get, EA_PTRSIZE, WasmRegToIndex(GetStackPointerReg()));
        genEmitHelperCall(m_compiler->acdHelper(codeKind), 0, EA_UNKNOWN);
        GetEmitter()->emitIns(INS_end);
    }
}

//---------------------------------------------------------------------
// genCodeForNullCheck - generate code for a GT_NULLCHECK node
//
// Arguments:
//    tree - the GT_NULLCHECK node
//
void CodeGen::genCodeForNullCheck(GenTreeIndir* tree)
{
    genConsumeAddress(tree->Addr());
    GetEmitter()->emitIns_I(INS_I_const, EA_PTRSIZE, m_compiler->compMaxUncheckedOffsetForNullObject);
    GetEmitter()->emitIns(INS_I_le_u);
    genJumpToThrowHlpBlk(SCK_NULL_CHECK);
}

//------------------------------------------------------------------------
// genRangeCheck - generate code for a GT_BOUNDS_CHECK node
//
// Arguments:
//    tree - the GT_BOUNDS_CHECK node
//
// Notes:
//    Incoming stack args are index; length (tos).
//