From e93e57de65650c5a7c1b0cf39d7a7ff9358932b7 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Mon, 8 Dec 2025 20:20:12 -0800 Subject: [PATCH 01/27] Revert wasm method stub method in crossgen to allow invoking the actual JIT, since the RyuJIT backend is now emitting Wasm --- .../tools/Common/JitInterface/CorInfoImpl.cs | 42 +------------------ 1 file changed, 2 insertions(+), 40 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 8ffe8f69227e5f..636caacee76ca2 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -317,36 +317,6 @@ IntPtr LocalObjectToHandle(object input) return null; } - private CorJitResult CompileWasmStub(out IntPtr exception, ref CORINFO_METHOD_INFO methodInfo, out uint codeSize) - { - byte[] stub = - [ - 0x00, // local variable count - 0x41, // i32.const - 0x0, // uleb128 0 - 0x0F, // return - 0x0B, // end - ]; - AllocMemArgs args = new AllocMemArgs - { - hotCodeSize = (uint)stub.Length, - coldCodeSize = (uint)0, - roDataSize = (uint)0, - xcptnsCount = _ehClauses != null ? (uint)_ehClauses.Length : 0, - flag = CorJitAllocMemFlag.CORJIT_ALLOCMEM_DEFAULT_CODE_ALIGN, - }; - allocMem(ref args); - - _code = stub; - - codeSize = (uint)stub.Length; - exception = IntPtr.Zero; - - _codeRelocs = new(); - - return CorJitResult.CORJIT_OK; - } - private CompilationResult CompileMethodInternal(IMethodNode methodCodeNodeNeedingCode, MethodIL methodIL) { // methodIL must not be null @@ -369,17 +339,9 @@ private CompilationResult CompileMethodInternal(IMethodNode methodCodeNodeNeedin IntPtr exception; IntPtr nativeEntry; uint codeSize; - - TargetArchitecture architecture = _compilation.TypeSystemContext.Target.Architecture; - var result = architecture switch - { - // We currently do not have WASM codegen support, but for testing, we will return a stub - TargetArchitecture.Wasm32 => CompileWasmStub(out exception, ref methodInfo, out codeSize), - _ => JitCompileMethod(out exception, + var result = JitCompileMethod(out exception, _jit, (IntPtr)(&_this), _unmanagedCallbacks, - ref methodInfo, (uint)CorJitFlag.CORJIT_FLAG_CALL_GETJITFLAGS, out nativeEntry, out codeSize) - }; - + ref methodInfo, (uint)CorJitFlag.CORJIT_FLAG_CALL_GETJITFLAGS, out nativeEntry, out codeSize); if (exception != IntPtr.Zero) { if (_lastException != null) From 4b991daa0255fa3aa15f707ef29d3a59d0d24d10 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Tue, 9 Dec 2025 18:13:20 -0800 Subject: [PATCH 02/27] WIP add args as locals to the beginning of method epilog in Wasm codegen --- src/coreclr/jit/codegen.h | 7 +++++ src/coreclr/jit/codegencommon.cpp | 46 ++++++++++++++++++++++++++++++- src/coreclr/jit/codegenwasm.cpp | 1 + src/coreclr/jit/emit.h | 40 +++++++++++++++++++++++++++ src/coreclr/jit/emitfmtswasm.h | 20 ++++++++------ src/coreclr/jit/emitwasm.cpp | 32 +++++++++++++++++++++ src/coreclr/jit/emitwasm.h | 3 ++ src/coreclr/jit/instrswasm.h | 2 ++ src/coreclr/jit/registeropswasm.h | 1 - src/coreclr/jit/wasmtypes.h | 22 +++++++++++++++ 10 files changed, 163 insertions(+), 11 deletions(-) create mode 100644 src/coreclr/jit/wasmtypes.h diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 028b98f2c39080..6bbf62cfc8e435 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -14,6 +14,9 @@ #include "regset.h" #include "jitgcinfo.h" +#include "wasmtypes.h" +#include "vartype.h" + class CodeGen final : public CodeGenInterface { friend class emitter; @@ -589,6 +592,10 @@ class CodeGen final : public CodeGenInterface void genReserveProlog(BasicBlock* block); // currently unused void genReserveEpilog(BasicBlock* block); void genFnProlog(); +#if defined(TARGET_WASM) + void genWasmArgsAsLocals(); + instWasmValueType genWasmTypeFromVarType(var_types type); +#endif void genFnEpilog(BasicBlock* block); void genReserveFuncletProlog(BasicBlock* block); diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 77486f17c82545..1a64cf33094527 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -32,6 +32,11 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "jitstd/algorithm.h" +#if defined(TARGET_WASM) +#include "vartypedef.h" +#include "wasmtypes.h" +#endif + /*****************************************************************************/ void CodeGenInterface::setFramePointerRequiredEH(bool value) @@ -5650,13 +5655,52 @@ void CodeGen::genFnProlog() #endif // defined(DEBUG) && defined(TARGET_XARCH) #else // defined(TARGET_WASM) - // TODO-WASM: prolog zeroing, shadow stack maintenance + // TODO-WASM: proper local count, local declarations, and shadow stack maintenance + GetEmitter()->emitIns_I(INS_local_cnt, EA_8BYTE, compiler->info.compArgsCount); GetEmitter()->emitMarkPrologEnd(); #endif // !defined(TARGET_WASM) GetEmitter()->emitEndProlog(); } +#if defined(TARGET_WASM) +//------------------------------------------------------------------------ +// genWasmTypeFromVarType: map var_type to wasm type +// +instWasmValueType CodeGen::genWasmTypeFromVarType(var_types type) +{ + switch (type) + { + case TYP_INT: + case TYP_REF: + case TYP_BYREF: + return instWasmValueType ; + case TYP_LONG: + return instWasmValueType::I64; + case TYP_FLOAT: + return instWasmValueType::F32; + case TYP_DOUBLE: + return instWasmValueType::F64; + default: + unreached(); + } +} + +//------------------------------------------------------------------------ +// genWasmArgsAsLocals: generate wasm locals for all incoming arguments +void CodeGen::genWasmArgsAsLocals() +{ + // Declare locals for all incoming arguments + for (unsigned i = 0; i < compiler->info.compArgsCount; i++) + { + LclVarDsc* varDsc = compiler->lvaGetDesc(i); + assert(varDsc->lvIsParam); + instWasmValueType type = genWasmTypeFromVarType(varDsc->TypeGet()); + GetEmitter()->emitIns_I_Ty(INS_local_decl, emitTypeSize(varDsc->TypeGet()), type); + } +} +#endif + #if !defined(TARGET_WASM) //---------------------------------------------------------------------------------- diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index ba5d87b1f1cffb..3667fb016746b5 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -42,6 +42,7 @@ void CodeGen::genFnEpilog(BasicBlock* block) // TODO-WASM-CQ: do not emit "return" in case this is the last block instGen(INS_return); + instGen(INS_end); } void CodeGen::genCaptureFuncletPrologEpilogInfo() diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 4418dc4c2e93bb..cdc2a1621980e4 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -52,6 +52,7 @@ #define DEBUG_EMIT 0 #endif + #if EMITTER_STATS void emitterStats(FILE* fout); void emitterStaticStats(FILE* fout); // Static stats about the emitter (data structure offsets, sizes, etc.) @@ -2334,6 +2335,27 @@ class emitter }; #endif +#ifdef TARGET_WASM +#include "wasmtypes.h" + + struct instrDescLclVarDecl : instrDesc + { + instrDescLclVarDecl() = delete; + cnsval_ssize_t lclCnt; + instWasmValueType lclType; + + void idLclType(instWasmValueType type) + { + lclType = type; + } + + void idLclCnt(cnsval_ssize_t cnt) + { + lclCnt = cnt; + } + }; +#endif // TARGET_WASM + #ifdef TARGET_RISCV64 struct instrDescLoadImm : instrDescCns { @@ -3292,6 +3314,11 @@ class emitter instrDesc* emitNewInstrCns(emitAttr attr, cnsval_ssize_t cns); instrDesc* emitNewInstrDsp(emitAttr attr, target_ssize_t dsp); instrDesc* emitNewInstrCnsDsp(emitAttr attr, target_ssize_t cns, int dsp); + +#ifdef TARGET_WASM + instrDesc* emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_t localCount, instWasmValueType type); +#endif + #ifdef TARGET_ARM instrDesc* emitNewInstrReloc(emitAttr attr, BYTE* addr); #endif // TARGET_ARM @@ -4107,6 +4134,19 @@ inline emitter::instrDesc* emitter::emitNewInstrSC(emitAttr attr, cnsval_ssize_t } } +#ifdef TARGET_WASM +#include "wasmtypes.h" + +inline emitter::instrDesc* emitter::emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_t localCount, instWasmValueType type) +{ + instrDescLclVarDecl* id = static_cast(emitAllocAnyInstr(sizeof(instrDescLclVarDecl), attr)); + id->idLclCnt(localCount); + id->idLclType(type); + + return id; +} +#endif + #ifdef TARGET_ARM inline emitter::instrDesc* emitter::emitNewInstrReloc(emitAttr attr, BYTE* addr) diff --git a/src/coreclr/jit/emitfmtswasm.h b/src/coreclr/jit/emitfmtswasm.h index a9e3861ff16707..e785c91e99cc9f 100644 --- a/src/coreclr/jit/emitfmtswasm.h +++ b/src/coreclr/jit/emitfmtswasm.h @@ -26,15 +26,17 @@ enum ID_OPS // (unused) ////////////////////////////////////////////////////////////////////////////// -IF_DEF(NONE, IS_NONE, NONE) -IF_DEF(OPCODE, IS_NONE, NONE) // -IF_DEF(BLOCK, IS_NONE, NONE) // <0x40> -IF_DEF(LABEL, IS_NONE, NONE) // -IF_DEF(ULEB128, IS_NONE, NONE) // -IF_DEF(SLEB128, IS_NONE, NONE) // -IF_DEF(F32, IS_NONE, NONE) // -IF_DEF(F64, IS_NONE, NONE) // -IF_DEF(MEMARG, IS_NONE, NONE) // ( ) +IF_DEF(NONE, IS_NONE, NONE) +IF_DEF(OPCODE, IS_NONE, NONE) // +IF_DEF(BLOCK, IS_NONE, NONE) // <0x40> +IF_DEF(LABEL, IS_NONE, NONE) // +IF_DEF(ULEB128, IS_NONE, NONE) // +IF_DEF(SLEB128, IS_NONE, NONE) // +IF_DEF(F32, IS_NONE, NONE) // +IF_DEF(F64, IS_NONE, NONE) // +IF_DEF(MEMARG, IS_NONE, NONE) // ( ) +IF_DEF(LOCAL_CNT, IS_NONE, NONE) // +IF_DEF(LOCAL_DECL, IS_NONE, NONE) // #undef IF_DEF #endif // !DEFINE_ID_OPS diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index 694a572471752f..5364b5897d7b25 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -85,6 +85,21 @@ bool emitter::emitInsIsStore(instruction ins) return false; } +//------------------------------------------------------------------------ +// emitIns_I: Emit an instruction with an immediate operand and an encoded valye type +// +void emitter::emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, instWasmValueType valType) +{ + instrDesc* id = this->emitNewInstrLclVarDecl(EA_8BYTE, imm, valType); + insFormat fmt = this->emitInsFormat(ins); + + id->idIns(ins); + id->idInsFmt(fmt); + + this->dispIns(id); + this->appendToCurIG(id); +} + emitter::insFormat emitter::emitInsFormat(instruction ins) { static_assert(IF_COUNT < 255); @@ -166,6 +181,7 @@ unsigned emitter::instrDesc::idCodeSize() const size += 1; break; case IF_LABEL: + case IF_LOCAL_CNT: assert(!idIsCnsReloc()); size = SizeOfULEB128(emitGetInsSC(this)); break; @@ -317,6 +333,21 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) dst += emitOutputULEB128(dst, offset); break; } + case IF_LOCAL_CNT: + { + cnsval_ssize_t constant = emitGetInsSC(id); + dst += emitOutputULEB128(dst, (uint64_t)constant); + break; + } + case IF_LOCAL_DECL: + { + cnsval_ssize_t constant = emitGetInsSC(id); + dst += emitOutputULEB128(dst, (uint64_t)constant); + // TODO-WASM: currently assuming all locals are numtypes which are single byte encoded. + // vec types are also single byte encoded. If we end up using reftypes, we'll need to handle the more complex encoding. + dst += emitOutputByte(dst, static_cast(instWasmValueType::I32)); + break; + } default: NYI_WASM("emitOutputInstr"); break; @@ -421,6 +452,7 @@ void emitter::emitDispIns( case IF_LABEL: case IF_ULEB128: + case IF_LOCAL_CNT: { cnsval_ssize_t imm = emitGetInsSC(id); printf(" %llu", (uint64_t)imm); diff --git a/src/coreclr/jit/emitwasm.h b/src/coreclr/jit/emitwasm.h index c3d4c04ddc0815..7e6833ecf6ba98 100644 --- a/src/coreclr/jit/emitwasm.h +++ b/src/coreclr/jit/emitwasm.h @@ -5,6 +5,8 @@ /* Debug-only routines to display instructions */ /************************************************************************/ +#include "wasmtypes.h" + #if defined(DEBUG) || defined(LATE_DISASM) void getInsSveExecutionCharacteristics(instrDesc* id, insExecutionCharacteristics& result); #endif // defined(DEBUG) || defined(LATE_DISASM) @@ -18,6 +20,7 @@ void emitDispInst(instruction ins); public: void emitIns(instruction ins); void emitIns_I(instruction ins, emitAttr attr, cnsval_ssize_t imm); +void emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, instWasmValueType valType); void emitIns_S(instruction ins, emitAttr attr, int varx, int offs); void emitIns_R(instruction ins, emitAttr attr, regNumber reg); diff --git a/src/coreclr/jit/instrswasm.h b/src/coreclr/jit/instrswasm.h index 5264fdf0817d16..28002bd70f2154 100644 --- a/src/coreclr/jit/instrswasm.h +++ b/src/coreclr/jit/instrswasm.h @@ -37,6 +37,8 @@ INST(br, "br", 0, IF_ULEB128, 0x0C) INST(br_if, "br_if", 0, IF_ULEB128, 0x0D) INST(br_table, "br_table", 0, IF_ULEB128, 0x0E) INST(return, "return", 0, IF_OPCODE, 0x0F) +INST(local_cnt, "local.cnt", 0, IF_LOCAL_CNT, 0x00) +INST(local_decl, "local", 0, IF_LOCAL_DECL, 0x00) INST(local_get, "local.get", 0, IF_ULEB128, 0x20) INST(i32_load, "i32.load", 0, IF_MEMARG, 0x28) diff --git a/src/coreclr/jit/registeropswasm.h b/src/coreclr/jit/registeropswasm.h index 1bb8bd79b016a3..06a7c44cb5f767 100644 --- a/src/coreclr/jit/registeropswasm.h +++ b/src/coreclr/jit/registeropswasm.h @@ -9,7 +9,6 @@ enum class WasmValueType : unsigned F32, F64, Count, - #ifdef TARGET_64BIT I = I64, #else diff --git a/src/coreclr/jit/wasmtypes.h b/src/coreclr/jit/wasmtypes.h new file mode 100644 index 00000000000000..7274b8654dd4b7 --- /dev/null +++ b/src/coreclr/jit/wasmtypes.h @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*****************************************************************************/ +#ifndef _WASMTYPESDEF_H_ +#define _WASMTYPESDEF_H_ +/*****************************************************************************/ + +#include + +enum class instWasmValueType : uint8_t +{ + I32 = 0x7F, + I64 = 0x7E, + F32 = 0x7D, + F64 = 0x7C, +}; + +/*****************************************************************************/ +#endif // _WASMTYPESDEF_H_ +/*****************************************************************************/ + From 5933e67ea2812f916456d28e07dc1499c4e09504 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Wed, 10 Dec 2025 16:05:29 -0800 Subject: [PATCH 03/27] Multiple fixes for emitting local declarations for incoming args --- src/coreclr/jit/codegen.h | 5 +-- src/coreclr/jit/codegencommon.cpp | 31 ++------------- src/coreclr/jit/emit.h | 37 +++++++++++------- src/coreclr/jit/emitwasm.cpp | 65 ++++++++++++++++++++++++++++--- src/coreclr/jit/emitwasm.h | 6 ++- src/coreclr/jit/wasmtypes.h | 22 ----------- src/coreclr/jit/wasmtypesdef.h | 62 +++++++++++++++++++++++++++++ 7 files changed, 154 insertions(+), 74 deletions(-) delete mode 100644 src/coreclr/jit/wasmtypes.h create mode 100644 src/coreclr/jit/wasmtypesdef.h diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 6bbf62cfc8e435..cedaaf256ff665 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -13,9 +13,7 @@ #include "compiler.h" // temporary?? #include "regset.h" #include "jitgcinfo.h" - -#include "wasmtypes.h" -#include "vartype.h" +#include "wasmtypesdef.h" class CodeGen final : public CodeGenInterface { @@ -594,7 +592,6 @@ class CodeGen final : public CodeGenInterface void genFnProlog(); #if defined(TARGET_WASM) void genWasmArgsAsLocals(); - instWasmValueType genWasmTypeFromVarType(var_types type); #endif void genFnEpilog(BasicBlock* block); diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 1a64cf33094527..62c7d617af4bf1 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -32,11 +32,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #include "jitstd/algorithm.h" -#if defined(TARGET_WASM) -#include "vartypedef.h" -#include "wasmtypes.h" -#endif - /*****************************************************************************/ void CodeGenInterface::setFramePointerRequiredEH(bool value) @@ -5657,6 +5652,7 @@ void CodeGen::genFnProlog() #else // defined(TARGET_WASM) // TODO-WASM: proper local count, local declarations, and shadow stack maintenance GetEmitter()->emitIns_I(INS_local_cnt, EA_8BYTE, compiler->info.compArgsCount); + genWasmArgsAsLocals(); GetEmitter()->emitMarkPrologEnd(); #endif // !defined(TARGET_WASM) @@ -5664,27 +5660,6 @@ void CodeGen::genFnProlog() } #if defined(TARGET_WASM) -//------------------------------------------------------------------------ -// genWasmTypeFromVarType: map var_type to wasm type -// -instWasmValueType CodeGen::genWasmTypeFromVarType(var_types type) -{ - switch (type) - { - case TYP_INT: - case TYP_REF: - case TYP_BYREF: - return instWasmValueType ; - case TYP_LONG: - return instWasmValueType::I64; - case TYP_FLOAT: - return instWasmValueType::F32; - case TYP_DOUBLE: - return instWasmValueType::F64; - default: - unreached(); - } -} //------------------------------------------------------------------------ // genWasmArgsAsLocals: generate wasm locals for all incoming arguments @@ -5695,8 +5670,8 @@ void CodeGen::genWasmArgsAsLocals() { LclVarDsc* varDsc = compiler->lvaGetDesc(i); assert(varDsc->lvIsParam); - instWasmValueType type = genWasmTypeFromVarType(varDsc->TypeGet()); - GetEmitter()->emitIns_I_Ty(INS_local_decl, emitTypeSize(varDsc->TypeGet()), type); + emitter::instWasmValueType type = GetEmitter()->genWasmTypeFromVarType(varDsc->TypeGet()); + GetEmitter()->emitIns_I_Ty(INS_local_decl, 1, type); } } #endif diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index cdc2a1621980e4..b4ab2f6069f566 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -640,6 +640,10 @@ class emitter CORINFO_SIG_INFO* idCallSig; // Used to report native call site signatures to the EE }; +#if defined(TARGET_WASM) +#include "wasmtypesdef.h" +#endif + #ifdef TARGET_ARM unsigned insEncodeSetFlags(insFlags sf); @@ -892,6 +896,9 @@ class emitter unsigned _idLclFPBase : 1; // access a local on stack - SP based offset insOpts _idInsOpt : 3; // options for Load/Store instructions #endif +#ifdef TARGET_WASM + unsigned _idLclDecl : 1; // is this a local declaration? +#endif //////////////////////////////////////////////////////////////////////// // Space taken up to here: @@ -919,7 +926,7 @@ class emitter #elif defined(TARGET_AMD64) #define ID_EXTRA_BITFIELD_BITS (20) #elif defined(TARGET_WASM) -#define ID_EXTRA_BITFIELD_BITS (-4) +#define ID_EXTRA_BITFIELD_BITS (-3) #else #error Unsupported or unset target architecture #endif @@ -1296,6 +1303,18 @@ class emitter assert(reg == _idReg1); } +#ifdef TARGET_WASM + bool idIsLclVarDecl() const + { + return (_idLclDecl != 0); + } + + void idSetIsLclVarDecl(bool isDecl) + { + _idLclDecl = isDecl ? 1 : 0; + } +#endif + #ifdef TARGET_ARM64 GCtype idGCrefReg2() const { @@ -2335,9 +2354,7 @@ class emitter }; #endif -#ifdef TARGET_WASM -#include "wasmtypes.h" - +#if defined(TARGET_WASM) struct instrDescLclVarDecl : instrDesc { instrDescLclVarDecl() = delete; @@ -3316,7 +3333,7 @@ class emitter instrDesc* emitNewInstrCnsDsp(emitAttr attr, target_ssize_t cns, int dsp); #ifdef TARGET_WASM - instrDesc* emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_t localCount, instWasmValueType type); + #endif #ifdef TARGET_ARM @@ -4135,16 +4152,10 @@ inline emitter::instrDesc* emitter::emitNewInstrSC(emitAttr attr, cnsval_ssize_t } #ifdef TARGET_WASM -#include "wasmtypes.h" +#include "wasmtypesdef.h" + -inline emitter::instrDesc* emitter::emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_t localCount, instWasmValueType type) -{ - instrDescLclVarDecl* id = static_cast(emitAllocAnyInstr(sizeof(instrDescLclVarDecl), attr)); - id->idLclCnt(localCount); - id->idLclType(type); - return id; -} #endif #ifdef TARGET_ARM diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index 5364b5897d7b25..f3b3b6fdf45246 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -85,10 +85,20 @@ bool emitter::emitInsIsStore(instruction ins) return false; } -//------------------------------------------------------------------------ -// emitIns_I: Emit an instruction with an immediate operand and an encoded valye type +emitter::instrDesc* emitter::emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_t localCount, instWasmValueType type) +{ + instrDescLclVarDecl* id = static_cast(emitAllocAnyInstr(sizeof(instrDescLclVarDecl), attr)); + id->idSetIsLclVarDecl(true); + id->idLclCnt(localCount); + id->idLclType(type); + + return id; +} + +//----------------------------------------------------------------------------------- +// emitIns_I: Emit an instruction with an immediate operand and an encoded value type // -void emitter::emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, instWasmValueType valType) +void emitter::emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, emitter::instWasmValueType valType) { instrDesc* id = this->emitNewInstrLclVarDecl(EA_8BYTE, imm, valType); insFormat fmt = this->emitInsFormat(ins); @@ -100,6 +110,26 @@ void emitter::emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, instWasmValueTyp this->appendToCurIG(id); } +emitter::instWasmValueType emitter::emitGetLclVarDeclType(instrDesc* id) +{ + if (!id->idIsLclVarDecl()) + { + noway_assert("not a LclVarDecl instrDesc"); + } + + return static_cast(id)->lclType; +} + +cnsval_ssize_t emitter::emitGetLclVarDeclCount(instrDesc* id) +{ + if (!id->idIsLclVarDecl()) + { + noway_assert("not a LclVarDecl instrDesc"); + } + + return static_cast(id)->lclCnt; +} + emitter::insFormat emitter::emitInsFormat(instruction ins) { static_assert(IF_COUNT < 255); @@ -136,6 +166,12 @@ size_t emitter::emitSizeOfInsDsc(instrDesc* id) const assert(!id->idIsLargeCall()); return sizeof(instrDescCns); } + + if (id->idIsLclVarDecl()) + { + return sizeof(instrDescLclVarDecl); + } + return sizeof(instrDesc); } @@ -185,6 +221,13 @@ unsigned emitter::instrDesc::idCodeSize() const assert(!idIsCnsReloc()); size = SizeOfULEB128(emitGetInsSC(this)); break; + case IF_LOCAL_DECL: + { + assert(idIsLclVarDecl()); + instrDescLclVarDecl* idl = static_cast(const_cast(this)); + size = SizeOfULEB128(idl->lclCnt) + sizeof(emitter::instWasmValueType); + break; + } case IF_ULEB128: size += idIsCnsReloc() ? PADDED_RELOC_SIZE : SizeOfULEB128(emitGetInsSC(this)); break; @@ -341,11 +384,13 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) } case IF_LOCAL_DECL: { - cnsval_ssize_t constant = emitGetInsSC(id); - dst += emitOutputULEB128(dst, (uint64_t)constant); + assert(id->idIsLclVarDecl()); + cnsval_ssize_t count = emitGetLclVarDeclCount(id); + instWasmValueType valType = emitGetLclVarDeclType(id); + dst += emitOutputULEB128(dst, (uint64_t)count); // TODO-WASM: currently assuming all locals are numtypes which are single byte encoded. // vec types are also single byte encoded. If we end up using reftypes, we'll need to handle the more complex encoding. - dst += emitOutputByte(dst, static_cast(instWasmValueType::I32)); + dst += emitOutputByte(dst, static_cast(valType)); break; } default: @@ -459,6 +504,14 @@ void emitter::emitDispIns( } break; + case IF_LOCAL_DECL: + { + cnsval_ssize_t imm = emitGetLclVarDeclCount(id); + instWasmValueType valType = emitGetLclVarDeclType(id); + printf(" %llu %s", (uint64_t)imm, instWasmValueTypeToStr(valType)); + } + break; + case IF_SLEB128: { cnsval_ssize_t imm = emitGetInsSC(id); diff --git a/src/coreclr/jit/emitwasm.h b/src/coreclr/jit/emitwasm.h index 7e6833ecf6ba98..57dad108cf7697 100644 --- a/src/coreclr/jit/emitwasm.h +++ b/src/coreclr/jit/emitwasm.h @@ -5,7 +5,7 @@ /* Debug-only routines to display instructions */ /************************************************************************/ -#include "wasmtypes.h" +#include "wasmtypesdef.h" #if defined(DEBUG) || defined(LATE_DISASM) void getInsSveExecutionCharacteristics(instrDesc* id, insExecutionCharacteristics& result); @@ -35,6 +35,10 @@ static unsigned SizeOfSLEB128(int64_t value); static unsigned emitGetAlignHintLog2(const instrDesc* id); +instrDesc* emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_t localCount, instWasmValueType type); +static instWasmValueType emitGetLclVarDeclType(instrDesc* id); +static cnsval_ssize_t emitGetLclVarDeclCount(instrDesc* id); + /************************************************************************/ /* Private members that deal with target-dependent instr. descriptors */ /************************************************************************/ diff --git a/src/coreclr/jit/wasmtypes.h b/src/coreclr/jit/wasmtypes.h deleted file mode 100644 index 7274b8654dd4b7..00000000000000 --- a/src/coreclr/jit/wasmtypes.h +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*****************************************************************************/ -#ifndef _WASMTYPESDEF_H_ -#define _WASMTYPESDEF_H_ -/*****************************************************************************/ - -#include - -enum class instWasmValueType : uint8_t -{ - I32 = 0x7F, - I64 = 0x7E, - F32 = 0x7D, - F64 = 0x7C, -}; - -/*****************************************************************************/ -#endif // _WASMTYPESDEF_H_ -/*****************************************************************************/ - diff --git a/src/coreclr/jit/wasmtypesdef.h b/src/coreclr/jit/wasmtypesdef.h new file mode 100644 index 00000000000000..ddd830cc99f6a8 --- /dev/null +++ b/src/coreclr/jit/wasmtypesdef.h @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*****************************************************************************/ +#ifndef _WASMTYPESDEF_H_ +#define _WASMTYPESDEF_H_ +/*****************************************************************************/ + +#include +#include "vartypesdef.h" + +#if defined(TARGET_WASM) +enum class instWasmValueType : uint8_t +{ + F64 = 0x7C, + F32 = 0x7D, + I64 = 0x7E, + I32 = 0x7F, +}; + +const char *const wasmValueTypeNames[4] = { + "f64", + "f32", + "i64", + "i32", +}; + +//------------------------------------------------------------------------ +// wasmValueTypeToStr: map wasm type to string +const char *const instWasmValueTypeToStr(instWasmValueType type) +{ + assert((type >= instWasmValueType::F64) && (type <= instWasmValueType::I32)); + return wasmValueTypeNames[static_cast(type) - static_cast(instWasmValueType::F64)]; +} + +//------------------------------------------------------------------------ +// genWasmTypeFromVarType: map var_type to wasm type +// +instWasmValueType genWasmTypeFromVarType(var_types type) +{ + switch (type) + { + case TYP_INT: + case TYP_REF: + case TYP_BYREF: + return instWasmValueType::I32; + case TYP_LONG: + return instWasmValueType::I64; + case TYP_FLOAT: + return instWasmValueType::F32; + case TYP_DOUBLE: + return instWasmValueType::F64; + default: + unreached(); + } +} +#endif + +/*****************************************************************************/ +#endif // _WASMTYPESDEF_H_ +/*****************************************************************************/ + From 7b8e339510cbe45f8c0a16754283fbcbee8ab956 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Wed, 10 Dec 2025 16:06:39 -0800 Subject: [PATCH 04/27] jit-format --- src/coreclr/jit/codegencommon.cpp | 2 +- src/coreclr/jit/emit.h | 11 ++++------- src/coreclr/jit/emitwasm.cpp | 11 ++++++----- src/coreclr/jit/emitwasm.h | 4 ++-- src/coreclr/jit/wasmtypesdef.h | 7 +++---- 5 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 62c7d617af4bf1..4e31db009a8780 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -5663,7 +5663,7 @@ void CodeGen::genFnProlog() //------------------------------------------------------------------------ // genWasmArgsAsLocals: generate wasm locals for all incoming arguments -void CodeGen::genWasmArgsAsLocals() +void CodeGen::genWasmArgsAsLocals() { // Declare locals for all incoming arguments for (unsigned i = 0; i < compiler->info.compArgsCount; i++) diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index b4ab2f6069f566..67ca7468c09b5c 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -52,7 +52,6 @@ #define DEBUG_EMIT 0 #endif - #if EMITTER_STATS void emitterStats(FILE* fout); void emitterStaticStats(FILE* fout); // Static stats about the emitter (data structure offsets, sizes, etc.) @@ -1304,12 +1303,12 @@ class emitter } #ifdef TARGET_WASM - bool idIsLclVarDecl() const + bool idIsLclVarDecl() const { return (_idLclDecl != 0); } - void idSetIsLclVarDecl(bool isDecl) + void idSetIsLclVarDecl(bool isDecl) { _idLclDecl = isDecl ? 1 : 0; } @@ -2358,7 +2357,7 @@ class emitter struct instrDescLclVarDecl : instrDesc { instrDescLclVarDecl() = delete; - cnsval_ssize_t lclCnt; + cnsval_ssize_t lclCnt; instWasmValueType lclType; void idLclType(instWasmValueType type) @@ -3333,7 +3332,7 @@ class emitter instrDesc* emitNewInstrCnsDsp(emitAttr attr, target_ssize_t cns, int dsp); #ifdef TARGET_WASM - + #endif #ifdef TARGET_ARM @@ -4154,8 +4153,6 @@ inline emitter::instrDesc* emitter::emitNewInstrSC(emitAttr attr, cnsval_ssize_t #ifdef TARGET_WASM #include "wasmtypesdef.h" - - #endif #ifdef TARGET_ARM diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index f3b3b6fdf45246..3c855946a70920 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -126,7 +126,7 @@ cnsval_ssize_t emitter::emitGetLclVarDeclCount(instrDesc* id) { noway_assert("not a LclVarDecl instrDesc"); } - + return static_cast(id)->lclCnt; } @@ -225,7 +225,7 @@ unsigned emitter::instrDesc::idCodeSize() const { assert(idIsLclVarDecl()); instrDescLclVarDecl* idl = static_cast(const_cast(this)); - size = SizeOfULEB128(idl->lclCnt) + sizeof(emitter::instWasmValueType); + size = SizeOfULEB128(idl->lclCnt) + sizeof(emitter::instWasmValueType); break; } case IF_ULEB128: @@ -385,11 +385,12 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) case IF_LOCAL_DECL: { assert(id->idIsLclVarDecl()); - cnsval_ssize_t count = emitGetLclVarDeclCount(id); + cnsval_ssize_t count = emitGetLclVarDeclCount(id); instWasmValueType valType = emitGetLclVarDeclType(id); dst += emitOutputULEB128(dst, (uint64_t)count); // TODO-WASM: currently assuming all locals are numtypes which are single byte encoded. - // vec types are also single byte encoded. If we end up using reftypes, we'll need to handle the more complex encoding. + // vec types are also single byte encoded. If we end up using reftypes, we'll need to handle the more + // complex encoding. dst += emitOutputByte(dst, static_cast(valType)); break; } @@ -506,7 +507,7 @@ void emitter::emitDispIns( case IF_LOCAL_DECL: { - cnsval_ssize_t imm = emitGetLclVarDeclCount(id); + cnsval_ssize_t imm = emitGetLclVarDeclCount(id); instWasmValueType valType = emitGetLclVarDeclType(id); printf(" %llu %s", (uint64_t)imm, instWasmValueTypeToStr(valType)); } diff --git a/src/coreclr/jit/emitwasm.h b/src/coreclr/jit/emitwasm.h index 57dad108cf7697..c0067a7dcffd28 100644 --- a/src/coreclr/jit/emitwasm.h +++ b/src/coreclr/jit/emitwasm.h @@ -35,9 +35,9 @@ static unsigned SizeOfSLEB128(int64_t value); static unsigned emitGetAlignHintLog2(const instrDesc* id); -instrDesc* emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_t localCount, instWasmValueType type); +instrDesc* emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_t localCount, instWasmValueType type); static instWasmValueType emitGetLclVarDeclType(instrDesc* id); -static cnsval_ssize_t emitGetLclVarDeclCount(instrDesc* id); +static cnsval_ssize_t emitGetLclVarDeclCount(instrDesc* id); /************************************************************************/ /* Private members that deal with target-dependent instr. descriptors */ diff --git a/src/coreclr/jit/wasmtypesdef.h b/src/coreclr/jit/wasmtypesdef.h index ddd830cc99f6a8..15c0f32402a478 100644 --- a/src/coreclr/jit/wasmtypesdef.h +++ b/src/coreclr/jit/wasmtypesdef.h @@ -18,7 +18,7 @@ enum class instWasmValueType : uint8_t I32 = 0x7F, }; -const char *const wasmValueTypeNames[4] = { +const char* const wasmValueTypeNames[4] = { "f64", "f32", "i64", @@ -27,7 +27,7 @@ const char *const wasmValueTypeNames[4] = { //------------------------------------------------------------------------ // wasmValueTypeToStr: map wasm type to string -const char *const instWasmValueTypeToStr(instWasmValueType type) +const char* const instWasmValueTypeToStr(instWasmValueType type) { assert((type >= instWasmValueType::F64) && (type <= instWasmValueType::I32)); return wasmValueTypeNames[static_cast(type) - static_cast(instWasmValueType::F64)]; @@ -35,7 +35,7 @@ const char *const instWasmValueTypeToStr(instWasmValueType type) //------------------------------------------------------------------------ // genWasmTypeFromVarType: map var_type to wasm type -// +// instWasmValueType genWasmTypeFromVarType(var_types type) { switch (type) @@ -59,4 +59,3 @@ instWasmValueType genWasmTypeFromVarType(var_types type) /*****************************************************************************/ #endif // _WASMTYPESDEF_H_ /*****************************************************************************/ - From 9fbfb86466f9a2b064b614c4d4d10b2de5443552 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Wed, 10 Dec 2025 16:50:00 -0800 Subject: [PATCH 05/27] Generate locals only for non-args --- src/coreclr/jit/codegen.h | 2 +- src/coreclr/jit/codegencommon.cpp | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index bfce34aaddb5cc..afd821342eb1f3 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -591,7 +591,7 @@ class CodeGen final : public CodeGenInterface void genReserveEpilog(BasicBlock* block); void genFnProlog(); #if defined(TARGET_WASM) - void genWasmArgsAsLocals(); + void genWasmLocals(); #endif void genFnEpilog(BasicBlock* block); diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 4e31db009a8780..67e7a075c59b1c 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -5651,8 +5651,9 @@ void CodeGen::genFnProlog() #else // defined(TARGET_WASM) // TODO-WASM: proper local count, local declarations, and shadow stack maintenance - GetEmitter()->emitIns_I(INS_local_cnt, EA_8BYTE, compiler->info.compArgsCount); - genWasmArgsAsLocals(); + assert(compiler->info.compLocalsCount >= compiler->info.compArgsCount); + GetEmitter()->emitIns_I(INS_local_cnt, EA_8BYTE, compiler->info.compLocalsCount - compiler->info.compArgsCount); + genWasmLocals(); GetEmitter()->emitMarkPrologEnd(); #endif // !defined(TARGET_WASM) @@ -5662,11 +5663,13 @@ void CodeGen::genFnProlog() #if defined(TARGET_WASM) //------------------------------------------------------------------------ -// genWasmArgsAsLocals: generate wasm locals for all incoming arguments -void CodeGen::genWasmArgsAsLocals() +// genWasmLocals: generate wasm locals for all locals +// +// TODO-WASM: re-evaluate this, we may not want a 1:1 mapping of locals to wasm locals +// TODO-WASM: pre-declare all "register" locals +void CodeGen::genWasmLocals() { - // Declare locals for all incoming arguments - for (unsigned i = 0; i < compiler->info.compArgsCount; i++) + for (unsigned i = 0; i < compiler->info.compLocalsCount - compiler->info.compArgsCount; i++) { LclVarDsc* varDsc = compiler->lvaGetDesc(i); assert(varDsc->lvIsParam); From 7d12af03b46f49098257b77dbe5bbe365af1e9cc Mon Sep 17 00:00:00 2001 From: Adam Perlin Date: Thu, 11 Dec 2025 09:16:47 -0800 Subject: [PATCH 06/27] Update src/coreclr/jit/emit.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreclr/jit/emit.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 5a0c9b505917bf..c0ed1d52a96fe0 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -4151,11 +4151,6 @@ inline emitter::instrDesc* emitter::emitNewInstrSC(emitAttr attr, cnsval_ssize_t } } -#ifdef TARGET_WASM -#include "wasmtypesdef.h" - -#endif - #ifdef TARGET_ARM inline emitter::instrDesc* emitter::emitNewInstrReloc(emitAttr attr, BYTE* addr) From 4b91b3af1786e7233f6b6eb5044833cbb9d91454 Mon Sep 17 00:00:00 2001 From: Adam Perlin Date: Thu, 11 Dec 2025 09:17:12 -0800 Subject: [PATCH 07/27] Update src/coreclr/jit/emitfmtswasm.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreclr/jit/emitfmtswasm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/emitfmtswasm.h b/src/coreclr/jit/emitfmtswasm.h index e785c91e99cc9f..227107c6d34008 100644 --- a/src/coreclr/jit/emitfmtswasm.h +++ b/src/coreclr/jit/emitfmtswasm.h @@ -35,7 +35,7 @@ IF_DEF(SLEB128, IS_NONE, NONE) // IF_DEF(F32, IS_NONE, NONE) // IF_DEF(F64, IS_NONE, NONE) // IF_DEF(MEMARG, IS_NONE, NONE) // ( ) -IF_DEF(LOCAL_CNT, IS_NONE, NONE) // +IF_DEF(LOCAL_CNT, IS_NONE, NONE) // IF_DEF(LOCAL_DECL, IS_NONE, NONE) // #undef IF_DEF From 183851f504d71f89f8f8185771c64e05dc0716f7 Mon Sep 17 00:00:00 2001 From: Adam Perlin Date: Thu, 11 Dec 2025 09:17:27 -0800 Subject: [PATCH 08/27] Update src/coreclr/jit/codegencommon.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreclr/jit/codegencommon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 67e7a075c59b1c..aa087b682f0323 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -5672,7 +5672,7 @@ void CodeGen::genWasmLocals() for (unsigned i = 0; i < compiler->info.compLocalsCount - compiler->info.compArgsCount; i++) { LclVarDsc* varDsc = compiler->lvaGetDesc(i); - assert(varDsc->lvIsParam); + assert(!varDsc->lvIsParam); emitter::instWasmValueType type = GetEmitter()->genWasmTypeFromVarType(varDsc->TypeGet()); GetEmitter()->emitIns_I_Ty(INS_local_decl, 1, type); } From c9d30462f795355d27bd111ec7c1a2e28a068f98 Mon Sep 17 00:00:00 2001 From: Adam Perlin Date: Thu, 11 Dec 2025 09:18:01 -0800 Subject: [PATCH 09/27] Update src/coreclr/jit/emitwasm.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreclr/jit/emitwasm.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index 318fea7a3c3546..24940bb170c028 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -127,7 +127,9 @@ emitter::instrDesc* emitter::emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_ } //----------------------------------------------------------------------------------- -// emitIns_I: Emit an instruction with an immediate operand and an encoded value type +// emitIns_I_Ty: Emit an instruction for a local variable declaration, encoding both +// a count (immediate) and a value type. This is specifically used for local variable +// declarations that require both the number of locals and their type to be encoded. // void emitter::emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, emitter::instWasmValueType valType) { From 0482ee34d778efe00f6e926e0f1ce126de253d9f Mon Sep 17 00:00:00 2001 From: adamperlin Date: Thu, 11 Dec 2025 09:20:52 -0800 Subject: [PATCH 10/27] Remove incorrect noway_asserts --- src/coreclr/jit/emitwasm.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index 24940bb170c028..2711aa25efddb5 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -145,21 +145,13 @@ void emitter::emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, emitter::instWas emitter::instWasmValueType emitter::emitGetLclVarDeclType(instrDesc* id) { - if (!id->idIsLclVarDecl()) - { - noway_assert("not a LclVarDecl instrDesc"); - } - + assert(id->idIsLclVarDecl()); return static_cast(id)->lclType; } cnsval_ssize_t emitter::emitGetLclVarDeclCount(instrDesc* id) { - if (!id->idIsLclVarDecl()) - { - noway_assert("not a LclVarDecl instrDesc"); - } - + assert(id->idIsLclVarDecl()); return static_cast(id)->lclCnt; } From dfb06271bbb531ef11e43672e1a762d049407c91 Mon Sep 17 00:00:00 2001 From: Adam Perlin Date: Thu, 11 Dec 2025 09:22:32 -0800 Subject: [PATCH 11/27] Remove empty ifdef block Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreclr/jit/emit.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index c0ed1d52a96fe0..54a7c38eb11b55 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -3332,10 +3332,6 @@ class emitter instrDesc* emitNewInstrDsp(emitAttr attr, target_ssize_t dsp); instrDesc* emitNewInstrCnsDsp(emitAttr attr, target_ssize_t cns, int dsp); -#ifdef TARGET_WASM - -#endif - #ifdef TARGET_ARM instrDesc* emitNewInstrReloc(emitAttr attr, BYTE* addr); #endif // TARGET_ARM From b237c171ebd93a29a3514568b70f3f06eba8e0da Mon Sep 17 00:00:00 2001 From: adamperlin Date: Thu, 11 Dec 2025 09:29:00 -0800 Subject: [PATCH 12/27] Apply some copilot review suggestions --- src/coreclr/jit/codegen.h | 3 +++ src/coreclr/jit/wasmtypesdef.h | 11 ++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index afd821342eb1f3..75a40ad9941d0f 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -13,7 +13,10 @@ #include "compiler.h" // temporary?? #include "regset.h" #include "jitgcinfo.h" + +#if defined(TARGET_WASM) #include "wasmtypesdef.h" +#endif class CodeGen final : public CodeGenInterface { diff --git a/src/coreclr/jit/wasmtypesdef.h b/src/coreclr/jit/wasmtypesdef.h index 15c0f32402a478..3f06d0c4721ddd 100644 --- a/src/coreclr/jit/wasmtypesdef.h +++ b/src/coreclr/jit/wasmtypesdef.h @@ -18,7 +18,7 @@ enum class instWasmValueType : uint8_t I32 = 0x7F, }; -const char* const wasmValueTypeNames[4] = { +const char* const instWasmValueTypeNames[4] = { "f64", "f32", "i64", @@ -26,17 +26,18 @@ const char* const wasmValueTypeNames[4] = { }; //------------------------------------------------------------------------ -// wasmValueTypeToStr: map wasm type to string -const char* const instWasmValueTypeToStr(instWasmValueType type) +// instWasmValueTypeToStr: map wasm type to string +// +inline const char* const instWasmValueTypeToStr(instWasmValueType type) { assert((type >= instWasmValueType::F64) && (type <= instWasmValueType::I32)); - return wasmValueTypeNames[static_cast(type) - static_cast(instWasmValueType::F64)]; + return instWasmValueTypeNames[static_cast(type) - static_cast(instWasmValueType::F64)]; } //------------------------------------------------------------------------ // genWasmTypeFromVarType: map var_type to wasm type // -instWasmValueType genWasmTypeFromVarType(var_types type) +inline instWasmValueType genWasmTypeFromVarType(var_types type) { switch (type) { From 19c9eaf65fd2f8e2460ca72ebe47ffb4bd4bc4c7 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 12 Dec 2025 10:04:50 -0800 Subject: [PATCH 13/27] Address some review feedback --- src/coreclr/jit/emit.h | 20 ++++---------------- src/coreclr/jit/emitwasm.cpp | 25 ++++++++++++++++++++++--- src/coreclr/jit/emitwasm.h | 4 +--- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 54a7c38eb11b55..c3c58141e890e8 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -640,10 +640,6 @@ class emitter BasicBlock* idTargetBlock; // Target block for branches }; -#if defined(TARGET_WASM) -#include "wasmtypesdef.h" -#endif - #ifdef TARGET_ARM unsigned insEncodeSetFlags(insFlags sf); @@ -896,9 +892,6 @@ class emitter unsigned _idLclFPBase : 1; // access a local on stack - SP based offset insOpts _idInsOpt : 3; // options for Load/Store instructions #endif -#ifdef TARGET_WASM - unsigned _idLclDecl : 1; // is this a local declaration? -#endif //////////////////////////////////////////////////////////////////////// // Space taken up to here: @@ -926,7 +919,7 @@ class emitter #elif defined(TARGET_AMD64) #define ID_EXTRA_BITFIELD_BITS (20) #elif defined(TARGET_WASM) -#define ID_EXTRA_BITFIELD_BITS (-3) +#define ID_EXTRA_BITFIELD_BITS (-4) #else #error Unsupported or unset target architecture #endif @@ -1306,12 +1299,7 @@ class emitter #ifdef TARGET_WASM bool idIsLclVarDecl() const { - return (_idLclDecl != 0); - } - - void idSetIsLclVarDecl(bool isDecl) - { - _idLclDecl = isDecl ? 1 : 0; + return _idInsFmt == IF_LOCAL_DECL } #endif @@ -2359,9 +2347,9 @@ class emitter { instrDescLclVarDecl() = delete; cnsval_ssize_t lclCnt; - instWasmValueType lclType; + WasmValueType lclType; - void idLclType(instWasmValueType type) + void idLclType(WasmValueType type) { lclType = type; } diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index 2711aa25efddb5..70ba94a9be3614 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -116,7 +116,7 @@ bool emitter::emitInsIsStore(instruction ins) return false; } -emitter::instrDesc* emitter::emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_t localCount, instWasmValueType type) +emitter::instrDesc* emitter::emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_t localCount, WasmValueType type) { instrDescLclVarDecl* id = static_cast(emitAllocAnyInstr(sizeof(instrDescLclVarDecl), attr)); id->idSetIsLclVarDecl(true); @@ -131,7 +131,7 @@ emitter::instrDesc* emitter::emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_ // a count (immediate) and a value type. This is specifically used for local variable // declarations that require both the number of locals and their type to be encoded. // -void emitter::emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, emitter::instWasmValueType valType) +void emitter::emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, WasmValueType valType) { instrDesc* id = this->emitNewInstrLclVarDecl(EA_8BYTE, imm, valType); insFormat fmt = this->emitInsFormat(ins); @@ -225,6 +225,24 @@ unsigned emitter::SizeOfSLEB128(int64_t value) return (x * 37) >> 8; } +uint8_t getWasmValueTypeCode(WasmValueType type) +{ + // clang-format off + static const WasmValueType typecode_mapping[] = { + 0x00, // WasmValueType::Invalid = 0, + 0x7C, // WasmValueType::F64 = 1, + 0x7D, // WasmValueType::F32 = 2, + 0x7E, // WasmValueType::I64 = 3, + 0x7F, // WasmValueType::I32 = 4, + }; + + static_assert(ArrLen(mapping) == TYP_COUNT); + // clang-format on + + assert(0 <= type && type < WasmValueType::Count); + return typecode_mapping[static_cast(type)]; +} + unsigned emitter::instrDesc::idCodeSize() const { #ifdef TARGET_WASM32 @@ -252,7 +270,8 @@ unsigned emitter::instrDesc::idCodeSize() const { assert(idIsLclVarDecl()); instrDescLclVarDecl* idl = static_cast(const_cast(this)); - size = SizeOfULEB128(idl->lclCnt) + sizeof(emitter::instWasmValueType); + uint8_t typeCode = getWasmValueTypeCode(idl->lclType); + size = SizeOfULEB128(idl->lclCnt) + sizeof(typeCode); break; } case IF_ULEB128: diff --git a/src/coreclr/jit/emitwasm.h b/src/coreclr/jit/emitwasm.h index dc20013e41917c..1cd8375404e3a2 100644 --- a/src/coreclr/jit/emitwasm.h +++ b/src/coreclr/jit/emitwasm.h @@ -5,8 +5,6 @@ /* Debug-only routines to display instructions */ /************************************************************************/ -#include "wasmtypesdef.h" - #if defined(DEBUG) || defined(LATE_DISASM) void getInsSveExecutionCharacteristics(instrDesc* id, insExecutionCharacteristics& result); #endif // defined(DEBUG) || defined(LATE_DISASM) @@ -20,7 +18,7 @@ void emitDispInst(instruction ins); public: void emitIns(instruction ins); void emitIns_I(instruction ins, emitAttr attr, cnsval_ssize_t imm); -void emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, instWasmValueType valType); +void emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, WasmValueType valType); void emitIns_J(instruction ins, emitAttr attr, cnsval_ssize_t imm, BasicBlock* tgtBlock); void emitIns_S(instruction ins, emitAttr attr, int varx, int offs); void emitIns_R(instruction ins, emitAttr attr, regNumber reg); From 76a15f2364ebc657a277c81390b4197942b91ac1 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 12 Dec 2025 12:07:05 -0800 Subject: [PATCH 14/27] Finish addressing review feedback --- src/coreclr/jit/codegencommon.cpp | 11 +++++------ src/coreclr/jit/codegenlinear.cpp | 5 +++++ src/coreclr/jit/codegenwasm.cpp | 1 - src/coreclr/jit/emit.h | 2 +- src/coreclr/jit/emitwasm.cpp | 20 +++++++++----------- src/coreclr/jit/emitwasm.h | 4 ++-- src/coreclr/jit/registeropswasm.h | 15 +++++++++++++++ 7 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index aa087b682f0323..d0e0f657808685 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -5665,16 +5665,15 @@ void CodeGen::genFnProlog() //------------------------------------------------------------------------ // genWasmLocals: generate wasm locals for all locals // -// TODO-WASM: re-evaluate this, we may not want a 1:1 mapping of locals to wasm locals // TODO-WASM: pre-declare all "register" locals void CodeGen::genWasmLocals() { - for (unsigned i = 0; i < compiler->info.compLocalsCount - compiler->info.compArgsCount; i++) + // Emit 1 local of each supported value type to ensure + // the declarations can be encoded. + // TODO-WASM: remove and declare locals based on RA assignments once this is supported. + for (unsigned i = (uint8_t)WasmValueType::Invalid + 1; i < (unsigned)WasmValueType::Count; i++) { - LclVarDsc* varDsc = compiler->lvaGetDesc(i); - assert(!varDsc->lvIsParam); - emitter::instWasmValueType type = GetEmitter()->genWasmTypeFromVarType(varDsc->TypeGet()); - GetEmitter()->emitIns_I_Ty(INS_local_decl, 1, type); + GetEmitter()->emitIns_I_Ty(INS_local_decl, 1, static_cast(i)); } } #endif diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index b71d8efffefa13..3f11bbae11a668 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -658,6 +658,11 @@ void CodeGen::genCodeForBBlist() #endif // DEBUG } //------------------ END-FOR each block of the method ------------------- +#if defined(TARGET_WASM) + // The last instruction in a wasm function must be an explicit 'end' instruction. + instGen(INS_end); +#endif + #if defined(FEATURE_EH_WINDOWS_X86) // If this is a synchronized method on x86, and we generated all the code without // generating the "exit monitor" call, then we must have deleted the single return block diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index 6c0f4d6ec8edb7..b2ded7c7f8931e 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -42,7 +42,6 @@ void CodeGen::genFnEpilog(BasicBlock* block) // TODO-WASM-CQ: do not emit "return" in case this is the last block instGen(INS_return); - instGen(INS_end); } void CodeGen::genCaptureFuncletPrologEpilogInfo() diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index c3c58141e890e8..1d0bc11b05bfd9 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -1299,7 +1299,7 @@ class emitter #ifdef TARGET_WASM bool idIsLclVarDecl() const { - return _idInsFmt == IF_LOCAL_DECL + return _idInsFmt == IF_LOCAL_DECL; } #endif diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index 70ba94a9be3614..7519bc8bf750f1 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -119,7 +119,6 @@ bool emitter::emitInsIsStore(instruction ins) emitter::instrDesc* emitter::emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_t localCount, WasmValueType type) { instrDescLclVarDecl* id = static_cast(emitAllocAnyInstr(sizeof(instrDescLclVarDecl), attr)); - id->idSetIsLclVarDecl(true); id->idLclCnt(localCount); id->idLclType(type); @@ -143,7 +142,7 @@ void emitter::emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, WasmValueType va this->appendToCurIG(id); } -emitter::instWasmValueType emitter::emitGetLclVarDeclType(instrDesc* id) +WasmValueType emitter::emitGetLclVarDeclType(instrDesc* id) { assert(id->idIsLclVarDecl()); return static_cast(id)->lclType; @@ -225,21 +224,20 @@ unsigned emitter::SizeOfSLEB128(int64_t value) return (x * 37) >> 8; } -uint8_t getWasmValueTypeCode(WasmValueType type) +static uint8_t getWasmValueTypeCode(WasmValueType type) { // clang-format off - static const WasmValueType typecode_mapping[] = { + static const uint8_t typecode_mapping[] = { 0x00, // WasmValueType::Invalid = 0, 0x7C, // WasmValueType::F64 = 1, 0x7D, // WasmValueType::F32 = 2, 0x7E, // WasmValueType::I64 = 3, 0x7F, // WasmValueType::I32 = 4, }; - - static_assert(ArrLen(mapping) == TYP_COUNT); + static const int WASM_TYP_COUNT = ArrLen(typecode_mapping); + static_assert(ArrLen(typecode_mapping) == (int)WasmValueType::Count); // clang-format on - assert(0 <= type && type < WasmValueType::Count); return typecode_mapping[static_cast(type)]; } @@ -435,12 +433,12 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) { assert(id->idIsLclVarDecl()); cnsval_ssize_t count = emitGetLclVarDeclCount(id); - instWasmValueType valType = emitGetLclVarDeclType(id); + uint8_t valType = getWasmValueTypeCode(emitGetLclVarDeclType(id)); dst += emitOutputULEB128(dst, (uint64_t)count); // TODO-WASM: currently assuming all locals are numtypes which are single byte encoded. // vec types are also single byte encoded. If we end up using reftypes, we'll need to handle the more // complex encoding. - dst += emitOutputByte(dst, static_cast(valType)); + dst += emitOutputByte(dst, valType); break; } default: @@ -572,8 +570,8 @@ void emitter::emitDispIns( case IF_LOCAL_DECL: { cnsval_ssize_t imm = emitGetLclVarDeclCount(id); - instWasmValueType valType = emitGetLclVarDeclType(id); - printf(" %llu %s", (uint64_t)imm, instWasmValueTypeToStr(valType)); + WasmValueType valType = emitGetLclVarDeclType(id); + printf(" %llu %s", (uint64_t)imm, WasmValueTypeName(valType)); } break; diff --git a/src/coreclr/jit/emitwasm.h b/src/coreclr/jit/emitwasm.h index 1cd8375404e3a2..90c5d355376e11 100644 --- a/src/coreclr/jit/emitwasm.h +++ b/src/coreclr/jit/emitwasm.h @@ -34,8 +34,8 @@ static unsigned SizeOfSLEB128(int64_t value); static unsigned emitGetAlignHintLog2(const instrDesc* id); -instrDesc* emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_t localCount, instWasmValueType type); -static instWasmValueType emitGetLclVarDeclType(instrDesc* id); +instrDesc* emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_t localCount, WasmValueType type); +static WasmValueType emitGetLclVarDeclType(instrDesc* id); static cnsval_ssize_t emitGetLclVarDeclCount(instrDesc* id); /************************************************************************/ diff --git a/src/coreclr/jit/registeropswasm.h b/src/coreclr/jit/registeropswasm.h index 06a7c44cb5f767..385aa00076065c 100644 --- a/src/coreclr/jit/registeropswasm.h +++ b/src/coreclr/jit/registeropswasm.h @@ -16,6 +16,21 @@ enum class WasmValueType : unsigned #endif }; +static const char *const WasmValueTypeNames[] = { + "Invalid", + "i32", + "i64", + "f32", + "f64", + "Count", +}; + +inline const char* WasmValueTypeName(WasmValueType type) +{ + static_assert(ArrLen(WasmValueTypeNames) == static_cast(WasmValueType::Count) + 1); + return WasmValueTypeNames[static_cast(type)]; +} + regNumber MakeWasmReg(unsigned index, var_types type); unsigned UnpackWasmReg(regNumber reg, WasmValueType* pType = nullptr); unsigned WasmRegToIndex(regNumber reg); From d32f69591ad7592a711bf1874c423957a945e7b8 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 12 Dec 2025 13:29:47 -0800 Subject: [PATCH 15/27] Fix instGen placement --- src/coreclr/jit/codegenlinear.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 3f11bbae11a668..4bcdfc4d22ff4d 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -563,6 +563,13 @@ void CodeGen::genCodeForBBlist() #endif // EMIT_GENERATE_GCINFO #endif // DEBUG +#ifdef TARGET_WASM + if (block->IsLast()) + { + instGen(INS_end); + } +#endif + #if defined(DEBUG) if (block->IsLast()) { @@ -574,7 +581,7 @@ void CodeGen::genCodeForBBlist() // For example, if the following IR ends the current block, no code will have been generated for // offset 21: // - // ( 0, 0) [000040] ------------ il_offset void IL offset: 21 + // ( 0, 0) [000040] ------------ / il_offset void IL offset: 21 // // N001 ( 0, 0) [000039] ------------ nop void // @@ -658,11 +665,6 @@ void CodeGen::genCodeForBBlist() #endif // DEBUG } //------------------ END-FOR each block of the method ------------------- -#if defined(TARGET_WASM) - // The last instruction in a wasm function must be an explicit 'end' instruction. - instGen(INS_end); -#endif - #if defined(FEATURE_EH_WINDOWS_X86) // If this is a synchronized method on x86, and we generated all the code without // generating the "exit monitor" call, then we must have deleted the single return block From f3e2f829c418cef380a29f354925d54035bbfc80 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 12 Dec 2025 18:45:17 -0800 Subject: [PATCH 16/27] Add block->IsLast() guard for emitting end instruction in Wasm genFnEpilog --- src/coreclr/jit/codegenlinear.cpp | 7 ------- src/coreclr/jit/codegenwasm.cpp | 7 ++++++- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 4bcdfc4d22ff4d..b0e5bec80d542d 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -563,13 +563,6 @@ void CodeGen::genCodeForBBlist() #endif // EMIT_GENERATE_GCINFO #endif // DEBUG -#ifdef TARGET_WASM - if (block->IsLast()) - { - instGen(INS_end); - } -#endif - #if defined(DEBUG) if (block->IsLast()) { diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index b2ded7c7f8931e..8adb164fc4fbd7 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -40,8 +40,13 @@ void CodeGen::genFnEpilog(BasicBlock* block) // TODO-WASM: shadow stack maintenance // TODO-WASM-CQ: do not emit "return" in case this is the last block - instGen(INS_return); + // TODO-WASM: this condition will not be sufficient if we have funclet to determine if we're at the end of + // codegen for a method. Revisit later. + if (block->IsLast()) + { + instGen(INS_end); + } } void CodeGen::genCaptureFuncletPrologEpilogInfo() From 0820a24490c264ee6a457ace0c41359895f9bd7c Mon Sep 17 00:00:00 2001 From: adamperlin Date: Fri, 12 Dec 2025 18:51:07 -0800 Subject: [PATCH 17/27] Emit correct local count for current state of Wasm codegen --- src/coreclr/jit/codegencommon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index d0e0f657808685..4ad0decf5b7f64 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -5652,7 +5652,7 @@ void CodeGen::genFnProlog() #else // defined(TARGET_WASM) // TODO-WASM: proper local count, local declarations, and shadow stack maintenance assert(compiler->info.compLocalsCount >= compiler->info.compArgsCount); - GetEmitter()->emitIns_I(INS_local_cnt, EA_8BYTE, compiler->info.compLocalsCount - compiler->info.compArgsCount); + GetEmitter()->emitIns_I(INS_local_cnt, EA_8BYTE, (unsigned)WasmValueType::Count - 1); genWasmLocals(); GetEmitter()->emitMarkPrologEnd(); #endif // !defined(TARGET_WASM) From 78945a355c01c026172607b9f72a43c015c2b24b Mon Sep 17 00:00:00 2001 From: adamperlin Date: Mon, 15 Dec 2025 18:58:20 -0800 Subject: [PATCH 18/27] Address some more review feedback --- src/coreclr/jit/codegen.h | 4 -- src/coreclr/jit/codegencommon.cpp | 21 ---------- src/coreclr/jit/codegenlinear.cpp | 2 +- src/coreclr/jit/codegenwasm.cpp | 25 +++++++++++- src/coreclr/jit/emitwasm.cpp | 4 +- src/coreclr/jit/instrswasm.h | 4 +- src/coreclr/jit/registeropswasm.cpp | 15 +++++++ src/coreclr/jit/registeropswasm.h | 16 +------- src/coreclr/jit/wasmtypesdef.h | 62 ----------------------------- 9 files changed, 44 insertions(+), 109 deletions(-) delete mode 100644 src/coreclr/jit/wasmtypesdef.h diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 75a40ad9941d0f..a3b73955593b81 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -14,10 +14,6 @@ #include "regset.h" #include "jitgcinfo.h" -#if defined(TARGET_WASM) -#include "wasmtypesdef.h" -#endif - class CodeGen final : public CodeGenInterface { friend class emitter; diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 4ad0decf5b7f64..4113e40f1d44be 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -5650,9 +5650,6 @@ void CodeGen::genFnProlog() #endif // defined(DEBUG) && defined(TARGET_XARCH) #else // defined(TARGET_WASM) - // TODO-WASM: proper local count, local declarations, and shadow stack maintenance - assert(compiler->info.compLocalsCount >= compiler->info.compArgsCount); - GetEmitter()->emitIns_I(INS_local_cnt, EA_8BYTE, (unsigned)WasmValueType::Count - 1); genWasmLocals(); GetEmitter()->emitMarkPrologEnd(); #endif // !defined(TARGET_WASM) @@ -5660,24 +5657,6 @@ void CodeGen::genFnProlog() GetEmitter()->emitEndProlog(); } -#if defined(TARGET_WASM) - -//------------------------------------------------------------------------ -// genWasmLocals: generate wasm locals for all locals -// -// TODO-WASM: pre-declare all "register" locals -void CodeGen::genWasmLocals() -{ - // Emit 1 local of each supported value type to ensure - // the declarations can be encoded. - // TODO-WASM: remove and declare locals based on RA assignments once this is supported. - for (unsigned i = (uint8_t)WasmValueType::Invalid + 1; i < (unsigned)WasmValueType::Count; i++) - { - GetEmitter()->emitIns_I_Ty(INS_local_decl, 1, static_cast(i)); - } -} -#endif - #if !defined(TARGET_WASM) //---------------------------------------------------------------------------------- diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index b0e5bec80d542d..b71d8efffefa13 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -574,7 +574,7 @@ void CodeGen::genCodeForBBlist() // For example, if the following IR ends the current block, no code will have been generated for // offset 21: // - // ( 0, 0) [000040] ------------ / il_offset void IL offset: 21 + // ( 0, 0) [000040] ------------ il_offset void IL offset: 21 // // N001 ( 0, 0) [000039] ------------ nop void // diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index 8adb164fc4fbd7..585c464cf1871b 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -15,6 +15,23 @@ void CodeGen::genMarkLabelsForCodegen() // We mark labels as needed in genEmitStartBlock. } +//------------------------------------------------------------------------ +// genWasmLocals: generate wasm locals for all locals +// +// TODO-WASM: pre-declare all "register" locals +void CodeGen::genWasmLocals() +{ + // TODO-WASM: proper local count, local declarations, and shadow stack maintenance + GetEmitter()->emitIns_I(INS_local_cnt, EA_8BYTE, (unsigned)WasmValueType::Count - 1); + // Emit 1 local of each supported value type to ensure + // the declarations can be encoded. + // TODO-WASM: remove and declare locals based on RA assignments once this is supported. + for (unsigned i = (uint8_t)WasmValueType::Invalid + 1; i < (unsigned)WasmValueType::Count; i++) + { + GetEmitter()->emitIns_I_Ty(INS_local_decl, 1, static_cast(i)); + } +} + void CodeGen::genFnEpilog(BasicBlock* block) { #ifdef DEBUG @@ -40,13 +57,17 @@ void CodeGen::genFnEpilog(BasicBlock* block) // TODO-WASM: shadow stack maintenance // TODO-WASM-CQ: do not emit "return" in case this is the last block - instGen(INS_return); + // instGen(INS_return); // TODO-WASM: this condition will not be sufficient if we have funclet to determine if we're at the end of // codegen for a method. Revisit later. - if (block->IsLast()) + if (block->IsLast() || compiler->bbIsFuncletBeg(block)) { instGen(INS_end); } + else + { + instGen(INS_return); + } } void CodeGen::genCaptureFuncletPrologEpilogInfo() diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index 7519bc8bf750f1..48bdc87038dc03 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -138,8 +138,8 @@ void emitter::emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, WasmValueType va id->idIns(ins); id->idInsFmt(fmt); - this->dispIns(id); - this->appendToCurIG(id); + dispIns(id); + appendToCurIG(id); } WasmValueType emitter::emitGetLclVarDeclType(instrDesc* id) diff --git a/src/coreclr/jit/instrswasm.h b/src/coreclr/jit/instrswasm.h index 28002bd70f2154..a29a2cf3ec37ee 100644 --- a/src/coreclr/jit/instrswasm.h +++ b/src/coreclr/jit/instrswasm.h @@ -27,6 +27,8 @@ INST(invalid, "INVALID", 0, IF_NONE, BAD_CODE) INST(unreachable, "unreachable", 0, IF_OPCODE, 0x00) INST(label, "label", 0, IF_LABEL, 0x00) +INST(local_cnt, "local.cnt", 0, IF_LOCAL_CNT, 0x00) +INST(local_decl, "local", 0, IF_LOCAL_DECL, 0x00) INST(nop, "nop", 0, IF_OPCODE, 0x01) INST(block, "block", 0, IF_BLOCK, 0x02) INST(loop, "loop", 0, IF_BLOCK, 0x03) @@ -37,8 +39,6 @@ INST(br, "br", 0, IF_ULEB128, 0x0C) INST(br_if, "br_if", 0, IF_ULEB128, 0x0D) INST(br_table, "br_table", 0, IF_ULEB128, 0x0E) INST(return, "return", 0, IF_OPCODE, 0x0F) -INST(local_cnt, "local.cnt", 0, IF_LOCAL_CNT, 0x00) -INST(local_decl, "local", 0, IF_LOCAL_DECL, 0x00) INST(local_get, "local.get", 0, IF_ULEB128, 0x20) INST(i32_load, "i32.load", 0, IF_MEMARG, 0x28) diff --git a/src/coreclr/jit/registeropswasm.cpp b/src/coreclr/jit/registeropswasm.cpp index 724c69154faf1e..3a6ed7b8a48f29 100644 --- a/src/coreclr/jit/registeropswasm.cpp +++ b/src/coreclr/jit/registeropswasm.cpp @@ -61,6 +61,21 @@ regNumber MakeWasmReg(unsigned index, var_types type) return static_cast(regValue); } +const char* WasmValueTypeName(WasmValueType type) +{ + static const char* const WasmValueTypeNames[] = { + "Invalid", + "i32", + "i64", + "f32", + "f64", + }; + static_assert(ArrLen(WasmValueTypeNames) == static_cast(WasmValueType::Count)); + + assert(type < WasmValueType::Count); + return WasmValueTypeNames[static_cast(type)]; +} + //------------------------------------------------------------------------ // UnpackWasmReg: Extract the WASM local's index and type out of a register // diff --git a/src/coreclr/jit/registeropswasm.h b/src/coreclr/jit/registeropswasm.h index 385aa00076065c..63f044626595a1 100644 --- a/src/coreclr/jit/registeropswasm.h +++ b/src/coreclr/jit/registeropswasm.h @@ -16,21 +16,7 @@ enum class WasmValueType : unsigned #endif }; -static const char *const WasmValueTypeNames[] = { - "Invalid", - "i32", - "i64", - "f32", - "f64", - "Count", -}; - -inline const char* WasmValueTypeName(WasmValueType type) -{ - static_assert(ArrLen(WasmValueTypeNames) == static_cast(WasmValueType::Count) + 1); - return WasmValueTypeNames[static_cast(type)]; -} - +const char* WasmValueTypeName(WasmValueType type); regNumber MakeWasmReg(unsigned index, var_types type); unsigned UnpackWasmReg(regNumber reg, WasmValueType* pType = nullptr); unsigned WasmRegToIndex(regNumber reg); diff --git a/src/coreclr/jit/wasmtypesdef.h b/src/coreclr/jit/wasmtypesdef.h deleted file mode 100644 index 3f06d0c4721ddd..00000000000000 --- a/src/coreclr/jit/wasmtypesdef.h +++ /dev/null @@ -1,62 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*****************************************************************************/ -#ifndef _WASMTYPESDEF_H_ -#define _WASMTYPESDEF_H_ -/*****************************************************************************/ - -#include -#include "vartypesdef.h" - -#if defined(TARGET_WASM) -enum class instWasmValueType : uint8_t -{ - F64 = 0x7C, - F32 = 0x7D, - I64 = 0x7E, - I32 = 0x7F, -}; - -const char* const instWasmValueTypeNames[4] = { - "f64", - "f32", - "i64", - "i32", -}; - -//------------------------------------------------------------------------ -// instWasmValueTypeToStr: map wasm type to string -// -inline const char* const instWasmValueTypeToStr(instWasmValueType type) -{ - assert((type >= instWasmValueType::F64) && (type <= instWasmValueType::I32)); - return instWasmValueTypeNames[static_cast(type) - static_cast(instWasmValueType::F64)]; -} - -//------------------------------------------------------------------------ -// genWasmTypeFromVarType: map var_type to wasm type -// -inline instWasmValueType genWasmTypeFromVarType(var_types type) -{ - switch (type) - { - case TYP_INT: - case TYP_REF: - case TYP_BYREF: - return instWasmValueType::I32; - case TYP_LONG: - return instWasmValueType::I64; - case TYP_FLOAT: - return instWasmValueType::F32; - case TYP_DOUBLE: - return instWasmValueType::F64; - default: - unreached(); - } -} -#endif - -/*****************************************************************************/ -#endif // _WASMTYPESDEF_H_ -/*****************************************************************************/ From ddb4801c25e64075c6c483285a2c7aae72e0e0b6 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Tue, 16 Dec 2025 16:48:50 -0800 Subject: [PATCH 19/27] Address additional review feedback --- src/coreclr/jit/codegenwasm.cpp | 13 ++++----- src/coreclr/jit/emit.h | 7 +++-- src/coreclr/jit/emitwasm.cpp | 42 +++++++++++++++++++++-------- src/coreclr/jit/emitwasm.h | 8 +++--- src/coreclr/jit/registeropswasm.cpp | 11 +++----- src/coreclr/jit/registeropswasm.h | 14 +++++----- 6 files changed, 57 insertions(+), 38 deletions(-) diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index ef530b52020bae..e40a125457b9c0 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -26,9 +26,12 @@ void CodeGen::genWasmLocals() // Emit 1 local of each supported value type to ensure // the declarations can be encoded. // TODO-WASM: remove and declare locals based on RA assignments once this is supported. + int localOffset = 0; + int countPerType = 1; for (unsigned i = (uint8_t)WasmValueType::Invalid + 1; i < (unsigned)WasmValueType::Count; i++) { - GetEmitter()->emitIns_I_Ty(INS_local_decl, 1, static_cast(i)); + GetEmitter()->emitIns_I_Ty(INS_local_decl, countPerType, static_cast(i), localOffset); + localOffset += countPerType; } } @@ -56,11 +59,9 @@ void CodeGen::genFnEpilog(BasicBlock* block) } // TODO-WASM: shadow stack maintenance - // TODO-WASM-CQ: do not emit "return" in case this is the last block - // instGen(INS_return); - // TODO-WASM: this condition will not be sufficient if we have funclet to determine if we're at the end of - // codegen for a method. Revisit later. - if (block->IsLast() || compiler->bbIsFuncletBeg(block)) + // TODO-WASM: we need to handle the end-of-function case if we reach the end of a codegen for a function + // and do NOT have an epilog. In those cases we currently will not emit an end instruction. + if (block->IsLast() || compiler->bbIsFuncletBeg(block)) { instGen(INS_end); } diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 1d0bc11b05bfd9..c6373f67b4e998 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -638,6 +638,9 @@ class emitter bool idCatchRet; // Instruction is for a catch 'return' CORINFO_SIG_INFO* idCallSig; // Used to report native call site signatures to the EE BasicBlock* idTargetBlock; // Target block for branches +#ifdef TARGET_WASM + int lclOffset; +#endif }; #ifdef TARGET_ARM @@ -2346,8 +2349,8 @@ class emitter struct instrDescLclVarDecl : instrDesc { instrDescLclVarDecl() = delete; - cnsval_ssize_t lclCnt; - WasmValueType lclType; + cnsval_ssize_t lclCnt; + WasmValueType lclType; void idLclType(WasmValueType type) { diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index 48bdc87038dc03..7a883a9561bfb5 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -116,11 +116,15 @@ bool emitter::emitInsIsStore(instruction ins) return false; } -emitter::instrDesc* emitter::emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_t localCount, WasmValueType type) +emitter::instrDesc* emitter::emitNewInstrLclVarDecl(emitAttr attr, + cnsval_ssize_t localCount, + WasmValueType type, + int lclOffset) { instrDescLclVarDecl* id = static_cast(emitAllocAnyInstr(sizeof(instrDescLclVarDecl), attr)); id->idLclCnt(localCount); id->idLclType(type); + id->idDebugOnlyInfo()->lclOffset = lclOffset; return id; } @@ -130,13 +134,19 @@ emitter::instrDesc* emitter::emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_ // a count (immediate) and a value type. This is specifically used for local variable // declarations that require both the number of locals and their type to be encoded. // -void emitter::emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, WasmValueType valType) +// Arguments: +// ins - instruction to emit +// imm - immediate value (local count) +// valType - value type of the local variable +// offs - local variable offset (= count of preceding locals) for debug info +void emitter::emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, WasmValueType valType, int offs) { - instrDesc* id = this->emitNewInstrLclVarDecl(EA_8BYTE, imm, valType); - insFormat fmt = this->emitInsFormat(ins); + instrDesc* id = emitNewInstrLclVarDecl(EA_8BYTE, imm, valType, offs); + insFormat fmt = emitInsFormat(ins); id->idIns(ins); id->idInsFmt(fmt); + id->idDebugOnlyInfo()->lclOffset = offs; dispIns(id); appendToCurIG(id); @@ -267,9 +277,9 @@ unsigned emitter::instrDesc::idCodeSize() const case IF_LOCAL_DECL: { assert(idIsLclVarDecl()); - instrDescLclVarDecl* idl = static_cast(const_cast(this)); - uint8_t typeCode = getWasmValueTypeCode(idl->lclType); - size = SizeOfULEB128(idl->lclCnt) + sizeof(typeCode); + instrDescLclVarDecl* idl = static_cast(const_cast(this)); + uint8_t typeCode = getWasmValueTypeCode(idl->lclType); + size = SizeOfULEB128(idl->lclCnt) + sizeof(typeCode); break; } case IF_ULEB128: @@ -432,8 +442,8 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) case IF_LOCAL_DECL: { assert(id->idIsLclVarDecl()); - cnsval_ssize_t count = emitGetLclVarDeclCount(id); - uint8_t valType = getWasmValueTypeCode(emitGetLclVarDeclType(id)); + cnsval_ssize_t count = emitGetLclVarDeclCount(id); + uint8_t valType = getWasmValueTypeCode(emitGetLclVarDeclType(id)); dst += emitOutputULEB128(dst, (uint64_t)count); // TODO-WASM: currently assuming all locals are numtypes which are single byte encoded. // vec types are also single byte encoded. If we end up using reftypes, we'll need to handle the more @@ -569,9 +579,19 @@ void emitter::emitDispIns( case IF_LOCAL_DECL: { - cnsval_ssize_t imm = emitGetLclVarDeclCount(id); + cnsval_ssize_t count = emitGetLclVarDeclCount(id); WasmValueType valType = emitGetLclVarDeclType(id); - printf(" %llu %s", (uint64_t)imm, WasmValueTypeName(valType)); + int offs = id->idDebugOnlyInfo()->lclOffset; + + assert(count > 0); // we should not be declaring a local entry with zero count. + if (count > 1) + { + printf("[%llu..%llu] type=%s", offs, offs + (uint64_t)count - 1, WasmValueTypeName(valType)); + } + else // single local case + { + printf("[%llu] type=%s", offs, WasmValueTypeName(valType)); + } } break; diff --git a/src/coreclr/jit/emitwasm.h b/src/coreclr/jit/emitwasm.h index 90c5d355376e11..76b03d39c55a64 100644 --- a/src/coreclr/jit/emitwasm.h +++ b/src/coreclr/jit/emitwasm.h @@ -18,7 +18,7 @@ void emitDispInst(instruction ins); public: void emitIns(instruction ins); void emitIns_I(instruction ins, emitAttr attr, cnsval_ssize_t imm); -void emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, WasmValueType valType); +void emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, WasmValueType valType, int offs); void emitIns_J(instruction ins, emitAttr attr, cnsval_ssize_t imm, BasicBlock* tgtBlock); void emitIns_S(instruction ins, emitAttr attr, int varx, int offs); void emitIns_R(instruction ins, emitAttr attr, regNumber reg); @@ -34,9 +34,9 @@ static unsigned SizeOfSLEB128(int64_t value); static unsigned emitGetAlignHintLog2(const instrDesc* id); -instrDesc* emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_t localCount, WasmValueType type); -static WasmValueType emitGetLclVarDeclType(instrDesc* id); -static cnsval_ssize_t emitGetLclVarDeclCount(instrDesc* id); +instrDesc* emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_t localCount, WasmValueType type, int lclOffset); +static WasmValueType emitGetLclVarDeclType(instrDesc* id); +static cnsval_ssize_t emitGetLclVarDeclCount(instrDesc* id); /************************************************************************/ /* Private members that deal with target-dependent instr. descriptors */ diff --git a/src/coreclr/jit/registeropswasm.cpp b/src/coreclr/jit/registeropswasm.cpp index 3a6ed7b8a48f29..5e83468c1ce38a 100644 --- a/src/coreclr/jit/registeropswasm.cpp +++ b/src/coreclr/jit/registeropswasm.cpp @@ -64,16 +64,11 @@ regNumber MakeWasmReg(unsigned index, var_types type) const char* WasmValueTypeName(WasmValueType type) { static const char* const WasmValueTypeNames[] = { - "Invalid", - "i32", - "i64", - "f32", - "f64", + "Invalid", "i32", "i64", "f32", "f64", }; static_assert(ArrLen(WasmValueTypeNames) == static_cast(WasmValueType::Count)); - - assert(type < WasmValueType::Count); - return WasmValueTypeNames[static_cast(type)]; + assert(WasmValueType::Invalid <= type && type < WasmValueType::Count); + return WasmValueTypeNames[(unsigned)type]; } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/registeropswasm.h b/src/coreclr/jit/registeropswasm.h index 63f044626595a1..ae695493547d0d 100644 --- a/src/coreclr/jit/registeropswasm.h +++ b/src/coreclr/jit/registeropswasm.h @@ -17,10 +17,10 @@ enum class WasmValueType : unsigned }; const char* WasmValueTypeName(WasmValueType type); -regNumber MakeWasmReg(unsigned index, var_types type); -unsigned UnpackWasmReg(regNumber reg, WasmValueType* pType = nullptr); -unsigned WasmRegToIndex(regNumber reg); -bool genIsValidReg(regNumber reg); -bool genIsValidIntReg(regNumber reg); -bool genIsValidIntOrFakeReg(regNumber reg); -bool genIsValidFloatReg(regNumber reg); +regNumber MakeWasmReg(unsigned index, var_types type); +unsigned UnpackWasmReg(regNumber reg, WasmValueType* pType = nullptr); +unsigned WasmRegToIndex(regNumber reg); +bool genIsValidReg(regNumber reg); +bool genIsValidIntReg(regNumber reg); +bool genIsValidIntOrFakeReg(regNumber reg); +bool genIsValidFloatReg(regNumber reg); From 7bb7cfc76ba5155214e6bb9b5e732956fc12f429 Mon Sep 17 00:00:00 2001 From: Adam Perlin Date: Tue, 16 Dec 2025 17:56:00 -0800 Subject: [PATCH 20/27] Apply suggestion from @SingleAccretion Co-authored-by: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> --- src/coreclr/jit/emitwasm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index 7a883a9561bfb5..5fbbeb7f0afbe0 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -234,7 +234,7 @@ unsigned emitter::SizeOfSLEB128(int64_t value) return (x * 37) >> 8; } -static uint8_t getWasmValueTypeCode(WasmValueType type) +static uint8_t GetWasmValueTypeCode(WasmValueType type) { // clang-format off static const uint8_t typecode_mapping[] = { From 06207d5a26d0ba2219436bc73bff2b23706a023f Mon Sep 17 00:00:00 2001 From: adamperlin Date: Tue, 16 Dec 2025 18:21:57 -0800 Subject: [PATCH 21/27] Address additional review feedback; properly guard access to wasm-specific debug info field on m_debugInfoSize --- src/coreclr/jit/emitfmtswasm.h | 21 ++++++------ src/coreclr/jit/emitwasm.cpp | 53 ++++++++++++++++------------- src/coreclr/jit/instrswasm.h | 4 +-- src/coreclr/jit/registeropswasm.cpp | 9 ++++- 4 files changed, 50 insertions(+), 37 deletions(-) diff --git a/src/coreclr/jit/emitfmtswasm.h b/src/coreclr/jit/emitfmtswasm.h index 227107c6d34008..55b852649192f5 100644 --- a/src/coreclr/jit/emitfmtswasm.h +++ b/src/coreclr/jit/emitfmtswasm.h @@ -26,17 +26,16 @@ enum ID_OPS // (unused) ////////////////////////////////////////////////////////////////////////////// -IF_DEF(NONE, IS_NONE, NONE) -IF_DEF(OPCODE, IS_NONE, NONE) // -IF_DEF(BLOCK, IS_NONE, NONE) // <0x40> -IF_DEF(LABEL, IS_NONE, NONE) // -IF_DEF(ULEB128, IS_NONE, NONE) // -IF_DEF(SLEB128, IS_NONE, NONE) // -IF_DEF(F32, IS_NONE, NONE) // -IF_DEF(F64, IS_NONE, NONE) // -IF_DEF(MEMARG, IS_NONE, NONE) // ( ) -IF_DEF(LOCAL_CNT, IS_NONE, NONE) // -IF_DEF(LOCAL_DECL, IS_NONE, NONE) // +IF_DEF(NONE, IS_NONE, NONE) +IF_DEF(OPCODE, IS_NONE, NONE) // +IF_DEF(BLOCK, IS_NONE, NONE) // <0x40> +IF_DEF(RAW_ULEB128, IS_NONE, NONE) // +IF_DEF(ULEB128, IS_NONE, NONE) // +IF_DEF(SLEB128, IS_NONE, NONE) // +IF_DEF(F32, IS_NONE, NONE) // +IF_DEF(F64, IS_NONE, NONE) // +IF_DEF(MEMARG, IS_NONE, NONE) // ( ) +IF_DEF(LOCAL_DECL, IS_NONE, NONE) // #undef IF_DEF #endif // !DEFINE_ID_OPS diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index 5fbbeb7f0afbe0..44e03bd7a023d7 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -124,7 +124,11 @@ emitter::instrDesc* emitter::emitNewInstrLclVarDecl(emitAttr attr, instrDescLclVarDecl* id = static_cast(emitAllocAnyInstr(sizeof(instrDescLclVarDecl), attr)); id->idLclCnt(localCount); id->idLclType(type); - id->idDebugOnlyInfo()->lclOffset = lclOffset; + + if (m_debugInfoSize > 0) + { + id->idDebugOnlyInfo()->lclOffset = lclOffset; + } return id; } @@ -146,7 +150,11 @@ void emitter::emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, WasmValueType va id->idIns(ins); id->idInsFmt(fmt); - id->idDebugOnlyInfo()->lclOffset = offs; + + if (m_debugInfoSize > 0) + { + id->idDebugOnlyInfo()->lclOffset = offs; + } dispIns(id); appendToCurIG(id); @@ -269,8 +277,7 @@ unsigned emitter::instrDesc::idCodeSize() const case IF_BLOCK: size += 1; break; - case IF_LABEL: - case IF_LOCAL_CNT: + case IF_RAW_ULEB128: assert(!idIsCnsReloc()); size = SizeOfULEB128(emitGetInsSC(this)); break; @@ -278,7 +285,7 @@ unsigned emitter::instrDesc::idCodeSize() const { assert(idIsLclVarDecl()); instrDescLclVarDecl* idl = static_cast(const_cast(this)); - uint8_t typeCode = getWasmValueTypeCode(idl->lclType); + uint8_t typeCode = GetWasmValueTypeCode(idl->lclType); size = SizeOfULEB128(idl->lclCnt) + sizeof(typeCode); break; } @@ -416,7 +423,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) dst += emitRawBytes(dst, &bits, sizeof(cnsval_ssize_t)); break; } - case IF_LABEL: + case IF_RAW_ULEB128: { cnsval_ssize_t constant = emitGetInsSC(id); dst += emitOutputULEB128(dst, (uint64_t)constant); @@ -433,21 +440,12 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) dst += emitOutputULEB128(dst, offset); break; } - case IF_LOCAL_CNT: - { - cnsval_ssize_t constant = emitGetInsSC(id); - dst += emitOutputULEB128(dst, (uint64_t)constant); - break; - } case IF_LOCAL_DECL: { assert(id->idIsLclVarDecl()); cnsval_ssize_t count = emitGetLclVarDeclCount(id); - uint8_t valType = getWasmValueTypeCode(emitGetLclVarDeclType(id)); + uint8_t valType = GetWasmValueTypeCode(emitGetLclVarDeclType(id)); dst += emitOutputULEB128(dst, (uint64_t)count); - // TODO-WASM: currently assuming all locals are numtypes which are single byte encoded. - // vec types are also single byte encoded. If we end up using reftypes, we'll need to handle the more - // complex encoding. dst += emitOutputByte(dst, valType); break; } @@ -567,9 +565,8 @@ void emitter::emitDispIns( case IF_BLOCK: break; - case IF_LABEL: + case IF_RAW_ULEB128: case IF_ULEB128: - case IF_LOCAL_CNT: { cnsval_ssize_t imm = emitGetInsSC(id); printf(" %llu", (uint64_t)imm); @@ -582,15 +579,25 @@ void emitter::emitDispIns( cnsval_ssize_t count = emitGetLclVarDeclCount(id); WasmValueType valType = emitGetLclVarDeclType(id); int offs = id->idDebugOnlyInfo()->lclOffset; + assert(count > 0); // we should not be declaring a local entry with zero count - assert(count > 0); // we should not be declaring a local entry with zero count. - if (count > 1) + if (m_debugInfoSize > 0) { - printf("[%llu..%llu] type=%s", offs, offs + (uint64_t)count - 1, WasmValueTypeName(valType)); + // With debug info: print the local offsets being declared + if (count > 1) + { + printf("[%llu..%llu] type=%s", offs, offs + (uint64_t)count - 1, WasmValueTypeName(valType)); + } + else // single local case + { + printf("[%llu] type=%s", offs, WasmValueTypeName(valType)); + } } - else // single local case + else { - printf("[%llu] type=%s", offs, WasmValueTypeName(valType)); + // No debug info case: just print the count and type of the locals + printf(" count=%llu type=%s", (uint64_t)count, WasmValueTypeName(valType)); + break; } } break; diff --git a/src/coreclr/jit/instrswasm.h b/src/coreclr/jit/instrswasm.h index 45fc4c6316ebf1..f2115bb1c84cee 100644 --- a/src/coreclr/jit/instrswasm.h +++ b/src/coreclr/jit/instrswasm.h @@ -26,8 +26,8 @@ // INST(invalid, "INVALID", 0, IF_NONE, BAD_CODE) INST(unreachable, "unreachable", 0, IF_OPCODE, 0x00) -INST(label, "label", 0, IF_LABEL, 0x00) -INST(local_cnt, "local.cnt", 0, IF_LOCAL_CNT, 0x00) +INST(label, "label", 0, IF_RAW_ULEB128, 0x00) +INST(local_cnt, "local.cnt", 0, IF_RAW_ULEB128, 0x00) INST(local_decl, "local", 0, IF_LOCAL_DECL, 0x00) INST(nop, "nop", 0, IF_OPCODE, 0x01) INST(block, "block", 0, IF_BLOCK, 0x02) diff --git a/src/coreclr/jit/registeropswasm.cpp b/src/coreclr/jit/registeropswasm.cpp index 5e83468c1ce38a..0386d477d1b3cd 100644 --- a/src/coreclr/jit/registeropswasm.cpp +++ b/src/coreclr/jit/registeropswasm.cpp @@ -63,10 +63,17 @@ regNumber MakeWasmReg(unsigned index, var_types type) const char* WasmValueTypeName(WasmValueType type) { + // clang-format off static const char* const WasmValueTypeNames[] = { - "Invalid", "i32", "i64", "f32", "f64", + "Invalid", + "i32", + "i64", + "f32", + "f64", }; static_assert(ArrLen(WasmValueTypeNames) == static_cast(WasmValueType::Count)); + // clang-format on + assert(WasmValueType::Invalid <= type && type < WasmValueType::Count); return WasmValueTypeNames[(unsigned)type]; } From c52da98030cc86cced471ffccadc6c7e5dd46fa1 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Tue, 16 Dec 2025 19:00:52 -0800 Subject: [PATCH 22/27] Address additional review feedback --- src/coreclr/jit/emitwasm.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index 44e03bd7a023d7..a713e8e129889e 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -116,6 +116,19 @@ bool emitter::emitInsIsStore(instruction ins) return false; } +//----------------------------------------------------------------------------- +// emitNewInstrLclVarDecl: Construct an instrDesc corresponding to a wasm local +// declaration. +// +// Arguments: +// attr - emit attributes +// localCount - the count of locals in this declaration +// type - the type of local in the declaration +// lclOffset - used to provide the starting index of this local +// +// Notes: +// `lclOffset` is stored as debug info attached to the instruction, +// so the offset will only be used if m_debugInfoSize > 0 emitter::instrDesc* emitter::emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_t localCount, WasmValueType type, @@ -151,11 +164,6 @@ void emitter::emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, WasmValueType va id->idIns(ins); id->idInsFmt(fmt); - if (m_debugInfoSize > 0) - { - id->idDebugOnlyInfo()->lclOffset = offs; - } - dispIns(id); appendToCurIG(id); } From 93731818095a40eee11bbec3588f5e2e1fea2a39 Mon Sep 17 00:00:00 2001 From: Adam Perlin Date: Tue, 16 Dec 2025 19:04:28 -0800 Subject: [PATCH 23/27] Fix condition for `end` generation in src/coreclr/jit/codegenwasm.cpp Co-authored-by: Andy Ayers --- src/coreclr/jit/codegenwasm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index e40a125457b9c0..0614765dc939a2 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -61,7 +61,7 @@ void CodeGen::genFnEpilog(BasicBlock* block) // TODO-WASM: shadow stack maintenance // TODO-WASM: we need to handle the end-of-function case if we reach the end of a codegen for a function // and do NOT have an epilog. In those cases we currently will not emit an end instruction. - if (block->IsLast() || compiler->bbIsFuncletBeg(block)) + if (block->IsLast() || compiler->bbIsFuncletBeg(block->Next())) { instGen(INS_end); } From 29a82df5ce10e6e10e9bdc155513f1472704b041 Mon Sep 17 00:00:00 2001 From: Adam Perlin Date: Wed, 17 Dec 2025 09:55:19 -0800 Subject: [PATCH 24/27] Update src/coreclr/jit/codegenwasm.cpp Co-authored-by: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> --- src/coreclr/jit/codegenwasm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index 0614765dc939a2..e5f32d47e1099b 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -16,7 +16,7 @@ void CodeGen::genMarkLabelsForCodegen() } //------------------------------------------------------------------------ -// genWasmLocals: generate wasm locals for all locals +// genWasmLocals: generate wasm local declarations // // TODO-WASM: pre-declare all "register" locals void CodeGen::genWasmLocals() From afc4638d7e1d9535b5ab9a42376058e13e5dbd10 Mon Sep 17 00:00:00 2001 From: Adam Perlin Date: Wed, 17 Dec 2025 09:55:49 -0800 Subject: [PATCH 25/27] Update src/coreclr/jit/emit.h Co-authored-by: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com> --- src/coreclr/jit/emit.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index c6373f67b4e998..78c3d9b198ef87 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -639,7 +639,7 @@ class emitter CORINFO_SIG_INFO* idCallSig; // Used to report native call site signatures to the EE BasicBlock* idTargetBlock; // Target block for branches #ifdef TARGET_WASM - int lclOffset; + int lclOffset; // Base index of the WASM locals being declared #endif }; From 5a32a13d3b43bae495b83a2f78d6468c0f36cba9 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Wed, 17 Dec 2025 10:20:36 -0800 Subject: [PATCH 26/27] Address additional review feedback --- src/coreclr/jit/emit.h | 6 +++--- src/coreclr/jit/emitwasm.cpp | 36 +++++++++++++++++------------------- src/coreclr/jit/emitwasm.h | 8 ++++---- src/coreclr/jit/instrswasm.h | 4 ++-- 4 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 78c3d9b198ef87..2e01d58b6f0e0f 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -2349,15 +2349,15 @@ class emitter struct instrDescLclVarDecl : instrDesc { instrDescLclVarDecl() = delete; - cnsval_ssize_t lclCnt; - WasmValueType lclType; + unsigned int lclCnt; + WasmValueType lclType; void idLclType(WasmValueType type) { lclType = type; } - void idLclCnt(cnsval_ssize_t cnt) + void idLclCnt(unsigned int cnt) { lclCnt = cnt; } diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index a713e8e129889e..983fb87d75a53c 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -129,10 +129,10 @@ bool emitter::emitInsIsStore(instruction ins) // Notes: // `lclOffset` is stored as debug info attached to the instruction, // so the offset will only be used if m_debugInfoSize > 0 -emitter::instrDesc* emitter::emitNewInstrLclVarDecl(emitAttr attr, - cnsval_ssize_t localCount, - WasmValueType type, - int lclOffset) +emitter::instrDesc* emitter::emitNewInstrLclVarDecl(emitAttr attr, + unsigned int localCount, + WasmValueType type, + int lclOffset) { instrDescLclVarDecl* id = static_cast(emitAllocAnyInstr(sizeof(instrDescLclVarDecl), attr)); id->idLclCnt(localCount); @@ -156,7 +156,7 @@ emitter::instrDesc* emitter::emitNewInstrLclVarDecl(emitAttr attr, // imm - immediate value (local count) // valType - value type of the local variable // offs - local variable offset (= count of preceding locals) for debug info -void emitter::emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, WasmValueType valType, int offs) +void emitter::emitIns_I_Ty(instruction ins, unsigned int imm, WasmValueType valType, int offs) { instrDesc* id = emitNewInstrLclVarDecl(EA_8BYTE, imm, valType, offs); insFormat fmt = emitInsFormat(ins); @@ -168,16 +168,16 @@ void emitter::emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, WasmValueType va appendToCurIG(id); } -WasmValueType emitter::emitGetLclVarDeclType(instrDesc* id) +WasmValueType emitter::emitGetLclVarDeclType(const instrDesc* id) { assert(id->idIsLclVarDecl()); - return static_cast(id)->lclType; + return static_cast(id)->lclType; } -cnsval_ssize_t emitter::emitGetLclVarDeclCount(instrDesc* id) +unsigned int emitter::emitGetLclVarDeclCount(const instrDesc* id) { assert(id->idIsLclVarDecl()); - return static_cast(id)->lclCnt; + return static_cast(id)->lclCnt; } emitter::insFormat emitter::emitInsFormat(instruction ins) @@ -292,9 +292,8 @@ unsigned emitter::instrDesc::idCodeSize() const case IF_LOCAL_DECL: { assert(idIsLclVarDecl()); - instrDescLclVarDecl* idl = static_cast(const_cast(this)); - uint8_t typeCode = GetWasmValueTypeCode(idl->lclType); - size = SizeOfULEB128(idl->lclCnt) + sizeof(typeCode); + uint8_t typeCode = GetWasmValueTypeCode(emitGetLclVarDeclType(this)); + size = SizeOfULEB128(emitGetLclVarDeclCount(this)) + sizeof(typeCode); break; } case IF_ULEB128: @@ -584,28 +583,27 @@ void emitter::emitDispIns( case IF_LOCAL_DECL: { - cnsval_ssize_t count = emitGetLclVarDeclCount(id); - WasmValueType valType = emitGetLclVarDeclType(id); - int offs = id->idDebugOnlyInfo()->lclOffset; + unsigned int count = emitGetLclVarDeclCount(id); + WasmValueType valType = emitGetLclVarDeclType(id); assert(count > 0); // we should not be declaring a local entry with zero count if (m_debugInfoSize > 0) { // With debug info: print the local offsets being declared + int offs = id->idDebugOnlyInfo()->lclOffset; if (count > 1) { - printf("[%llu..%llu] type=%s", offs, offs + (uint64_t)count - 1, WasmValueTypeName(valType)); + printf("[%u..%u] type=%s", offs, offs + count - 1, WasmValueTypeName(valType)); } else // single local case { - printf("[%llu] type=%s", offs, WasmValueTypeName(valType)); + printf("[%u] type=%s", offs, WasmValueTypeName(valType)); } } else { // No debug info case: just print the count and type of the locals - printf(" count=%llu type=%s", (uint64_t)count, WasmValueTypeName(valType)); - break; + printf(" count=%u type=%s", count, WasmValueTypeName(valType)); } } break; diff --git a/src/coreclr/jit/emitwasm.h b/src/coreclr/jit/emitwasm.h index 76b03d39c55a64..52300e925d183d 100644 --- a/src/coreclr/jit/emitwasm.h +++ b/src/coreclr/jit/emitwasm.h @@ -18,7 +18,7 @@ void emitDispInst(instruction ins); public: void emitIns(instruction ins); void emitIns_I(instruction ins, emitAttr attr, cnsval_ssize_t imm); -void emitIns_I_Ty(instruction ins, cnsval_ssize_t imm, WasmValueType valType, int offs); +void emitIns_I_Ty(instruction ins, unsigned int imm, WasmValueType valType, int offs); void emitIns_J(instruction ins, emitAttr attr, cnsval_ssize_t imm, BasicBlock* tgtBlock); void emitIns_S(instruction ins, emitAttr attr, int varx, int offs); void emitIns_R(instruction ins, emitAttr attr, regNumber reg); @@ -34,9 +34,9 @@ static unsigned SizeOfSLEB128(int64_t value); static unsigned emitGetAlignHintLog2(const instrDesc* id); -instrDesc* emitNewInstrLclVarDecl(emitAttr attr, cnsval_ssize_t localCount, WasmValueType type, int lclOffset); -static WasmValueType emitGetLclVarDeclType(instrDesc* id); -static cnsval_ssize_t emitGetLclVarDeclCount(instrDesc* id); +instrDesc* emitNewInstrLclVarDecl(emitAttr attr, unsigned int localCount, WasmValueType type, int lclOffset); +static WasmValueType emitGetLclVarDeclType(const instrDesc* id); +static unsigned int emitGetLclVarDeclCount(const instrDesc* id); /************************************************************************/ /* Private members that deal with target-dependent instr. descriptors */ diff --git a/src/coreclr/jit/instrswasm.h b/src/coreclr/jit/instrswasm.h index f2115bb1c84cee..f8562c071c30cb 100644 --- a/src/coreclr/jit/instrswasm.h +++ b/src/coreclr/jit/instrswasm.h @@ -26,9 +26,9 @@ // INST(invalid, "INVALID", 0, IF_NONE, BAD_CODE) INST(unreachable, "unreachable", 0, IF_OPCODE, 0x00) -INST(label, "label", 0, IF_RAW_ULEB128, 0x00) +INST(label, "label", 0, IF_RAW_ULEB128, 0x00) INST(local_cnt, "local.cnt", 0, IF_RAW_ULEB128, 0x00) -INST(local_decl, "local", 0, IF_LOCAL_DECL, 0x00) +INST(local_decl, "local", 0, IF_LOCAL_DECL, 0x00) INST(nop, "nop", 0, IF_OPCODE, 0x01) INST(block, "block", 0, IF_BLOCK, 0x02) INST(loop, "loop", 0, IF_BLOCK, 0x03) From ef2c690a0e588139e8f1789072df339eda720be1 Mon Sep 17 00:00:00 2001 From: adamperlin Date: Wed, 17 Dec 2025 14:18:08 -0800 Subject: [PATCH 27/27] Fix bug in jitutil.py determine_jit_name logic (cherry-pick from #122619) --- src/coreclr/scripts/jitutil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/scripts/jitutil.py b/src/coreclr/scripts/jitutil.py index a44b5e8605fc68..5e5424d9572b7a 100644 --- a/src/coreclr/scripts/jitutil.py +++ b/src/coreclr/scripts/jitutil.py @@ -494,7 +494,7 @@ def determine_jit_name(host_os, target_os=None, host_arch=None, target_arch=None if use_cross_compile_jit or (host_arch != target_arch) or ((target_os is not None) and (host_os != target_os)): if target_arch.startswith("arm"): jit_os_name = "universal" - if target_arch.startswith("wasm"): + elif target_arch.startswith("wasm"): jit_os_name = "universal" elif target_os == "windows": jit_os_name = "win"