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
137 changes: 128 additions & 9 deletions src/coreclr/jit/codegenwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2597,8 +2597,15 @@ void CodeGen::genCodeForLclFld(GenTreeLclFld* tree)
NYI_WASM_SIMD("SIMD16 local field load");
}

GetEmitter()->emitIns_I(INS_local_get, EA_PTRSIZE, GetFramePointerRegIndex());
GetEmitter()->emitIns_S(ins_Load(type), emitTypeSize(tree), tree->GetLclNum(), tree->GetLclOffs());
if (type == TYP_SIMD12)
{
genLoadLclTypeSimd12(tree);
}
else
{
GetEmitter()->emitIns_I(INS_local_get, EA_PTRSIZE, GetFramePointerRegIndex());
GetEmitter()->emitIns_S(ins_Load(type), emitTypeSize(tree), tree->GetLclNum(), tree->GetLclOffs());
}
WasmProduceReg(tree);
}

Expand All @@ -2621,8 +2628,15 @@ void CodeGen::genCodeForLclVar(GenTreeLclVar* tree)
{
var_types type = varDsc->GetRegisterType(tree);

GetEmitter()->emitIns_I(INS_local_get, EA_PTRSIZE, GetFramePointerRegIndex());
GetEmitter()->emitIns_S(ins_Load(type), emitTypeSize(type), tree->GetLclNum(), 0);
if (type == TYP_SIMD12)
{
genLoadLclTypeSimd12(tree);
}
else
{
GetEmitter()->emitIns_I(INS_local_get, EA_PTRSIZE, GetFramePointerRegIndex());
GetEmitter()->emitIns_S(ins_Load(type), emitTypeSize(type), tree->GetLclNum(), 0);
}
WasmProduceReg(tree);
}
else
Expand Down Expand Up @@ -2695,6 +2709,94 @@ void CodeGen::genCodeForFrameSize(GenTree* tree)
WasmProduceReg(tree);
}

//------------------------------------------------------------------------
// genLoadLclTypeSimd12: Load a TYP_SIMD12 (i.e. Vector3) local into a v128.
//
// Arguments:
// tree - the GT_LCL_FLD or GT_LCL_VAR node
//
// Notes:
// Vector3 has no native wasm valtype, so it lives as a v128 with the low 12 bytes
// populated. The frame address is pushed twice: v128.load64_zero fills lanes 0-1
// (zeroing the rest) and v128.load32_lane fills lane 2 from bytes 8-11.
//
void CodeGen::genLoadLclTypeSimd12(GenTreeLclVarCommon* tree)
{
bool fpBased;
int frameOffset = m_compiler->lvaFrameAddress(tree->GetLclNum(), &fpBased) + (int)tree->GetLclOffs();
noway_assert(frameOffset >= 0); // WASM address modes are unsigned.
assert(fpBased);
unsigned fpIndex = GetFramePointerRegIndex();
emitter* emit = GetEmitter();

emit->emitIns_I(INS_local_get, EA_PTRSIZE, fpIndex);
emit->emitIns_I(INS_local_get, EA_PTRSIZE, fpIndex);
emit->emitIns_I(INS_v128_load64_zero, EA_8BYTE, frameOffset);
emit->emitIns_MemargLane(INS_v128_load32_lane, EA_4BYTE, frameOffset + 8, 2);
}

//------------------------------------------------------------------------
// genLoadIndTypeSimd12: Load a TYP_SIMD12 (i.e. Vector3) value through an indirection.
//
// Arguments:
// tree - the GT_IND node
//
// Notes:
// The address is left on the value stack by prior codegen and is multiply-used, so the
// trailing v128.load32_lane can re-push it for the upper 4 bytes.
//
void CodeGen::genLoadIndTypeSimd12(GenTreeIndir* tree)
{
emitter* emit = GetEmitter();

emit->emitIns_I(INS_local_get, EA_PTRSIZE, WasmRegToIndex(GetMultiUseOperandReg(tree->Addr())));
emit->emitIns_I(INS_v128_load64_zero, EA_8BYTE, 0);
emit->emitIns_MemargLane(INS_v128_load32_lane, EA_4BYTE, 8, 2);
}

