a64compiler.h (12219B)
1 // This file is part of AsmJit project <https://asmjit.com> 2 // 3 // See <asmjit/core.h> or LICENSE.md for license and copyright information 4 // SPDX-License-Identifier: Zlib 5 6 #ifndef ASMJIT_ARM_A64COMPILER_H_INCLUDED 7 #define ASMJIT_ARM_A64COMPILER_H_INCLUDED 8 9 #include "../core/api-config.h" 10 #ifndef ASMJIT_NO_COMPILER 11 12 #include "../core/compiler.h" 13 #include "../core/type.h" 14 #include "../arm/a64emitter.h" 15 16 ASMJIT_BEGIN_SUB_NAMESPACE(a64) 17 18 //! \addtogroup asmjit_a64 19 //! \{ 20 21 //! AArch64 compiler implementation. 22 class ASMJIT_VIRTAPI Compiler 23 : public BaseCompiler, 24 public EmitterExplicitT<Compiler> { 25 public: 26 ASMJIT_NONCOPYABLE(Compiler) 27 using Base = BaseCompiler; 28 29 //! \name Construction & Destruction 30 //! \{ 31 32 ASMJIT_API explicit Compiler(CodeHolder* code = nullptr) noexcept; 33 ASMJIT_API ~Compiler() noexcept override; 34 35 //! \} 36 37 //! \name Virtual Registers 38 //! \{ 39 40 //! Creates a new general-purpose register with `type_id` type and optional name passed via `args`. 41 //! 42 //! \note Using \ref TypeId is too generic. In general it's recommended to use \ref new_gp32(), 43 //! \ref new_gp64(), and \ref new_gpz() or \ref new_gp_ptr(). 44 template<typename... Args> 45 ASMJIT_INLINE_NODEBUG Gp new_gp(TypeId type_id, Args&&... args) { return new_reg<Gp>(type_id, std::forward<Args>(args)...); } 46 47 //! Creates a new vector register with `type_id` type and optional name passed via `args`. 48 //! 49 //! \note Using \ref TypeId is too generic. In general it's recommended to use \ref new_vec128(), 50 //! \ref new_vec_s(), \ref new_vec_d(), \ref new_vec_q(), ... 51 template<typename... Args> 52 ASMJIT_INLINE_NODEBUG Vec new_vec(TypeId type_id, Args&&... args) { return new_reg<Vec>(type_id, std::forward<Args>(args)...); } 53 54 //! Creates a new 32-bit general purpose register mapped to low 32 bits of a full register (on 64-bit targets). 55 template<typename... Args> 56 ASMJIT_INLINE_NODEBUG Gp new_gp32(Args&&... args) { return new_reg<Gp>(TypeId::kUInt32, std::forward<Args>(args)...); } 57 58 //! Creates a new 64-bit general purpose register. 59 template<typename... Args> 60 ASMJIT_INLINE_NODEBUG Gp new_gp64(Args&&... args) { return new_reg<Gp>(TypeId::kUInt64, std::forward<Args>(args)...); } 61 62 //! Creates a new 32-bit general purpose register. 63 //! 64 //! \note This is a convenience function alias of \ref new_gp32(). 65 template<typename... Args> 66 ASMJIT_INLINE_NODEBUG Gp new_gpw(Args&&... args) { return new_reg<Gp>(TypeId::kUIntPtr, std::forward<Args>(args)...); } 67 68 //! Creates a new 64-bit general purpose register. 69 //! 70 //! \note This is a convenience function alias of \ref new_gp64(). 71 template<typename... Args> 72 ASMJIT_INLINE_NODEBUG Gp new_gpx(Args&&... args) { return new_reg<Gp>(TypeId::kUIntPtr, std::forward<Args>(args)...); } 73 74 //! Creates a new 32-bit or 64-bit general purpose register depending on the target register width. 75 //! 76 //! \note This is a convenience function, on aarch64 target it always creates a 64-bit general-purpose register. 77 template<typename... Args> 78 ASMJIT_INLINE_NODEBUG Gp new_gpz(Args&&... args) { return new_reg<Gp>(TypeId::kUIntPtr, std::forward<Args>(args)...); } 79 80 //! Creates a new 32-bit or 64-bit general purpose register depending on the target register width. 81 //! 82 //! \note This is a convenience function, on aarch64 target it always creates a 64-bit general-purpose register. 83 template<typename... Args> 84 ASMJIT_INLINE_NODEBUG Gp new_gp_ptr(Args&&... args) { return new_reg<Gp>(TypeId::kUIntPtr, std::forward<Args>(args)...); } 85 86 //! Creates a new 128-bit vector register. 87 template<typename... Args> 88 ASMJIT_INLINE_NODEBUG Vec new_vec128(Args&&... args) { return new_reg<Vec>(TypeId::kInt32x4, std::forward<Args>(args)...); } 89 90 //! Creates a new 128-bit vector register that will be used for scalar 32-bit floating point operation. 91 template<typename... Args> 92 ASMJIT_INLINE_NODEBUG Vec new_vec128_f32x1(Args&&... args) { return new_reg<Vec>(TypeId::kFloat32x1, std::forward<Args>(args)...); } 93 94 //! Creates a new 128-bit vector register that will be used for scalar 64-bit floating point operation. 95 template<typename... Args> 96 ASMJIT_INLINE_NODEBUG Vec new_vec128_f64x1(Args&&... args) { return new_reg<Vec>(TypeId::kFloat64x1, std::forward<Args>(args)...); } 97 98 //! Creates a new 128-bit vector register that will be used for packed 32-bit floating point operation. 99 template<typename... Args> 100 ASMJIT_INLINE_NODEBUG Vec new_vec128_f32x4(Args&&... args) { return new_reg<Vec>(TypeId::kFloat32x4, std::forward<Args>(args)...); } 101 102 //! Creates a new 128-bit vector register that will be used for packed 64-bit floating point operation. 103 template<typename... Args> 104 ASMJIT_INLINE_NODEBUG Vec new_vec128_f64x2(Args&&... args) { return new_reg<Vec>(TypeId::kFloat64x2, std::forward<Args>(args)...); } 105 106 //! Creates a new 32-bit vector register (S). 107 //! 108 //! \note This may look like an alias of \ref new_vec128_f32x1(), but it's not. This really creates a 32-bit 109 //! register, which has a type \ref RegType::kVec32, whereas \ref new_vec128_f32x1() creates a register, 110 //! which has a type \ref RegType::kVec64 111 template<typename... Args> 112 ASMJIT_INLINE_NODEBUG Vec new_vec_s(Args&&... args) { return new_reg<Vec>(TypeId::kFloat32, std::forward<Args>(args)...); } 113 114 //! Alias of \ref new_vec128_f64x1() that matches aarch64 architecture terminology. 115 template<typename... Args> 116 ASMJIT_INLINE_NODEBUG Vec new_vec_d(Args&&... args) { return new_reg<Vec>(TypeId::kFloat64, std::forward<Args>(args)...); } 117 118 //! Alias of \ref new_vec128() that matches aarch64 architecture terminology. 119 template<typename... Args> 120 ASMJIT_INLINE_NODEBUG Vec new_vec_q(Args&&... args) { return new_reg<Vec>(TypeId::kInt32x4, std::forward<Args>(args)...); } 121 122 //! \} 123 124 //! \name Stack 125 //! \{ 126 127 //! Creates a new stack and returns a \ref Mem operand that can be used to address it. 128 ASMJIT_INLINE_NODEBUG Mem new_stack(uint32_t size, uint32_t alignment, const char* name = nullptr) { 129 Mem m(Globals::NoInit); 130 _new_stack(Out<BaseMem>(m), size, alignment, name); 131 return m; 132 } 133 134 //! \} 135 136 //! \name Constants 137 //! \{ 138 139 //! Put data to a constant-pool and get a memory reference to it. 140 ASMJIT_INLINE_NODEBUG Mem new_const(ConstPoolScope scope, const void* data, size_t size) { 141 Mem m(Globals::NoInit); 142 _new_const(Out<BaseMem>(m), scope, data, size); 143 return m; 144 } 145 146 //! Put a BYTE `val` to a constant-pool (8 bits). 147 ASMJIT_INLINE_NODEBUG Mem new_byte_const(ConstPoolScope scope, uint8_t val) noexcept { return new_const(scope, &val, 1); } 148 //! Put a HWORD `val` to a constant-pool (16 bits). 149 ASMJIT_INLINE_NODEBUG Mem new_half_const(ConstPoolScope scope, uint16_t val) noexcept { return new_const(scope, &val, 2); } 150 //! Put a WORD `val` to a constant-pool (32 bits). 151 ASMJIT_INLINE_NODEBUG Mem new_word_const(ConstPoolScope scope, uint32_t val) noexcept { return new_const(scope, &val, 4); } 152 //! Put a DWORD `val` to a constant-pool (64 bits). 153 ASMJIT_INLINE_NODEBUG Mem new_dword_const(ConstPoolScope scope, uint64_t val) noexcept { return new_const(scope, &val, 8); } 154 155 //! Put a WORD `val` to a constant-pool. 156 ASMJIT_INLINE_NODEBUG Mem new_int16_const(ConstPoolScope scope, int16_t val) noexcept { return new_const(scope, &val, 2); } 157 //! Put a WORD `val` to a constant-pool. 158 ASMJIT_INLINE_NODEBUG Mem new_uint16_const(ConstPoolScope scope, uint16_t val) noexcept { return new_const(scope, &val, 2); } 159 //! Put a DWORD `val` to a constant-pool. 160 ASMJIT_INLINE_NODEBUG Mem new_int32_const(ConstPoolScope scope, int32_t val) noexcept { return new_const(scope, &val, 4); } 161 //! Put a DWORD `val` to a constant-pool. 162 ASMJIT_INLINE_NODEBUG Mem new_uint32_const(ConstPoolScope scope, uint32_t val) noexcept { return new_const(scope, &val, 4); } 163 //! Put a QWORD `val` to a constant-pool. 164 ASMJIT_INLINE_NODEBUG Mem new_int64_const(ConstPoolScope scope, int64_t val) noexcept { return new_const(scope, &val, 8); } 165 //! Put a QWORD `val` to a constant-pool. 166 ASMJIT_INLINE_NODEBUG Mem new_uint64_const(ConstPoolScope scope, uint64_t val) noexcept { return new_const(scope, &val, 8); } 167 168 //! Put a SP-FP `val` to a constant-pool. 169 ASMJIT_INLINE_NODEBUG Mem new_float_const(ConstPoolScope scope, float val) noexcept { return new_const(scope, &val, 4); } 170 //! Put a DP-FP `val` to a constant-pool. 171 ASMJIT_INLINE_NODEBUG Mem new_double_const(ConstPoolScope scope, double val) noexcept { return new_const(scope, &val, 8); } 172 173 //! \} 174 175 //! \name Instruction Options 176 //! \{ 177 178 //! Force the compiler to not follow the conditional or unconditional jump. 179 ASMJIT_INLINE_NODEBUG Compiler& unfollow() noexcept { _inst_options |= InstOptions::kUnfollow; return *this; } 180 181 //! \} 182 183 //! \name Compiler specific 184 //! \{ 185 186 //! Special pseudo-instruction that can be used to load a memory address into `o0` GP register. 187 //! 188 //! \note At the moment this instruction is only useful to load a stack allocated address into a GP register 189 //! for further use. It makes very little sense to use it for anything else. The semantics of this instruction 190 //! is the same as X86 `LEA` (load effective address) instruction. 191 ASMJIT_INLINE_NODEBUG Error load_address_of(const Gp& o0, const Mem& o1) { return _emitter()->_emitI(Inst::kIdAdr, o0, o1); } 192 193 //! \} 194 195 //! \name Function Call & Ret Intrinsics 196 //! \{ 197 198 //! Invoke a function call without `target` type enforcement. 199 ASMJIT_INLINE_NODEBUG Error invoke_(Out<InvokeNode*> out, const Operand_& target, const FuncSignature& signature) { 200 return add_invoke_node(out, Inst::kIdBlr, target, signature); 201 } 202 203 //! Invoke a function call of the given `target` and `signature` and store the added node to `out`. 204 //! 205 //! Creates a new \ref InvokeNode, initializes all the necessary members to match the given function `signature`, 206 //! adds the node to the compiler, and stores its pointer to `out`. The operation is atomic, if anything fails 207 //! nullptr is stored in `out` and error code is returned. 208 ASMJIT_INLINE_NODEBUG Error invoke(Out<InvokeNode*> out, const Gp& target, const FuncSignature& signature) { return invoke_(out, target, signature); } 209 //! \overload 210 ASMJIT_INLINE_NODEBUG Error invoke(Out<InvokeNode*> out, const Mem& target, const FuncSignature& signature) { return invoke_(out, target, signature); } 211 //! \overload 212 ASMJIT_INLINE_NODEBUG Error invoke(Out<InvokeNode*> out, const Label& target, const FuncSignature& signature) { return invoke_(out, target, signature); } 213 //! \overload 214 ASMJIT_INLINE_NODEBUG Error invoke(Out<InvokeNode*> out, const Imm& target, const FuncSignature& signature) { return invoke_(out, target, signature); } 215 //! \overload 216 ASMJIT_INLINE_NODEBUG Error invoke(Out<InvokeNode*> out, uint64_t target, const FuncSignature& signature) { return invoke_(out, Imm(int64_t(target)), signature); } 217 218 //! Return from function. 219 //! 220 //! \note This doesn't end the function - it just emits a return. 221 ASMJIT_INLINE_NODEBUG Error ret() { return add_ret(Operand(), Operand()); } 222 223 //! Return from function - one value. 224 //! 225 //! \note This doesn't end the function - it just emits a return. 226 ASMJIT_INLINE_NODEBUG Error ret(const Reg& o0) { return add_ret(o0, Operand()); } 227 228 //! Return from function - two values / register pair. 229 //! 230 //! \note This doesn't end the function - it just emits a return. 231 ASMJIT_INLINE_NODEBUG Error ret(const Reg& o0, const Reg& o1) { return add_ret(o0, o1); } 232 233 //! \} 234 235 //! \name Jump Tables Support 236 //! \{ 237 238 using EmitterExplicitT<Compiler>::br; 239 240 //! Adds a jump to the given `target` with the provided jump `annotation`. 241 ASMJIT_INLINE_NODEBUG Error br(const Reg& target, JumpAnnotation* annotation) { return emit_annotated_jump(Inst::kIdBr, target, annotation); } 242 243 //! \} 244 245 //! \name Events 246 //! \{ 247 248 ASMJIT_API Error on_attach(CodeHolder& code) noexcept override; 249 ASMJIT_API Error on_detach(CodeHolder& code) noexcept override; 250 ASMJIT_API Error on_reinit(CodeHolder& code) noexcept override; 251 252 //! \} 253 254 //! \name Finalize 255 //! \{ 256 257 ASMJIT_API Error finalize() override; 258 259 //! \} 260 }; 261 262 //! \} 263 264 ASMJIT_END_SUB_NAMESPACE 265 266 #endif // !ASMJIT_NO_COMPILER 267 #endif // ASMJIT_ARM_A64COMPILER_H_INCLUDED