Skip to content
Draft
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
1 change: 1 addition & 0 deletions src/coreclr/jit/emitfmtswasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ IF_DEF(F32, IS_NONE, NONE) // <opcode> <f32 immediate (stored as 64-bit
IF_DEF(F64, IS_NONE, NONE) // <opcode> <f64 immediate (stored as 64-bit integer constant)>
IF_DEF(MEMARG, IS_NONE, NONE) // <opcode> <memarg> (<align> <offset>)
IF_DEF(LOCAL_DECL, IS_NONE, NONE) // <ULEB128 immediate> <byte>
IF_DEF(MEMCPY, IS_NONE, NONE) // <memory index> <memory index>
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment should be <opcode> <memory index> <memory index> to be consistent with other instruction format comments that include the opcode in their description (e.g., ULEB128, SLEB128, MEMARG).

Suggested change
IF_DEF(MEMCPY, IS_NONE, NONE) // <memory index> <memory index>
IF_DEF(MEMCPY, IS_NONE, NONE) // <opcode> <memory index> <memory index>

Copilot uses AI. Check for mistakes.

#undef IF_DEF
#endif // !DEFINE_ID_OPS
Expand Down
20 changes: 20 additions & 0 deletions src/coreclr/jit/emitwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,11 @@ unsigned emitter::instrDesc::idCodeSize() const
size += idIsCnsReloc() ? PADDED_RELOC_SIZE : SizeOfULEB128(emitGetInsSC(this));
break;
}
case IF_MEMCPY:
{
size += idIsCnsReloc() ? PADDED_RELOC_SIZE : SizeOfULEB128(emitGetInsSC(this));
size += idIsCnsReloc() ? PADDED_RELOC_SIZE : SizeOfULEB128(emitGetInsSC(this));
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing break statement causes fall-through to the default case, which will call unreached() and cause a runtime error. This will prevent the IF_MEMCPY instruction format from working correctly.

Suggested change
size += idIsCnsReloc() ? PADDED_RELOC_SIZE : SizeOfULEB128(emitGetInsSC(this));
size += idIsCnsReloc() ? PADDED_RELOC_SIZE : SizeOfULEB128(emitGetInsSC(this));
break;

Copilot uses AI. Check for mistakes.
}
default:
unreached();
}
Expand Down Expand Up @@ -481,6 +486,14 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
dst += emitOutputByte(dst, valType);
break;
}
case IF_MEMCPY:
{
dst += emitOutputOpcode(dst, ins);
cnsval_ssize_t constant = emitGetInsSC(id);
dst += emitOutputULEB128(dst, (uint64_t)constant);
dst += emitOutputULEB128(dst, (uint64_t)constant);
break;
}
default:
NYI_WASM("emitOutputInstr");
break;
Expand Down Expand Up @@ -606,6 +619,13 @@ void emitter::emitDispIns(
}
break;

case IF_MEMCPY:
{
cnsval_ssize_t imm = emitGetInsSC(id);
printf(" %llu %llu", (uint64_t)imm, (uint64_t)imm);
dispJumpTargetIfAny();
}
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing break statement causes fall-through to the IF_LOCAL_DECL case. This will result in incorrect output when displaying IF_MEMCPY instructions, as it will also print the local declaration information.

Suggested change
}
}
break;

Copilot uses AI. Check for mistakes.

case IF_LOCAL_DECL:
{
unsigned int count = emitGetLclVarDeclCount(id);
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/jit/instrswasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,10 @@ INST(i64_trunc_sat_f32_u, "i64.trunc_sat_f32_u", 0, IF_OPCODE, 0x05FC)
INST(i64_trunc_sat_f64_s, "i64.trunc_sat_f64_s", 0, IF_OPCODE, 0x06FC)
INST(i64_trunc_sat_f64_u, "i64.trunc_sat_f64_u", 0, IF_OPCODE, 0x07FC)

INST(memory_copy, "memory.copy", 0, IF_MEMCPY, 0x10FC)
INST(memory_fill, "memory.fill", 0, IF_ULEB128, 0x11FC)


Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line should be removed for consistency with the rest of the file, which uses single blank lines between sections.

Suggested change

Copilot uses AI. Check for mistakes.
// clang-format on

#undef INST
57 changes: 56 additions & 1 deletion src/coreclr/jit/lowerwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,62 @@ GenTree* Lowering::LowerBinaryArithmetic(GenTreeOp* binOp)
//
void Lowering::LowerBlockStore(GenTreeBlk* blkNode)
{
NYI_WASM("LowerBlockStore");
GenTree* dstAddr = blkNode->Addr();
GenTree* src = blkNode->Data();
unsigned size = blkNode->Size();

if (blkNode->OperIsInitBlkOp())
{
if (src->OperIs(GT_INIT_VAL))
{
src->SetContained();
src = src->AsUnOp()->gtGetOp1();
}

if (blkNode->IsZeroingGcPointersOnHeap())
{
blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindLoop;
src->SetContained();
}
else
{
// memory.fill
}
Comment on lines +191 to +194
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code path leaves blkNode->gtBlkOpKind unset (it remains BlkOpKindInvalid). When codegen encounters this node, it will fail because there's no handling for WASM memory.fill instruction yet. Either this should set an appropriate operation kind, or GT_STORE_BLK handling should be added to codegenwasm.cpp before this PR is merged.

Copilot uses AI. Check for mistakes.
}
else
{
assert(src->OperIs(GT_IND, GT_LCL_VAR, GT_LCL_FLD));
src->SetContained();

if (src->OperIs(GT_LCL_VAR))
{
// TODO-1stClassStructs: for now we can't work with STORE_BLOCK source in register.
// TODO-WASM: Is this true for wasm as well?
const unsigned srcLclNum = src->AsLclVar()->GetLclNum();
comp->lvaSetVarDoNotEnregister(srcLclNum DEBUGARG(DoNotEnregisterReason::BlockOp));
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using DoNotEnregisterReason::StoreBlkSrc instead of DoNotEnregisterReason::BlockOp for consistency with other architectures (see lowerxarch.cpp:473). StoreBlkSrc is more specific and indicates this is specifically for STORE_BLK sources.

Suggested change
comp->lvaSetVarDoNotEnregister(srcLclNum DEBUGARG(DoNotEnregisterReason::BlockOp));
comp->lvaSetVarDoNotEnregister(srcLclNum DEBUGARG(DoNotEnregisterReason::StoreBlkSrc));

Copilot uses AI. Check for mistakes.
}

ClassLayout* layout = blkNode->GetLayout();
bool doCpObj = layout->HasGCPtr();

// CopyObj or CopyBlk
if (doCpObj)
{
// Try to use bulk copy helper
if (TryLowerBlockStoreAsGcBulkCopyCall(blkNode))
{
return;
}

assert(dstAddr->TypeIs(TYP_BYREF, TYP_I_IMPL));
blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindCpObjUnroll;
}
else
{
assert(blkNode->OperIs(GT_STORE_BLK));
// memory.copy
}
Comment on lines +224 to +228
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code path leaves blkNode->gtBlkOpKind unset (it remains BlkOpKindInvalid). When codegen encounters this node, it will fail because there's no handling for WASM memory.copy instruction yet. Either this should set an appropriate operation kind, or GT_STORE_BLK handling should be added to codegenwasm.cpp before this PR is merged.

Copilot uses AI. Check for mistakes.
}
}

//------------------------------------------------------------------------
Expand Down
Loading