//------------------------------------------------------------------------
// genStoreIndTypeSimd12: Store a TYP_SIMD12 (i.e. Vector3) value through an indirection.
//
// Arguments:
// tree - the GT_STOREIND node
//
// Notes:
// On entry the value stack holds [addr, value]. The value is teed into an internal v128
// local so it survives the low-8 store; the address is then re-materialized to store the
// upper 4 bytes via a lane store - re-emitting the frame pointer for a LCL_ADDR, or
// re-pushing the multiply-used address register otherwise.
//
void CodeGen::genStoreIndTypeSimd12(GenTreeStoreInd* tree)
{
emitter* emit = GetEmitter();
GenTree* addr = tree->Addr();

InternalRegs* regs = internalRegisters.GetAll(tree);
assert(regs->Count() == 1);
regNumber valReg = regs->Extract();

emit->emitIns_I(INS_local_tee, EA_16BYTE, WasmRegToIndex(valReg)); // [addr, value]
emit->emitIns_MemargLane(INS_v128_store64_lane, EA_8BYTE, 0, 0); // []

if (addr->OperIs(GT_LCL_ADDR))
{
bool fpBased;
int frameOffset = m_compiler->lvaFrameAddress(addr->AsLclVarCommon()->GetLclNum(), &fpBased) +
(int)addr->AsLclVarCommon()->GetLclOffs();
noway_assert(frameOffset >= 0); // WASM address modes are unsigned.
assert(fpBased);
emit->emitIns_I(INS_local_get, EA_PTRSIZE, GetFramePointerRegIndex()); // [fp]
emit->emitIns_I(INS_local_get, EA_16BYTE, WasmRegToIndex(valReg)); // [fp, value]
emit->emitIns_MemargLane(INS_v128_store32_lane, EA_4BYTE, frameOffset + 8, 2); // []
}
else
{
emit->emitIns_I(INS_local_get, EA_PTRSIZE, WasmRegToIndex(GetMultiUseOperandReg(addr))); // [addr]
emit->emitIns_I(INS_local_get, EA_16BYTE, WasmRegToIndex(valReg)); // [addr, value]
emit->emitIns_MemargLane(INS_v128_store32_lane, EA_4BYTE, 8, 2); // []
}
}

//------------------------------------------------------------------------
// genCodeForIndir: Produce code for a GT_IND node.
//
Expand All @@ -2705,8 +2807,7 @@ void CodeGen::genCodeForIndir(GenTreeIndir* tree)
{
assert(tree->OperIs(GT_IND));

var_types type = tree->TypeGet();
instruction ins = ins_Load(type);
var_types type = tree->TypeGet();

genConsumeAddress(tree->Addr());

Expand All @@ -2718,7 +2819,14 @@ void CodeGen::genCodeForIndir(GenTreeIndir* tree)

// TODO-WASM: Memory barriers

GetEmitter()->emitIns_I(ins, emitActualTypeSize(type), 0);
if (type == TYP_SIMD12)
{
genLoadIndTypeSimd12(tree);
}
else
{
GetEmitter()->emitIns_I(ins_Load(type), emitActualTypeSize(type), 0);
}

WasmProduceReg(tree);
}
Expand Down Expand Up @@ -2762,11 +2870,22 @@ void CodeGen::genCodeForStoreInd(GenTreeStoreInd* tree)
// module. Bail until SIMD16 store is properly supported.
NYI_WASM_SIMD("SIMD16 store indirect");
}
instruction ins = ins_Store(type);

// TODO-WASM: Memory barriers

GetEmitter()->emitIns_I(ins, emitActualTypeSize(type), 0);
if (type == TYP_SIMD8)
{
// stack: [addr, value] -> store the low 8 bytes.
GetEmitter()->emitIns_MemargLane(INS_v128_store64_lane, EA_8BYTE, 0, 0);
}
else if (type == TYP_SIMD12)
{
genStoreIndTypeSimd12(tree);
}
Comment thread
tannergooding marked this conversation as resolved.
else
{
GetEmitter()->emitIns_I(ins_Store(type), emitActualTypeSize(type), 0);
}
}

