-
Notifications
You must be signed in to change notification settings - Fork 5.4k
[Wasm RyuJIT] Address block store issues from #125756 #125770
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2789,6 +2789,7 @@ void CodeGen::genCodeForStoreBlk(GenTreeBlk* blkOp) | |||||||||||||||||||||||||||||||||||||||||||||
| switch (blkOp->gtBlkOpKind) | ||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||
| case GenTreeBlk::BlkOpKindCpObjUnroll: | ||||||||||||||||||||||||||||||||||||||||||||||
| case GenTreeBlk::BlkOpKindNativeOpcode: | ||||||||||||||||||||||||||||||||||||||||||||||
| genCodeForCpObj(blkOp->AsBlk()); | ||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -2797,13 +2798,6 @@ void CodeGen::genCodeForStoreBlk(GenTreeBlk* blkOp) | |||||||||||||||||||||||||||||||||||||||||||||
| genCodeForInitBlkLoop(blkOp); | ||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| case GenTreeBlk::BlkOpKindNativeOpcode: | ||||||||||||||||||||||||||||||||||||||||||||||
| genConsumeOperands(blkOp); | ||||||||||||||||||||||||||||||||||||||||||||||
| // Emit the size constant expected by the memory.copy and memory.fill opcodes | ||||||||||||||||||||||||||||||||||||||||||||||
| GetEmitter()->emitIns_I(INS_i32_const, EA_4BYTE, blkOp->Size()); | ||||||||||||||||||||||||||||||||||||||||||||||
| GetEmitter()->emitIns_I(isCopyBlk ? INS_memory_copy : INS_memory_fill, EA_8BYTE, LINEAR_MEMORY_INDEX); | ||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||||||||||||
| unreached(); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -2812,74 +2806,111 @@ void CodeGen::genCodeForStoreBlk(GenTreeBlk* blkOp) | |||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| //------------------------------------------------------------------------ | ||||||||||||||||||||||||||||||||||||||||||||||
| // genCodeForCpObj: Produce code for a GT_STORE_BLK node that represents a cpobj operation. | ||||||||||||||||||||||||||||||||||||||||||||||
| // genCodeForCpObj: Produce code for a GT_STORE_BLK node that represents a cpobj operation | ||||||||||||||||||||||||||||||||||||||||||||||
| // or a native memory.copy or memory.fill opcode. We share this function for both types of | ||||||||||||||||||||||||||||||||||||||||||||||
| // block stores because they have a lot of common logic. | ||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||
| // Arguments: | ||||||||||||||||||||||||||||||||||||||||||||||
| // cpObjNode - the node | ||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||
| void CodeGen::genCodeForCpObj(GenTreeBlk* cpObjNode) | ||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||
| GenTree* dstAddr = cpObjNode->Addr(); | ||||||||||||||||||||||||||||||||||||||||||||||
| GenTree* source = cpObjNode->Data(); | ||||||||||||||||||||||||||||||||||||||||||||||
| var_types srcAddrType = TYP_BYREF; | ||||||||||||||||||||||||||||||||||||||||||||||
| regNumber dstReg = GetMultiUseOperandReg(dstAddr); | ||||||||||||||||||||||||||||||||||||||||||||||
| unsigned dstOffset = 0; | ||||||||||||||||||||||||||||||||||||||||||||||
| regNumber srcReg; | ||||||||||||||||||||||||||||||||||||||||||||||
| unsigned srcOffset; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Identify the register containing our source base address, either a multi-use | ||||||||||||||||||||||||||||||||||||||||||||||
| // reg representing the operand of a GT_IND, or the frame pointer for LCL_VAR/LCL_FLD. | ||||||||||||||||||||||||||||||||||||||||||||||
| if (source->OperIs(GT_IND)) | ||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||
| bool doNullCheck = (source->gtFlags & GTF_IND_NONFAULTING) == 0; | ||||||||||||||||||||||||||||||||||||||||||||||
| source = source->gtGetOp1(); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert(!source->isContained()); | ||||||||||||||||||||||||||||||||||||||||||||||
| srcAddrType = source->TypeGet(); | ||||||||||||||||||||||||||||||||||||||||||||||
| srcReg = GetMultiUseOperandReg(source); | ||||||||||||||||||||||||||||||||||||||||||||||
| srcOffset = 0; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| if (doNullCheck) | ||||||||||||||||||||||||||||||||||||||||||||||
| struct operandRec | ||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Naming nit: (What does "rec" mean?) |
||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||
| var_types addrType; | ||||||||||||||||||||||||||||||||||||||||||||||
| unsigned offset; | ||||||||||||||||||||||||||||||||||||||||||||||
| regNumber reg; | ||||||||||||||||||||||||||||||||||||||||||||||
| bool isContained; | ||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| auto makeOperandRec = [&](GenTree* operand, bool isSource) { | ||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using this for the destination looks unnecessary, since we don't need to materialize the destination address manually. The pattern we had here before was simpler. More generally it feels like open-coding the various possibilities would lead to simpler code. |
||||||||||||||||||||||||||||||||||||||||||||||
| var_types addrType = TYP_BYREF; | ||||||||||||||||||||||||||||||||||||||||||||||
| regNumber reg; | ||||||||||||||||||||||||||||||||||||||||||||||
| unsigned offset; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Identify the register containing our source base address, either a multi-use | ||||||||||||||||||||||||||||||||||||||||||||||
| // reg representing the operand of a GT_IND, or the frame pointer for LCL_VAR/LCL_FLD. | ||||||||||||||||||||||||||||||||||||||||||||||
| if (operand->OperIs(GT_IND)) | ||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||
| genEmitNullCheck(srcReg); | ||||||||||||||||||||||||||||||||||||||||||||||
| bool doNullCheck = (operand->gtFlags & GTF_IND_NONFAULTING) == 0; | ||||||||||||||||||||||||||||||||||||||||||||||
| operand = operand->gtGetOp1(); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert(!operand->isContained()); | ||||||||||||||||||||||||||||||||||||||||||||||
| addrType = operand->TypeGet(); | ||||||||||||||||||||||||||||||||||||||||||||||
| reg = GetMultiUseOperandReg(operand); | ||||||||||||||||||||||||||||||||||||||||||||||
| offset = 0; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| if (doNullCheck) | ||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||
| genEmitNullCheck(reg); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||
| assert(source->OperIs(GT_LCL_FLD, GT_LCL_VAR)); | ||||||||||||||||||||||||||||||||||||||||||||||
| GenTreeLclVarCommon* lclVar = source->AsLclVarCommon(); | ||||||||||||||||||||||||||||||||||||||||||||||
| bool fpBased; | ||||||||||||||||||||||||||||||||||||||||||||||
| srcOffset = m_compiler->lvaFrameAddress(lclVar->GetLclNum(), &fpBased) + lclVar->GetLclOffs(); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert(fpBased); | ||||||||||||||||||||||||||||||||||||||||||||||
| srcReg = GetFramePointerReg(); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| else if (isSource && operand->OperIs(GT_CNS_INT)) | ||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||
| addrType = TYP_INT; | ||||||||||||||||||||||||||||||||||||||||||||||
| offset = 0; | ||||||||||||||||||||||||||||||||||||||||||||||
| reg = REG_NA; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||
| assert(operand->OperIs(GT_LCL_FLD, GT_LCL_VAR, GT_LCL_ADDR)); | ||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah when I expanded the cases this got called in, this assert tripped up on a LCL_ADDR at least once. Conceptually it seems reasonable to get an address here. |
||||||||||||||||||||||||||||||||||||||||||||||
| GenTreeLclVarCommon* lclVar = operand->AsLclVarCommon(); | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+2847
to
+2856
|
||||||||||||||||||||||||||||||||||||||||||||||
| bool fpBased; | ||||||||||||||||||||||||||||||||||||||||||||||
| reg = GetFramePointerReg(); | ||||||||||||||||||||||||||||||||||||||||||||||
| offset = m_compiler->lvaFrameAddress(lclVar->GetLclNum(), &fpBased) + lclVar->GetLclOffs(); | ||||||||||||||||||||||||||||||||||||||||||||||
| assert(fpBased); | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+2857
to
+2860
|
||||||||||||||||||||||||||||||||||||||||||||||
| bool fpBased; | |
| reg = GetFramePointerReg(); | |
| offset = m_compiler->lvaFrameAddress(lclVar->GetLclNum(), &fpBased) + lclVar->GetLclOffs(); | |
| assert(fpBased); | |
| // For address-valued locals (e.g. GT_LCL_ADDR) or destination operands, the | |
| // address is already materialized in a register; use that directly. | |
| if (operand->OperIs(GT_LCL_ADDR) || !isSource) | |
| { | |
| addrType = operand->TypeGet(); | |
| reg = GetMultiUseOperandReg(operand); | |
| offset = 0; | |
| } | |
| else | |
| { | |
| // For source locals that represent stack-resident buffers, compute | |
| // the address as frame-pointer + frame offset. | |
| bool fpBased; | |
| reg = GetFramePointerReg(); | |
| offset = m_compiler->lvaFrameAddress(lclVar->GetLclNum(), &fpBased) + lclVar->GetLclOffs(); | |
| assert(fpBased); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this correct? I don't really understand it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are quite a few different cases and it's hard to be sure without tracing through what happens upstream.
You might want to work through a set of examples just to see the variety of things that need to be handled. Something like:
struct S { ... }
S getS();
void useS(ref S s);
void foo(ref S src, ref S dst) { dst = src }
void foo(S src, ref S dst) { dst = src }
void foo(ref S src) { S dst = src; }
void foo(ref S src) { useS(src); }
void foo (ref S dst) { S src = new S(...); dst = src }
void foo (ref S dst) { S src = new S; dst = src }
void foo (ref S dst) { dst = getS(); }
and try cases where S is a single-field wrapper and also has multiple fields.
kg marked this conversation as resolved.
Show resolved
Hide resolved
kg marked this conversation as resolved.
Show resolved
Hide resolved
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -275,18 +275,19 @@ void Lowering::LowerBlockStore(GenTreeBlk* blkNode) | |
| } | ||
|
|
||
| blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindCpObjUnroll; | ||
| SetMultiplyUsed(dstAddr); | ||
| if (src->OperIs(GT_IND)) | ||
| { | ||
| SetMultiplyUsed(src->gtGetOp1()); | ||
| } | ||
| } | ||
| else | ||
| { | ||
| assert(blkNode->OperIs(GT_STORE_BLK)); | ||
| // memory.copy | ||
| blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindNativeOpcode; | ||
| } | ||
|
|
||
| SetMultiplyUsed(dstAddr); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Checks missing for whether this actually needs the null check ( Also, since we're fixing this code, may as well fix it completely by adding the null check for init blocks too. |
||
| if (src->OperIs(GT_IND)) | ||
| { | ||
| SetMultiplyUsed(src->gtGetOp1()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no specific need to overload
genCodeForCpObj... we can just house all this logic ingenCodeForStoreBlkif it makes things easier to share. It actually looks like it would flush out the logic to look more straight as well.