diff options
Diffstat (limited to '3rdparty/asmjit/src/asmjit/core/compiler.h')
-rw-r--r-- | 3rdparty/asmjit/src/asmjit/core/compiler.h | 639 |
1 files changed, 280 insertions, 359 deletions
diff --git a/3rdparty/asmjit/src/asmjit/core/compiler.h b/3rdparty/asmjit/src/asmjit/core/compiler.h index 71f89ec701e..709fd952acc 100644 --- a/3rdparty/asmjit/src/asmjit/core/compiler.h +++ b/3rdparty/asmjit/src/asmjit/core/compiler.h @@ -1,25 +1,7 @@ -// AsmJit - Machine code generation for C++ +// This file is part of AsmJit project <https://asmjit.com> // -// * Official AsmJit Home Page: https://asmjit.com -// * Official Github Repository: https://github.com/asmjit/asmjit -// -// Copyright (c) 2008-2020 The AsmJit Authors -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. +// See asmjit.h or LICENSE.md for license and copyright information +// SPDX-License-Identifier: Zlib #ifndef ASMJIT_CORE_COMPILER_H_INCLUDED #define ASMJIT_CORE_COMPILER_H_INCLUDED @@ -30,6 +12,7 @@ #include "../core/assembler.h" #include "../core/builder.h" #include "../core/constpool.h" +#include "../core/compilerdefs.h" #include "../core/func.h" #include "../core/inst.h" #include "../core/operand.h" @@ -39,15 +22,7 @@ ASMJIT_BEGIN_NAMESPACE -// ============================================================================ -// [Forward Declarations] -// ============================================================================ - -struct RATiedReg; -class RAWorkReg; - class JumpAnnotation; - class JumpNode; class FuncNode; class FuncRetNode; @@ -56,150 +31,18 @@ class InvokeNode; //! \addtogroup asmjit_compiler //! \{ -// ============================================================================ -// [asmjit::VirtReg] -// ============================================================================ - -//! Virtual register data, managed by \ref BaseCompiler. -class VirtReg { -public: - ASMJIT_NONCOPYABLE(VirtReg) - - //! Virtual register id. - uint32_t _id; - //! Virtual register info (signature). - RegInfo _info; - //! Virtual register size (can be smaller than `regInfo._size`). - uint32_t _virtSize; - //! Virtual register alignment (for spilling). - uint8_t _alignment; - //! Type-id. - uint8_t _typeId; - //! Virtual register weight for alloc/spill decisions. - uint8_t _weight; - //! True if this is a fixed register, never reallocated. - uint8_t _isFixed : 1; - //! True if the virtual register is only used as a stack (never accessed as register). - uint8_t _isStack : 1; - uint8_t _reserved : 6; - - //! Virtual register name (user provided or automatically generated). - ZoneString<16> _name; - - // ------------------------------------------------------------------------- - // The following members are used exclusively by RAPass. They are initialized - // when the VirtReg is created to NULL pointers and then changed during RAPass - // execution. RAPass sets them back to NULL before it returns. - // ------------------------------------------------------------------------- - - //! Reference to `RAWorkReg`, used during register allocation. - RAWorkReg* _workReg; - - //! \name Construction & Destruction - //! \{ - - inline VirtReg(uint32_t id, uint32_t signature, uint32_t virtSize, uint32_t alignment, uint32_t typeId) noexcept - : _id(id), - _virtSize(virtSize), - _alignment(uint8_t(alignment)), - _typeId(uint8_t(typeId)), - _weight(1), - _isFixed(false), - _isStack(false), - _reserved(0), - _name(), - _workReg(nullptr) { _info._signature = signature; } - - //! \} - - //! \name Accessors - //! \{ - - //! Returns the virtual register id. - inline uint32_t id() const noexcept { return _id; } - - //! Returns the virtual register name. - inline const char* name() const noexcept { return _name.data(); } - //! Returns the size of the virtual register name. - inline uint32_t nameSize() const noexcept { return _name.size(); } - - //! Returns a register information that wraps the register signature. - inline const RegInfo& info() const noexcept { return _info; } - //! Returns a virtual register type (maps to the physical register type as well). - inline uint32_t type() const noexcept { return _info.type(); } - //! Returns a virtual register group (maps to the physical register group as well). - inline uint32_t group() const noexcept { return _info.group(); } - - //! Returns a real size of the register this virtual register maps to. - //! - //! For example if this is a 128-bit SIMD register used for a scalar single - //! precision floating point value then its virtSize would be 4, however, the - //! `regSize` would still say 16 (128-bits), because it's the smallest size - //! of that register type. - inline uint32_t regSize() const noexcept { return _info.size(); } - - //! Returns a register signature of this virtual register. - inline uint32_t signature() const noexcept { return _info.signature(); } - - //! Returns the virtual register size. - //! - //! The virtual register size describes how many bytes the virtual register - //! needs to store its content. It can be smaller than the physical register - //! size, see `regSize()`. - inline uint32_t virtSize() const noexcept { return _virtSize; } - - //! Returns the virtual register alignment. - inline uint32_t alignment() const noexcept { return _alignment; } - - //! Returns the virtual register type id, see `Type::Id`. - inline uint32_t typeId() const noexcept { return _typeId; } - - //! Returns the virtual register weight - the register allocator can use it - //! as explicit hint for alloc/spill decisions. - inline uint32_t weight() const noexcept { return _weight; } - //! Sets the virtual register weight (0 to 255) - the register allocator can - //! use it as explicit hint for alloc/spill decisions and initial bin-packing. - inline void setWeight(uint32_t weight) noexcept { _weight = uint8_t(weight); } - - //! Returns whether the virtual register is always allocated to a fixed - //! physical register (and never reallocated). - //! - //! \note This is only used for special purposes and it's mostly internal. - inline bool isFixed() const noexcept { return bool(_isFixed); } - - //! Returns whether the virtual register is indeed a stack that only uses - //! the virtual register id for making it accessible. - //! - //! \note It's an error if a stack is accessed as a register. - inline bool isStack() const noexcept { return bool(_isStack); } - - inline bool hasWorkReg() const noexcept { return _workReg != nullptr; } - inline RAWorkReg* workReg() const noexcept { return _workReg; } - inline void setWorkReg(RAWorkReg* workReg) noexcept { _workReg = workReg; } - inline void resetWorkReg() noexcept { _workReg = nullptr; } - - //! \} -}; - -// ============================================================================ -// [asmjit::BaseCompiler] -// ============================================================================ - //! Code emitter that uses virtual registers and performs register allocation. //! -//! Compiler is a high-level code-generation tool that provides register -//! allocation and automatic handling of function calling conventions. It was -//! primarily designed for merging multiple parts of code into a function -//! without worrying about registers and function calling conventions. +//! Compiler is a high-level code-generation tool that provides register allocation and automatic handling of function +//! calling conventions. It was primarily designed for merging multiple parts of code into a function without worrying +//! about registers and function calling conventions. //! -//! BaseCompiler can be used, with a minimum effort, to handle 32-bit and -//! 64-bit code generation within a single code base. +//! BaseCompiler can be used, with a minimum effort, to handle 32-bit and 64-bit code generation within a single code +//! base. //! -//! BaseCompiler is based on BaseBuilder and contains all the features it -//! provides. It means that the code it stores can be modified (removed, added, -//! injected) and analyzed. When the code is finalized the compiler can emit -//! the code into an Assembler to translate the abstract representation into a -//! machine code. +//! BaseCompiler is based on BaseBuilder and contains all the features it provides. It means that the code it stores +//! can be modified (removed, added, injected) and analyzed. When the code is finalized the compiler can emit the code +//! into an Assembler to translate the abstract representation into a machine code. //! //! Check out architecture specific compilers for more details and examples: //! @@ -209,6 +52,9 @@ public: ASMJIT_NONCOPYABLE(BaseCompiler) typedef BaseBuilder Base; + //! \name Members + //! \{ + //! Current function. FuncNode* _func; //! Allocates `VirtReg` objects. @@ -218,10 +64,12 @@ public: //! Stores jump annotations. ZoneVector<JumpAnnotation*> _jumpAnnotations; - //! Local constant pool, flushed at the end of each function. - ConstPoolNode* _localConstPool; - //! Global constant pool, flushed by `finalize()`. - ConstPoolNode* _globalConstPool; + //! Local and global constant pools. + //! + //! Local constant pool is flushed with each function, global constant pool is flushed only by \ref finalize(). + ConstPoolNode* _constPools[2]; + + //! \} //! \name Construction & Destruction //! \{ @@ -236,52 +84,54 @@ public: //! \name Function Management //! \{ - //! Returns the current function. - inline FuncNode* func() const noexcept { return _func; } - //! Creates a new \ref FuncNode. - ASMJIT_API Error _newFuncNode(FuncNode** out, const FuncSignature& signature); - //! Creates a new \ref FuncNode adds it to the compiler. - ASMJIT_API Error _addFuncNode(FuncNode** out, const FuncSignature& signature); + ASMJIT_API Error newFuncNode(FuncNode** ASMJIT_NONNULL(out), const FuncSignature& signature); + //! Creates a new \ref FuncNode adds it to the instruction stream. + ASMJIT_API Error addFuncNode(FuncNode** ASMJIT_NONNULL(out), const FuncSignature& signature); //! Creates a new \ref FuncRetNode. - ASMJIT_API Error _newRetNode(FuncRetNode** out, const Operand_& o0, const Operand_& o1); - //! Creates a new \ref FuncRetNode and adds it to the compiler. - ASMJIT_API Error _addRetNode(FuncRetNode** out, const Operand_& o0, const Operand_& o1); + ASMJIT_API Error newFuncRetNode(FuncRetNode** ASMJIT_NONNULL(out), const Operand_& o0, const Operand_& o1); + //! Creates a new \ref FuncRetNode and adds it to the instruction stream. + ASMJIT_API Error addFuncRetNode(FuncRetNode** ASMJIT_NONNULL(out), const Operand_& o0, const Operand_& o1); + + //! Returns the current function. + inline FuncNode* func() const noexcept { return _func; } //! Creates a new \ref FuncNode with the given `signature` and returns it. inline FuncNode* newFunc(const FuncSignature& signature) { FuncNode* node; - _newFuncNode(&node, signature); + newFuncNode(&node, signature); return node; } - //! Creates a new \ref FuncNode with the given `signature`, adds it to the - //! compiler by using the \ref addFunc(FuncNode*) overload, and returns it. + //! Creates a new \ref FuncNode with the given `signature`, adds it to the instruction stream by using + //! the \ref addFunc(FuncNode*) overload, and returns it. inline FuncNode* addFunc(const FuncSignature& signature) { FuncNode* node; - _addFuncNode(&node, signature); + addFuncNode(&node, signature); return node; } //! Adds a function `node` to the instruction stream. - ASMJIT_API FuncNode* addFunc(FuncNode* func); + ASMJIT_API FuncNode* addFunc(FuncNode* ASMJIT_NONNULL(func)); //! Emits a sentinel that marks the end of the current function. ASMJIT_API Error endFunc(); +#if !defined(ASMJIT_NO_DEPRECATED) + inline Error _setArg(size_t argIndex, size_t valueIndex, const BaseReg& reg); + //! Sets a function argument at `argIndex` to `reg`. - ASMJIT_API Error setArg(uint32_t argIndex, const BaseReg& reg); + ASMJIT_DEPRECATED("Setting arguments through Compiler is deprecated, use FuncNode->setArg() instead") + inline Error setArg(size_t argIndex, const BaseReg& reg) { return _setArg(argIndex, 0, reg); } - inline FuncRetNode* newRet(const Operand_& o0, const Operand_& o1) { - FuncRetNode* node; - _newRetNode(&node, o0, o1); - return node; - } + //! Sets a function argument at `argIndex` at `valueIndex` to `reg`. + ASMJIT_DEPRECATED("Setting arguments through Compiler is deprecated, use FuncNode->setArg() instead") + inline Error setArg(size_t argIndex, size_t valueIndex, const BaseReg& reg) { return _setArg(argIndex, valueIndex, reg); } +#endif - inline FuncRetNode* addRet(const Operand_& o0, const Operand_& o1) { + inline Error addRet(const Operand_& o0, const Operand_& o1) { FuncRetNode* node; - _addRetNode(&node, o0, o1); - return node; + return addFuncRetNode(&node, o0, o1); } //! \} @@ -290,23 +140,9 @@ public: //! \{ //! Creates a new \ref InvokeNode. - ASMJIT_API Error _newInvokeNode(InvokeNode** out, uint32_t instId, const Operand_& o0, const FuncSignature& signature); - //! Creates a new \ref InvokeNode and adds it to Compiler. - ASMJIT_API Error _addInvokeNode(InvokeNode** out, uint32_t instId, const Operand_& o0, const FuncSignature& signature); - - //! Creates a new `InvokeNode`. - inline InvokeNode* newCall(uint32_t instId, const Operand_& o0, const FuncSignature& signature) { - InvokeNode* node; - _newInvokeNode(&node, instId, o0, signature); - return node; - } - - //! Adds a new `InvokeNode`. - inline InvokeNode* addCall(uint32_t instId, const Operand_& o0, const FuncSignature& signature) { - InvokeNode* node; - _addInvokeNode(&node, instId, o0, signature); - return node; - } + ASMJIT_API Error newInvokeNode(InvokeNode** ASMJIT_NONNULL(out), InstId instId, const Operand_& o0, const FuncSignature& signature); + //! Creates a new \ref InvokeNode and adds it to the instruction stream. + ASMJIT_API Error addInvokeNode(InvokeNode** ASMJIT_NONNULL(out), InstId instId, const Operand_& o0, const FuncSignature& signature); //! \} @@ -315,26 +151,25 @@ public: //! Creates a new virtual register representing the given `typeId` and `signature`. //! - //! \note This function is public, but it's not generally recommended to be used - //! by AsmJit users, use architecture-specific `newReg()` functionality instead - //! or functions like \ref _newReg() and \ref _newRegFmt(). - ASMJIT_API Error newVirtReg(VirtReg** out, uint32_t typeId, uint32_t signature, const char* name); + //! \note This function is public, but it's not generally recommended to be used by AsmJit users, use architecture + //! specific `newReg()` functionality instead or functions like \ref _newReg() and \ref _newRegFmt(). + ASMJIT_API Error newVirtReg(VirtReg** ASMJIT_NONNULL(out), TypeId typeId, OperandSignature signature, const char* name); //! Creates a new virtual register of the given `typeId` and stores it to `out` operand. - ASMJIT_API Error _newReg(BaseReg* out, uint32_t typeId, const char* name = nullptr); + ASMJIT_API Error _newReg(BaseReg* ASMJIT_NONNULL(out), TypeId typeId, const char* name = nullptr); //! Creates a new virtual register of the given `typeId` and stores it to `out` operand. //! //! \note This version accepts a snprintf() format `fmt` followed by a variadic arguments. - ASMJIT_API Error _newRegFmt(BaseReg* out, uint32_t typeId, const char* fmt, ...); + ASMJIT_API Error _newRegFmt(BaseReg* ASMJIT_NONNULL(out), TypeId typeId, const char* fmt, ...); //! Creates a new virtual register compatible with the provided reference register `ref`. - ASMJIT_API Error _newReg(BaseReg* out, const BaseReg& ref, const char* name = nullptr); + ASMJIT_API Error _newReg(BaseReg* ASMJIT_NONNULL(out), const BaseReg& ref, const char* name = nullptr); //! Creates a new virtual register compatible with the provided reference register `ref`. //! //! \note This version accepts a snprintf() format `fmt` followed by a variadic arguments. - ASMJIT_API Error _newRegFmt(BaseReg* out, const BaseReg& ref, const char* fmt, ...); + ASMJIT_API Error _newRegFmt(BaseReg* ASMJIT_NONNULL(out), const BaseReg& ref, const char* fmt, ...); //! Tests whether the given `id` is a valid virtual register id. inline bool isVirtIdValid(uint32_t id) const noexcept { @@ -357,9 +192,8 @@ public: //! Returns \ref VirtReg associated with the given virtual register `index`. //! - //! \note This is not the same as virtual register id. The conversion between - //! id and its index is implemented by \ref Operand_::virtIdToIndex() and \ref - //! Operand_::indexToVirtId() functions. + //! \note This is not the same as virtual register id. The conversion between id and its index is implemented + //! by \ref Operand_::virtIdToIndex() and \ref Operand_::indexToVirtId() functions. inline VirtReg* virtRegByIndex(uint32_t index) const noexcept { return _vRegArray[index]; } //! Returns an array of all virtual registers managed by the Compiler. @@ -371,7 +205,7 @@ public: //! Creates a new stack of the given `size` and `alignment` and stores it to `out`. //! //! \note `name` can be used to give the stack a name, for debugging purposes. - ASMJIT_API Error _newStack(BaseMem* out, uint32_t size, uint32_t alignment, const char* name = nullptr); + ASMJIT_API Error _newStack(BaseMem* ASMJIT_NONNULL(out), uint32_t size, uint32_t alignment, const char* name = nullptr); //! Updates the stack size of a stack created by `_newStack()` by its `virtId`. ASMJIT_API Error setStackSize(uint32_t virtId, uint32_t newSize, uint32_t newAlignment = 0); @@ -386,11 +220,11 @@ public: //! \name Constants //! \{ - //! Creates a new constant of the given `scope` (see \ref ConstPool::Scope). + //! Creates a new constant of the given `scope` (see \ref ConstPoolScope). //! - //! This function adds a constant of the given `size` to the built-in \ref - //! ConstPool and stores the reference to that constant to the `out` operand. - ASMJIT_API Error _newConst(BaseMem* out, uint32_t scope, const void* data, size_t size); + //! This function adds a constant of the given `size` to the built-in \ref ConstPool and stores the reference to that + //! constant to the `out` operand. + ASMJIT_API Error _newConst(BaseMem* ASMJIT_NONNULL(out), ConstPoolScope scope, const void* data, size_t size); //! \} @@ -409,23 +243,15 @@ public: return _jumpAnnotations; } - ASMJIT_API Error newJumpNode(JumpNode** out, uint32_t instId, uint32_t instOptions, const Operand_& o0, JumpAnnotation* annotation); - ASMJIT_API Error emitAnnotatedJump(uint32_t instId, const Operand_& o0, JumpAnnotation* annotation); + ASMJIT_API Error newJumpNode(JumpNode** ASMJIT_NONNULL(out), InstId instId, InstOptions instOptions, const Operand_& o0, JumpAnnotation* annotation); + ASMJIT_API Error emitAnnotatedJump(InstId instId, const Operand_& o0, JumpAnnotation* annotation); - //! Returns a new `JumpAnnotation` instance, which can be used to aggregate - //! possible targets of a jump where the target is not a label, for example - //! to implement jump tables. + //! Returns a new `JumpAnnotation` instance, which can be used to aggregate possible targets of a jump where the + //! target is not a label, for example to implement jump tables. ASMJIT_API JumpAnnotation* newJumpAnnotation(); //! \} -#ifndef ASMJIT_NO_DEPRECATED - ASMJIT_DEPRECATED("alloc() has no effect, it will be removed in the future") - inline void alloc(BaseReg&) {} - ASMJIT_DEPRECATED("spill() has no effect, it will be removed in the future") - inline void spill(BaseReg&) {} -#endif // !ASMJIT_NO_DEPRECATED - //! \name Events //! \{ @@ -435,22 +261,19 @@ public: //! \} }; -// ============================================================================ -// [asmjit::JumpAnnotation] -// ============================================================================ - //! Jump annotation used to annotate jumps. //! -//! \ref BaseCompiler allows to emit jumps where the target is either register -//! or memory operand. Such jumps cannot be trivially inspected, so instead of -//! doing heuristics AsmJit allows to annotate such jumps with possible targets. -//! Register allocator then use the annotation to construct control-flow, which -//! is then used by liveness analysis and other tools to prepare ground for -//! register allocation. +//! \ref BaseCompiler allows to emit jumps where the target is either register or memory operand. Such jumps cannot be +//! trivially inspected, so instead of doing heuristics AsmJit allows to annotate such jumps with possible targets. +//! Register allocator then uses the annotation to construct control-flow, which is then used by liveness analysis and +//! other tools to prepare ground for register allocation. class JumpAnnotation { public: ASMJIT_NONCOPYABLE(JumpAnnotation) + //! \name Members + //! \{ + //! Compiler that owns this JumpAnnotation. BaseCompiler* _compiler; //! Annotation identifier. @@ -458,10 +281,20 @@ public: //! Vector of label identifiers, see \ref labelIds(). ZoneVector<uint32_t> _labelIds; - inline JumpAnnotation(BaseCompiler* compiler, uint32_t annotationId) noexcept + //! \} + + //! \name Construction & Destruction + //! \{ + + inline JumpAnnotation(BaseCompiler* ASMJIT_NONNULL(compiler), uint32_t annotationId) noexcept : _compiler(compiler), _annotationId(annotationId) {} + //! \} + + //! \name Accessors + //! \{ + //! Returns the compiler that owns this JumpAnnotation. inline BaseCompiler* compiler() const noexcept { return _compiler; } //! Returns the annotation id. @@ -474,35 +307,42 @@ public: //! Tests whether the given `labelId` is a target of this JumpAnnotation. inline bool hasLabelId(uint32_t labelId) const noexcept { return _labelIds.contains(labelId); } + //! \} + + //! \name Annotation Building API + //! \{ + //! Adds the `label` to the list of targets of this JumpAnnotation. inline Error addLabel(const Label& label) noexcept { return addLabelId(label.id()); } //! Adds the `labelId` to the list of targets of this JumpAnnotation. inline Error addLabelId(uint32_t labelId) noexcept { return _labelIds.append(&_compiler->_allocator, labelId); } -}; -// ============================================================================ -// [asmjit::JumpNode] -// ============================================================================ + //! \} +}; //! Jump instruction with \ref JumpAnnotation. //! -//! \note This node should be only used to represent jump where the jump target -//! cannot be deduced by examining instruction operands. For example if the jump -//! target is register or memory location. This pattern is often used to perform -//! indirect jumps that use jump table, e.g. to implement `switch{}` statement. +//! \note This node should be only used to represent jump where the jump target cannot be deduced by examining +//! instruction operands. For example if the jump target is register or memory location. This pattern is often +//! used to perform indirect jumps that use jump table, e.g. to implement `switch{}` statement. class JumpNode : public InstNode { public: ASMJIT_NONCOPYABLE(JumpNode) + //! \name Members + //! \{ + JumpAnnotation* _annotation; + //! \} + //! \name Construction & Destruction //! \{ - ASMJIT_INLINE JumpNode(BaseCompiler* cc, uint32_t instId, uint32_t options, uint32_t opCount, JumpAnnotation* annotation) noexcept + inline JumpNode(BaseCompiler* ASMJIT_NONNULL(cc), InstId instId, InstOptions options, uint32_t opCount, JumpAnnotation* annotation) noexcept : InstNode(cc, instId, options, opCount, kBaseOpCapacity), _annotation(annotation) { - setType(kNodeJump); + setType(NodeType::kJump); } //! \} @@ -520,31 +360,24 @@ public: //! \} }; -// ============================================================================ -// [asmjit::FuncNode] -// ============================================================================ - //! Function node represents a function used by \ref BaseCompiler. //! //! A function is composed of the following: //! -//! - Function entry, \ref FuncNode acts as a label, so the entry is implicit. -//! To get the entry, simply use \ref FuncNode::label(), which is the same -//! as \ref LabelNode::label(). +//! - Function entry, \ref FuncNode acts as a label, so the entry is implicit. To get the entry, simply use +//! \ref FuncNode::label(), which is the same as \ref LabelNode::label(). //! -//! - Function exit, which is represented by \ref FuncNode::exitNode(). A -//! helper function \ref FuncNode::exitLabel() exists and returns an exit -//! label instead of node. +//! - Function exit, which is represented by \ref FuncNode::exitNode(). A helper function +//! \ref FuncNode::exitLabel() exists and returns an exit label instead of node. //! -//! - Function \ref FuncNode::endNode() sentinel. This node marks the end of -//! a function - there should be no code that belongs to the function after -//! this node, but the Compiler doesn't enforce that at the moment. +//! - Function \ref FuncNode::endNode() sentinel. This node marks the end of a function - there should be no +//! code that belongs to the function after this node, but the Compiler doesn't enforce that at the moment. //! //! - Function detail, see \ref FuncNode::detail(). //! //! - Function frame, see \ref FuncNode::frame(). //! -//! - Function arguments mapped to virtual registers, see \ref FuncNode::args(). +//! - Function arguments mapped to virtual registers, see \ref FuncNode::argPacks(). //! //! In a node list, the function and its body looks like the following: //! @@ -563,16 +396,30 @@ public: //! [...] - Anything after the function. //! \endcode //! -//! When a function is added to the compiler by \ref BaseCompiler::addFunc() it -//! actually inserts 3 nodes (FuncNode, ExitLabel, and FuncEnd) and sets the -//! current cursor to be FuncNode. When \ref BaseCompiler::endFunc() is called -//! the cursor is set to FuncEnd. This guarantees that user can use ExitLabel -//! as a marker after additional code or data can be placed, and it's a common -//! practice. +//! When a function is added to the instruction stream by \ref BaseCompiler::addFunc() it actually inserts 3 nodes +//! (FuncNode, ExitLabel, and FuncEnd) and sets the current cursor to be FuncNode. When \ref BaseCompiler::endFunc() +//! is called the cursor is set to FuncEnd. This guarantees that user can use ExitLabel as a marker after additional +//! code or data can be placed, which is a common practice. class FuncNode : public LabelNode { public: ASMJIT_NONCOPYABLE(FuncNode) + //! Arguments pack. + struct ArgPack { + RegOnly _data[Globals::kMaxValuePack]; + + inline void reset() noexcept { + for (size_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) + _data[valueIndex].reset(); + } + + inline RegOnly& operator[](size_t valueIndex) noexcept { return _data[valueIndex]; } + inline const RegOnly& operator[](size_t valueIndex) const noexcept { return _data[valueIndex]; } + }; + + //! \name Members + //! \{ + //! Function detail. FuncDetail _funcDetail; //! Function frame. @@ -581,23 +428,25 @@ public: LabelNode* _exitNode; //! Function end (sentinel). SentinelNode* _end; - //! Arguments array as `VirtReg`. - VirtReg** _args; + //! Argument packs. + ArgPack* _args; + + //! \} //! \name Construction & Destruction //! \{ //! Creates a new `FuncNode` instance. //! - //! Always use `BaseCompiler::addFunc()` to create `FuncNode`. - ASMJIT_INLINE FuncNode(BaseBuilder* cb) noexcept + //! Always use `BaseCompiler::addFunc()` to create a new `FuncNode`. + inline FuncNode(BaseBuilder* ASMJIT_NONNULL(cb)) noexcept : LabelNode(cb), _funcDetail(), _frame(), _exitNode(nullptr), _end(nullptr), _args(nullptr) { - setType(kNodeFunc); + setType(NodeType::kFunc); } //! \} @@ -610,12 +459,12 @@ public: //! Returns function exit label. inline Label exitLabel() const noexcept { return _exitNode->label(); } - //! Returns "End of Func" sentinel. + //! Returns "End of Func" sentinel node. inline SentinelNode* endNode() const noexcept { return _end; } - //! Returns function declaration. + //! Returns function detail. inline FuncDetail& detail() noexcept { return _funcDetail; } - //! Returns function declaration. + //! Returns function detail. inline const FuncDetail& detail() const noexcept { return _funcDetail; } //! Returns function frame. @@ -623,44 +472,64 @@ public: //! Returns function frame. inline const FuncFrame& frame() const noexcept { return _frame; } + //! Returns function attributes. + inline FuncAttributes attributes() const noexcept { return _frame.attributes(); } + //! Adds `attrs` to the function attributes. + inline void addAttributes(FuncAttributes attrs) noexcept { _frame.addAttributes(attrs); } + //! Returns arguments count. inline uint32_t argCount() const noexcept { return _funcDetail.argCount(); } - //! Returns returns count. - inline uint32_t retCount() const noexcept { return _funcDetail.retCount(); } + //! Returns argument packs. + inline ArgPack* argPacks() const noexcept { return _args; } - //! Returns arguments list. - inline VirtReg** args() const noexcept { return _args; } + //! Tests whether the function has a return value. + inline bool hasRet() const noexcept { return _funcDetail.hasRet(); } - //! Returns argument at `i`. - inline VirtReg* arg(uint32_t i) const noexcept { - ASMJIT_ASSERT(i < argCount()); - return _args[i]; + //! Returns argument pack at `argIndex`. + inline ArgPack& argPack(size_t argIndex) const noexcept { + ASMJIT_ASSERT(argIndex < argCount()); + return _args[argIndex]; } - //! Sets argument at `i`. - inline void setArg(uint32_t i, VirtReg* vReg) noexcept { - ASMJIT_ASSERT(i < argCount()); - _args[i] = vReg; + //! Sets argument at `argIndex`. + inline void setArg(size_t argIndex, const BaseReg& vReg) noexcept { + ASMJIT_ASSERT(argIndex < argCount()); + _args[argIndex][0].init(vReg); } - //! Resets argument at `i`. - inline void resetArg(uint32_t i) noexcept { - ASMJIT_ASSERT(i < argCount()); - _args[i] = nullptr; + //! \overload + inline void setArg(size_t argIndex, const RegOnly& vReg) noexcept { + ASMJIT_ASSERT(argIndex < argCount()); + _args[argIndex][0].init(vReg); } - //! Returns function attributes. - inline uint32_t attributes() const noexcept { return _frame.attributes(); } - //! Adds `attrs` to the function attributes. - inline void addAttributes(uint32_t attrs) noexcept { _frame.addAttributes(attrs); } + //! Sets argument at `argIndex` and `valueIndex`. + inline void setArg(size_t argIndex, size_t valueIndex, const BaseReg& vReg) noexcept { + ASMJIT_ASSERT(argIndex < argCount()); + _args[argIndex][valueIndex].init(vReg); + } + + //! \overload + inline void setArg(size_t argIndex, size_t valueIndex, const RegOnly& vReg) noexcept { + ASMJIT_ASSERT(argIndex < argCount()); + _args[argIndex][valueIndex].init(vReg); + } + + //! Resets argument pack at `argIndex`. + inline void resetArg(size_t argIndex) noexcept { + ASMJIT_ASSERT(argIndex < argCount()); + _args[argIndex].reset(); + } + + //! Resets argument pack at `argIndex`. + inline void resetArg(size_t argIndex, size_t valueIndex) noexcept { + ASMJIT_ASSERT(argIndex < argCount()); + _args[argIndex][valueIndex].reset(); + } //! \} }; -// ============================================================================ -// [asmjit::FuncRetNode] -// ============================================================================ - //! Function return, used by \ref BaseCompiler. class FuncRetNode : public InstNode { public: @@ -670,42 +539,68 @@ public: //! \{ //! Creates a new `FuncRetNode` instance. - inline FuncRetNode(BaseBuilder* cb) noexcept : InstNode(cb, BaseInst::kIdAbstract, 0, 0) { - _any._nodeType = kNodeFuncRet; + inline FuncRetNode(BaseBuilder* ASMJIT_NONNULL(cb)) noexcept : InstNode(cb, BaseInst::kIdAbstract, InstOptions::kNone, 0) { + _any._nodeType = NodeType::kFuncRet; } //! \} }; -// ============================================================================ -// [asmjit::InvokeNode] -// ============================================================================ - //! Function invocation, used by \ref BaseCompiler. class InvokeNode : public InstNode { public: ASMJIT_NONCOPYABLE(InvokeNode) + //! Operand pack provides multiple operands that can be associated with a single return value of function + //! argument. Sometims this is necessary to express an argument or return value that requires multiple + //! registers, for example 64-bit value in 32-bit mode or passing / returning homogeneous data structures. + struct OperandPack { + //! Operands. + Operand_ _data[Globals::kMaxValuePack]; + + //! Reset the pack by resetting all operands in the pack. + inline void reset() noexcept { + for (size_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) + _data[valueIndex].reset(); + } + + //! Returns an operand at the given `valueIndex`. + inline Operand& operator[](size_t valueIndex) noexcept { + ASMJIT_ASSERT(valueIndex < Globals::kMaxValuePack); + return _data[valueIndex].as<Operand>(); + } + + //! Returns an operand at the given `valueIndex` (const). + const inline Operand& operator[](size_t valueIndex) const noexcept { + ASMJIT_ASSERT(valueIndex < Globals::kMaxValuePack); + return _data[valueIndex].as<Operand>(); + } + }; + + //! \name Members + //! \{ + //! Function detail. FuncDetail _funcDetail; - //! Returns. - Operand_ _rets[2]; - //! Arguments. - Operand_* _args; + //! Function return value(s). + OperandPack _rets; + //! Function arguments. + OperandPack* _args; + + //! \} //! \name Construction & Destruction //! \{ //! Creates a new `InvokeNode` instance. - inline InvokeNode(BaseBuilder* cb, uint32_t instId, uint32_t options) noexcept + inline InvokeNode(BaseBuilder* ASMJIT_NONNULL(cb), InstId instId, InstOptions options) noexcept : InstNode(cb, instId, options, kBaseOpCapacity), _funcDetail(), _args(nullptr) { - setType(kNodeInvoke); + setType(NodeType::kInvoke); _resetOps(); - _rets[0].reset(); - _rets[1].reset(); - addFlags(kFlagIsRemovable); + _rets.reset(); + addFlags(NodeFlags::kIsRemovable); } //! \} @@ -728,53 +623,67 @@ public: //! \overload inline const Operand& target() const noexcept { return _opArray[0].as<Operand>(); } + //! Returns the number of function return values. + inline bool hasRet() const noexcept { return _funcDetail.hasRet(); } //! Returns the number of function arguments. inline uint32_t argCount() const noexcept { return _funcDetail.argCount(); } - //! Returns the number of function return values. - inline uint32_t retCount() const noexcept { return _funcDetail.retCount(); } - //! Returns the return value at `i`. - inline Operand& ret(uint32_t i = 0) noexcept { - ASMJIT_ASSERT(i < 2); - return _rets[i].as<Operand>(); + //! Returns operand pack representing function return value(s). + inline OperandPack& retPack() noexcept { return _rets; } + //! Returns operand pack representing function return value(s). + inline const OperandPack& retPack() const noexcept { return _rets; } + + //! Returns the return value at the given `valueIndex`. + inline Operand& ret(size_t valueIndex = 0) noexcept { return _rets[valueIndex]; } + //! \overload + inline const Operand& ret(size_t valueIndex = 0) const noexcept { return _rets[valueIndex]; } + + //! Returns operand pack representing function return value(s). + inline OperandPack& argPack(size_t argIndex) noexcept { + ASMJIT_ASSERT(argIndex < argCount()); + return _args[argIndex]; } //! \overload - inline const Operand& ret(uint32_t i = 0) const noexcept { - ASMJIT_ASSERT(i < 2); - return _rets[i].as<Operand>(); + inline const OperandPack& argPack(size_t argIndex) const noexcept { + ASMJIT_ASSERT(argIndex < argCount()); + return _args[argIndex]; } - //! Returns the function argument at `i`. - inline Operand& arg(uint32_t i) noexcept { - ASMJIT_ASSERT(i < kFuncArgCountLoHi); - return _args[i].as<Operand>(); + //! Returns a function argument at the given `argIndex`. + inline Operand& arg(size_t argIndex, size_t valueIndex) noexcept { + ASMJIT_ASSERT(argIndex < argCount()); + return _args[argIndex][valueIndex]; } //! \overload - inline const Operand& arg(uint32_t i) const noexcept { - ASMJIT_ASSERT(i < kFuncArgCountLoHi); - return _args[i].as<Operand>(); + inline const Operand& arg(size_t argIndex, size_t valueIndex) const noexcept { + ASMJIT_ASSERT(argIndex < argCount()); + return _args[argIndex][valueIndex]; } - //! Sets the function argument at `i` to `op`. - ASMJIT_API bool _setArg(uint32_t i, const Operand_& op) noexcept; //! Sets the function return value at `i` to `op`. - ASMJIT_API bool _setRet(uint32_t i, const Operand_& op) noexcept; + inline void _setRet(size_t valueIndex, const Operand_& op) noexcept { _rets[valueIndex] = op; } + //! Sets the function argument at `i` to `op`. + inline void _setArg(size_t argIndex, size_t valueIndex, const Operand_& op) noexcept { + ASMJIT_ASSERT(argIndex < argCount()); + _args[argIndex][valueIndex] = op; + } - //! Sets the function argument at `i` to `reg`. - inline bool setArg(uint32_t i, const BaseReg& reg) noexcept { return _setArg(i, reg); } - //! Sets the function argument at `i` to `imm`. - inline bool setArg(uint32_t i, const Imm& imm) noexcept { return _setArg(i, imm); } + //! Sets the function return value at `valueIndex` to `reg`. + inline void setRet(size_t valueIndex, const BaseReg& reg) noexcept { _setRet(valueIndex, reg); } - //! Sets the function return value at `i` to `var`. - inline bool setRet(uint32_t i, const BaseReg& reg) noexcept { return _setRet(i, reg); } + //! Sets the first function argument in a value-pack at `argIndex` to `reg`. + inline void setArg(size_t argIndex, const BaseReg& reg) noexcept { _setArg(argIndex, 0, reg); } + //! Sets the first function argument in a value-pack at `argIndex` to `imm`. + inline void setArg(size_t argIndex, const Imm& imm) noexcept { _setArg(argIndex, 0, imm); } + + //! Sets the function argument at `argIndex` and `valueIndex` to `reg`. + inline void setArg(size_t argIndex, size_t valueIndex, const BaseReg& reg) noexcept { _setArg(argIndex, valueIndex, reg); } + //! Sets the function argument at `argIndex` and `valueIndex` to `imm`. + inline void setArg(size_t argIndex, size_t valueIndex, const Imm& imm) noexcept { _setArg(argIndex, valueIndex, imm); } //! \} }; -// ============================================================================ -// [asmjit::FuncPass] -// ============================================================================ - //! Function pass extends \ref Pass with \ref FuncPass::runOnFunction(). class ASMJIT_VIRTAPI FuncPass : public Pass { public: @@ -796,7 +705,7 @@ public: //! \} - //! \name Run + //! \name Pass Interface //! \{ //! Calls `runOnFunction()` on each `FuncNode` node found. @@ -808,6 +717,18 @@ public: //! \} }; +#if !defined(ASMJIT_NO_DEPRECATED) +inline Error BaseCompiler::_setArg(size_t argIndex, size_t valueIndex, const BaseReg& reg) { + FuncNode* func = _func; + + if (ASMJIT_UNLIKELY(!func)) + return reportError(DebugUtils::errored(kErrorInvalidState)); + + func->setArg(argIndex, valueIndex, reg); + return kErrorOk; +} +#endif + //! \} ASMJIT_END_NAMESPACE |