genUpdateLife(tree);
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/jit/instr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2070,6 +2070,10 @@ instruction CodeGenInterface::ins_Load(var_types srcType, bool aligned /*=false*
case TYP_DOUBLE:
return INS_f64_load;
#if defined(FEATURE_SIMD)
case TYP_SIMD8:
// SIMD8 (Vector2) lives as a v128 with the low 8 bytes populated. SIMD12 (Vector3) is
// handled at the callers since it needs a trailing lane load for the upper 4 bytes.
return INS_v128_load64_zero;
case TYP_SIMD16:
return INS_v128_load;
#endif
Expand Down
14 changes: 10 additions & 4 deletions src/coreclr/jit/lowerwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,14 @@ GenTree* Lowering::LowerStoreLoc(GenTreeLclVarCommon* storeLoc)
//
GenTree* Lowering::LowerStoreIndir(GenTreeStoreInd* node)
{
if ((node->gtFlags & GTF_IND_NONFAULTING) == 0)
if (((node->gtFlags & GTF_IND_NONFAULTING) == 0) ||
(node->TypeIs(TYP_SIMD12) && !node->Addr()->OperIs(GT_LCL_ADDR)))
{
// We need to be able to null check the address, and that requires multiple uses of the address operand.
SetMultiplyUsed(node->Addr() DEBUGARG("LowerStoreIndir faulting Addr"));
// SIMD12 stores also re-materialize the address for the trailing lane store, so force it there as well -
// unless the address is a re-materializable LCL_ADDR (the local-to-stack store rewrite), which codegen
// re-emits directly.
SetMultiplyUsed(node->Addr() DEBUGARG("LowerStoreIndir Addr (null check or simd12 lane store)"));
}

ContainCheckStoreIndir(node);
Expand Down Expand Up @@ -459,9 +463,11 @@ void Lowering::ContainCheckIndir(GenTreeIndir* indirNode)
return;
}

if (indirNode->OperIs(GT_IND) && ((indirNode->gtFlags & GTF_IND_NONFAULTING) == 0))
if (indirNode->OperIs(GT_IND) &&
(((indirNode->gtFlags & GTF_IND_NONFAULTING) == 0) || indirNode->TypeIs(TYP_SIMD12)))
{
SetMultiplyUsed(indirNode->Addr() DEBUGARG("ContainCheckIndir faulting load Addr"));
// SIMD12 loads re-materialize the address for the trailing lane load, so force it there regardless.
SetMultiplyUsed(indirNode->Addr() DEBUGARG("ContainCheckIndir load Addr (null check or simd12 lane load)"));
}

// TODO-WASM-CQ: contain suitable LEAs here. Take note of the fact that for this to be correct we must prove the
Expand Down
18 changes: 18 additions & 0 deletions src/coreclr/jit/regallocwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,14 @@ void WasmRegAlloc::CollectReferencesForIndir(GenTreeIndir* node)
{
GenTree* const addr = node->Addr();
ConsumeTemporaryRegForOperand(addr DEBUGARG("indirection address"));

if (node->OperIs(GT_STOREIND) && node->TypeIs(TYP_SIMD12))
{
// The SIMD12 store stashes the v128 value so it can re-push it for the trailing lane store.
regNumber internalReg = RequestInternalRegister(node, TYP_SIMD16);
regNumber releasedReg = ReleaseTemporaryRegister(WasmRegToType(internalReg));
assert(releasedReg == internalReg);
}
}

//------------------------------------------------------------------------
Expand Down Expand Up @@ -800,6 +808,16 @@ void WasmRegAlloc::RewriteLocalStackStore(GenTreeLclVarCommon* lclNode)
LIR::ReadOnlyRange storeRange(store, store);
m_compiler->GetLowering()->LowerRange(m_currentBlock, storeRange);

if (store->OperIs(GT_STOREIND) && store->TypeIs(TYP_SIMD12))
{
// genStoreIndTypeSimd12 tees the value into a v128 temporary to split the store into an 8-byte and a
// 4-byte lane store. The main collection walk does not revisit this freshly-introduced node, so request
// that internal register here. The re-materializable LCL_ADDR address needs no temporary.
regNumber internalReg = RequestInternalRegister(store, TYP_SIMD16);
regNumber releasedReg = ReleaseTemporaryRegister(WasmRegToType(internalReg));
assert(releasedReg == internalReg);
}

// FIXME-WASM: Should we be doing this here?
// CollectReferencesForNode(store);
}
Expand Down
Loading