diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/jit/shared/Lowering-shared.h | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit/shared/Lowering-shared.h')
-rw-r--r-- | js/src/jit/shared/Lowering-shared.h | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/js/src/jit/shared/Lowering-shared.h b/js/src/jit/shared/Lowering-shared.h new file mode 100644 index 0000000000..d26e349d8c --- /dev/null +++ b/js/src/jit/shared/Lowering-shared.h @@ -0,0 +1,371 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef jit_shared_Lowering_shared_h +#define jit_shared_Lowering_shared_h + +// This file declares the structures that are used for attaching LIR to a +// MIRGraph. + +#include "jit/LIR.h" +#include "jit/MIRGenerator.h" + +namespace js { +namespace jit { + +class MIRGenerator; +class MIRGraph; +class MDefinition; +class MInstruction; +class LOsiPoint; + +class LIRGeneratorShared { + protected: + MIRGenerator* gen; + MIRGraph& graph; + LIRGraph& lirGraph_; + LBlock* current; + MResumePoint* lastResumePoint_; + LRecoverInfo* cachedRecoverInfo_; + LOsiPoint* osiPoint_; + + LIRGeneratorShared(MIRGenerator* gen, MIRGraph& graph, LIRGraph& lirGraph) + : gen(gen), + graph(graph), + lirGraph_(lirGraph), + current(nullptr), + lastResumePoint_(nullptr), + cachedRecoverInfo_(nullptr), + osiPoint_(nullptr) {} + + MIRGenerator* mir() { return gen; } + + // Abort errors are caught at end of visitInstruction. It is possible for + // multiple errors to be detected before the end of visitInstruction. In + // this case, we only report the first back to the MIRGenerator. + bool errored() { return gen->getOffThreadStatus().isErr(); } + void abort(AbortReason r, const char* message, ...) MOZ_FORMAT_PRINTF(3, 4) { + if (errored()) { + return; + } + + va_list ap; + va_start(ap, message); + auto reason_ = gen->abortFmt(r, message, ap); + va_end(ap); + gen->setOffThreadStatus(reason_); + } + void abort(AbortReason r) { + if (errored()) { + return; + } + + auto reason_ = gen->abort(r); + gen->setOffThreadStatus(reason_); + } + + static void ReorderCommutative(MDefinition** lhsp, MDefinition** rhsp, + MInstruction* ins); + static bool ShouldReorderCommutative(MDefinition* lhs, MDefinition* rhs, + MInstruction* ins); + + // A backend can decide that an instruction should be emitted at its uses, + // rather than at its definition. To communicate this, set the + // instruction's virtual register set to 0. When using the instruction, + // its virtual register is temporarily reassigned. To know to clear it + // after constructing the use information, the worklist bit is temporarily + // unset. + // + // The backend can use the worklist bit to determine whether or not a + // definition should be created. + inline void emitAtUses(MInstruction* mir); + + // The lowest-level calls to use, those that do not wrap another call to + // use(), must prefix grabbing virtual register IDs by these calls. + inline void ensureDefined(MDefinition* mir); + + void visitEmittedAtUses(MInstruction* ins); + + // These all create a use of a virtual register, with an optional + // allocation policy. + // + // Some of these use functions have atStart variants. + // - non-atStart variants will tell the register allocator that the input + // allocation must be different from any Temp or Definition also needed for + // this LInstruction. + // - atStart variants relax that restriction and allow the input to be in + // the same register as any output Definition (but not Temps) used by the + // LInstruction. Note that it doesn't *imply* this will actually happen, + // but gives a hint to the register allocator that it can do it. + // + // TL;DR: Use non-atStart variants only if you need the input value after + // writing to any definitions (excluding temps), during code generation of + // this LInstruction. Otherwise, use atStart variants, which will lower + // register pressure. + // + // There is an additional constraint. Consider a MIR node with two + // MDefinition* operands, op1 and op2. If the node reuses the register of op1 + // for its output then op1 must be used as atStart. Then, if op1 and op2 + // represent the same LIR node then op2 must be an atStart use too; otherwise + // op2 must be a non-atStart use. There is however not always a 1-1 mapping + // from MDefinition* to LNode*, so to determine whether two MDefinition* map + // to the same LNode*, ALWAYS go via the willHaveDifferentLIRNodes() + // predicate. Do not use pointer equality on the MIR nodes. + // + // Do not add other conditions when using willHaveDifferentLIRNodes(). The + // predicate is the source of truth about whether to use atStart or not, no + // other conditions may apply in contexts when it is appropriate to use it. + inline LUse use(MDefinition* mir, LUse policy); + inline LUse use(MDefinition* mir); + inline LUse useAtStart(MDefinition* mir); + inline LUse useRegister(MDefinition* mir); + inline LUse useRegisterAtStart(MDefinition* mir); + inline LUse useFixed(MDefinition* mir, Register reg); + inline LUse useFixed(MDefinition* mir, FloatRegister reg); + inline LUse useFixed(MDefinition* mir, AnyRegister reg); + inline LUse useFixedAtStart(MDefinition* mir, Register reg); + inline LUse useFixedAtStart(MDefinition* mir, AnyRegister reg); + inline LAllocation useOrConstant(MDefinition* mir); + inline LAllocation useOrConstantAtStart(MDefinition* mir); + // "Any" is architecture dependent, and will include registers and stack + // slots on X86, and only registers on ARM. + inline LAllocation useAny(MDefinition* mir); + inline LAllocation useAnyAtStart(MDefinition* mir); + inline LAllocation useAnyOrConstant(MDefinition* mir); + // "Storable" is architecture dependend, and will include registers and + // constants on X86 and only registers on ARM. This is a generic "things + // we can expect to write into memory in 1 instruction". + inline LAllocation useStorable(MDefinition* mir); + inline LAllocation useStorableAtStart(MDefinition* mir); + inline LAllocation useKeepalive(MDefinition* mir); + inline LAllocation useKeepaliveOrConstant(MDefinition* mir); + inline LAllocation useRegisterOrConstant(MDefinition* mir); + inline LAllocation useRegisterOrConstantAtStart(MDefinition* mir); + inline LAllocation useRegisterOrZeroAtStart(MDefinition* mir); + inline LAllocation useRegisterOrZero(MDefinition* mir); + inline LAllocation useRegisterOrNonDoubleConstant(MDefinition* mir); + + // These methods accept either an Int32 or IntPtr value. A constant is used if + // the value fits in an int32. + inline LAllocation useRegisterOrInt32Constant(MDefinition* mir); + inline LAllocation useAnyOrInt32Constant(MDefinition* mir); + + // Like useRegisterOrInt32Constant, but uses a constant only if + // |int32val * Scalar::byteSize(type) + offsetAdjustment| doesn't overflow + // int32. + LAllocation useRegisterOrIndexConstant(MDefinition* mir, Scalar::Type type, + int32_t offsetAdjustment = 0); + + inline LUse useRegisterForTypedLoad(MDefinition* mir, MIRType type); + +#ifdef JS_NUNBOX32 + inline LUse useType(MDefinition* mir, LUse::Policy policy); + inline LUse usePayload(MDefinition* mir, LUse::Policy policy); + inline LUse usePayloadAtStart(MDefinition* mir, LUse::Policy policy); + inline LUse usePayloadInRegisterAtStart(MDefinition* mir); + + // Adds a box input to an instruction, setting operand |n| to the type and + // |n+1| to the payload. Does not modify the operands, instead expecting a + // policy to already be set. + inline void fillBoxUses(LInstruction* lir, size_t n, MDefinition* mir); +#endif + + // Test whether mir1 and mir2 may give rise to different LIR nodes even if + // mir1 == mir2; use it to guide the selection of the use directive for one of + // the nodes in the context of a reused input. See comments above about why + // it's important to use this predicate and not pointer equality. + // + // This predicate may be called before or after the application of a use + // directive to the first of the nodes, but it is meaningless to call it after + // the application of a directive to the second node. + inline bool willHaveDifferentLIRNodes(MDefinition* mir1, MDefinition* mir2); + + // These create temporary register requests. + inline LDefinition temp(LDefinition::Type type = LDefinition::GENERAL, + LDefinition::Policy policy = LDefinition::REGISTER); + inline LInt64Definition tempInt64( + LDefinition::Policy policy = LDefinition::REGISTER); + inline LDefinition tempFloat32(); + inline LDefinition tempDouble(); +#ifdef ENABLE_WASM_SIMD + inline LDefinition tempSimd128(); +#endif + inline LDefinition tempCopy(MDefinition* input, uint32_t reusedInput); + + // Note that the fixed register has a GENERAL type, + // unless the arg is of FloatRegister type + inline LDefinition tempFixed(Register reg); + inline LDefinition tempFixed(FloatRegister reg); + inline LInt64Definition tempInt64Fixed(Register64 reg); + + template <size_t Ops, size_t Temps> + inline void defineFixed(LInstructionHelper<1, Ops, Temps>* lir, + MDefinition* mir, const LAllocation& output); + + template <size_t Temps> + inline void defineBox( + details::LInstructionFixedDefsTempsHelper<BOX_PIECES, Temps>* lir, + MDefinition* mir, LDefinition::Policy policy = LDefinition::REGISTER); + + template <size_t Ops, size_t Temps> + inline void defineInt64(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, + MDefinition* mir, + LDefinition::Policy policy = LDefinition::REGISTER); + + template <size_t Ops, size_t Temps> + inline void defineInt64Fixed( + LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir, + const LInt64Allocation& output); + + inline void defineReturn(LInstruction* lir, MDefinition* mir); + + template <size_t X> + inline void define(details::LInstructionFixedDefsTempsHelper<1, X>* lir, + MDefinition* mir, + LDefinition::Policy policy = LDefinition::REGISTER); + template <size_t X> + inline void define(details::LInstructionFixedDefsTempsHelper<1, X>* lir, + MDefinition* mir, const LDefinition& def); + + template <size_t Ops, size_t Temps> + inline void defineReuseInput(LInstructionHelper<1, Ops, Temps>* lir, + MDefinition* mir, uint32_t operand); + + template <size_t Ops, size_t Temps> + inline void defineBoxReuseInput( + LInstructionHelper<BOX_PIECES, Ops, Temps>* lir, MDefinition* mir, + uint32_t operand); + + template <size_t Ops, size_t Temps> + inline void defineInt64ReuseInput( + LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir, + uint32_t operand); + + // Returns a box allocation for a Value-typed instruction. + inline LBoxAllocation useBox(MDefinition* mir, + LUse::Policy policy = LUse::REGISTER, + bool useAtStart = false); + + // Returns a box allocation. The use is either typed, a Value, or + // a constant (if useConstant is true). + inline LBoxAllocation useBoxOrTypedOrConstant(MDefinition* mir, + bool useConstant, + bool useAtStart = false); + inline LBoxAllocation useBoxOrTyped(MDefinition* mir, + bool useAtStart = false); + + // Returns an int64 allocation for an Int64-typed instruction. + inline LInt64Allocation useInt64(MDefinition* mir, LUse::Policy policy, + bool useAtStart); + inline LInt64Allocation useInt64(MDefinition* mir, bool useAtStart = false); + inline LInt64Allocation useInt64AtStart(MDefinition* mir); + inline LInt64Allocation useInt64OrConstant(MDefinition* mir, + bool useAtStart = false); + inline LInt64Allocation useInt64Register(MDefinition* mir, + bool useAtStart = false); + inline LInt64Allocation useInt64RegisterOrConstant(MDefinition* mir, + bool useAtStart = false); + inline LInt64Allocation useInt64Fixed(MDefinition* mir, Register64 regs, + bool useAtStart = false); + inline LInt64Allocation useInt64FixedAtStart(MDefinition* mir, + Register64 regs); + + inline LInt64Allocation useInt64RegisterAtStart(MDefinition* mir); + inline LInt64Allocation useInt64RegisterOrConstantAtStart(MDefinition* mir); + inline LInt64Allocation useInt64OrConstantAtStart(MDefinition* mir); + + // Rather than defining a new virtual register, sets |ins| to have the same + // virtual register as |as|. + inline void redefine(MDefinition* ins, MDefinition* as); + + template <typename LClass, typename... Args> + inline LClass* allocateVariadic(uint32_t numOperands, Args&&... args); + + TempAllocator& alloc() const { return graph.alloc(); } + + uint32_t getVirtualRegister() { + uint32_t vreg = lirGraph_.getVirtualRegister(); + + // If we run out of virtual registers, mark code generation as having + // failed and return a dummy vreg. Include a + 1 here for NUNBOX32 + // platforms that expect Value vregs to be adjacent. + if (vreg + 1 >= MAX_VIRTUAL_REGISTERS) { + abort(AbortReason::Alloc, "max virtual registers"); + return 1; + } + return vreg; + } + + template <typename T> + void annotate(T* ins); + template <typename T> + void add(T* ins, MInstruction* mir = nullptr); + + void lowerTypedPhiInput(MPhi* phi, uint32_t inputPosition, LBlock* block, + size_t lirIndex); + + void definePhiOneRegister(MPhi* phi, size_t lirIndex); +#ifdef JS_NUNBOX32 + void definePhiTwoRegisters(MPhi* phi, size_t lirIndex); +#endif + + void defineTypedPhi(MPhi* phi, size_t lirIndex) { + // One register containing the payload. + definePhiOneRegister(phi, lirIndex); + } + void defineUntypedPhi(MPhi* phi, size_t lirIndex) { +#ifdef JS_NUNBOX32 + // Two registers: one for the type, one for the payload. + definePhiTwoRegisters(phi, lirIndex); +#else + // One register containing the full Value. + definePhiOneRegister(phi, lirIndex); +#endif + } + + LOsiPoint* popOsiPoint() { + LOsiPoint* tmp = osiPoint_; + osiPoint_ = nullptr; + return tmp; + } + + LRecoverInfo* getRecoverInfo(MResumePoint* rp); + LSnapshot* buildSnapshot(MResumePoint* rp, BailoutKind kind); + bool assignPostSnapshot(MInstruction* mir, LInstruction* ins); + + // Marks this instruction as fallible, meaning that before it performs + // effects (if any), it may check pre-conditions and bailout if they do not + // hold. This function informs the register allocator that it will need to + // capture appropriate state. + void assignSnapshot(LInstruction* ins, BailoutKind kind); + + // Marks this instruction as needing to call into either the VM or GC. This + // function may build a snapshot that captures the result of its own + // instruction, and as such, should generally be called after define*(). + void assignSafepoint(LInstruction* ins, MInstruction* mir, + BailoutKind kind = BailoutKind::DuringVMCall); + + // Marks this instruction as needing a wasm safepoint. + void assignWasmSafepoint(LInstruction* ins); + + inline void lowerConstantDouble(double d, MInstruction* mir); + inline void lowerConstantFloat32(float f, MInstruction* mir); + + bool canSpecializeWasmCompareAndSelect(MCompare::CompareType compTy, + MIRType insTy); + void lowerWasmCompareAndSelect(MWasmSelect* ins, MDefinition* lhs, + MDefinition* rhs, MCompare::CompareType compTy, + JSOp jsop); + + public: + // Whether to generate typed reads for element accesses with hole checks. + static bool allowTypedElementHoleCheck() { return false; } +}; + +} // namespace jit +} // namespace js + +#endif /* jit_shared_Lowering_shared_h */ |