Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 5.6k
[Wasm RyuJIT] Block stores#123738
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
Uh oh!
There was an error while loading. Please reload this page.
[Wasm RyuJIT] Block stores #123738
Changes from all commits
d90c4f6e902420b1935c4b9ac6d95f0e66ba762b9e8625798ff7dea5a08dea16c57d89a24be65File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -10,6 +10,8 @@ | ||
| #include "regallocwasm.h" | ||
| #include "fgwasm.h" | ||
| static const int LINEAR_MEMORY_INDEX = 0; | ||
| #ifdef TARGET_64BIT | ||
| static const instruction INS_I_const = INS_i64_const; | ||
| static const instruction INS_I_add = INS_i64_add; | ||
| @@ -599,6 +601,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) | ||
| genLeaInstruction(treeNode->AsAddrMode()); | ||
| break; | ||
| case GT_STORE_BLK: | ||
| genCodeForStoreBlk(treeNode->AsBlk()); | ||
| break; | ||
| case GT_MEMORYBARRIER: | ||
| // No-op for single-threaded wasm. | ||
| assert(!WASM_THREAD_SUPPORT); | ||
| @@ -1964,6 +1970,68 @@ void CodeGen::genCompareFloat(GenTreeOp* treeNode) | ||
| WasmProduceReg(treeNode); | ||
| } | ||
| //------------------------------------------------------------------------ | ||
| // genCodeForStoreBlk: Produce code for a GT_STORE_BLK node. | ||
| // | ||
| // Arguments: | ||
| // blkOp - the node | ||
| // | ||
| void CodeGen::genCodeForStoreBlk(GenTreeBlk* blkOp) | ||
| { | ||
| assert(blkOp->OperIs(GT_STORE_BLK)); | ||
| bool isCopyBlk = blkOp->OperIsCopyBlkOp(); | ||
| switch (blkOp->gtBlkOpKind) | ||
| { | ||
| case GenTreeBlk::BlkOpKindCpObjUnroll: | ||
| genCodeForCpObj(blkOp->AsBlk()); | ||
| break; | ||
| case GenTreeBlk::BlkOpKindLoop: | ||
| assert(!isCopyBlk); | ||
| genCodeForInitBlkLoop(blkOp); | ||
| break; | ||
| case GenTreeBlk::BlkOpKindNativeOpcode: | ||
| genConsumeOperands(blkOp); | ||
kg marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. kg marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| // 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; | ||
kg marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| default: | ||
| unreached(); | ||
| } | ||
| } | ||
| void CodeGen::genCodeForCpObj(GenTreeBlk* cpObjNode) | ||
| { | ||
| NYI_WASM("genCodeForCpObj"); | ||
| } | ||
| //------------------------------------------------------------------------ | ||
| // genCodeForInitBlkLoop - Generate code for an InitBlk using an inlined for-loop. | ||
| // It's needed for cases when size is too big to unroll and we're not allowed | ||
| // to use memset call due to atomicity requirements. | ||
| // | ||
| // Arguments: | ||
| // blkOp - the GT_STORE_BLK node | ||
| // | ||
| void CodeGen::genCodeForInitBlkLoop(GenTreeBlk* blkOp) | ||
| { | ||
| // TODO-WASM: In multi-threaded wasm we will need to generate a for loop that atomically zeroes one GC ref | ||
| // at a time. Right now we're single-threaded, so we can just use memory.fill. | ||
| assert(!WASM_THREAD_SUPPORT); | ||
| genConsumeOperands(blkOp); | ||
| // Emit the value constant expected by the memory.fill opcode (zero) | ||
| GetEmitter()->emitIns_I(INS_i32_const, EA_4BYTE, 0); | ||
| // 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(INS_memory_fill, EA_8BYTE, LINEAR_MEMORY_INDEX); | ||
| } | ||
| BasicBlock* CodeGen::genCallFinally(BasicBlock* block) | ||
| { | ||
| assert(block->KindIs(BBJ_CALLFINALLY)); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -430,6 +430,12 @@ unsigned emitter::instrDesc::idCodeSize() const | ||
| size += idIsCnsReloc() ? PADDED_RELOC_SIZE : SizeOfULEB128(emitGetInsSC(this)); | ||
| break; | ||
| } | ||
| case IF_MEMIDX_MEMIDX: | ||
| { | ||
| size += idIsCnsReloc() ? PADDED_RELOC_SIZE : SizeOfULEB128(emitGetInsSC(this)); | ||
| size += idIsCnsReloc() ? PADDED_RELOC_SIZE : SizeOfULEB128(emitGetInsSC(this)); | ||
kg marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| break; | ||
| } | ||
| default: | ||
| unreached(); | ||
| } | ||
| @@ -651,6 +657,14 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) | ||
| dst += emitOutputByte(dst, valType); | ||
| break; | ||
| } | ||
| case IF_MEMIDX_MEMIDX: | ||
| { | ||
| 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; | ||
| @@ -792,7 +806,12 @@ void emitter::emitDispIns( | ||
| dispHandleIfAny(); | ||
| } | ||
| break; | ||
| case IF_MEMIDX_MEMIDX: | ||
| { | ||
| cnsval_ssize_t imm = emitGetInsSC(id); | ||
| printf(" %llu %llu", (uint64_t)imm, (uint64_t)imm); | ||
| } | ||
kg marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| break; | ||
| case IF_LOCAL_DECL: | ||
| { | ||
| unsigned int count = emitGetLclVarDeclCount(id); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -12846,6 +12846,12 @@ void Compiler::gtDispTree(GenTree* tree, | ||
| printf(" (Loop)"); | ||
| break; | ||
| #ifdef TARGET_WASM | ||
| case GenTreeBlk::BlkOpKindNativeOpcode: | ||
| printf(" (memory.copy|fill)"); | ||
kg marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| break; | ||
| #endif | ||
| default: | ||
| unreached(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -195,7 +195,61 @@ void Lowering::LowerDivOrMod(GenTreeOp* divMod) | ||
| // | ||
| void Lowering::LowerBlockStore(GenTreeBlk* blkNode) | ||
| { | ||
| NYI_WASM("LowerBlockStore"); | ||
| GenTree* dstAddr = blkNode->Addr(); | ||
| GenTree* src = blkNode->Data(); | ||
kg marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| 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 | ||
| blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindNativeOpcode; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| assert(src->OperIs(GT_IND, GT_LCL_VAR, GT_LCL_FLD)); | ||
| src->SetContained(); | ||
kg marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| if (src->OperIs(GT_LCL_VAR)) | ||
| { | ||
| // TODO-1stClassStructs: for now we can't work with STORE_BLOCK source in register. | ||
| const unsigned srcLclNum = src->AsLclVar()->GetLclNum(); | ||
| m_compiler->lvaSetVarDoNotEnregister(srcLclNum DEBUGARG(DoNotEnregisterReason::StoreBlkSrc)); | ||
| } | ||
| ClassLayout* layout = blkNode->GetLayout(); | ||
| bool doCpObj = layout->HasGCPtr(); | ||
| // CopyObj or CopyBlk | ||
| if (doCpObj) | ||
| { | ||
| // Try to use bulk copy helper | ||
| if (TryLowerBlockStoreAsGcBulkCopyCall(blkNode)) | ||
| { | ||
| return; | ||
| } | ||
| blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindCpObjUnroll; | ||
| } | ||
| else | ||
| { | ||
| assert(blkNode->OperIs(GT_STORE_BLK)); | ||
| // memory.copy | ||
| blkNode->gtBlkOpKind = GenTreeBlk::BlkOpKindNativeOpcode; | ||
kg marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| } | ||
| } | ||
| //------------------------------------------------------------------------ | ||
| @@ -440,6 +494,8 @@ void Lowering::AfterLowerBlock() | ||
| // rare, introduced in lowering only. All HIR-induced cases (such as from "gtSetEvalOrder") should | ||
| // instead be ifdef-ed out for WASM. | ||
| m_anyChanges = true; | ||
| JITDUMP("node==[%06u] prev==[%06u]\n", Compiler::dspTreeID(node), Compiler::dspTreeID(prev)); | ||
| NYI_WASM("IR not in a stackified form"); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -2,6 +2,7 @@ | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
kg marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| using Internal.TypeSystem; | ||
| using ILCompiler.ObjectWriter; | ||
| @@ -148,17 +149,8 @@ public static WasmFuncType GetSignature(MethodDesc method) | ||
| returnIsVoid = true; | ||
| } | ||
| int parameterCount = signature.Length; | ||
| if (hasReturnBuffer) | ||
| { | ||
| parameterCount++; // return buffer | ||
| } | ||
| if (!method.IsUnmanagedCallersOnly) | ||
| { | ||
| parameterCount += 2; // sp and pe | ||
| } | ||
| // Reserve space for potential implicit this, stack pointer parameter, portable entrypoint parameter, and return buffer | ||
| ArrayBuilder<WasmValueType> result = new(signature.Length + 4); | ||
| if (!signature.IsStatic) | ||
| { | ||
| @@ -168,48 +160,41 @@ public static WasmFuncType GetSignature(MethodDesc method) | ||
| { | ||
| explicitThis = true; | ||
| } | ||
| else | ||
| { | ||
| parameterCount += 1; // implicit this | ||
| } | ||
| } | ||
| Span<WasmValueType> wasmParameters = new WasmValueType[parameterCount]; | ||
| int index = 0; | ||
| if (method.IsUnmanagedCallersOnly) // reverse P/Invoke | ||
| { | ||
| if (hasReturnBuffer) | ||
| { | ||
| wasmParameters[index++] = pointerType; | ||
| result.Add(pointerType); | ||
| } | ||
| } | ||
| else // managed call | ||
| { | ||
| wasmParameters[0] = pointerType; // Stack pointer parameter | ||
| // Return buffer is first after this. | ||
| result.Add(pointerType); // Stack pointer parameter | ||
| if (hasThis) | ||
| { | ||
| wasmParameters[index++] = pointerType; | ||
| result.Add(pointerType); | ||
| } | ||
| if (hasReturnBuffer) | ||
| { | ||
| wasmParameters[index++] = pointerType; | ||
| result.Add(pointerType); | ||
| } | ||
| wasmParameters[wasmParameters.Length - 1] = pointerType; // PE entrypoint parameter | ||
| } | ||
| for (int i = explicitThis ? 1 : 0; i < signature.Length; i++) | ||
| { | ||
| wasmParameters[index++] = LowerType(signature[i]); | ||
| result.Add(LowerType(signature[i])); | ||
kg marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| if (!method.IsUnmanagedCallersOnly) | ||
| { | ||
| result.Add(pointerType); // PE entrypoint parameter | ||
| } | ||
| WasmResultType ps = new(wasmParameters.ToArray()); | ||
| WasmResultType ps = new(result.ToArray()); | ||
| WasmResultType ret = returnIsVoid ? new(Array.Empty<WasmValueType>()) | ||
| : new([LowerType(loweredReturnType)]); | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.