Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
33 changes: 33 additions & 0 deletions src/coreclr/jit/codegenwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2652,6 +2652,16 @@ void CodeGen::genCodeForIndir(GenTreeIndir* tree)

genConsumeAddress(tree->Addr());

// On wasm a load from a null address does not fault, so emit an explicit null check
// for faulting loads (mirrors genCodeForStoreInd). Use the same predicate the throw-
// helper reservation phase uses (StackLevelSetter), so we never demand an unreserved
// throw helper block here.
if (((tree->gtFlags & GTF_EXCEPT) != 0) && tree->OperMayThrow(m_compiler))
{
regNumber addrReg = GetMultiUseOperandReg(tree->Addr());
genEmitNullCheck(addrReg);
}

// TODO-WASM: Memory barriers

GetEmitter()->emitIns_I(ins, emitActualTypeSize(type), 0);
Expand Down Expand Up @@ -3587,6 +3597,29 @@ void CodeGen::genCallFinally(BasicBlock* block)
{
GetEmitter()->emitIns(INS_end);
}

return;
}

// After call_indirect returns, control falls through to the next wasm instruction.
// The paired BBJ_CALLFINALLYRET has no code emitted (see genCodeForBlock), so the
// CALLFINALLYRET's continuation must be reached either by fall-through (when it's
// the next block in linear order) or by an explicit branch emitted here.
//
// This is wasm-specific: other targets rely on the native `call`/`ret` to land at
// the instruction after the call, and the JIT layout places the CALLFINALLYRET's
// continuation immediately after. On wasm the layout may place the continuation
// elsewhere (e.g. a loop header reached via back-edge, as produced by a leave from
// a catch handler that targets a label outside its enclosing try-finally).
//
assert(block->isBBCallFinallyPair());
BasicBlock* const callFinallyRet = block->Next();
assert(callFinallyRet->KindIs(BBJ_CALLFINALLYRET));
BasicBlock* const continuation = callFinallyRet->GetTarget();

if (continuation->bbPreorderNum != callFinallyRet->bbPreorderNum + 1)
{
inst_JMP(EJ_jmp, continuation);
}
}

Expand Down
7 changes: 7 additions & 0 deletions src/coreclr/jit/lowerwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,13 @@ void Lowering::ContainCheckIndir(GenTreeIndir* indirNode)
return;
}

if (indirNode->OperIs(GT_IND) && ((indirNode->gtFlags & GTF_IND_NONFAULTING) == 0))
{
// On wasm a load from a null address does not fault, so a faulting load needs an
// explicit null check, which requires multiple uses of the address operand.
SetMultiplyUsed(indirNode->Addr() DEBUGARG("ContainCheckIndir faulting load Addr"));
}

// TODO-WASM-CQ: contain suitable LEAs here. Take note of the fact that for this to be correct we must prove the
// LEA doesn't overflow. It will involve creating a new frontend node to represent "nuw" (offset) addition.
}
Expand Down
9 changes: 9 additions & 0 deletions src/coreclr/jit/regallocwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,15 @@ void WasmRegAlloc::CollectReferencesForNode(GenTree* node)
CollectReferencesForStoreInd(node->AsStoreInd());
break;

case GT_IND:
if (node->AsIndir()->Addr()->gtLIRFlags & LIR::Flags::MultiplyUsed)
{
// Faulting loads are null checked on wasm; consume the temporary register
// holding the address so it is available for both the check and the load.
ConsumeTemporaryRegForOperand(node->AsIndir()->Addr() DEBUGARG("indir load null check"));
}
break;

case GT_STORE_BLK:
CollectReferencesForBlockStore(node->AsBlk());
break;
Expand Down
Loading