/* * Copyright (C) 2023 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "WasmIPIntGenerator.h" #include #if ENABLE(WEBASSEMBLY) #include "BytecodeGeneratorBaseInlines.h" #include "BytecodeStructs.h" #include "InstructionStream.h" #include "JSCJSValueInlines.h" #include "Label.h" #include "WasmCallingConvention.h" #include "WasmContext.h" #include "WasmFunctionIPIntMetadataGenerator.h" #include "WasmFunctionParser.h" #include "WasmModuleDebugInfo.h" #include /* * WebAssembly in-place interpreter metadata generator * * docs by Daniel Liu ; 2023 intern project * * 1. Why Metadata? * ---------------- * * WebAssembly's bytecode format isn't always the easiest to interpret by itself: jumps would require parsing * through many bytes to find their target, constants are stored in LEB128, and a myriad of other reasons. * For IPInt, we design metadata to act as "supporting information" for the interpreter, allowing it to quickly * find important values such as constants, indices, and branch targets. * * FIXME: We should consider not aligning on Apple ARM64 cores since they don't typically have a penatly for unaligned loads/stores. * * 2. Metadata Structure * --------------------- * * Metadata is kept in a vector of UInt8 (bytes). We handle metadata in "metadata entries", which are groups of * 8 metadata bytes. We keep metadata aligned to 8B to improve access times. Sometimes, this results in higher * memory overhead; however, these cases are relatively sparse. Each instruction pushes a certain number of * entries to the metadata vector. * * 3. Metadata for Instructions * ---------------------------- * * block (0x02): 1 entry; 8B PC of next instruction * loop (0x03): 1 entry; 8B PC of next instruction * if (0x04): 2 entries; 4B new PC, 4B new MC for `else`, 8B new PC for `if` * else (0x05): 1 entry; 4B new PC, 4B new MC for `end` * end (0x0b): If exiting the function: ceil((# return values + 2) / 8) entries; 2B for total entry size, 1B / value returned * br (0x0c): 2 entries; 4B new PC, 4B new MC, 2B number of values to pop, 2B arity, 4B PC after br * br_if (0x0d): 2 entries; same as br * br_table (0x0e): 1 + 2n entries for n branches: 8B number of targets; n br metadata entries * local.get (0x20): 1 entry; 4B index of local, 4B size of instruction * local.set (0x21): 1 entry; 4B index of local, 4B size of instruction * local.tee (0x22): 2 entries because of how FunctionParser works * global.get (0x23): 1 entry; 4B index of global, 4B size of instruction * global.set (0x24): 1 entry; 4B index of global, 4B size of instruction * table.get (0x23): 1 entry; 4B index of table, 4B size of instruction * table.set (0x24): 1 entry; 4B index of table, 4B size of instruction * mem load (0x28 - 0x35): 1 entry; 4B memarg, 4B size of instruction * mem store (0x28 - 0x35): 1 entry; 4B memarg, 4B size of instruction * i32.const (0x41): 1 entry; 4B value, 4B size of instruction * i64.const (0x42): 2 entries; 8B value, 8B size of instruction * * i32, i64, f32, and f64 operations (besides the ones shown above) do not require metadata * */ WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN #if ENABLE(WEBASSEMBLY_DEBUGGER) #define RECORD_NEXT_INSTRUCTION(fromPC, toPC) \ do { \ if (m_debugInfo) [[unlikely]] { \ uint32_t fromOffset = fromPC + m_metadata->m_bytecodeOffset + m_functionStartByteOffset; \ uint32_t toOffset = toPC + m_metadata->m_bytecodeOffset + m_functionStartByteOffset; \ m_debugInfo->addNextInstruction(fromOffset, toOffset); \ } \ } while (0) #else #define RECORD_NEXT_INSTRUCTION(fromPC, toPC) do { (void)(fromPC); (void)(toPC); } while (0) #endif namespace JSC { namespace Wasm { using ErrorType = String; using PartialResult = std::expected; using UnexpectedResult = std::unexpected; struct IPIntValue { }; // ControlBlock struct IPIntLocation { uint32_t pc; uint32_t mc; }; struct IPIntControlType { friend class IPIntGenerator; IPIntControlType() { } IPIntControlType(BlockSignature&& signature, uint32_t stackSize, BlockType blockType, CatchKind catchKind = CatchKind::Catch) : m_signature(WTF::move(signature)) , m_blockType(blockType) , m_catchKind(catchKind) , m_stackSize(stackSize) { } static bool LEGACY_NODELETE isIf(const IPIntControlType& control) { return control.blockType() == BlockType::If; } static bool LEGACY_NODELETE isElse(const IPIntControlType& control) { return control.blockType() == BlockType::Else; } static bool LEGACY_NODELETE isTry(const IPIntControlType& control) { return control.blockType() == BlockType::Try; } static bool LEGACY_NODELETE isTryTable(const IPIntControlType& control) { return control.blockType() == BlockType::TryTable; } static bool LEGACY_NODELETE isAnyCatch(const IPIntControlType& control) { return control.blockType() == BlockType::Catch; } static bool LEGACY_NODELETE isTopLevel(const IPIntControlType& control) { return control.blockType() == BlockType::TopLevel; } static bool LEGACY_NODELETE isLoop(const IPIntControlType& control) { return control.blockType() == BlockType::Loop; } static bool LEGACY_NODELETE isBlock(const IPIntControlType& control) { return control.blockType() == BlockType::Block; } static bool LEGACY_NODELETE isCatch(const IPIntControlType& control) { if (control.blockType() != BlockType::Catch) return false; return control.catchKind() == CatchKind::Catch; } void LEGACY_NODELETE dump(PrintStream&) const { } BlockType LEGACY_NODELETE blockType() const { return m_blockType; } CatchKind LEGACY_NODELETE catchKind() const { return m_catchKind; } const BlockSignature& LEGACY_NODELETE signature() const { return m_signature; } unsigned LEGACY_NODELETE stackSize() const { return m_stackSize; } Type branchTargetType(unsigned i) const { ASSERT(i < branchTargetArity()); if (blockType() == BlockType::Loop) return m_signature.argumentType(i); return m_signature.returnType(i); } unsigned branchTargetArity() const { return isLoop(*this) ? m_signature.argumentCount() : m_signature.returnCount(); } private: BlockSignature m_signature; BlockType m_blockType; CatchKind m_catchKind; std::optional m_pendingOffset; uint32_t m_index { 0 }; uint32_t m_pc { 0 }; // where am i? uint32_t m_mc { 0 }; uint32_t m_pcEnd { 0 }; uint32_t m_stackSize { 0 }; uint32_t m_tryDepth { 0 }; Vector m_catchesAwaitingFixup; struct TryTableTarget { CatchKind type; uint32_t tag; const RTT* exceptionSignature; ControlRef target; }; Vector m_tryTableTargets; }; class IPIntGenerator { public: IPIntGenerator(ModuleInformation&, FunctionCodeIndex, const RTT&, std::span, FunctionDebugInfo* = nullptr); static constexpr bool shouldFuseBranchCompare = false; using ControlType = IPIntControlType; using ExpressionType = IPIntValue; using CallType = CallLinkInfo::CallType; using ResultList = Vector; using ExpressionList = Vector; using ControlEntry = FunctionParser::ControlEntry; using ControlStack = FunctionParser::ControlStack; using Stack = FunctionParser::Stack; using TypedExpression = FunctionParser::TypedExpression; using CatchHandler = FunctionParser::CatchHandler; using ArgumentList = FunctionParser::ArgumentList; static ExpressionType LEGACY_NODELETE emptyExpression() { return { }; }; [[nodiscard]] PartialResult addDrop(ExpressionType); template [[nodiscard]] NEVER_INLINE UnexpectedResult fail(Args... args) const { using namespace FailureHelper; // See ADL comment in WasmParser.h. return UnexpectedResult(makeString("WebAssembly.Module failed compiling: "_s, makeString(args)...)); } #define WASM_COMPILE_FAIL_IF(condition, ...) do { \ if (condition) [[unlikely]] \ return fail(__VA_ARGS__); \ } while (0) std::unique_ptr finalize(); [[nodiscard]] PartialResult addArguments(const RTT&); [[nodiscard]] PartialResult addLocal(Type, uint32_t); IPIntValue addConstant(Type, uint64_t); // SIMD bool LEGACY_NODELETE usesSIMD() { return m_usesSIMD; } void LEGACY_NODELETE notifyFunctionUsesSIMD() { ASSERT(Options::useWasmSIMD()); m_usesSIMD = true; } [[nodiscard]] PartialResult addSIMDLoad(ExpressionType, uint64_t, ExpressionType&, uint8_t); [[nodiscard]] PartialResult addSIMDStore(ExpressionType, ExpressionType, uint64_t, uint8_t); [[nodiscard]] PartialResult addSIMDSplat(SIMDLane, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addSIMDShuffle(v128_t, ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addSIMDShift(SIMDLaneOperation, SIMDInfo, ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addSIMDExtmul(SIMDLaneOperation, SIMDInfo, ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addSIMDLoadSplat(SIMDLaneOperation, ExpressionType, uint64_t, ExpressionType&, uint8_t); [[nodiscard]] PartialResult addSIMDLoadLane(SIMDLaneOperation, ExpressionType, ExpressionType, uint64_t, uint8_t, ExpressionType&, uint8_t); [[nodiscard]] PartialResult addSIMDStoreLane(SIMDLaneOperation, ExpressionType, ExpressionType, uint64_t, uint8_t, uint8_t); [[nodiscard]] PartialResult addSIMDLoadExtend(SIMDLaneOperation, ExpressionType, uint64_t, ExpressionType&, uint8_t); [[nodiscard]] PartialResult addSIMDLoadPad(SIMDLaneOperation, ExpressionType, uint64_t, ExpressionType&, uint8_t); ExpressionType addSIMDConstant(v128_t); // SIMD generated [[nodiscard]] PartialResult addSIMDExtractLane(SIMDInfo, uint8_t, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addSIMDReplaceLane(SIMDInfo, uint8_t, ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addSIMDI_V(SIMDLaneOperation, SIMDInfo, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addSIMDV_V(SIMDLaneOperation, SIMDInfo, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addSIMDBitwiseSelect(ExpressionType, ExpressionType, ExpressionType, ExpressionType&); #if ENABLE(B3_JIT) [[nodiscard]] PartialResult addSIMDRelOp(SIMDLaneOperation, SIMDInfo, ExpressionType, ExpressionType, B3::Air::Arg, ExpressionType&); #endif [[nodiscard]] PartialResult addSIMDV_VV(SIMDLaneOperation, SIMDInfo, ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addSIMDRelaxedFMA(SIMDLaneOperation, SIMDInfo, ExpressionType, ExpressionType, ExpressionType, ExpressionType&); // References [[nodiscard]] PartialResult addRefIsNull(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addRefFunc(FunctionSpaceIndex, ExpressionType&); [[nodiscard]] PartialResult addRefAsNonNull(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addRefEq(ExpressionType, ExpressionType, ExpressionType&); // Tables [[nodiscard]] PartialResult addTableGet(unsigned, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addTableSet(unsigned, ExpressionType, ExpressionType); [[nodiscard]] PartialResult addTableInit(unsigned, unsigned, ExpressionType, ExpressionType, ExpressionType); [[nodiscard]] PartialResult addElemDrop(unsigned); [[nodiscard]] PartialResult addTableSize(unsigned, ExpressionType&); [[nodiscard]] PartialResult addTableGrow(unsigned, ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addTableFill(unsigned, ExpressionType, ExpressionType, ExpressionType); [[nodiscard]] PartialResult addTableCopy(unsigned, unsigned, ExpressionType, ExpressionType, ExpressionType); // Locals [[nodiscard]] PartialResult getLocal(uint32_t index, ExpressionType&); [[nodiscard]] PartialResult setLocal(uint32_t, ExpressionType); [[nodiscard]] PartialResult teeLocal(uint32_t, ExpressionType, ExpressionType& result); // Globals [[nodiscard]] PartialResult getGlobal(uint32_t, ExpressionType&); [[nodiscard]] PartialResult setGlobal(uint32_t, ExpressionType); // Memory [[nodiscard]] PartialResult load(LoadOpType, ExpressionType, ExpressionType&, uint64_t, uint8_t); [[nodiscard]] PartialResult store(StoreOpType, ExpressionType, ExpressionType, uint64_t, uint8_t); [[nodiscard]] PartialResult addGrowMemory(ExpressionType, ExpressionType&, uint8_t); [[nodiscard]] PartialResult addCurrentMemory(ExpressionType&, uint8_t); [[nodiscard]] PartialResult addMemoryFill(ExpressionType, ExpressionType, ExpressionType, uint8_t); [[nodiscard]] PartialResult addMemoryCopy(ExpressionType, ExpressionType, ExpressionType, uint8_t, uint8_t); [[nodiscard]] PartialResult addMemoryInit(unsigned, ExpressionType, ExpressionType, ExpressionType, uint8_t); [[nodiscard]] PartialResult addDataDrop(unsigned); // Atomics [[nodiscard]] PartialResult atomicLoad(ExtAtomicOpType, Type, ExpressionType, ExpressionType&, uint64_t, uint8_t); [[nodiscard]] PartialResult atomicStore(ExtAtomicOpType, Type, ExpressionType, ExpressionType, uint64_t, uint8_t); [[nodiscard]] PartialResult atomicBinaryRMW(ExtAtomicOpType, Type, ExpressionType, ExpressionType, ExpressionType&, uint64_t, uint8_t); [[nodiscard]] PartialResult atomicCompareExchange(ExtAtomicOpType, Type, ExpressionType, ExpressionType, ExpressionType, ExpressionType&, uint64_t, uint8_t); [[nodiscard]] PartialResult atomicWait(ExtAtomicOpType, ExpressionType, ExpressionType, ExpressionType, ExpressionType&, uint64_t, uint8_t); [[nodiscard]] PartialResult atomicNotify(ExtAtomicOpType, ExpressionType, ExpressionType, ExpressionType&, uint64_t, uint8_t); [[nodiscard]] PartialResult atomicFence(ExtAtomicOpType, uint8_t); // Saturated truncation [[nodiscard]] PartialResult truncSaturated(Ext1OpType, ExpressionType, ExpressionType&, Type, Type); // Wide arithmetic [[nodiscard]] PartialResult addI64Add128(ExpressionType, ExpressionType, ExpressionType, ExpressionType, ExpressionType&, ExpressionType&); [[nodiscard]] PartialResult addI64Sub128(ExpressionType, ExpressionType, ExpressionType, ExpressionType, ExpressionType&, ExpressionType&); [[nodiscard]] PartialResult addI64MulWideS(ExpressionType, ExpressionType, ExpressionType&, ExpressionType&); [[nodiscard]] PartialResult addI64MulWideU(ExpressionType, ExpressionType, ExpressionType&, ExpressionType&); // GC [[nodiscard]] PartialResult addRefI31(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI31GetS(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI31GetU(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addArrayNew(TypeSignatureIndex, ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addArrayNewDefault(TypeSignatureIndex, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addArrayNewData(TypeSignatureIndex, uint32_t, ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addArrayNewElem(TypeSignatureIndex, uint32_t, ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addArrayNewFixed(TypeSignatureIndex, ArgumentList&, ExpressionType&); [[nodiscard]] PartialResult addArrayGet(ExtGCOpType, TypeSignatureIndex, ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addArraySet(TypeSignatureIndex, ExpressionType, ExpressionType, ExpressionType); [[nodiscard]] PartialResult addArrayLen(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addArrayFill(TypeSignatureIndex, ExpressionType, ExpressionType, ExpressionType, ExpressionType); [[nodiscard]] PartialResult addArrayCopy(TypeSignatureIndex, ExpressionType, ExpressionType, TypeSignatureIndex, ExpressionType, ExpressionType, ExpressionType); [[nodiscard]] PartialResult addArrayInitElem(TypeSignatureIndex, ExpressionType, ExpressionType, uint32_t, ExpressionType, ExpressionType); [[nodiscard]] PartialResult addArrayInitData(TypeSignatureIndex, ExpressionType, ExpressionType, uint32_t, ExpressionType, ExpressionType); [[nodiscard]] PartialResult addStructNew(TypeSignatureIndex, ArgumentList&, ExpressionType&); [[nodiscard]] PartialResult addStructNewDefault(TypeSignatureIndex, ExpressionType&); [[nodiscard]] PartialResult addStructGet(ExtGCOpType, ExpressionType, const RTT&, uint32_t, ExpressionType&); [[nodiscard]] PartialResult addStructSet(ExpressionType, const RTT&, uint32_t, ExpressionType); [[nodiscard]] PartialResult addRefTest(ExpressionType, bool, int32_t, bool, ExpressionType&); [[nodiscard]] PartialResult addRefCast(ExpressionType, bool, int32_t, ExpressionType&); [[nodiscard]] PartialResult addAnyConvertExtern(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addExternConvertAny(ExpressionType, ExpressionType&); // Basic operators [[nodiscard]] PartialResult addI32DivS(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32RemS(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32DivU(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32RemU(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64DivS(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64RemS(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64DivU(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64RemU(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32Ctz(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32Popcnt(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64Popcnt(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Nearest(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Nearest(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Trunc(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Trunc(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32TruncSF64(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32TruncSF32(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32TruncUF64(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32TruncUF32(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64TruncSF64(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64TruncSF32(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64TruncUF64(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64TruncUF32(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Ceil(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32Mul(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32Sub(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Le(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32DemoteF64(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Ne(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Lt(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Min(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Max(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Min(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Max(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Mul(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Div(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32Clz(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Copysign(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32ReinterpretI32(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Ne(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Gt(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Sqrt(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Ge(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64GtS(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64GtU(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Div(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Add(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32LeU(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32LeS(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64Ne(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64Clz(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Neg(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32And(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32LtU(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64Rotr(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Abs(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32LtS(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32Eq(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Copysign(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32ConvertSI64(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64Rotl(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Lt(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64ConvertSI32(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Eq(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Le(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Ge(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32ShrU(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32ConvertUI32(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32ShrS(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32GeU(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Ceil(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32GeS(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32Shl(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Floor(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32Xor(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Abs(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Mul(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64Sub(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32ReinterpretF32(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32Add(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Sub(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32Or(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64LtU(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64LtS(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64ConvertSI64(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64Xor(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64GeU(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64Mul(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Sub(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64PromoteF32(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Add(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64GeS(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64ExtendUI32(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32Ne(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64ReinterpretI64(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Eq(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64Eq(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Floor(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32ConvertSI32(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64And(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64Or(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64Ctz(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32Eqz(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64Eqz(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64ReinterpretF64(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64ConvertUI32(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32ConvertUI64(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64ConvertUI64(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64ShrS(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64ShrU(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Sqrt(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64Shl(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF32Gt(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32WrapI64(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32Rotl(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32Rotr(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32GtU(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64ExtendSI32(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32Extend8S(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32Extend16S(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64Extend8S(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64Extend16S(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64Extend32S(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI32GtS(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addF64Neg(ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64LeU(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64LeS(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addI64Add(ExpressionType, ExpressionType, ExpressionType&); [[nodiscard]] PartialResult addSelect(ExpressionType, ExpressionType, ExpressionType, ExpressionType&); // Control flow [[nodiscard]] ControlType addTopLevel(BlockSignature&&); [[nodiscard]] PartialResult addBlock(BlockSignature&&, std::span, ControlType&); [[nodiscard]] PartialResult addLoop(BlockSignature&&, std::span, ControlType&, uint32_t); [[nodiscard]] PartialResult addIf(ExpressionType, BlockSignature&&, std::span, ControlType&); [[nodiscard]] PartialResult addElse(ControlType&, std::span); [[nodiscard]] PartialResult addElseToUnreachable(ControlType&); [[nodiscard]] PartialResult addTry(BlockSignature&&, std::span, ControlType&); [[nodiscard]] PartialResult addTryTable(BlockSignature&&, std::span args, const Vector& targets, ControlType& result); [[nodiscard]] PartialResult addCatch(unsigned, const RTT&, std::span, ControlType&, ResultList&); [[nodiscard]] PartialResult addCatchToUnreachable(unsigned, const RTT&, ControlType&, ResultList&); [[nodiscard]] PartialResult addCatchAll(std::span, ControlType&); [[nodiscard]] PartialResult addCatchAllToUnreachable(ControlType&); [[nodiscard]] PartialResult addDelegate(ControlType&, ControlType&); [[nodiscard]] PartialResult addDelegateToUnreachable(ControlType&, ControlType&); [[nodiscard]] PartialResult addThrow(unsigned, ArgumentList&, std::span); [[nodiscard]] PartialResult addRethrow(unsigned, ControlType&); [[nodiscard]] PartialResult addThrowRef(ExpressionType, std::span); [[nodiscard]] PartialResult addReturn(const ControlType&, std::span); [[nodiscard]] PartialResult addBranch(ControlType&, ExpressionType, std::span); [[nodiscard]] PartialResult addBranchNull(ControlType&, ExpressionType, std::span, bool, ExpressionType&); [[nodiscard]] PartialResult addBranchCast(ControlType&, ExpressionType, std::span, bool, int32_t, bool); [[nodiscard]] PartialResult addSwitch(ExpressionType, const Vector&, ControlType&, std::span); [[nodiscard]] PartialResult endBlock(ControlEntry&, std::span); void endTryTable(const ControlType& data); [[nodiscard]] PartialResult addEndToUnreachable(ControlEntry&, std::span); [[nodiscard]] PartialResult endTopLevel(std::span); // Fused comparison stubs (TODO: make use of these for better codegen) [[nodiscard]] PartialResult LEGACY_NODELETE addFusedBranchCompare(OpType, ControlType&, ExpressionType, std::span) { RELEASE_ASSERT_NOT_REACHED(); } [[nodiscard]] PartialResult LEGACY_NODELETE addFusedBranchCompare(OpType, ControlType&, ExpressionType, ExpressionType, std::span) { RELEASE_ASSERT_NOT_REACHED(); } [[nodiscard]] PartialResult LEGACY_NODELETE addFusedIfCompare(OpType, ExpressionType, BlockSignature&&, std::span, ControlType&) { RELEASE_ASSERT_NOT_REACHED(); } [[nodiscard]] PartialResult LEGACY_NODELETE addFusedIfCompare(OpType, ExpressionType, ExpressionType, BlockSignature&&, std::span, ControlType&) { RELEASE_ASSERT_NOT_REACHED(); } // Calls [[nodiscard]] PartialResult addCall(unsigned, FunctionSpaceIndex, const RTT&, ArgumentList&, ResultList&, CallType = CallType::Call); [[nodiscard]] PartialResult addCallIndirect(unsigned, unsigned, const RTT&, ArgumentList&, ResultList&, CallType = CallType::Call); [[nodiscard]] PartialResult addCallRef(unsigned, const RTT&, ArgumentList&, ResultList&, CallType = CallType::Call); [[nodiscard]] PartialResult addUnreachable(); [[nodiscard]] PartialResult addCrash(); void LEGACY_NODELETE setParser(FunctionParser* parser) { m_parser = parser; }; size_t LEGACY_NODELETE getCurrentInstructionLength() { return m_parser->offset() - m_parser->currentOpcodeStartingOffset(); } void addTailCallCommonData(const RTT&, const CallInformation&); void LEGACY_NODELETE didFinishParsingLocals() { m_metadata->m_bytecodeOffset = m_parser->offset(); } void LEGACY_NODELETE didPopValueFromStack(ExpressionType, ASCIILiteral) { } void LEGACY_NODELETE willParseOpcode() { } void LEGACY_NODELETE willParseExtendedOpcode() { } void didParseOpcode() { #if ENABLE(WEBASSEMBLY_DEBUGGER) if (m_debugInfo) [[unlikely]] { uint32_t instructionStart = m_parser->currentOpcodeStartingOffset() + m_functionStartByteOffset; m_debugInfo->addInstructionStart(instructionStart); } #endif if (!m_parser->unreachableBlocks()) { ASSERT(m_parser->getStackHeightInValues() == m_stackSize.value()); #if ENABLE(WEBASSEMBLY_DEBUGGER) if (m_debugInfo) [[unlikely]] { OpType currentOpcode = m_parser->currentOpcode(); bool isControlFlowInstruction = Wasm::isControlFlowInstructionWithExtGC(currentOpcode, [this]() { return m_parser->currentExtendedOpcode(); }); if (!isControlFlowInstruction || currentOpcode == AnnotatedSelect) RECORD_NEXT_INSTRUCTION(curPC(), nextPC()); } #endif } } void dump(const ControlStack&, const Stack*); void convertTryToCatch(ControlType& tryBlock, CatchKind); ALWAYS_INLINE void changeStackSize(int delta) { m_stackSize += delta; if (delta > 0) m_maxStackSize = std::max(m_maxStackSize, m_stackSize.value()); } void coalesceControlFlow(bool force = false); void resolveEntryTarget(unsigned, IPIntLocation); void resolveExitTarget(unsigned, IPIntLocation); void tryToResolveEntryTarget(uint32_t index, IPIntLocation loc, uint8_t*) { m_controlStructuresAwaitingCoalescing[index].m_awaitingEntryTarget.append(loc); } void tryToResolveExitTarget(uint32_t index, IPIntLocation loc, uint8_t*) { m_controlStructuresAwaitingCoalescing[index].m_awaitingExitTarget.append(loc); } void tryToResolveBranchTarget(ControlType& targetBlock, IPIntLocation loc, uint8_t* metadata) { if (ControlType::isTopLevel(targetBlock)) { m_jumpLocationsAwaitingEnd.append(loc); return; } auto index = targetBlock.m_index; auto& target = m_controlStructuresAwaitingCoalescing[index]; if (target.isLoop) { ASSERT(target.m_entryResolved); IPInt::BlockMetadata md = checkedDelta(target.m_entryTarget, loc); WRITE_TO_METADATA(metadata + loc.mc, md, IPInt::BlockMetadata); RECORD_NEXT_INSTRUCTION(loc.pc, target.m_entryTarget.pc); } else { ASSERT(!target.m_exitResolved); target.m_awaitingBranchTarget.append(loc); } } ALWAYS_INLINE const CallInformation& cachedCallInformationFor(const RTT& signature) { if (m_cachedSignature != &signature) { m_cachedSignature = &signature; m_cachedCallInformation = wasmCallingConvention().callInformationFor(signature, CallRole::Caller); } return m_cachedCallInformation; } static constexpr bool LEGACY_NODELETE tierSupportsSIMD() { return true; } static constexpr bool validateFunctionBodySize = true; private: Checked m_stackSize { 0 }; uint32_t m_maxStackSize { 0 }; Checked m_tryDepth { 0 }; uint32_t m_maxTryDepth { 0 }; FunctionParser* m_parser { nullptr }; ModuleInformation& m_info; const FunctionCodeIndex m_functionIndex; std::unique_ptr m_metadata; struct ControlStructureAwaitingCoalescing { Vector m_awaitingEntryTarget { }; Vector m_awaitingBranchTarget { }; Vector m_awaitingExitTarget { }; IPIntLocation m_entryTarget { 0, 0 }; // where do we go when entering normally? IPIntLocation m_exitTarget { 0, 0 }; // where do we go when leaving? uint32_t startPC { 0 }; bool isLoop { false }; bool m_entryResolved { false }; bool m_exitResolved { false }; }; Vector m_controlStructuresAwaitingCoalescing; struct QueuedCoalesceRequest { size_t index; bool isEntry; }; Vector m_coalesceQueue; // if this is 0, all our control structures have been coalesced and we can clean up the vector unsigned m_coalesceDebt { 0 }; // exit loations can still be unresolved when the ControlType* dies, so we put them here Vector m_exitHandlersAwaitingCoalescing; // all jumps that go to the top level and return Vector m_jumpLocationsAwaitingEnd; inline uint32_t LEGACY_NODELETE curPC() { return Checked(m_parser->currentOpcodeStartingOffset()) - m_metadata->m_bytecodeOffset; } inline uint32_t LEGACY_NODELETE nextPC() { return Checked(m_parser->offset()) - m_metadata->m_bytecodeOffset; } // FIXME: Should return size_t, but BlockMetadata::deltaMC is int32_t and the interpreter loads it with loadpairi. inline uint32_t LEGACY_NODELETE curMC() { Checked size = m_metadata->m_metadata.size(); return size; } static ALWAYS_INLINE IPInt::BlockMetadata checkedDelta(IPIntLocation to, IPIntLocation from) { Checked dPC = static_cast(to.pc) - static_cast(from.pc); Checked dMC = static_cast(to.mc) - static_cast(from.mc); return { dPC, dMC }; } CallInformation m_cachedCallInformation { }; const RTT* m_cachedSignature { nullptr }; Checked m_argumentAndResultsStackSize; bool m_usesRethrow { false }; bool m_usesSIMD { false }; size_t m_functionStartByteOffset { 0 }; FunctionDebugInfo* m_debugInfo { nullptr }; }; // use if (true) to avoid warnings. #define IPINT_UNIMPLEMENTED { if (true) { CRASH(); } return { }; } IPIntGenerator::IPIntGenerator(ModuleInformation& info, FunctionCodeIndex functionIndex, const RTT&, std::span bytecode, FunctionDebugInfo* debugInfo) : m_info(info) , m_functionIndex(functionIndex) , m_metadata(WTF::makeUnique(functionIndex, bytecode)) , m_functionStartByteOffset(info.functions[functionIndex].start) , m_debugInfo(debugInfo) { } [[nodiscard]] PartialResult IPIntGenerator::addDrop(ExpressionType) { changeStackSize(-1); return { }; } IPIntValue IPIntGenerator::addConstant(Type, uint64_t) { changeStackSize(1); return { }; } // SIMD [[nodiscard]] PartialResult IPIntGenerator::addSIMDLoad(ExpressionType, uint64_t, ExpressionType&, uint8_t) { changeStackSize(0); // Pop address, push v128 value (net change = 0) return { }; } [[nodiscard]] PartialResult IPIntGenerator::addSIMDStore(ExpressionType, ExpressionType, uint64_t, uint8_t) { changeStackSize(-2); // Pop address and v128 value return { }; } [[nodiscard]] PartialResult IPIntGenerator::addSIMDSplat(SIMDLane, ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addSIMDShuffle(v128_t, ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addSIMDShift(SIMDLaneOperation, SIMDInfo, ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addSIMDExtmul(SIMDLaneOperation, SIMDInfo, ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addSIMDLoadSplat(SIMDLaneOperation, ExpressionType pointer, uint64_t offset, ExpressionType& result, uint8_t memoryIndex) { return addSIMDLoad(pointer, offset, result, memoryIndex); } [[nodiscard]] PartialResult IPIntGenerator::addSIMDLoadLane(SIMDLaneOperation, ExpressionType, ExpressionType, uint64_t, uint8_t, ExpressionType&, uint8_t) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addSIMDStoreLane(SIMDLaneOperation, ExpressionType, ExpressionType, uint64_t, uint8_t, uint8_t) { changeStackSize(-2); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addSIMDLoadExtend(SIMDLaneOperation, ExpressionType pointer, uint64_t offset, ExpressionType& result, uint8_t memoryIndex) { return addSIMDLoad(pointer, offset, result, memoryIndex); } [[nodiscard]] PartialResult IPIntGenerator::addSIMDLoadPad(SIMDLaneOperation, ExpressionType pointer, uint64_t offset, ExpressionType& result, uint8_t memoryIndex) { return addSIMDLoad(pointer, offset, result, memoryIndex); } IPIntGenerator::ExpressionType IPIntGenerator::addSIMDConstant(v128_t) { changeStackSize(1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addSIMDExtractLane(SIMDInfo, uint8_t, ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addSIMDReplaceLane(SIMDInfo, uint8_t, ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addSIMDI_V(SIMDLaneOperation, SIMDInfo, ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addSIMDV_V(SIMDLaneOperation, SIMDInfo, ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addSIMDBitwiseSelect(ExpressionType, ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-2); // 3 operands, 1 result return { }; } #if ENABLE(B3_JIT) [[nodiscard]] PartialResult IPIntGenerator::addSIMDRelOp(SIMDLaneOperation, SIMDInfo, ExpressionType, ExpressionType, B3::Air::Arg, ExpressionType&) { changeStackSize(-1); return { }; } #endif [[nodiscard]] PartialResult IPIntGenerator::addSIMDV_VV(SIMDLaneOperation, SIMDInfo, ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); // Pop two v128 values, push one v128 value return { }; } [[nodiscard]] PartialResult IPIntGenerator::addSIMDRelaxedFMA(SIMDLaneOperation, SIMDInfo, ExpressionType, ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-2); // Pop three v128 values, push one v128 value return { }; } // References [[nodiscard]] PartialResult IPIntGenerator::addRefIsNull(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addRefFunc(FunctionSpaceIndex index, ExpressionType&) { changeStackSize(1); m_metadata->addRefFunc(index, getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addRefAsNonNull(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addRefEq(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } // Tables [[nodiscard]] PartialResult IPIntGenerator::addTableGet(unsigned index, ExpressionType, ExpressionType&) { m_metadata->addTableAccess(index, getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addTableSet(unsigned index, ExpressionType, ExpressionType) { changeStackSize(-2); m_metadata->addTableAccess(index, getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addTableInit(unsigned elementIndex, unsigned tableIndex, ExpressionType, ExpressionType, ExpressionType) { changeStackSize(-3); IPInt::TableInitMetadata table { .elementIndex = safeCast(elementIndex), .tableIndex = safeCast(tableIndex), .instructionLength = { .length = safeCast(getCurrentInstructionLength()) } }; m_metadata->appendMetadata(table); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addElemDrop(unsigned elementIndex) { m_metadata->addElemDrop(elementIndex, getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addTableSize(unsigned tableIndex, ExpressionType&) { changeStackSize(1); m_metadata->addTableAccess(tableIndex, getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addTableGrow(unsigned tableIndex, ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); IPInt::TableGrowMetadata table { .tableIndex = safeCast(tableIndex), .instructionLength = { .length = safeCast(getCurrentInstructionLength()) } }; m_metadata->appendMetadata(table); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addTableFill(unsigned tableIndex, ExpressionType, ExpressionType, ExpressionType) { changeStackSize(-3); IPInt::TableFillMetadata table { .tableIndex = safeCast(tableIndex), .instructionLength = { .length = safeCast(getCurrentInstructionLength()) } }; m_metadata->appendMetadata(table); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addTableCopy(unsigned dstTableIndex, unsigned srcTableIndex, ExpressionType, ExpressionType, ExpressionType) { changeStackSize(-3); IPInt::TableCopyMetadata table { .dstTableIndex = safeCast(dstTableIndex), .srcTableIndex = safeCast(srcTableIndex), .instructionLength = { .length = safeCast(getCurrentInstructionLength()) } }; m_metadata->appendMetadata(table); return { }; } // Locals and Globals [[nodiscard]] PartialResult IPIntGenerator::addArguments(const RTT& signature) { const CallInformation callCC = wasmCallingConvention().callInformationFor(signature, CallRole::Callee); ASSERT(callCC.headerAndArgumentStackSizeInBytes >= callCC.headerIncludingThisSizeInBytes); m_argumentAndResultsStackSize = WTF::roundUpToMultipleOf(callCC.headerAndArgumentStackSizeInBytes) - callCC.headerIncludingThisSizeInBytes; ASSERT(!Options::useWasmIPInt() || !(m_argumentAndResultsStackSize % 16)); // mINT requires this auto numArgs = signature.argumentCount(); m_metadata->m_numLocals += numArgs; m_metadata->m_numArguments = numArgs; #if ENABLE(WEBASSEMBLY_DEBUGGER) if (m_debugInfo) [[unlikely]] { auto* localTypes = &m_debugInfo->locals; for (size_t i = 0; i < numArgs; ++i) localTypes->append(signature.argumentType(i)); } #endif signature.ensureArgumINTBytecode(callCC); signature.ensureUINTBytecode(callCC); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addLocal(Type localType, uint32_t count) { // push 0x00 or 0xff (for bit hacks) to the metadata depending on if we have a primitive or a reference if (isRefType(localType)) { for (unsigned i = 0; i < count; ++i) m_metadata->m_localInitBytecode.append(0xff); } else { for (unsigned i = 0; i < count; ++i) m_metadata->m_localInitBytecode.append(0); } m_metadata->m_numLocals += count; #if ENABLE(WEBASSEMBLY_DEBUGGER) if (m_debugInfo) [[unlikely]] { auto* localTypes = &m_debugInfo->locals; for (unsigned i = 0; i < count; ++i) localTypes->append(localType); } #endif return { }; } [[nodiscard]] PartialResult IPIntGenerator::getLocal(uint32_t index, ExpressionType&) { // Local indices are usually very small, so we decode them on the fly // instead of generating metadata. m_metadata->recordLocalRead(index); changeStackSize(1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::setLocal(uint32_t index, ExpressionType) { // Local indices are usually very small, so we decode them on the fly // instead of generating metadata. m_metadata->recordLocalWrite(index); changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::teeLocal(uint32_t index, ExpressionType, ExpressionType&) { m_metadata->recordLocalRead(index); m_metadata->recordLocalWrite(index); return { }; } [[nodiscard]] PartialResult IPIntGenerator::getGlobal(uint32_t index, ExpressionType&) { changeStackSize(1); const Wasm::GlobalInformation& global = m_info.globals[index]; IPInt::GlobalMetadata mdGlobal { .index = index, .instructionLength = { .length = safeCast(getCurrentInstructionLength()) }, .bindingMode = safeCast(global.bindingMode), .isRef = safeCast(isRefType(m_info.globals[index].type)) }; m_metadata->appendMetadata(mdGlobal); return { }; } [[nodiscard]] PartialResult IPIntGenerator::setGlobal(uint32_t index, ExpressionType) { changeStackSize(-1); const Wasm::GlobalInformation& global = m_info.globals[index]; IPInt::GlobalMetadata mdGlobal { .index = index, .instructionLength = { .length = safeCast(getCurrentInstructionLength()) }, .bindingMode = safeCast(global.bindingMode), .isRef = safeCast(isRefType(m_info.globals[index].type)) }; m_metadata->appendMetadata(mdGlobal); return { }; } // Loads and Stores [[nodiscard]] PartialResult IPIntGenerator::load(LoadOpType, ExpressionType, ExpressionType&, uint64_t, uint8_t) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::store(StoreOpType, ExpressionType, ExpressionType, uint64_t, uint8_t) { changeStackSize(-2); return { }; } // Memories [[nodiscard]] PartialResult IPIntGenerator::addGrowMemory(ExpressionType, ExpressionType&, uint8_t memoryIndex) { m_metadata->addMemoryGrow(memoryIndex, getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addCurrentMemory(ExpressionType&, uint8_t memoryIndex) { changeStackSize(1); m_metadata->addMemorySize(memoryIndex, getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addMemoryFill(ExpressionType, ExpressionType, ExpressionType, uint8_t memoryIndex) { changeStackSize(-3); m_metadata->addMemoryFill(memoryIndex, getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addMemoryCopy(ExpressionType, ExpressionType, ExpressionType, uint8_t dstMemoryIndex, uint8_t srcMemoryIndex) { changeStackSize(-3); m_metadata->addMemoryCopy(dstMemoryIndex, srcMemoryIndex, getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addMemoryInit(unsigned dataIndex, ExpressionType, ExpressionType, ExpressionType, uint8_t memoryIndex) { changeStackSize(-3); m_metadata->addMemoryInit(memoryIndex, dataIndex, getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addDataDrop(unsigned dataIndex) { m_metadata->addDataAccess(dataIndex, getCurrentInstructionLength()); return { }; } // Atomics [[nodiscard]] PartialResult IPIntGenerator::atomicLoad(ExtAtomicOpType, Type, ExpressionType, ExpressionType&, uint64_t, uint8_t) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::atomicStore(ExtAtomicOpType, Type, ExpressionType, ExpressionType, uint64_t, uint8_t) { changeStackSize(-2); return { }; } [[nodiscard]] PartialResult IPIntGenerator::atomicBinaryRMW(ExtAtomicOpType, Type, ExpressionType, ExpressionType, ExpressionType&, uint64_t, uint8_t) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::atomicCompareExchange(ExtAtomicOpType, Type, ExpressionType, ExpressionType, ExpressionType, ExpressionType&, uint64_t, uint8_t) { changeStackSize(-2); return { }; } [[nodiscard]] PartialResult IPIntGenerator::atomicWait(ExtAtomicOpType, ExpressionType, ExpressionType, ExpressionType, ExpressionType&, uint64_t offset, uint8_t memoryIndex) { changeStackSize(-2); m_metadata->addAtomicMemoryAccess(memoryIndex, offset, getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::atomicNotify(ExtAtomicOpType, ExpressionType, ExpressionType, ExpressionType&, uint64_t offset, uint8_t memoryIndex) { changeStackSize(-1); m_metadata->addAtomicMemoryAccess(memoryIndex, offset, getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::atomicFence(ExtAtomicOpType, uint8_t) { return { }; } // GC [[nodiscard]] PartialResult IPIntGenerator::addRefI31(ExpressionType, ExpressionType&) { m_metadata->addLength(getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI31GetS(ExpressionType, ExpressionType&) { m_metadata->addLength(getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI31GetU(ExpressionType, ExpressionType&) { m_metadata->addLength(getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addArrayNew(TypeSignatureIndex index, ExpressionType, ExpressionType, ExpressionType&) { m_metadata->appendMetadata({ index.rawIndex(), static_cast(getCurrentInstructionLength()) }); changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addArrayNewData(TypeSignatureIndex index, uint32_t dataSegmentIndex, ExpressionType, ExpressionType, ExpressionType&) { m_metadata->appendMetadata({ index.rawIndex(), dataSegmentIndex, static_cast(getCurrentInstructionLength()) }); changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addArrayNewElem(TypeSignatureIndex index, uint32_t elemSegmentIndex, ExpressionType, ExpressionType, ExpressionType&) { m_metadata->appendMetadata({ index.rawIndex(), elemSegmentIndex, static_cast(getCurrentInstructionLength()) }); changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addArrayNewFixed(TypeSignatureIndex index, ArgumentList& args, ExpressionType&) { m_metadata->appendMetadata({ index.rawIndex(), static_cast(args.size()), static_cast(getCurrentInstructionLength()) }); changeStackSize(-args.size() + 1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addArrayNewDefault(TypeSignatureIndex index, ExpressionType, ExpressionType&) { m_metadata->appendMetadata({ index.rawIndex(), static_cast(getCurrentInstructionLength()) }); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addArrayGet(ExtGCOpType, TypeSignatureIndex index, ExpressionType, ExpressionType, ExpressionType&) { m_metadata->appendMetadata({ index.rawIndex(), static_cast(getCurrentInstructionLength()) }); changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addArraySet(TypeSignatureIndex index, ExpressionType, ExpressionType, ExpressionType) { m_metadata->appendMetadata({ index.rawIndex(), static_cast(getCurrentInstructionLength()) }); changeStackSize(-3); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addArrayLen(ExpressionType, ExpressionType&) { m_metadata->addLength(getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addArrayFill(TypeSignatureIndex, ExpressionType, ExpressionType, ExpressionType, ExpressionType) { changeStackSize(-4); m_metadata->addLength(getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addArrayCopy(TypeSignatureIndex, ExpressionType, ExpressionType, TypeSignatureIndex, ExpressionType, ExpressionType, ExpressionType) { changeStackSize(-5); m_metadata->addLength(getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addArrayInitElem(TypeSignatureIndex, ExpressionType, ExpressionType, uint32_t elemSegmentIndex, ExpressionType, ExpressionType) { changeStackSize(-4); m_metadata->appendMetadata({ elemSegmentIndex, static_cast(getCurrentInstructionLength()) }); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addArrayInitData(TypeSignatureIndex, ExpressionType, ExpressionType, uint32_t dataSegmentIndex, ExpressionType, ExpressionType) { changeStackSize(-4); m_metadata->appendMetadata({ dataSegmentIndex, static_cast(getCurrentInstructionLength()) }); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addStructNew(TypeSignatureIndex index, ArgumentList&, ExpressionType&) { const RTT& type = m_info.rtt(index); ASSERT(type.kind() == RTTKind::Struct); m_metadata->appendMetadata({ index.rawIndex(), static_cast(type.fieldCount()), static_cast(getCurrentInstructionLength()) }); changeStackSize(-type.fieldCount() + 1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addStructNewDefault(TypeSignatureIndex index, ExpressionType&) { m_metadata->appendMetadata({ index.rawIndex(), static_cast(getCurrentInstructionLength()) }); changeStackSize(1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addStructGet(ExtGCOpType, ExpressionType, const RTT&, uint32_t fieldIndex, ExpressionType&) { m_metadata->appendMetadata({ fieldIndex, static_cast(getCurrentInstructionLength()) }); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addStructSet(ExpressionType, const RTT&, uint32_t fieldIndex, ExpressionType) { m_metadata->appendMetadata({ fieldIndex, static_cast(getCurrentInstructionLength()) }); changeStackSize(-2); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addRefTest(ExpressionType, bool allowNull, int32_t heapType, bool, ExpressionType&) { m_metadata->appendMetadata({ heapType, static_cast(getCurrentInstructionLength()), static_cast(allowNull), }); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addRefCast(ExpressionType, bool allowNull, int32_t heapType, ExpressionType&) { m_metadata->appendMetadata({ heapType, static_cast(getCurrentInstructionLength()), static_cast(allowNull), }); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addAnyConvertExtern(ExpressionType, ExpressionType&) { m_metadata->addLength(getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addExternConvertAny(ExpressionType, ExpressionType&) { m_metadata->addLength(getCurrentInstructionLength()); return { }; } // Integer Arithmetic [[nodiscard]] PartialResult IPIntGenerator::addI32Add(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Add(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32Sub(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Sub(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32Mul(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Mul(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32DivS(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32DivU(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64DivS(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64DivU(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32RemS(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32RemU(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64RemS(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64RemU(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } // Bitwise Operations [[nodiscard]] PartialResult IPIntGenerator::addI32And(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64And(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32Xor(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Xor(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32Or(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Or(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32Shl(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32ShrU(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32ShrS(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Shl(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64ShrU(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64ShrS(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32Rotl(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Rotl(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32Rotr(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Rotr(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32Popcnt(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Popcnt(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32Clz(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Clz(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32Ctz(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Ctz(ExpressionType, ExpressionType&) { return { }; } // Floating-Point Arithmetic [[nodiscard]] PartialResult IPIntGenerator::addF32Add(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Add(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32Sub(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Sub(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32Mul(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Mul(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32Div(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Div(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } // Other Floating-Point Instructions [[nodiscard]] PartialResult IPIntGenerator::addF32Min(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32Max(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Min(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Max(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32Nearest(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Nearest(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32Floor(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Floor(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32Ceil(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Ceil(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32Copysign(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Copysign(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32Sqrt(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Sqrt(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32Neg(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Neg(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32Abs(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Abs(ExpressionType, ExpressionType&) { return { }; } // Integer Comparisons [[nodiscard]] PartialResult IPIntGenerator::addI32Eq(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32Ne(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32LtS(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32LtU(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32LeS(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32LeU(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32GtS(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32GtU(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32GeU(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32GeS(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32Eqz(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Eq(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Ne(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64GtS(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64GtU(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64GeS(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64GeU(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64LtS(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64LtU(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64LeS(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64LeU(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Eqz(ExpressionType, ExpressionType&) { return { }; } // Floating-Point Comparisons [[nodiscard]] PartialResult IPIntGenerator::addF32Eq(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32Ne(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32Lt(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32Le(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32Gt(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32Ge(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Eq(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Ne(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Lt(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Le(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Gt(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64Ge(ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-1); return { }; } // Integer Extension [[nodiscard]] PartialResult IPIntGenerator::addI64ExtendSI32(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64ExtendUI32(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32Extend8S(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32Extend16S(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Extend8S(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Extend16S(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Extend32S(ExpressionType, ExpressionType&) { return { }; } // Truncation [[nodiscard]] PartialResult IPIntGenerator::addF64Trunc(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32Trunc(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32TruncSF64(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32TruncSF32(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32TruncUF64(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32TruncUF32(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64TruncSF64(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64TruncSF32(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64TruncUF64(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64TruncUF32(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::truncSaturated(Ext1OpType, ExpressionType, ExpressionType&, Type, Type) { m_metadata->addLength(getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Add128(ExpressionType, ExpressionType, ExpressionType, ExpressionType, ExpressionType&, ExpressionType&) { changeStackSize(-2); // pops 4, pushes 2 return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64Sub128(ExpressionType, ExpressionType, ExpressionType, ExpressionType, ExpressionType&, ExpressionType&) { changeStackSize(-2); // pops 4, pushes 2 return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64MulWideS(ExpressionType, ExpressionType, ExpressionType&, ExpressionType&) { changeStackSize(0); // pops 2, pushes 2 return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64MulWideU(ExpressionType, ExpressionType, ExpressionType&, ExpressionType&) { changeStackSize(0); // pops 2, pushes 2 return { }; } // Conversions [[nodiscard]] PartialResult IPIntGenerator::addI32WrapI64(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32DemoteF64(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64PromoteF32(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32ReinterpretI32(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI32ReinterpretF32(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64ReinterpretI64(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addI64ReinterpretF64(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32ConvertSI32(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32ConvertUI32(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32ConvertSI64(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF32ConvertUI64(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64ConvertSI32(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64ConvertUI32(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64ConvertSI64(ExpressionType, ExpressionType&) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addF64ConvertUI64(ExpressionType, ExpressionType&) { return { }; } // Control Flow Blocks void IPIntGenerator::coalesceControlFlow(bool force) { // Peek at the next opcode IPIntLocation here = { nextPC(), curMC() }; if (!force) { if (m_parser->offset() >= m_parser->source().size()) return; uint8_t nextOpcode = m_parser->source()[m_parser->offset()]; if (nextOpcode == Block || nextOpcode == End) return; } else here = { curPC(), curMC() }; // There's something useful after us. Resolve everything here. for (auto& entry : m_coalesceQueue) { if (entry.isEntry) resolveEntryTarget(entry.index, here); else resolveExitTarget(entry.index, here); } m_coalesceQueue.shrink(0); if (!m_coalesceDebt) m_controlStructuresAwaitingCoalescing.shrink(0); for (auto& src : m_exitHandlersAwaitingCoalescing) { IPInt::BlockMetadata md = checkedDelta(here, src); WRITE_TO_METADATA(m_metadata->m_metadata.mutableSpan().data() + src.mc, md, IPInt::BlockMetadata); RECORD_NEXT_INSTRUCTION(src.pc, here.pc); } m_exitHandlersAwaitingCoalescing.shrink(0); } void IPIntGenerator::resolveEntryTarget(unsigned index, IPIntLocation loc) { auto& control = m_controlStructuresAwaitingCoalescing[index]; ASSERT(!control.m_entryResolved); for (auto& src : control.m_awaitingEntryTarget) { // write delta PC and delta MC IPInt::BlockMetadata md = checkedDelta(loc, src); WRITE_TO_METADATA(m_metadata->m_metadata.mutableSpan().data() + src.mc, md, IPInt::BlockMetadata); RECORD_NEXT_INSTRUCTION(src.pc, loc.pc); // FIXME: coalescing sequential blocks - should update instead of adding } if (control.isLoop) { for (auto& src : control.m_awaitingBranchTarget) { IPInt::BlockMetadata md = checkedDelta(loc, src); WRITE_TO_METADATA(m_metadata->m_metadata.mutableSpan().data() + src.mc, md, IPInt::BlockMetadata); RECORD_NEXT_INSTRUCTION(src.pc, loc.pc); } control.m_awaitingBranchTarget.clear(); } control.m_awaitingEntryTarget.clear(); control.m_entryResolved = true; control.m_entryTarget = loc; } void IPIntGenerator::resolveExitTarget(unsigned index, IPIntLocation loc) { auto& control = m_controlStructuresAwaitingCoalescing[index]; ASSERT(!control.m_exitResolved); for (auto& src : control.m_awaitingExitTarget) { // write delta PC and delta MC IPInt::BlockMetadata md = checkedDelta(loc, src); WRITE_TO_METADATA(m_metadata->m_metadata.mutableSpan().data() + src.mc, md, IPInt::BlockMetadata); RECORD_NEXT_INSTRUCTION(src.pc, loc.pc); } if (!control.isLoop) { for (auto& src : control.m_awaitingBranchTarget) { IPInt::BlockMetadata md = checkedDelta(loc, src); WRITE_TO_METADATA(m_metadata->m_metadata.mutableSpan().data() + src.mc, md, IPInt::BlockMetadata); RECORD_NEXT_INSTRUCTION(src.pc, loc.pc); } control.m_awaitingBranchTarget.clear(); } control.m_awaitingExitTarget.clear(); control.m_exitResolved = true; control.m_exitTarget = loc; } [[nodiscard]] IPIntGenerator::ControlType IPIntGenerator::addTopLevel(BlockSignature&& signature) { ControlType topLevel = ControlType(WTF::move(signature), 0, BlockType::TopLevel); return topLevel; } [[nodiscard]] PartialResult IPIntGenerator::addSelect(ExpressionType, ExpressionType, ExpressionType, ExpressionType&) { changeStackSize(-2); m_metadata->addLength(getCurrentInstructionLength()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addBlock(BlockSignature&& signature, std::span args, ControlType& block) { block = ControlType(WTF::move(signature), m_stackSize.value() - args.size(), BlockType::Block); block.m_index = m_controlStructuresAwaitingCoalescing.size(); block.m_pc = curPC(); block.m_mc = curMC(); block.m_pendingOffset = curMC(); RECORD_NEXT_INSTRUCTION(block.m_pc, nextPC()); // Register to be coalesced if possible! m_coalesceQueue.append(QueuedCoalesceRequest { m_controlStructuresAwaitingCoalescing.size(), true }); m_controlStructuresAwaitingCoalescing.append(ControlStructureAwaitingCoalescing { .startPC = block.m_pc, .isLoop = false }); ++m_coalesceDebt; IPIntLocation here = { curPC(), curMC() }; m_metadata->addBlankSpace(); tryToResolveEntryTarget(block.m_index, here, m_metadata->m_metadata.mutableSpan().data()); coalesceControlFlow(); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addLoop(BlockSignature&& signature, std::span args, ControlType& block, uint32_t loopIndex) { block = ControlType(WTF::move(signature), m_stackSize.value() - args.size(), BlockType::Loop); m_metadata->enterLoop(); block.m_index = m_controlStructuresAwaitingCoalescing.size(); block.m_pendingOffset = std::nullopt; // no need to update! block.m_pc = curPC(); RECORD_NEXT_INSTRUCTION(block.m_pc, nextPC()); // Register to be coalesced if possible! m_controlStructuresAwaitingCoalescing.append(ControlStructureAwaitingCoalescing { .m_entryTarget = { curPC(), curMC() }, .startPC = block.m_pc, .isLoop = true, .m_entryResolved = true, }); ++m_coalesceDebt; IPInt::InstructionLengthMetadata md { static_cast(getCurrentInstructionLength()) }; m_metadata->appendMetadata(md); // Loop OSR ASSERT(m_parser->getStackHeightInValues() == m_stackSize.value()); unsigned numOSREntryDataValues = m_stackSize.value(); // Note the +1: we do this to avoid having 0 as a key in the map, since the current map can't handle 0 as a key m_metadata->tierUpCounter().add(m_parser->currentOpcodeStartingOffset() - m_metadata->m_bytecodeOffset + 1, IPIntTierUpCounter::OSREntryData { loopIndex, numOSREntryDataValues, m_tryDepth }); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addIf(ExpressionType, BlockSignature&& signature, std::span args, ControlType& block) { changeStackSize(-1); block = ControlType(WTF::move(signature), m_stackSize.value() - args.size(), BlockType::If); block.m_index = m_controlStructuresAwaitingCoalescing.size(); block.m_pc = curPC(); block.m_mc = curMC(); block.m_pendingOffset = curMC(); RECORD_NEXT_INSTRUCTION(block.m_pc, nextPC()); m_coalesceQueue.append(QueuedCoalesceRequest { m_controlStructuresAwaitingCoalescing.size(), true }); m_controlStructuresAwaitingCoalescing.append(ControlStructureAwaitingCoalescing { .startPC = block.m_pc, .isLoop = false }); ++m_coalesceDebt; IPInt::IfMetadata mdIf { .elseDeltaPC = 0xbeef, .elseDeltaMC = 0xbeef, .instructionLength = { .length = safeCast(getCurrentInstructionLength()) } }; m_metadata->appendMetadata(mdIf); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addElse(ControlType& block, std::span) { return addElseToUnreachable(block); } [[nodiscard]] PartialResult IPIntGenerator::addElseToUnreachable(ControlType& block) { auto blockSignature = block.signature(); m_stackSize = block.stackSize(); changeStackSize(blockSignature.argumentCount()); auto ifIndex = block.m_index; auto mdIf = reinterpret_cast(m_metadata->m_metadata.mutableSpan().data() + *block.m_pendingOffset); // delta PC mdIf->elseDeltaPC = nextPC() - block.m_pc; RECORD_NEXT_INSTRUCTION(block.m_pc, block.m_pc + mdIf->elseDeltaPC); // delta MC if (m_parser->currentOpcode() == OpType::End) { // Edge case: if ... end with no else mdIf->elseDeltaMC = curMC() - block.m_mc; block = ControlType(WTF::move(blockSignature), block.stackSize(), BlockType::Else); block.m_index = ifIndex; block.m_pendingOffset = std::nullopt; return { }; } // New MC, normal case mdIf->elseDeltaMC = safeCast(curMC() + sizeof(IPInt::BlockMetadata)) - block.m_mc; block = ControlType(WTF::move(blockSignature), block.stackSize(), BlockType::Else); block.m_index = ifIndex; block.m_pc = curPC(); block.m_mc = curMC(); block.m_pendingOffset = curMC(); m_metadata->addBlankSpace(); return { }; } // Exception Handling [[nodiscard]] PartialResult IPIntGenerator::addTry(BlockSignature&& signature, std::span args, ControlType& block) { m_tryDepth++; m_maxTryDepth = std::max(m_maxTryDepth, m_tryDepth.value()); block = ControlType(WTF::move(signature), m_stackSize.value() - args.size(), BlockType::Try); block.m_index = m_controlStructuresAwaitingCoalescing.size(); block.m_tryDepth = m_tryDepth; block.m_pc = curPC(); block.m_mc = curMC(); RECORD_NEXT_INSTRUCTION(block.m_pc, nextPC()); m_coalesceQueue.append(QueuedCoalesceRequest { m_controlStructuresAwaitingCoalescing.size(), true }); m_controlStructuresAwaitingCoalescing.append(ControlStructureAwaitingCoalescing { .startPC = block.m_pc, .isLoop = false }); ++m_coalesceDebt; // FIXME: Should this participate the same skipping that block does? // The upside is that we skip a bunch of sequential try/block instructions. // The downside is that try needs more metadata. // It's not clear that code would want to have many nested try blocks // though. m_metadata->addLength(getCurrentInstructionLength()); coalesceControlFlow(); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addTryTable(BlockSignature&& signature, std::span args, const Vector& targets, ControlType& result) { result = ControlType(WTF::move(signature), m_stackSize.value() - args.size(), BlockType::TryTable); result.m_tryTableTargets.reserveInitialCapacity(targets.size()); result.m_index = m_controlStructuresAwaitingCoalescing.size(); result.m_pc = curPC(); result.m_mc = curMC(); result.m_pendingOffset = curMC(); RECORD_NEXT_INSTRUCTION(result.m_pc, nextPC()); m_coalesceQueue.append(QueuedCoalesceRequest { m_controlStructuresAwaitingCoalescing.size(), true }); m_controlStructuresAwaitingCoalescing.append(ControlStructureAwaitingCoalescing { .startPC = result.m_pc, .isLoop = false }); ++m_coalesceDebt; IPIntLocation here = { curPC(), curMC() }; m_metadata->addBlankSpace(); tryToResolveEntryTarget(result.m_index, here, m_metadata->m_metadata.mutableSpan().data()); result.m_tryTableTargets.appendUsingFunctor(targets.size(), [&](unsigned i) -> ControlType::TryTableTarget { auto& target = targets[i]; return { target.type, target.tag, target.exceptionSignature, target.target }; } ); // append all the branch data first for (auto& target : targets) { auto entry = m_parser->resolveControlRef(target.target).controlData; // stack size at destination is (locals) + (everything below target) + (things we push) m_metadata->appendMetadata({ static_cast(entry.stackSize() + entry.branchTargetArity() + roundUpToMultipleOf<2>(m_metadata->m_numLocals)) }); IPIntLocation here = { curPC(), curMC() }; m_metadata->appendMetadata({ .deltaPC = 0xbeef, .deltaMC = 0xbeef }); tryToResolveBranchTarget(entry, here, m_metadata->m_metadata.mutableSpan().data()); } coalesceControlFlow(); return { }; } void IPIntGenerator::convertTryToCatch(ControlType& tryBlock, CatchKind catchKind) { ASSERT(ControlType::isTry(tryBlock)); ControlType catchBlock = ControlType(BlockSignature { tryBlock.signature() }, tryBlock.stackSize(), BlockType::Catch, catchKind); catchBlock.m_pc = tryBlock.m_pc; catchBlock.m_pcEnd = m_parser->currentOpcodeStartingOffset() - m_metadata->m_bytecodeOffset; catchBlock.m_tryDepth = tryBlock.m_tryDepth; catchBlock.m_index = tryBlock.m_index; catchBlock.m_mc = tryBlock.m_mc; tryBlock = WTF::move(catchBlock); } [[nodiscard]] PartialResult IPIntGenerator::addCatch(unsigned exceptionIndex, const RTT& exceptionSignature, std::span, ControlType& block, ResultList& results) { return addCatchToUnreachable(exceptionIndex, exceptionSignature, block, results); } [[nodiscard]] PartialResult IPIntGenerator::addCatchToUnreachable(unsigned exceptionIndex, const RTT& signature, ControlType& block, ResultList& results) { if (ControlType::isTry(block)) convertTryToCatch(block, CatchKind::Catch); for (unsigned i = 0; i < signature.argumentCount(); i++) results.append(IPIntValue { }); ASSERT(block.stackSize() == m_parser->getControlEntryStackHeightInValues()); m_stackSize = block.stackSize(); changeStackSize(signature.argumentCount()); // FIXME: If this is actually unreachable we shouldn't need metadata. block.m_catchesAwaitingFixup.append({ curPC(), curMC() }); m_metadata->addBlankSpace(); m_metadata->m_exceptionHandlers.append({ HandlerType::Catch, static_cast(block.m_pc), static_cast(block.m_pcEnd + 1), // + 1 since m_pcEnd is the PC of the catch bytecode, which should be included in the range nextPC(), curMC(), m_tryDepth, exceptionIndex }); uint32_t stackSizeInV128 = m_stackSize.value() + roundUpToMultipleOf<2>(m_metadata->m_numLocals); IPInt::CatchMetadata mdCatch { .stackSizeInV128 = stackSizeInV128 }; m_metadata->appendMetadata(mdCatch); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addCatchAll(std::span, ControlType& block) { return addCatchAllToUnreachable(block); } [[nodiscard]] PartialResult IPIntGenerator::addCatchAllToUnreachable(ControlType& block) { UNUSED_PARAM(block); if (ControlType::isTry(block)) convertTryToCatch(block, CatchKind::CatchAll); else block.m_catchKind = CatchKind::CatchAll; ASSERT(block.stackSize() == m_parser->getControlEntryStackHeightInValues()); m_stackSize = block.stackSize(); // FIXME: If this is actually unreachable we shouldn't need metadata. block.m_catchesAwaitingFixup.append({ curPC(), curMC() }); m_metadata->addBlankSpace(sizeof(IPInt::BlockMetadata)); m_metadata->m_exceptionHandlers.append({ HandlerType::CatchAll, static_cast(block.m_pc), static_cast(block.m_pcEnd + 1), // + 1 since m_pcEnd is the PC of the catch bytecode, which should be included in the range nextPC(), curMC(), m_tryDepth, 0 }); uint32_t stackSizeInV128 = m_stackSize.value() + roundUpToMultipleOf<2>(m_metadata->m_numLocals); IPInt::CatchMetadata mdCatch { .stackSizeInV128 = stackSizeInV128 }; m_metadata->appendMetadata(mdCatch); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addDelegate(ControlType& target, ControlType& data) { return addDelegateToUnreachable(target, data); } [[nodiscard]] PartialResult IPIntGenerator::addDelegateToUnreachable(ControlType& target, ControlType& data) { UNUSED_PARAM(target); UNUSED_PARAM(data); data.m_pcEnd = curPC(); // FIXME: If this is actually unreachable we shouldn't need metadata. data.m_catchesAwaitingFixup.append({ curPC(), curMC() }); m_metadata->addBlankSpace(); ASSERT(ControlType::isTry(target) || ControlType::isTopLevel(target)); unsigned targetDepth = ControlType::isTry(target) ? target.m_tryDepth : 0; m_metadata->m_exceptionHandlers.append({ HandlerType::Delegate, static_cast(data.m_pc), static_cast(data.m_pcEnd + 1), // + 1 since m_pcEnd is the PC of the delegate bytecode, which should be included in the range curPC(), curMC(), m_tryDepth, targetDepth }); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addThrow(unsigned exceptionIndex, ArgumentList&, std::span) { // IPInt reads throw arguments directly from the operand stack, but BBQ copies // them to a separate callee stack area. Track the size BBQ will need. const auto& signature = m_info.rtt(m_info.typeSignatureIndexFromExceptionIndexSpace(exceptionIndex)); unsigned offset = 0; for (unsigned i = 0; i < signature.argumentCount(); ++i) offset += signature.argumentType(i).kind() == TypeKind::V128 ? 2 : 1; unsigned throwCalleeStackSize = WTF::roundUpToMultipleOf(offset * sizeof(uint64_t)); m_metadata->m_maxCalleeStackSize = std::max(throwCalleeStackSize, m_metadata->m_maxCalleeStackSize); IPInt::ThrowMetadata mdThrow { .exceptionIndex = safeCast(exceptionIndex) }; m_metadata->appendMetadata(mdThrow); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addRethrow(unsigned, ControlType& catchBlock) { m_usesRethrow = true; IPInt::RethrowMetadata mdRethrow { .tryDepth = catchBlock.m_tryDepth }; auto size = m_metadata->m_metadata.size(); m_metadata->addBlankSpace(sizeof(mdRethrow)); WRITE_TO_METADATA(m_metadata->m_metadata.mutableSpan().data() + size, mdRethrow, IPInt::RethrowMetadata); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addThrowRef(ExpressionType, std::span) { changeStackSize(-1); return { }; } // Control Flow Branches [[nodiscard]] PartialResult IPIntGenerator::addReturn(const ControlType&, std::span) { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addBranch(ControlType& block, ExpressionType, std::span) { bool isBrIf = (m_parser->currentOpcode() == OpType::BrIf); if (isBrIf) changeStackSize(-1); IPIntLocation here = { curPC(), curMC() }; if (isBrIf) RECORD_NEXT_INSTRUCTION(here.pc, nextPC()); IPInt::BranchMetadata branch { .target = { .block = { .deltaPC = 0xbeef, .deltaMC = 0xbeef }, .toPop = safeCast(m_stackSize - block.stackSize() - block.branchTargetArity()), .toKeep = safeCast(block.branchTargetArity()), }, .instructionLength = { .length = safeCast(getCurrentInstructionLength()) } }; m_metadata->appendMetadata(branch); tryToResolveBranchTarget(block, here, m_metadata->m_metadata.mutableSpan().data()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addBranchNull(ControlType& block, ExpressionType, std::span, bool shouldNegate, ExpressionType&) { // We don't need shouldNegate in the metadata since it's in the opcode IPIntLocation here = { curPC(), curMC() }; RECORD_NEXT_INSTRUCTION(here.pc, nextPC()); unsigned toPop = m_stackSize - block.stackSize() - block.branchTargetArity(); // if we branch_on_null, we'll pop the null first if (!shouldNegate) toPop -= 1; IPInt::BranchMetadata branch { .target = { .block = { .deltaPC = 0xbeef, .deltaMC = 0xbeef }, .toPop = safeCast(toPop), .toKeep = safeCast(block.branchTargetArity()), }, .instructionLength = { .length = safeCast(getCurrentInstructionLength()) } }; m_metadata->appendMetadata(branch); tryToResolveBranchTarget(block, here, m_metadata->m_metadata.mutableSpan().data()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addBranchCast(ControlType& block, ExpressionType, std::span, bool allowNull, int32_t heapType, bool) { m_metadata->appendMetadata({ heapType, 0, static_cast(allowNull), }); IPIntLocation here = { curPC(), curMC() }; RECORD_NEXT_INSTRUCTION(here.pc, nextPC()); m_metadata->appendMetadata({ .target = { .block = { .deltaPC = 0xbeef, .deltaMC = 0xbeef }, .toPop = safeCast(m_stackSize - block.stackSize() - block.branchTargetArity()), .toKeep = safeCast(block.branchTargetArity()), }, .instructionLength = { .length = safeCast(getCurrentInstructionLength()) } }); tryToResolveBranchTarget(block, here, m_metadata->m_metadata.mutableSpan().data()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addSwitch(ExpressionType, const Vector& jumps, ControlType& defaultJump, std::span) { changeStackSize(-1); IPInt::SwitchMetadata mdSwitch { .size = safeCast(jumps.size() + 1), .target = { } }; m_metadata->appendMetadata(mdSwitch); for (auto* block : jumps) { IPInt::BranchTargetMetadata target { .block = { .deltaPC = 0xbeef, .deltaMC = 0xbeef }, .toPop = safeCast(m_stackSize - block->stackSize() - block->branchTargetArity()), .toKeep = safeCast(block->branchTargetArity()) }; IPIntLocation here = { curPC(), curMC() }; m_metadata->appendMetadata(target); tryToResolveBranchTarget(*block, here, m_metadata->m_metadata.mutableSpan().data()); } IPInt::BranchTargetMetadata defaultTarget { .block = { .deltaPC = 0xbeef, .deltaMC = 0xbeef }, .toPop = safeCast(m_stackSize - defaultJump.stackSize() - defaultJump.branchTargetArity()), .toKeep = safeCast(defaultJump.branchTargetArity()) }; IPIntLocation here = { curPC(), curMC() }; m_metadata->appendMetadata(defaultTarget); tryToResolveBranchTarget(defaultJump, here, m_metadata->m_metadata.mutableSpan().data()); return { }; } [[nodiscard]] PartialResult IPIntGenerator::endBlock(ControlEntry& entry, std::span enclosedStack) { return addEndToUnreachable(entry, enclosedStack); } void IPIntGenerator::endTryTable(const ControlType& data) { auto targets = data.m_tryTableTargets; unsigned i = 0; for (auto& target : targets) { HandlerType targetType; switch (target.type) { case CatchKind::Catch: targetType = HandlerType::TryTableCatch; break; case CatchKind::CatchRef: targetType = HandlerType::TryTableCatchRef; break; case CatchKind::CatchAll: targetType = HandlerType::TryTableCatchAll; break; case CatchKind::CatchAllRef: targetType = HandlerType::TryTableCatchAllRef; break; } auto entry = m_parser->resolveControlRef(target.target).controlData; m_metadata->m_exceptionHandlers.append({ targetType, data.m_pc, curPC() + 1, // + 1 since the end bytecode should be included // index into the array of try_table targets data.m_pc, // PC will be fixed up relative to the try_table's PC static_cast(data.m_mc + sizeof(IPInt::BlockMetadata) + i * (sizeof(IPInt::CatchMetadata) + sizeof(IPInt::BlockMetadata))), m_tryDepth, target.tag }); ++i; } } [[nodiscard]] PartialResult IPIntGenerator::addEndToUnreachable(ControlEntry& entry, std::span enclosedStack) { const auto& block = entry.controlData; unsigned returnCount = block.signature().returnCount(); ASSERT(enclosedStack.size() >= returnCount); auto resultSlots = enclosedStack.last(returnCount); for (unsigned i = 0; i < returnCount; ++i) resultSlots[i] = TypedExpression(block.signature().returnType(i), IPIntValue { }); m_stackSize = block.stackSize(); changeStackSize(returnCount); if (ControlType::isLoop(block)) m_metadata->exitLoop(); if (ControlType::isTry(block) || ControlType::isAnyCatch(block)) { --m_tryDepth; m_exitHandlersAwaitingCoalescing.appendVector(block.m_catchesAwaitingFixup); } if (ControlType::isTryTable(block)) endTryTable(block); if (ControlType::isTopLevel(block)) { // Hit the end m_exitHandlersAwaitingCoalescing.appendVector(m_jumpLocationsAwaitingEnd); coalesceControlFlow(true); // Metadata = round up 8 bytes, one for each m_metadata->m_bytecode = m_metadata->m_bytecode.first(m_parser->offset()); return { }; } if (ControlType::isIf(block)) { m_exitHandlersAwaitingCoalescing.append({ block.m_pc, block.m_mc }); } else if (ControlType::isElse(block)) { // if it's not an if ... end, coalesce if (block.m_pendingOffset) m_exitHandlersAwaitingCoalescing.append({ block.m_pc, block.m_mc }); m_coalesceQueue.append({ static_cast(block.m_index), false }); --m_coalesceDebt; } else if (ControlType::isBlock(block)) { m_coalesceQueue.append({ static_cast(block.m_index), false }); --m_coalesceDebt; } else if (ControlType::isLoop(block)) { m_coalesceQueue.append({ static_cast(block.m_index), false }); --m_coalesceDebt; } else if (ControlType::isTryTable(block)) { m_coalesceQueue.append({ static_cast(block.m_index), false }); --m_coalesceDebt; } else if (ControlType::isTry(block) || ControlType::isAnyCatch(block)) { m_coalesceQueue.append({ static_cast(block.m_index), false }); --m_coalesceDebt; } // mark pending exit targets to be resolved // any pending branch targets must be blocks because a loop would've been resolved. if it's loop, end then there's nobody // asking for the target coalesceControlFlow(); return { }; } auto IPIntGenerator::endTopLevel(std::span) -> PartialResult { bool isNotDebugMode = !m_debugInfo; if (m_usesSIMD && isNotDebugMode) m_info.markUsesSIMD(m_metadata->functionIndex()); if (isNotDebugMode) m_info.doneSeeingFunction(m_metadata->m_functionIndex); return { }; } // Calls void IPIntGenerator::addTailCallCommonData(const RTT&, const CallInformation& callConvention) { size_t stackArgumentsAndResultsInBytes = roundUpToMultipleOf(callConvention.headerAndArgumentStackSizeInBytes) - callConvention.headerIncludingThisSizeInBytes; // The WASM stack slots are always 16-bytes. size_t extraWasmStackInBytes = roundUpToMultipleOf<16>(stackArgumentsAndResultsInBytes); if (m_stackSize + extraWasmStackInBytes / 16 > m_maxStackSize) m_maxStackSize = m_stackSize + extraWasmStackInBytes / 16; ASSERT(!(stackArgumentsAndResultsInBytes % 16)); // mINT requires this for 16-bytes at a time tail-call arguments copy } [[nodiscard]] PartialResult IPIntGenerator::addCall(unsigned callProfileIndex, FunctionSpaceIndex index, const RTT& signature, ArgumentList&, ResultList& results, CallType callType) { const CallInformation& callConvention = cachedCallInformationFor(signature); m_metadata->addCallTarget(callProfileIndex, index); if (callType == CallType::TailCall) { // on a tail call, we need to: // roll back to old SP, shift SP to accommodate arguments // put arguments into registers / sp (reutilize mINT) // jump to entrypoint changeStackSize(-signature.argumentCount()); Checked tailCallFrameSize = WTF::roundUpToMultipleOf(callConvention.headerAndArgumentStackSizeInBytes + sizeof(CallerFrameAndPC)); m_metadata->m_maxCalleeStackSize = std::max(static_cast(tailCallFrameSize), m_metadata->m_maxCalleeStackSize); IPInt::TailCallMetadata functionIndexMetadata { .length = safeCast(getCurrentInstructionLength()), .callProfileIndex = callProfileIndex, .functionIndex = index, .rtt = &signature, .callerStackArgSize = static_cast(m_argumentAndResultsStackSize), }; m_metadata->appendMetadata(functionIndexMetadata); addTailCallCommonData(signature, callConvention); return { }; } results.appendUsingFunctor(signature.returnCount(), [](unsigned) { return IPIntValue { }; }); changeStackSize(signature.returnCount() - signature.argumentCount()); Checked frameSize = WTF::roundUpToMultipleOf(callConvention.headerAndArgumentStackSizeInBytes); m_metadata->m_maxCalleeStackSize = std::max(static_cast(frameSize), m_metadata->m_maxCalleeStackSize); IPInt::CallMetadata functionIndexMetadata { .length = safeCast(getCurrentInstructionLength()), .callProfileIndex = callProfileIndex, .functionIndex = index, .signature = { .rtt = &signature, .stackFrameSize = frameSize, .numExtraResults = static_cast(signature.returnCount() > signature.argumentCount() ? signature.returnCount() - signature.argumentCount() : 0), .numArguments = static_cast(signature.argumentCount()), }, }; m_metadata->appendMetadata(functionIndexMetadata); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addCallIndirect(unsigned callProfileIndex, unsigned tableIndex, const RTT& signature, ArgumentList&, ResultList& results, CallType callType) { const CallInformation& callConvention = cachedCallInformationFor(signature); m_metadata->addCallTarget(callProfileIndex, { }); if (callType == CallType::TailCall) { const unsigned callIndex = 1; changeStackSize(-signature.argumentCount() - callIndex); Checked tailCallFrameSize = WTF::roundUpToMultipleOf(callConvention.headerAndArgumentStackSizeInBytes + sizeof(CallerFrameAndPC)); m_metadata->m_maxCalleeStackSize = std::max(static_cast(tailCallFrameSize), m_metadata->m_maxCalleeStackSize); // on a tail call, we need to: // roll back to old SP, shift SP to accommodate arguments // put arguments into registers / sp (reutilize mINT) // jump to entrypoint IPInt::TailCallIndirectMetadata functionIndexMetadata { .length = safeCast(getCurrentInstructionLength()), .callProfileIndex = callProfileIndex, .tableIndex = tableIndex, .rtt = &signature, .callerStackArgSize = static_cast(m_argumentAndResultsStackSize), }; m_metadata->appendMetadata(functionIndexMetadata); addTailCallCommonData(signature, callConvention); return { }; } results.appendUsingFunctor(signature.returnCount(), [](unsigned) { return IPIntValue { }; }); const unsigned callIndex = 1; changeStackSize(signature.returnCount() - signature.argumentCount() - callIndex); Checked frameSize = WTF::roundUpToMultipleOf(callConvention.headerAndArgumentStackSizeInBytes); m_metadata->m_maxCalleeStackSize = std::max(static_cast(frameSize), m_metadata->m_maxCalleeStackSize); IPInt::CallIndirectMetadata functionIndexMetadata { .length = safeCast(getCurrentInstructionLength()), .callProfileIndex = callProfileIndex, .tableIndex = tableIndex, .signature = { .rtt = &signature, .stackFrameSize = frameSize, .numExtraResults = static_cast(signature.returnCount() > signature.argumentCount() ? signature.returnCount() - signature.argumentCount() : 0), .numArguments = static_cast(signature.argumentCount()), }, }; m_metadata->appendMetadata(functionIndexMetadata); return { }; } [[nodiscard]] PartialResult IPIntGenerator::addCallRef(unsigned callProfileIndex, const RTT& signature, ArgumentList&, ResultList& results, CallType callType) { const CallInformation& callConvention = cachedCallInformationFor(signature); m_metadata->addCallTarget(callProfileIndex, { }); if (callType == CallType::TailCall) { const unsigned callIndex = 1; changeStackSize(-signature.argumentCount() - callIndex); Checked tailCallFrameSize = WTF::roundUpToMultipleOf(callConvention.headerAndArgumentStackSizeInBytes + sizeof(CallerFrameAndPC)); m_metadata->m_maxCalleeStackSize = std::max(static_cast(tailCallFrameSize), m_metadata->m_maxCalleeStackSize); // on a tail call, we need to: // roll back to old SP, shift SP to accommodate arguments // put arguments into registers / sp (reutilize mINT) // jump to entrypoint IPInt::TailCallRefMetadata callMetadata { .length = safeCast(getCurrentInstructionLength()), .callProfileIndex = callProfileIndex, .rtt = &signature, .callerStackArgSize = static_cast(m_argumentAndResultsStackSize), }; m_metadata->appendMetadata(callMetadata); addTailCallCommonData(signature, callConvention); return { }; } results.appendUsingFunctor(signature.returnCount(), [](unsigned) { return IPIntValue { }; }); const unsigned callRef = 1; changeStackSize(signature.returnCount() - signature.argumentCount() - callRef); Checked frameSize = WTF::roundUpToMultipleOf(callConvention.headerAndArgumentStackSizeInBytes); m_metadata->m_maxCalleeStackSize = std::max(static_cast(frameSize), m_metadata->m_maxCalleeStackSize); IPInt::CallRefMetadata callMetadata { .length = safeCast(getCurrentInstructionLength()), .callProfileIndex = callProfileIndex, .signature = { .rtt = &signature, .stackFrameSize = frameSize, .numExtraResults = static_cast(signature.returnCount() > signature.argumentCount() ? signature.returnCount() - signature.argumentCount() : 0), .numArguments = static_cast(signature.argumentCount()), }, }; m_metadata->appendMetadata(callMetadata); return { }; } // Traps [[nodiscard]] PartialResult IPIntGenerator::addUnreachable() { return { }; } [[nodiscard]] PartialResult IPIntGenerator::addCrash() { return { }; } // Finalize std::unique_ptr IPIntGenerator::finalize() { ASSERT(!m_metadata->loopDepth()); if (m_usesRethrow) m_metadata->m_numAlignedRethrowSlots = roundUpToMultipleOf<2>(m_maxTryDepth); // Pad the metadata to an even number since we will allocate the rounded up size if (m_metadata->m_numLocals % 2) m_metadata->m_localInitBytecode.append(0); m_metadata->m_maxFrameSizeInV128 = m_metadata->m_numLocals; m_metadata->m_maxFrameSizeInV128 += m_metadata->m_numAlignedRethrowSlots; m_metadata->m_maxFrameSizeInV128 += m_maxStackSize; if (m_metadata->m_callTargets.size() < m_parser->numCallProfiles()) m_metadata->m_callTargets.insertFill(m_metadata->m_callTargets.size(), FunctionSpaceIndex { }, m_parser->numCallProfiles() - m_metadata->m_callTargets.size()); return WTF::move(m_metadata); } std::expected, String> parseAndCompileMetadata(std::span function, const RTT& signature, ModuleInformation& info, FunctionCodeIndex functionIndex) { IPIntGenerator generator(info, functionIndex, signature, function); FunctionParser parser(generator, function, signature, info); WASM_FAIL_IF_HELPER_FAILS(parser.parse()); return generator.finalize(); } void parseForDebugInfo(std::span function, const RTT& signature, ModuleInformation& info, FunctionCodeIndex functionIndex, FunctionDebugInfo& debugInfo) { IPIntGenerator generator(info, functionIndex, signature, function, &debugInfo); FunctionParser parser(generator, function, signature, info); auto result = parser.parse(); if (!result) { WTF::dataLogLn("Failed to parse for debug info:", result.error()); RELEASE_ASSERT_NOT_REACHED(); } } void IPIntGenerator::dump(const ControlStack&, const Stack*) { dataLogLn("PC: ", m_parser->currentOpcodeStartingOffset() - m_metadata->m_bytecodeOffset, " MC: ", m_metadata->m_metadata.size()); } } } // namespace JSC::Wasm WTF_ALLOW_UNSAFE_BUFFER_USAGE_END #endif // ENABLE(WEBASSEMBLY)