Line data Source code
1 : #ifdef __PLUMED_HAS_ASMJIT
2 : // This file is part of AsmJit project <https://asmjit.com>
3 : //
4 : // See asmjit.h or LICENSE.md for license and copyright information
5 : // SPDX-License-Identifier: Zlib
6 :
7 : #ifndef ASMJIT_CORE_FUNC_H_INCLUDED
8 : #define ASMJIT_CORE_FUNC_H_INCLUDED
9 :
10 : #include "archtraits.h"
11 : #include "environment.h"
12 : #include "operand.h"
13 : #include "type.h"
14 : #include "support.h"
15 :
16 : ASMJIT_BEGIN_NAMESPACE
17 :
18 : //! \addtogroup asmjit_function
19 : //! \{
20 :
21 : //! Calling convention id.
22 : //!
23 : //! Calling conventions can be divided into the following groups:
24 : //!
25 : //! - Universal - calling conventions are applicable to any target. They will be converted to a target dependent
26 : //! calling convention at runtime by \ref CallConv::init() with some help from \ref Environment. The purpose of
27 : //! these calling conventions is to make using functions less target dependent and closer to C and C++.
28 : //!
29 : //! - Target specific - calling conventions that are used by a particular architecture and ABI. For example
30 : //! Windows 64-bit calling convention and AMD64 SystemV calling convention.
31 : enum class CallConvId : uint8_t {
32 : //! None or invalid (can't be used).
33 : kNone = 0,
34 :
35 : // Universal Calling Conventions
36 : // -----------------------------
37 :
38 : //! Standard function call or explicit `__cdecl` where it can be specified.
39 : //!
40 : //! This is a universal calling convention, which is used to initialize specific calling connventions based on
41 : //! architecture, platform, and its ABI.
42 : kCDecl = 1,
43 :
44 : //! `__stdcall` on targets that support this calling convention (X86).
45 : //!
46 : //! \note This calling convention is only supported on 32-bit X86. If used on environment that doesn't support
47 : //! this calling convention it will be replaced by \ref CallConvId::kCDecl.
48 : kStdCall = 2,
49 :
50 : //! `__fastcall` on targets that support this calling convention (X86).
51 : //!
52 : //! \note This calling convention is only supported on 32-bit X86. If used on environment that doesn't support
53 : //! this calling convention it will be replaced by \ref CallConvId::kCDecl.
54 : kFastCall = 3,
55 :
56 : //! `__vectorcall` on targets that support this calling convention (X86/X64).
57 : //!
58 : //! \note This calling convention is only supported on 32-bit and 64-bit X86 architecture on Windows platform.
59 : //! If used on environment that doesn't support this calling it will be replaced by \ref CallConvId::kCDecl.
60 : kVectorCall = 4,
61 :
62 : //! `__thiscall` on targets that support this calling convention (X86).
63 : //!
64 : //! \note This calling convention is only supported on 32-bit X86 Windows platform. If used on environment that
65 : //! doesn't support this calling convention it will be replaced by \ref CallConvId::kCDecl.
66 : kThisCall = 5,
67 :
68 : //! `__attribute__((regparm(1)))` convention (GCC and Clang).
69 : kRegParm1 = 6,
70 : //! `__attribute__((regparm(2)))` convention (GCC and Clang).
71 : kRegParm2 = 7,
72 : //! `__attribute__((regparm(3)))` convention (GCC and Clang).
73 : kRegParm3 = 8,
74 :
75 : //! Soft-float calling convention (ARM).
76 : //!
77 : //! Floating point arguments are passed via general purpose registers.
78 : kSoftFloat = 9,
79 :
80 : //! Hard-float calling convention (ARM).
81 : //!
82 : //! Floating point arguments are passed via SIMD registers.
83 : kHardFloat = 10,
84 :
85 : //! AsmJit specific calling convention designed for calling functions inside a multimedia code that don't use many
86 : //! registers internally, but are long enough to be called and not inlined. These functions are usually used to
87 : //! calculate trigonometric functions, logarithms, etc...
88 : kLightCall2 = 16,
89 : kLightCall3 = 17,
90 : kLightCall4 = 18,
91 :
92 : // ABI-Specific Calling Conventions
93 : // --------------------------------
94 :
95 : //! X64 System-V calling convention.
96 : kX64SystemV = 32,
97 : //! X64 Windows calling convention.
98 : kX64Windows = 33,
99 :
100 : //! Maximum value of `CallConvId`.
101 : kMaxValue = kX64Windows,
102 :
103 : // Host Calling Conventions
104 : // ------------------------
105 :
106 : //! Host calling convention detected at compile-time.
107 : kHost =
108 : #if defined(_DOXYGEN)
109 : DETECTED_AT_COMPILE_TIME
110 : #elif ASMJIT_ARCH_ARM == 32 && defined(__SOFTFP__)
111 : kSoftFloat
112 : #elif ASMJIT_ARCH_ARM == 32 && !defined(__SOFTFP__)
113 : kHardFloat
114 : #else
115 : kCDecl
116 : #endif
117 : };
118 :
119 : //! Strategy used by calling conventions to assign registers to function arguments.
120 : //!
121 : //! Calling convention strategy describes how AsmJit should convert function arguments used by \ref FuncSignature
122 : //! into register identifiers and stack offsets. The \ref CallConvStrategy::kDefault strategy assigns registers
123 : //! and then stack whereas \ref CallConvStrategy::kX64Windows strategy does register shadowing as defined by WIN64
124 : //! calling convention, which is only used by 64-bit Windows.
125 : enum class CallConvStrategy : uint8_t {
126 : //! Default register assignment strategy.
127 : kDefault = 0,
128 : //! Windows 64-bit ABI register assignment strategy.
129 : kX64Windows = 1,
130 : //! Windows 64-bit __vectorcall register assignment strategy.
131 : kX64VectorCall = 2,
132 :
133 : //! Maximum value of `CallConvStrategy`.
134 : kMaxValue = kX64VectorCall
135 : };
136 :
137 : //! Calling convention flags.
138 : enum class CallConvFlags : uint32_t {
139 : //! No flags.
140 : kNone = 0,
141 : //! Callee is responsible for cleaning up the stack.
142 : kCalleePopsStack = 0x0001u,
143 : //! Pass vector arguments indirectly (as a pointer).
144 : kIndirectVecArgs = 0x0002u,
145 : //! Pass F32 and F64 arguments via VEC128 register.
146 : kPassFloatsByVec = 0x0004u,
147 : //! Pass MMX and vector arguments via stack if the function has variable arguments.
148 : kPassVecByStackIfVA = 0x0008u,
149 : //! MMX registers are passed and returned via GP registers.
150 : kPassMmxByGp = 0x0010u,
151 : //! MMX registers are passed and returned via XMM registers.
152 : kPassMmxByXmm = 0x0020u,
153 : //! Calling convention can be used with variable arguments.
154 : kVarArgCompatible = 0x0080u
155 : };
156 0 : ASMJIT_DEFINE_ENUM_FLAGS(CallConvFlags)
157 :
158 : //! Function calling convention.
159 : //!
160 : //! Function calling convention is a scheme that defines how function parameters are passed and how function
161 : //! returns its result. AsmJit defines a variety of architecture and OS specific calling conventions and also
162 : //! provides a compile time detection to make the code-generation easier.
163 : struct CallConv {
164 : //! \name Constants
165 : //! \{
166 :
167 : enum : uint32_t {
168 : //! Maximum number of register arguments per register group.
169 : //!
170 : //! \note This is not really AsmJit's limitatation, it's just the number that makes sense considering all common
171 : //! calling conventions. Usually even conventions that use registers to pass function arguments are limited to 8
172 : //! and less arguments passed via registers per group.
173 : kMaxRegArgsPerGroup = 16
174 : };
175 :
176 : //! \}
177 :
178 : //! \name Members
179 : //! \{
180 :
181 : //! Target architecture.
182 : Arch _arch;
183 : //! Calling convention id.
184 : CallConvId _id;
185 : //! Register assignment strategy.
186 : CallConvStrategy _strategy;
187 :
188 : //! Red zone size (AMD64 == 128 bytes).
189 : uint8_t _redZoneSize;
190 : //! Spill zone size (WIN-X64 == 32 bytes).
191 : uint8_t _spillZoneSize;
192 : //! Natural stack alignment as defined by OS/ABI.
193 : uint8_t _naturalStackAlignment;
194 :
195 : //! Calling convention flags.
196 : CallConvFlags _flags;
197 :
198 : //! Size to save/restore per register group.
199 : Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreRegSize;
200 : //! Alignment of save/restore groups.
201 : Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreAlignment;
202 :
203 : //! Mask of all passed registers, per group.
204 : Support::Array<RegMask, Globals::kNumVirtGroups> _passedRegs;
205 : //! Mask of all preserved registers, per group.
206 : Support::Array<RegMask, Globals::kNumVirtGroups> _preservedRegs;
207 :
208 : //! Passed registers' order.
209 : union RegOrder {
210 : //! Passed registers, ordered.
211 : uint8_t id[kMaxRegArgsPerGroup];
212 : //! Packed IDs in `uint32_t` array.
213 : uint32_t packed[(kMaxRegArgsPerGroup + 3) / 4];
214 : };
215 :
216 : //! Passed registers' order, per register group.
217 : Support::Array<RegOrder, Globals::kNumVirtGroups> _passedOrder;
218 :
219 : //! \}
220 :
221 : //! \name Construction & Destruction
222 : //! \{
223 :
224 : //! Initializes this calling convention to the given `ccId` based on the `environment`.
225 : //!
226 : //! See \ref CallConvId and \ref Environment for more details.
227 : ASMJIT_API Error init(CallConvId ccId, const Environment& environment) noexcept;
228 :
229 : //! Resets this CallConv struct into a defined state.
230 : //!
231 : //! It's recommended to reset the \ref CallConv struct in case you would like create a custom calling convention
232 : //! as it prevents from using an uninitialized data (CallConv doesn't have a constructor that would initialize it,
233 : //! it's just a struct).
234 37131 : inline void reset() noexcept {
235 : memset(this, 0, sizeof(*this));
236 : memset(_passedOrder.data(), 0xFF, sizeof(_passedOrder));
237 37131 : }
238 :
239 : //! \}
240 :
241 : //! \name Accessors
242 : //! \{
243 :
244 : //! Returns the target architecture of this calling convention.
245 101942 : inline Arch arch() const noexcept { return _arch; }
246 : //! Sets the target architecture of this calling convention.
247 37131 : inline void setArch(Arch arch) noexcept { _arch = arch; }
248 :
249 : //! Returns the calling convention id.
250 0 : inline CallConvId id() const noexcept { return _id; }
251 : //! Sets the calling convention id.
252 37131 : inline void setId(CallConvId ccId) noexcept { _id = ccId; }
253 :
254 : //! Returns the strategy used to assign registers to arguments.
255 37131 : inline CallConvStrategy strategy() const noexcept { return _strategy; }
256 : //! Sets the strategy used to assign registers to arguments.
257 0 : inline void setStrategy(CallConvStrategy ccStrategy) noexcept { _strategy = ccStrategy; }
258 :
259 : //! Tests whether the calling convention has the given `flag` set.
260 46816 : inline bool hasFlag(CallConvFlags flag) const noexcept { return Support::test(_flags, flag); }
261 : //! Returns the calling convention flags, see `Flags`.
262 : inline CallConvFlags flags() const noexcept { return _flags; }
263 : //! Adds the calling convention flags, see `Flags`.
264 0 : inline void setFlags(CallConvFlags flag) noexcept { _flags = flag; };
265 : //! Adds the calling convention flags, see `Flags`.
266 0 : inline void addFlags(CallConvFlags flags) noexcept { _flags |= flags; };
267 :
268 : //! Tests whether this calling convention specifies 'RedZone'.
269 : inline bool hasRedZone() const noexcept { return _redZoneSize != 0; }
270 : //! Tests whether this calling convention specifies 'SpillZone'.
271 : inline bool hasSpillZone() const noexcept { return _spillZoneSize != 0; }
272 :
273 : //! Returns size of 'RedZone'.
274 27492 : inline uint32_t redZoneSize() const noexcept { return _redZoneSize; }
275 : //! Returns size of 'SpillZone'.
276 27492 : inline uint32_t spillZoneSize() const noexcept { return _spillZoneSize; }
277 :
278 : //! Sets size of 'RedZone'.
279 37131 : inline void setRedZoneSize(uint32_t size) noexcept { _redZoneSize = uint8_t(size); }
280 : //! Sets size of 'SpillZone'.
281 0 : inline void setSpillZoneSize(uint32_t size) noexcept { _spillZoneSize = uint8_t(size); }
282 :
283 : //! Returns a natural stack alignment.
284 54984 : inline uint32_t naturalStackAlignment() const noexcept { return _naturalStackAlignment; }
285 : //! Sets a natural stack alignment.
286 : //!
287 : //! This function can be used to override the default stack alignment in case that you know that it's alignment is
288 : //! different. For example it allows to implement custom calling conventions that guarantee higher stack alignment.
289 0 : inline void setNaturalStackAlignment(uint32_t value) noexcept { _naturalStackAlignment = uint8_t(value); }
290 :
291 : //! Returns the size of a register (or its part) to be saved and restored of the given `group`.
292 0 : inline uint32_t saveRestoreRegSize(RegGroup group) const noexcept { return _saveRestoreRegSize[group]; }
293 : //! Sets the size of a vector register (or its part) to be saved and restored.
294 74262 : inline void setSaveRestoreRegSize(RegGroup group, uint32_t size) noexcept { _saveRestoreRegSize[group] = uint8_t(size); }
295 :
296 : //! Returns the alignment of a save-restore area of the given `group`.
297 : inline uint32_t saveRestoreAlignment(RegGroup group) const noexcept { return _saveRestoreAlignment[group]; }
298 : //! Sets the alignment of a save-restore area of the given `group`.
299 37131 : inline void setSaveRestoreAlignment(RegGroup group, uint32_t alignment) noexcept { _saveRestoreAlignment[group] = uint8_t(alignment); }
300 :
301 : //! Returns the order of passed registers of the given `group`.
302 : inline const uint8_t* passedOrder(RegGroup group) const noexcept {
303 : ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
304 0 : return _passedOrder[size_t(group)].id;
305 : }
306 :
307 : //! Returns the mask of passed registers of the given `group`.
308 : inline RegMask passedRegs(RegGroup group) const noexcept {
309 : ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
310 : return _passedRegs[size_t(group)];
311 : }
312 :
313 : inline void _setPassedPacked(RegGroup group, uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3) noexcept {
314 : ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
315 :
316 37131 : _passedOrder[group].packed[0] = p0;
317 37131 : _passedOrder[group].packed[1] = p1;
318 37131 : _passedOrder[group].packed[2] = p2;
319 37131 : _passedOrder[group].packed[3] = p3;
320 : }
321 :
322 : //! Resets the order and mask of passed registers.
323 : inline void setPassedToNone(RegGroup group) noexcept {
324 : ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
325 :
326 : _setPassedPacked(group, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu);
327 : _passedRegs[size_t(group)] = 0u;
328 : }
329 :
330 : //! Sets the order and mask of passed registers.
331 : inline void setPassedOrder(RegGroup group, uint32_t a0, uint32_t a1 = 0xFF, uint32_t a2 = 0xFF, uint32_t a3 = 0xFF, uint32_t a4 = 0xFF, uint32_t a5 = 0xFF, uint32_t a6 = 0xFF, uint32_t a7 = 0xFF) noexcept {
332 : ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
333 :
334 : // NOTE: This should always be called with all arguments known at compile time, so even if it looks scary it
335 : // should be translated into few instructions.
336 : _setPassedPacked(group, Support::bytepack32_4x8(a0, a1, a2, a3),
337 : Support::bytepack32_4x8(a4, a5, a6, a7),
338 : 0xFFFFFFFFu,
339 : 0xFFFFFFFFu);
340 :
341 0 : _passedRegs[group] = (a0 != 0xFF ? 1u << a0 : 0u) |
342 : (a1 != 0xFF ? 1u << a1 : 0u) |
343 : (a2 != 0xFF ? 1u << a2 : 0u) |
344 : (a3 != 0xFF ? 1u << a3 : 0u) |
345 : (a4 != 0xFF ? 1u << a4 : 0u) |
346 : (a5 != 0xFF ? 1u << a5 : 0u) |
347 : (a6 != 0xFF ? 1u << a6 : 0u) |
348 : (a7 != 0xFF ? 1u << a7 : 0u) ;
349 : }
350 :
351 : //! Returns preserved register mask of the given `group`.
352 : inline RegMask preservedRegs(RegGroup group) const noexcept {
353 : ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
354 158163 : return _preservedRegs[group];
355 : }
356 :
357 : //! Sets preserved register mask of the given `group`.
358 : inline void setPreservedRegs(RegGroup group, RegMask regs) noexcept {
359 : ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
360 0 : _preservedRegs[group] = regs;
361 37131 : }
362 :
363 : //! \}
364 : };
365 :
366 : //! Function signature.
367 : //!
368 : //! Contains information about function return type, count of arguments and their TypeIds. Function signature is
369 : //! a low level structure which doesn't contain platform specific or calling convention specific information.
370 : struct FuncSignature {
371 : //! \name Constants
372 : //! \{
373 :
374 : enum : uint8_t {
375 : //! Doesn't have variable number of arguments (`...`).
376 : kNoVarArgs = 0xFFu
377 : };
378 :
379 : //! \}
380 :
381 : //! \name Members
382 : //! \{
383 :
384 : //! Calling convention id.
385 : CallConvId _ccId;
386 : //! Count of arguments.
387 : uint8_t _argCount;
388 : //! Index of a first VA or `kNoVarArgs`.
389 : uint8_t _vaIndex;
390 : //! Return value TypeId.
391 : TypeId _ret;
392 : //! Function arguments TypeIds.
393 : const TypeId* _args;
394 :
395 : //! \}
396 :
397 : //! \name Initializtion & Reset
398 : //! \{
399 :
400 : //! Initializes the function signature.
401 : inline void init(CallConvId ccId, uint32_t vaIndex, TypeId ret, const TypeId* args, uint32_t argCount) noexcept {
402 : ASMJIT_ASSERT(argCount <= 0xFF);
403 :
404 37131 : _ccId = ccId;
405 37131 : _argCount = uint8_t(argCount);
406 37131 : _vaIndex = uint8_t(vaIndex);
407 37131 : _ret = ret;
408 37131 : _args = args;
409 : }
410 :
411 : inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
412 :
413 : //! \}
414 :
415 : //! \name Accessors
416 : //! \{
417 :
418 : //! Returns the calling convention.
419 37131 : inline CallConvId callConvId() const noexcept { return _ccId; }
420 : //! Sets the calling convention to `ccId`;
421 : inline void setCallConvId(CallConvId ccId) noexcept { _ccId = ccId; }
422 :
423 : //! Tests whether the function has variable number of arguments (...).
424 0 : inline bool hasVarArgs() const noexcept { return _vaIndex != kNoVarArgs; }
425 : //! Returns the variable arguments (...) index, `kNoVarArgs` if none.
426 37131 : inline uint32_t vaIndex() const noexcept { return _vaIndex; }
427 : //! Sets the variable arguments (...) index to `index`.
428 : inline void setVaIndex(uint32_t index) noexcept { _vaIndex = uint8_t(index); }
429 : //! Resets the variable arguments index (making it a non-va function).
430 : inline void resetVaIndex() noexcept { _vaIndex = kNoVarArgs; }
431 :
432 : //! Returns the number of function arguments.
433 46770 : inline uint32_t argCount() const noexcept { return _argCount; }
434 :
435 : inline bool hasRet() const noexcept { return _ret != TypeId::kVoid; }
436 : //! Returns the return value type.
437 37131 : inline TypeId ret() const noexcept { return _ret; }
438 :
439 : //! Returns the type of the argument at index `i`.
440 : inline TypeId arg(uint32_t i) const noexcept {
441 : ASMJIT_ASSERT(i < _argCount);
442 : return _args[i];
443 : }
444 : //! Returns the array of function arguments' types.
445 37131 : inline const TypeId* args() const noexcept { return _args; }
446 :
447 : //! \}
448 : };
449 :
450 : template<typename... RET_ARGS>
451 : class FuncSignatureT : public FuncSignature {
452 : public:
453 37131 : inline FuncSignatureT(CallConvId ccId = CallConvId::kHost, uint32_t vaIndex = kNoVarArgs) noexcept {
454 : static constexpr TypeId ret_args[] = { (TypeId(TypeUtils::TypeIdOfT<RET_ARGS>::kTypeId))... };
455 : init(ccId, vaIndex, ret_args[0], ret_args + 1, uint32_t(ASMJIT_ARRAY_SIZE(ret_args) - 1));
456 : }
457 : };
458 :
459 : //! Function signature builder.
460 : class FuncSignatureBuilder : public FuncSignature {
461 : public:
462 : TypeId _builderArgList[Globals::kMaxFuncArgs];
463 :
464 : //! \name Initializtion & Reset
465 : //! \{
466 :
467 : inline FuncSignatureBuilder(CallConvId ccId = CallConvId::kHost, uint32_t vaIndex = kNoVarArgs) noexcept {
468 : init(ccId, vaIndex, TypeId::kVoid, _builderArgList, 0);
469 : }
470 :
471 : //! \}
472 :
473 : //! \name Accessors
474 : //! \{
475 :
476 : //! Sets the return type to `retType`.
477 : inline void setRet(TypeId retType) noexcept { _ret = retType; }
478 : //! Sets the return type based on `T`.
479 : template<typename T>
480 : inline void setRetT() noexcept { setRet(TypeId(TypeUtils::TypeIdOfT<T>::kTypeId)); }
481 :
482 : //! Sets the argument at index `index` to `argType`.
483 : inline void setArg(uint32_t index, TypeId argType) noexcept {
484 : ASMJIT_ASSERT(index < _argCount);
485 : _builderArgList[index] = argType;
486 : }
487 : //! Sets the argument at index `i` to the type based on `T`.
488 : template<typename T>
489 : inline void setArgT(uint32_t index) noexcept { setArg(index, TypeId(TypeUtils::TypeIdOfT<T>::kTypeId)); }
490 :
491 : //! Appends an argument of `type` to the function prototype.
492 : inline void addArg(TypeId type) noexcept {
493 : ASMJIT_ASSERT(_argCount < Globals::kMaxFuncArgs);
494 : _builderArgList[_argCount++] = type;
495 : }
496 : //! Appends an argument of type based on `T` to the function prototype.
497 : template<typename T>
498 : inline void addArgT() noexcept { addArg(TypeId(TypeUtils::TypeIdOfT<T>::kTypeId)); }
499 :
500 : //! \}
501 : };
502 :
503 : //! Argument or return value (or its part) as defined by `FuncSignature`, but with register or stack address
504 : //! (and other metadata) assigned.
505 : struct FuncValue {
506 : //! \name Constants
507 : //! \{
508 :
509 : enum Bits : uint32_t {
510 : kTypeIdShift = 0, //!< TypeId shift.
511 : kTypeIdMask = 0x000000FFu, //!< TypeId mask.
512 :
513 : kFlagIsReg = 0x00000100u, //!< Passed by register.
514 : kFlagIsStack = 0x00000200u, //!< Passed by stack.
515 : kFlagIsIndirect = 0x00000400u, //!< Passed indirectly by reference (internally a pointer).
516 : kFlagIsDone = 0x00000800u, //!< Used internally by arguments allocator.
517 :
518 : kStackOffsetShift = 12, //!< Stack offset shift.
519 : kStackOffsetMask = 0xFFFFF000u, //!< Stack offset mask (must occupy MSB bits).
520 :
521 : kRegIdShift = 16, //!< RegId shift.
522 : kRegIdMask = 0x00FF0000u, //!< RegId mask.
523 :
524 : kRegTypeShift = 24, //!< RegType shift.
525 : kRegTypeMask = 0xFF000000u //!< RegType mask.
526 : };
527 :
528 : //! \}
529 :
530 : //! \name Members
531 : //! \{
532 :
533 : uint32_t _data;
534 :
535 : //! \}
536 :
537 : //! \name Initializtion & Reset
538 : //!
539 : //! These initialize the whole `FuncValue` to either register or stack. Useful when you know all of these
540 : //! properties and wanna just set it up.
541 : //!
542 : //! \{
543 :
544 : //! Initializes the `typeId` of this `FuncValue`.
545 : inline void initTypeId(TypeId typeId) noexcept {
546 19558 : _data = uint32_t(typeId) << kTypeIdShift;
547 37131 : }
548 :
549 37131 : inline void initReg(RegType regType, uint32_t regId, TypeId typeId, uint32_t flags = 0) noexcept {
550 37131 : _data = (uint32_t(regType) << kRegTypeShift) | (regId << kRegIdShift) | (uint32_t(typeId) << kTypeIdShift) | kFlagIsReg | flags;
551 0 : }
552 :
553 : inline void initStack(int32_t offset, TypeId typeId) noexcept {
554 : _data = (uint32_t(offset) << kStackOffsetShift) | (uint32_t(typeId) << kTypeIdShift) | kFlagIsStack;
555 : }
556 :
557 : //! Resets the value to its unassigned state.
558 0 : inline void reset() noexcept { _data = 0; }
559 :
560 : //! \}
561 :
562 : //! \name Assign
563 : //!
564 : //! These initialize only part of `FuncValue`, useful when building `FuncValue` incrementally. The caller
565 : //! should first init the type-id by caliing `initTypeId` and then continue building either register or stack.
566 : //!
567 : //! \{
568 :
569 9873 : inline void assignRegData(RegType regType, uint32_t regId) noexcept {
570 : ASMJIT_ASSERT((_data & (kRegTypeMask | kRegIdMask)) == 0);
571 9873 : _data |= (uint32_t(regType) << kRegTypeShift) | (regId << kRegIdShift) | kFlagIsReg;
572 9873 : }
573 :
574 0 : inline void assignStackOffset(int32_t offset) noexcept {
575 : ASMJIT_ASSERT((_data & kStackOffsetMask) == 0);
576 0 : _data |= (uint32_t(offset) << kStackOffsetShift) | kFlagIsStack;
577 0 : }
578 :
579 : //! \}
580 :
581 : //! \name Accessors
582 : //! \{
583 :
584 : //! Returns true if the value is initialized (explicit bool cast).
585 164310 : inline explicit operator bool() const noexcept { return _data != 0; }
586 :
587 0 : inline void _replaceValue(uint32_t mask, uint32_t value) noexcept { _data = (_data & ~mask) | value; }
588 :
589 : //! Tests whether the `FuncValue` has a flag `flag` set.
590 3585304 : inline bool hasFlag(uint32_t flag) const noexcept { return Support::test(_data, flag); }
591 : //! Adds `flags` to `FuncValue`.
592 0 : inline void addFlags(uint32_t flags) noexcept { _data |= flags; }
593 : //! Clears `flags` of `FuncValue`.
594 : inline void clearFlags(uint32_t flags) noexcept { _data &= ~flags; }
595 :
596 : //! Tests whether the value is initialized (i.e. contains a valid data).
597 0 : inline bool isInitialized() const noexcept { return _data != 0; }
598 : //! Tests whether the argument is passed by register.
599 : inline bool isReg() const noexcept { return hasFlag(kFlagIsReg); }
600 : //! Tests whether the argument is passed by stack.
601 : inline bool isStack() const noexcept { return hasFlag(kFlagIsStack); }
602 : //! Tests whether the argument is passed by register.
603 : inline bool isAssigned() const noexcept { return hasFlag(kFlagIsReg | kFlagIsStack); }
604 : //! Tests whether the argument is passed through a pointer (used by WIN64 to pass XMM|YMM|ZMM).
605 : inline bool isIndirect() const noexcept { return hasFlag(kFlagIsIndirect); }
606 :
607 : //! Tests whether the argument was already processed (used internally).
608 : inline bool isDone() const noexcept { return hasFlag(kFlagIsDone); }
609 :
610 : //! Returns a register type of the register used to pass function argument or return value.
611 103459 : inline RegType regType() const noexcept { return RegType((_data & kRegTypeMask) >> kRegTypeShift); }
612 : //! Sets a register type of the register used to pass function argument or return value.
613 : inline void setRegType(RegType regType) noexcept { _replaceValue(kRegTypeMask, uint32_t(regType) << kRegTypeShift); }
614 :
615 : //! Returns a physical id of the register used to pass function argument or return value.
616 47004 : inline uint32_t regId() const noexcept { return (_data & kRegIdMask) >> kRegIdShift; }
617 : //! Sets a physical id of the register used to pass function argument or return value.
618 0 : inline void setRegId(uint32_t regId) noexcept { _replaceValue(kRegIdMask, regId << kRegIdShift); }
619 :
620 : //! Returns a stack offset of this argument.
621 0 : inline int32_t stackOffset() const noexcept { return int32_t(_data & kStackOffsetMask) >> kStackOffsetShift; }
622 : //! Sets a stack offset of this argument.
623 0 : inline void setStackOffset(int32_t offset) noexcept { _replaceValue(kStackOffsetMask, uint32_t(offset) << kStackOffsetShift); }
624 :
625 : //! Tests whether the argument or return value has associated `TypeId`.
626 0 : inline bool hasTypeId() const noexcept { return Support::test(_data, kTypeIdMask); }
627 : //! Returns a TypeId of this argument or return value.
628 131327 : inline TypeId typeId() const noexcept { return TypeId((_data & kTypeIdMask) >> kTypeIdShift); }
629 : //! Sets a TypeId of this argument or return value.
630 0 : inline void setTypeId(TypeId typeId) noexcept { _replaceValue(kTypeIdMask, uint32_t(typeId) << kTypeIdShift); }
631 :
632 : //! \}
633 : };
634 :
635 : //! Contains multiple `FuncValue` instances in an array so functions that use multiple registers for arguments or
636 : //! return values can represent all inputs and outputs.
637 : struct FuncValuePack {
638 : public:
639 : //! \name Members
640 : //! \{
641 :
642 : //! Values of the pack.
643 : FuncValue _values[Globals::kMaxValuePack];
644 :
645 : //! \}
646 :
647 : //! \name Initialization & Reset
648 : //! \{
649 :
650 : //! Resets all values in the pack.
651 : inline void reset() noexcept {
652 : for (size_t i = 0; i < Globals::kMaxValuePack; i++)
653 : _values[i].reset();
654 : }
655 :
656 : //! \}
657 :
658 : //! \name Accessors
659 : //! \{
660 :
661 : //! Calculates how many values are in the pack, checking for non-values from the end.
662 : inline uint32_t count() const noexcept {
663 : uint32_t n = Globals::kMaxValuePack;
664 0 : while (n && !_values[n - 1])
665 : n--;
666 : return n;
667 : }
668 :
669 : inline FuncValue* values() noexcept { return _values; }
670 : inline const FuncValue* values() const noexcept { return _values; }
671 :
672 : inline void resetValue(size_t index) noexcept {
673 : ASMJIT_ASSERT(index < Globals::kMaxValuePack);
674 : _values[index].reset();
675 : }
676 :
677 : inline bool hasValue(size_t index) noexcept {
678 : ASMJIT_ASSERT(index < Globals::kMaxValuePack);
679 : return _values[index].isInitialized();
680 : }
681 :
682 : inline void assignReg(size_t index, const BaseReg& reg, TypeId typeId = TypeId::kVoid) noexcept {
683 : ASMJIT_ASSERT(index < Globals::kMaxValuePack);
684 : ASMJIT_ASSERT(reg.isPhysReg());
685 : _values[index].initReg(reg.type(), reg.id(), typeId);
686 : }
687 :
688 : inline void assignReg(size_t index, RegType regType, uint32_t regId, TypeId typeId = TypeId::kVoid) noexcept {
689 : ASMJIT_ASSERT(index < Globals::kMaxValuePack);
690 : _values[index].initReg(regType, regId, typeId);
691 : }
692 :
693 : inline void assignStack(size_t index, int32_t offset, TypeId typeId = TypeId::kVoid) noexcept {
694 : ASMJIT_ASSERT(index < Globals::kMaxValuePack);
695 : _values[index].initStack(offset, typeId);
696 : }
697 :
698 : inline FuncValue& operator[](size_t index) {
699 : ASMJIT_ASSERT(index < Globals::kMaxValuePack);
700 56877 : return _values[index];
701 : }
702 :
703 : inline const FuncValue& operator[](size_t index) const {
704 : ASMJIT_ASSERT(index < Globals::kMaxValuePack);
705 9873 : return _values[index];
706 : }
707 :
708 : //! \}
709 : };
710 :
711 : //! Attributes are designed in a way that all are initially false, and user or \ref FuncFrame finalizer adds
712 : //! them when necessary.
713 : enum class FuncAttributes : uint32_t {
714 : //! No attributes.
715 : kNoAttributes = 0,
716 :
717 : //! Function has variable number of arguments.
718 : kHasVarArgs = 0x00000001u,
719 : //! Preserve frame pointer (don't omit FP).
720 : kHasPreservedFP = 0x00000010u,
721 : //! Function calls other functions (is not leaf).
722 : kHasFuncCalls = 0x00000020u,
723 : //! Function has aligned save/restore of vector registers.
724 : kAlignedVecSR = 0x00000040u,
725 : //! FuncFrame is finalized and can be used by prolog/epilog inserter (PEI).
726 : kIsFinalized = 0x00000800u,
727 :
728 : // X86 Specific Attributes
729 : // -----------------------
730 :
731 : //! Enables the use of AVX within the function's body, prolog, and epilog (X86).
732 : //!
733 : //! This flag instructs prolog and epilog emitter to use AVX instead of SSE for manipulating XMM registers.
734 : kX86_AVXEnabled = 0x00010000u,
735 :
736 : //! Enables the use of AVX-512 within the function's body, prolog, and epilog (X86).
737 : //!
738 : //! This flag instructs Compiler register allocator to use additional 16 registers introduced by AVX-512.
739 : //! Additionally, if the functions saves full width of ZMM registers (custom calling conventions only) then
740 : //! the prolog/epilog inserter would use AVX-512 move instructions to emit the save and restore sequence.
741 : kX86_AVX512Enabled = 0x00020000u,
742 :
743 : //! This flag instructs the epilog writer to emit EMMS instruction before RET (X86).
744 : kX86_MMXCleanup = 0x00040000u,
745 :
746 : //! This flag instructs the epilog writer to emit VZEROUPPER instruction before RET (X86).
747 : kX86_AVXCleanup = 0x00080000u
748 : };
749 37131 : ASMJIT_DEFINE_ENUM_FLAGS(FuncAttributes)
750 :
751 : //! Function detail - \ref CallConv and expanded \ref FuncSignature.
752 : //!
753 : //! Function detail is architecture and OS dependent representation of a function. It contains a materialized
754 : //! calling convention and expanded function signature so all arguments have assigned either register type/id
755 : //! or stack address.
756 : class FuncDetail {
757 : public:
758 : //! \name Constants
759 : //! \{
760 :
761 : enum : uint8_t {
762 : //! Doesn't have variable number of arguments (`...`).
763 : kNoVarArgs = 0xFFu
764 : };
765 :
766 : //! \}
767 :
768 : //! \name Members
769 : //! \{
770 :
771 : //! Calling convention.
772 : CallConv _callConv;
773 : //! Number of function arguments.
774 : uint8_t _argCount;
775 : //! Variable arguments index of `kNoVarArgs`.
776 : uint8_t _vaIndex;
777 : //! Reserved for future use.
778 : uint16_t _reserved;
779 : //! Registers that contain arguments.
780 : Support::Array<RegMask, Globals::kNumVirtGroups> _usedRegs;
781 : //! Size of arguments passed by stack.
782 : uint32_t _argStackSize;
783 : //! Function return value(s).
784 : FuncValuePack _rets;
785 : //! Function arguments.
786 : FuncValuePack _args[Globals::kMaxFuncArgs];
787 :
788 : //! \}
789 :
790 : //! \name Construction & Destruction
791 : //! \{
792 :
793 37131 : inline FuncDetail() noexcept { reset(); }
794 : inline FuncDetail(const FuncDetail& other) noexcept = default;
795 :
796 : //! Initializes this `FuncDetail` to the given signature.
797 : ASMJIT_API Error init(const FuncSignature& signature, const Environment& environment) noexcept;
798 37131 : inline void reset() noexcept { memset(this, 0, sizeof(*this)); }
799 :
800 : //! \}
801 :
802 : //! \name Accessors
803 : //! \{
804 :
805 : //! Returns the function's calling convention, see `CallConv`.
806 : inline const CallConv& callConv() const noexcept { return _callConv; }
807 :
808 : //! Returns the associated calling convention flags, see `CallConv::Flags`.
809 : inline CallConvFlags flags() const noexcept { return _callConv.flags(); }
810 : //! Checks whether a CallConv `flag` is set, see `CallConv::Flags`.
811 : inline bool hasFlag(CallConvFlags ccFlag) const noexcept { return _callConv.hasFlag(ccFlag); }
812 :
813 : //! Tests whether the function has a return value.
814 : inline bool hasRet() const noexcept { return bool(_rets[0]); }
815 : //! Returns the number of function arguments.
816 138885 : inline uint32_t argCount() const noexcept { return _argCount; }
817 :
818 : //! Returns function return values.
819 : inline FuncValuePack& retPack() noexcept { return _rets; }
820 : //! Returns function return values.
821 0 : inline const FuncValuePack& retPack() const noexcept { return _rets; }
822 :
823 : //! Returns a function return value associated with the given `valueIndex`.
824 : inline FuncValue& ret(size_t valueIndex = 0) noexcept { return _rets[valueIndex]; }
825 : //! Returns a function return value associated with the given `valueIndex` (const).
826 : inline const FuncValue& ret(size_t valueIndex = 0) const noexcept { return _rets[valueIndex]; }
827 :
828 : //! Returns function argument packs array.
829 : inline FuncValuePack* argPacks() noexcept { return _args; }
830 : //! Returns function argument packs array (const).
831 : inline const FuncValuePack* argPacks() const noexcept { return _args; }
832 :
833 : //! Returns function argument pack at the given `argIndex`.
834 : inline FuncValuePack& argPack(size_t argIndex) noexcept {
835 : ASMJIT_ASSERT(argIndex < Globals::kMaxFuncArgs);
836 : return _args[argIndex];
837 : }
838 :
839 : //! Returns function argument pack at the given `argIndex` (const).
840 : inline const FuncValuePack& argPack(size_t argIndex) const noexcept {
841 : ASMJIT_ASSERT(argIndex < Globals::kMaxFuncArgs);
842 0 : return _args[argIndex];
843 : }
844 :
845 : //! Returns an argument at `valueIndex` from the argument pack at the given `argIndex`.
846 : inline FuncValue& arg(size_t argIndex, size_t valueIndex = 0) noexcept {
847 : ASMJIT_ASSERT(argIndex < Globals::kMaxFuncArgs);
848 : return _args[argIndex][valueIndex];
849 : }
850 :
851 : //! Returns an argument at `valueIndex` from the argument pack at the given `argIndex` (const).
852 : inline const FuncValue& arg(size_t argIndex, size_t valueIndex = 0) const noexcept {
853 : ASMJIT_ASSERT(argIndex < Globals::kMaxFuncArgs);
854 : return _args[argIndex][valueIndex];
855 : }
856 :
857 : //! Resets an argument at the given `argIndex`.
858 : //!
859 : //! If the argument is a parameter pack (has multiple values) all values are reset.
860 : inline void resetArg(size_t argIndex) noexcept {
861 : ASMJIT_ASSERT(argIndex < Globals::kMaxFuncArgs);
862 : _args[argIndex].reset();
863 : }
864 :
865 : //! Tests whether the function has variable arguments.
866 9639 : inline bool hasVarArgs() const noexcept { return _vaIndex != kNoVarArgs; }
867 : //! Returns an index of a first variable argument.
868 : inline uint32_t vaIndex() const noexcept { return _vaIndex; }
869 :
870 : //! Tests whether the function passes one or more argument by stack.
871 : inline bool hasStackArgs() const noexcept { return _argStackSize != 0; }
872 : //! Returns stack size needed for function arguments passed on the stack.
873 9639 : inline uint32_t argStackSize() const noexcept { return _argStackSize; }
874 :
875 : //! Returns red zone size.
876 : inline uint32_t redZoneSize() const noexcept { return _callConv.redZoneSize(); }
877 : //! Returns spill zone size.
878 : inline uint32_t spillZoneSize() const noexcept { return _callConv.spillZoneSize(); }
879 : //! Returns natural stack alignment.
880 : inline uint32_t naturalStackAlignment() const noexcept { return _callConv.naturalStackAlignment(); }
881 :
882 : //! Returns a mask of all passed registers of the given register `group`.
883 : inline RegMask passedRegs(RegGroup group) const noexcept { return _callConv.passedRegs(group); }
884 : //! Returns a mask of all preserved registers of the given register `group`.
885 : inline RegMask preservedRegs(RegGroup group) const noexcept { return _callConv.preservedRegs(group); }
886 :
887 : //! Returns a mask of all used registers of the given register `group`.
888 : inline RegMask usedRegs(RegGroup group) const noexcept {
889 : ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
890 109968 : return _usedRegs[size_t(group)];
891 : }
892 :
893 : //! Adds `regs` to the mask of used registers of the given register `group`.
894 : inline void addUsedRegs(RegGroup group, RegMask regs) noexcept {
895 : ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
896 9873 : _usedRegs[size_t(group)] |= regs;
897 0 : }
898 :
899 : //! \}
900 : };
901 :
902 : //! Function frame.
903 : //!
904 : //! Function frame is used directly by prolog and epilog insertion (PEI) utils. It provides information necessary to
905 : //! insert a proper and ABI comforming prolog and epilog. Function frame calculation is based on `CallConv` and
906 : //! other function attributes.
907 : //!
908 : //! SSE vs AVX vs AVX-512
909 : //! ---------------------
910 : //!
911 : //! Function frame provides a way to tell prolog/epilog inserter to use AVX instructions instead of SSE. Use
912 : //! `setAvxEnabled()` and `setAvx512Enabled()` to enable AVX and/or AVX-512, respectively. Enabling AVX-512
913 : //! is mostly for Compiler as it would use 32 SIMD registers instead of 16 when enabled.
914 : //!
915 : //! \note If your code uses AVX instructions and AVX is not enabled there would be a performance hit in case that
916 : //! some registers had to be saved/restored in function's prolog/epilog, respectively. Thus, it's recommended to
917 : //! always let the function frame know about the use of AVX.
918 : //!
919 : //! Function Frame Structure
920 : //! ------------------------
921 : //!
922 : //! Various properties can contribute to the size and structure of the function frame. The function frame in most
923 : //! cases won't use all of the properties illustrated (for example Spill Zone and Red Zone are never used together).
924 : //!
925 : //! ```
926 : //! +-----------------------------+
927 : //! | Arguments Passed by Stack |
928 : //! +-----------------------------+
929 : //! | Spill Zone |
930 : //! +-----------------------------+ <- Stack offset (args) starts from here.
931 : //! | Return Address, if Pushed |
932 : //! +-----------------------------+ <- Stack pointer (SP) upon entry.
933 : //! | Save/Restore Stack. |
934 : //! +-----------------------------+-----------------------------+
935 : //! | Local Stack | |
936 : //! +-----------------------------+ Final Stack |
937 : //! | Call Stack | |
938 : //! +-----------------------------+-----------------------------+ <- SP after prolog.
939 : //! | Red Zone |
940 : //! +-----------------------------+
941 : //! ```
942 : class FuncFrame {
943 : public:
944 : //! \name Constants
945 : //! \{
946 :
947 : enum : uint32_t {
948 : //! Tag used to inform that some offset is invalid.
949 : kTagInvalidOffset = 0xFFFFFFFFu
950 : };
951 :
952 : //! \}
953 :
954 : //! \name Members
955 : //! \{
956 :
957 : //! Function attributes.
958 : FuncAttributes _attributes;
959 :
960 : //! Target architecture.
961 : Arch _arch;
962 : //! SP register ID (to access call stack and local stack).
963 : uint8_t _spRegId;
964 : //! SA register ID (to access stack arguments).
965 : uint8_t _saRegId;
966 :
967 : //! Red zone size (copied from CallConv).
968 : uint8_t _redZoneSize;
969 : //! Spill zone size (copied from CallConv).
970 : uint8_t _spillZoneSize;
971 : //! Natural stack alignment (copied from CallConv).
972 : uint8_t _naturalStackAlignment;
973 : //! Minimum stack alignment to turn on dynamic alignment.
974 : uint8_t _minDynamicAlignment;
975 :
976 : //! Call stack alignment.
977 : uint8_t _callStackAlignment;
978 : //! Local stack alignment.
979 : uint8_t _localStackAlignment;
980 : //! Final stack alignment.
981 : uint8_t _finalStackAlignment;
982 :
983 : //! Adjustment of the stack before returning (X86-STDCALL).
984 : uint16_t _calleeStackCleanup;
985 :
986 : //! Call stack size.
987 : uint32_t _callStackSize;
988 : //! Local stack size.
989 : uint32_t _localStackSize;
990 : //! Final stack size (sum of call stack and local stack).
991 : uint32_t _finalStackSize;
992 :
993 : //! Local stack offset (non-zero only if call stack is used).
994 : uint32_t _localStackOffset;
995 : //! Offset relative to SP that contains previous SP (before alignment).
996 : uint32_t _daOffset;
997 : //! Offset of the first stack argument relative to SP.
998 : uint32_t _saOffsetFromSP;
999 : //! Offset of the first stack argument relative to SA (_saRegId or FP).
1000 : uint32_t _saOffsetFromSA;
1001 :
1002 : //! Local stack adjustment in prolog/epilog.
1003 : uint32_t _stackAdjustment;
1004 :
1005 : //! Registers that are dirty.
1006 : Support::Array<RegMask, Globals::kNumVirtGroups> _dirtyRegs;
1007 : //! Registers that must be preserved (copied from CallConv).
1008 : Support::Array<RegMask, Globals::kNumVirtGroups> _preservedRegs;
1009 : //! Size to save/restore per register group.
1010 : Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreRegSize;
1011 : //! Alignment of save/restore area per register group.
1012 : Support::Array<uint8_t, Globals::kNumVirtGroups> _saveRestoreAlignment;
1013 :
1014 : //! Stack size required to save registers with push/pop.
1015 : uint16_t _pushPopSaveSize;
1016 : //! Stack size required to save extra registers that cannot use push/pop.
1017 : uint16_t _extraRegSaveSize;
1018 : //! Offset where registers saved/restored via push/pop are stored
1019 : uint32_t _pushPopSaveOffset;
1020 : //! Offset where extra ragisters that cannot use push/pop are stored.
1021 : uint32_t _extraRegSaveOffset;
1022 :
1023 : //! \}
1024 :
1025 : //! \name Construction & Destruction
1026 : //! \{
1027 :
1028 27492 : inline FuncFrame() noexcept { reset(); }
1029 : inline FuncFrame(const FuncFrame& other) noexcept = default;
1030 :
1031 : ASMJIT_API Error init(const FuncDetail& func) noexcept;
1032 :
1033 54984 : inline void reset() noexcept {
1034 : memset(this, 0, sizeof(FuncFrame));
1035 54984 : _spRegId = BaseReg::kIdBad;
1036 54984 : _saRegId = BaseReg::kIdBad;
1037 54984 : _daOffset = kTagInvalidOffset;
1038 54984 : }
1039 :
1040 : //! \}
1041 :
1042 : //! \name Accessors
1043 : //! \{
1044 :
1045 : //! Returns the target architecture of the function frame.
1046 109968 : inline Arch arch() const noexcept { return _arch; }
1047 :
1048 : //! Returns function frame attributes, see `Attributes`.
1049 : inline FuncAttributes attributes() const noexcept { return _attributes; }
1050 : //! Checks whether the FuncFame contains an attribute `attr`.
1051 326259 : inline bool hasAttribute(FuncAttributes attr) const noexcept { return Support::test(_attributes, attr); }
1052 : //! Adds attributes `attrs` to the FuncFrame.
1053 : inline void addAttributes(FuncAttributes attrs) noexcept { _attributes |= attrs; }
1054 : //! Clears attributes `attrs` from the FrameFrame.
1055 : inline void clearAttributes(FuncAttributes attrs) noexcept { _attributes &= ~attrs; }
1056 :
1057 : //! Tests whether the function has variable number of arguments.
1058 : inline bool hasVarArgs() const noexcept { return hasAttribute(FuncAttributes::kHasVarArgs); }
1059 : //! Sets the variable arguments flag.
1060 : inline void setVarArgs() noexcept { addAttributes(FuncAttributes::kHasVarArgs); }
1061 : //! Resets variable arguments flag.
1062 : inline void resetVarArgs() noexcept { clearAttributes(FuncAttributes::kHasVarArgs); }
1063 :
1064 : //! Tests whether the function preserves frame pointer (EBP|ESP on X86).
1065 : inline bool hasPreservedFP() const noexcept { return hasAttribute(FuncAttributes::kHasPreservedFP); }
1066 : //! Enables preserved frame pointer.
1067 : inline void setPreservedFP() noexcept { addAttributes(FuncAttributes::kHasPreservedFP); }
1068 : //! Disables preserved frame pointer.
1069 : inline void resetPreservedFP() noexcept { clearAttributes(FuncAttributes::kHasPreservedFP); }
1070 :
1071 : //! Tests whether the function calls other functions.
1072 : inline bool hasFuncCalls() const noexcept { return hasAttribute(FuncAttributes::kHasFuncCalls); }
1073 : //! Sets `kFlagHasCalls` to true.
1074 : inline void setFuncCalls() noexcept { addAttributes(FuncAttributes::kHasFuncCalls); }
1075 : //! Sets `kFlagHasCalls` to false.
1076 : inline void resetFuncCalls() noexcept { clearAttributes(FuncAttributes::kHasFuncCalls); }
1077 :
1078 : //! Tests whether the function has AVX enabled.
1079 : inline bool isAvxEnabled() const noexcept { return hasAttribute(FuncAttributes::kX86_AVXEnabled); }
1080 : //! Enables AVX use.
1081 : inline void setAvxEnabled() noexcept { addAttributes(FuncAttributes::kX86_AVXEnabled); }
1082 : //! Disables AVX use.
1083 : inline void resetAvxEnabled() noexcept { clearAttributes(FuncAttributes::kX86_AVXEnabled); }
1084 :
1085 : //! Tests whether the function has AVX-512 enabled.
1086 : inline bool isAvx512Enabled() const noexcept { return hasAttribute(FuncAttributes::kX86_AVX512Enabled); }
1087 : //! Enables AVX-512 use.
1088 : inline void setAvx512Enabled() noexcept { addAttributes(FuncAttributes::kX86_AVX512Enabled); }
1089 : //! Disables AVX-512 use.
1090 : inline void resetAvx512Enabled() noexcept { clearAttributes(FuncAttributes::kX86_AVX512Enabled); }
1091 :
1092 : //! Tests whether the function has MMX cleanup - 'emms' instruction in epilog.
1093 : inline bool hasMmxCleanup() const noexcept { return hasAttribute(FuncAttributes::kX86_MMXCleanup); }
1094 : //! Enables MMX cleanup.
1095 : inline void setMmxCleanup() noexcept { addAttributes(FuncAttributes::kX86_MMXCleanup); }
1096 : //! Disables MMX cleanup.
1097 : inline void resetMmxCleanup() noexcept { clearAttributes(FuncAttributes::kX86_MMXCleanup); }
1098 :
1099 : //! Tests whether the function has AVX cleanup - 'vzeroupper' instruction in epilog.
1100 : inline bool hasAvxCleanup() const noexcept { return hasAttribute(FuncAttributes::kX86_AVXCleanup); }
1101 : //! Enables AVX cleanup.
1102 : inline void setAvxCleanup() noexcept { addAttributes(FuncAttributes::kX86_AVXCleanup); }
1103 : //! Disables AVX cleanup.
1104 : inline void resetAvxCleanup() noexcept { clearAttributes(FuncAttributes::kX86_AVXCleanup); }
1105 :
1106 : //! Tests whether the function uses call stack.
1107 : inline bool hasCallStack() const noexcept { return _callStackSize != 0; }
1108 : //! Tests whether the function uses local stack.
1109 : inline bool hasLocalStack() const noexcept { return _localStackSize != 0; }
1110 : //! Tests whether vector registers can be saved and restored by using aligned reads and writes.
1111 : inline bool hasAlignedVecSR() const noexcept { return hasAttribute(FuncAttributes::kAlignedVecSR); }
1112 : //! Tests whether the function has to align stack dynamically.
1113 137460 : inline bool hasDynamicAlignment() const noexcept { return _finalStackAlignment >= _minDynamicAlignment; }
1114 :
1115 : //! Tests whether the calling convention specifies 'RedZone'.
1116 : inline bool hasRedZone() const noexcept { return _redZoneSize != 0; }
1117 : //! Tests whether the calling convention specifies 'SpillZone'.
1118 : inline bool hasSpillZone() const noexcept { return _spillZoneSize != 0; }
1119 :
1120 : //! Returns the size of 'RedZone'.
1121 : inline uint32_t redZoneSize() const noexcept { return _redZoneSize; }
1122 : //! Returns the size of 'SpillZone'.
1123 : inline uint32_t spillZoneSize() const noexcept { return _spillZoneSize; }
1124 : //! Returns natural stack alignment (guaranteed stack alignment upon entry).
1125 : inline uint32_t naturalStackAlignment() const noexcept { return _naturalStackAlignment; }
1126 : //! Returns natural stack alignment (guaranteed stack alignment upon entry).
1127 : inline uint32_t minDynamicAlignment() const noexcept { return _minDynamicAlignment; }
1128 :
1129 : //! Tests whether the callee must adjust SP before returning (X86-STDCALL only)
1130 27492 : inline bool hasCalleeStackCleanup() const noexcept { return _calleeStackCleanup != 0; }
1131 : //! Returns home many bytes of the stack the callee must adjust before returning (X86-STDCALL only)
1132 : inline uint32_t calleeStackCleanup() const noexcept { return _calleeStackCleanup; }
1133 :
1134 : //! Returns call stack alignment.
1135 : inline uint32_t callStackAlignment() const noexcept { return _callStackAlignment; }
1136 : //! Returns local stack alignment.
1137 : inline uint32_t localStackAlignment() const noexcept { return _localStackAlignment; }
1138 : //! Returns final stack alignment (the maximum value of call, local, and natural stack alignments).
1139 : inline uint32_t finalStackAlignment() const noexcept { return _finalStackAlignment; }
1140 :
1141 : //! Sets call stack alignment.
1142 : //!
1143 : //! \note This also updates the final stack alignment.
1144 : inline void setCallStackAlignment(uint32_t alignment) noexcept {
1145 : _callStackAlignment = uint8_t(alignment);
1146 : _finalStackAlignment = Support::max(_naturalStackAlignment, _callStackAlignment, _localStackAlignment);
1147 : }
1148 :
1149 : //! Sets local stack alignment.
1150 : //!
1151 : //! \note This also updates the final stack alignment.
1152 : inline void setLocalStackAlignment(uint32_t value) noexcept {
1153 27492 : _localStackAlignment = uint8_t(value);
1154 27492 : _finalStackAlignment = Support::max(_naturalStackAlignment, _callStackAlignment, _localStackAlignment);
1155 : }
1156 :
1157 : //! Combines call stack alignment with `alignment`, updating it to the greater value.
1158 : //!
1159 : //! \note This also updates the final stack alignment.
1160 : inline void updateCallStackAlignment(uint32_t alignment) noexcept {
1161 0 : _callStackAlignment = uint8_t(Support::max<uint32_t>(_callStackAlignment, alignment));
1162 0 : _finalStackAlignment = Support::max(_finalStackAlignment, _callStackAlignment);
1163 : }
1164 :
1165 : //! Combines local stack alignment with `alignment`, updating it to the greater value.
1166 : //!
1167 : //! \note This also updates the final stack alignment.
1168 : inline void updateLocalStackAlignment(uint32_t alignment) noexcept {
1169 : _localStackAlignment = uint8_t(Support::max<uint32_t>(_localStackAlignment, alignment));
1170 : _finalStackAlignment = Support::max(_finalStackAlignment, _localStackAlignment);
1171 : }
1172 :
1173 : //! Returns call stack size.
1174 27492 : inline uint32_t callStackSize() const noexcept { return _callStackSize; }
1175 : //! Returns local stack size.
1176 27492 : inline uint32_t localStackSize() const noexcept { return _localStackSize; }
1177 :
1178 : //! Sets call stack size.
1179 : inline void setCallStackSize(uint32_t size) noexcept { _callStackSize = size; }
1180 : //! Sets local stack size.
1181 27492 : inline void setLocalStackSize(uint32_t size) noexcept { _localStackSize = size; }
1182 :
1183 : //! Combines call stack size with `size`, updating it to the greater value.
1184 9639 : inline void updateCallStackSize(uint32_t size) noexcept { _callStackSize = Support::max(_callStackSize, size); }
1185 : //! Combines local stack size with `size`, updating it to the greater value.
1186 : inline void updateLocalStackSize(uint32_t size) noexcept { _localStackSize = Support::max(_localStackSize, size); }
1187 :
1188 : //! Returns final stack size (only valid after the FuncFrame is finalized).
1189 : inline uint32_t finalStackSize() const noexcept { return _finalStackSize; }
1190 :
1191 : //! Returns an offset to access the local stack (non-zero only if call stack is used).
1192 27492 : inline uint32_t localStackOffset() const noexcept { return _localStackOffset; }
1193 :
1194 : //! Tests whether the function prolog/epilog requires a memory slot for storing unaligned SP.
1195 0 : inline bool hasDAOffset() const noexcept { return _daOffset != kTagInvalidOffset; }
1196 : //! Returns a memory offset used to store DA (dynamic alignment) slot (relative to SP).
1197 : inline uint32_t daOffset() const noexcept { return _daOffset; }
1198 :
1199 : inline uint32_t saOffset(uint32_t regId) const noexcept {
1200 0 : return regId == _spRegId ? saOffsetFromSP()
1201 : : saOffsetFromSA();
1202 : }
1203 :
1204 0 : inline uint32_t saOffsetFromSP() const noexcept { return _saOffsetFromSP; }
1205 0 : inline uint32_t saOffsetFromSA() const noexcept { return _saOffsetFromSA; }
1206 :
1207 : //! Returns mask of registers of the given register `group` that are modified by the function. The engine would
1208 : //! then calculate which registers must be saved & restored by the function by using the data provided by the
1209 : //! calling convention.
1210 : inline RegMask dirtyRegs(RegGroup group) const noexcept {
1211 : ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
1212 219936 : return _dirtyRegs[group];
1213 : }
1214 :
1215 : //! Sets which registers (as a mask) are modified by the function.
1216 : //!
1217 : //! \remarks Please note that this will completely overwrite the existing register mask, use `addDirtyRegs()`
1218 : //! to modify the existing register mask.
1219 : inline void setDirtyRegs(RegGroup group, RegMask regs) noexcept {
1220 : ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
1221 : _dirtyRegs[group] = regs;
1222 : }
1223 :
1224 : //! Adds which registers (as a mask) are modified by the function.
1225 : inline void addDirtyRegs(RegGroup group, RegMask regs) noexcept {
1226 : ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
1227 219936 : _dirtyRegs[group] |= regs;
1228 0 : }
1229 :
1230 : //! \overload
1231 : inline void addDirtyRegs(const BaseReg& reg) noexcept {
1232 : ASMJIT_ASSERT(reg.id() < Globals::kMaxPhysRegs);
1233 : addDirtyRegs(reg.group(), Support::bitMask(reg.id()));
1234 : }
1235 :
1236 : //! \overload
1237 : template<typename... Args>
1238 : inline void addDirtyRegs(const BaseReg& reg, Args&&... args) noexcept {
1239 : addDirtyRegs(reg);
1240 : addDirtyRegs(std::forward<Args>(args)...);
1241 : }
1242 :
1243 : inline void setAllDirty() noexcept {
1244 : for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_dirtyRegs); i++)
1245 : _dirtyRegs[i] = 0xFFFFFFFFu;
1246 : }
1247 :
1248 : inline void setAllDirty(RegGroup group) noexcept {
1249 : ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
1250 : _dirtyRegs[group] = 0xFFFFFFFFu;
1251 : }
1252 :
1253 : //! Returns a calculated mask of registers of the given `group` that will be saved and restored in the function's
1254 : //! prolog and epilog, respectively. The register mask is calculated from both `dirtyRegs` (provided by user) and
1255 : //! `preservedMask` (provided by the calling convention).
1256 : inline RegMask savedRegs(RegGroup group) const noexcept {
1257 : ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
1258 329904 : return _dirtyRegs[group] & _preservedRegs[group];
1259 : }
1260 :
1261 : //! Returns the mask of preserved registers of the given register `group`.
1262 : //!
1263 : //! Preserved registers are those that must survive the function call unmodified. The function can only modify
1264 : //! preserved registers it they are saved and restored in funciton's prolog and epilog, respectively.
1265 : inline RegMask preservedRegs(RegGroup group) const noexcept {
1266 : ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
1267 219936 : return _preservedRegs[group];
1268 : }
1269 :
1270 : inline uint32_t saveRestoreRegSize(RegGroup group) const noexcept {
1271 : ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
1272 109968 : return _saveRestoreRegSize[group];
1273 : }
1274 :
1275 : inline uint32_t saveRestoreAlignment(RegGroup group) const noexcept {
1276 : ASMJIT_ASSERT(group <= RegGroup::kMaxVirt);
1277 109968 : return _saveRestoreAlignment[group];
1278 : }
1279 :
1280 : inline bool hasSARegId() const noexcept { return _saRegId != BaseReg::kIdBad; }
1281 82476 : inline uint32_t saRegId() const noexcept { return _saRegId; }
1282 0 : inline void setSARegId(uint32_t regId) { _saRegId = uint8_t(regId); }
1283 : inline void resetSARegId() { setSARegId(BaseReg::kIdBad); }
1284 :
1285 : //! Returns stack size required to save/restore registers via push/pop.
1286 4990 : inline uint32_t pushPopSaveSize() const noexcept { return _pushPopSaveSize; }
1287 : //! Returns an offset to the stack where registers are saved via push/pop.
1288 : inline uint32_t pushPopSaveOffset() const noexcept { return _pushPopSaveOffset; }
1289 :
1290 : //! Returns stack size required to save/restore extra registers that don't use push/pop/
1291 : //!
1292 : //! \note On X86 this covers all registers except GP registers, on other architectures it can be always
1293 : //! zero (for example AArch64 saves all registers via push/pop like instructions, so this would be zero).
1294 : inline uint32_t extraRegSaveSize() const noexcept { return _extraRegSaveSize; }
1295 : //! Returns an offset to the stack where extra registers are saved.
1296 54984 : inline uint32_t extraRegSaveOffset() const noexcept { return _extraRegSaveOffset; }
1297 :
1298 : //! Tests whether the functions contains stack adjustment.
1299 54984 : inline bool hasStackAdjustment() const noexcept { return _stackAdjustment != 0; }
1300 : //! Returns function's stack adjustment used in function's prolog and epilog.
1301 : //!
1302 : //! If the returned value is zero it means that the stack is not adjusted. This can mean both that the stack
1303 : //! is not used and/or the stack is only adjusted by instructions that pust/pop registers into/from stack.
1304 : inline uint32_t stackAdjustment() const noexcept { return _stackAdjustment; }
1305 :
1306 : //! \}
1307 :
1308 : //! \name Finaliztion
1309 : //! \{
1310 :
1311 : ASMJIT_API Error finalize() noexcept;
1312 :
1313 : //! \}
1314 : };
1315 :
1316 : //! A helper class that can be used to assign a physical register for each function argument. Use with
1317 : //! `BaseEmitter::emitArgsAssignment()`.
1318 : class FuncArgsAssignment {
1319 : public:
1320 : //! \name Members
1321 : //! \{
1322 :
1323 : //! Function detail.
1324 : const FuncDetail* _funcDetail;
1325 : //! Register that can be used to access arguments passed by stack.
1326 : uint8_t _saRegId;
1327 : //! Reserved for future use.
1328 : uint8_t _reserved[3];
1329 : //! Mapping of each function argument.
1330 : FuncValuePack _argPacks[Globals::kMaxFuncArgs];
1331 :
1332 : //! \}
1333 :
1334 : //! \name Construction & Destruction
1335 : //! \{
1336 :
1337 27492 : inline explicit FuncArgsAssignment(const FuncDetail* fd = nullptr) noexcept { reset(fd); }
1338 :
1339 : inline FuncArgsAssignment(const FuncArgsAssignment& other) noexcept {
1340 : memcpy(this, &other, sizeof(*this));
1341 : }
1342 :
1343 82476 : inline void reset(const FuncDetail* fd = nullptr) noexcept {
1344 82476 : _funcDetail = fd;
1345 82476 : _saRegId = uint8_t(BaseReg::kIdBad);
1346 82476 : memset(_reserved, 0, sizeof(_reserved));
1347 82476 : memset(_argPacks, 0, sizeof(_argPacks));
1348 82476 : }
1349 :
1350 : //! \}
1351 :
1352 : //! \name Accessors
1353 : //! \{
1354 :
1355 82476 : inline const FuncDetail* funcDetail() const noexcept { return _funcDetail; }
1356 : inline void setFuncDetail(const FuncDetail* fd) noexcept { _funcDetail = fd; }
1357 :
1358 : inline bool hasSARegId() const noexcept { return _saRegId != BaseReg::kIdBad; }
1359 54984 : inline uint32_t saRegId() const noexcept { return _saRegId; }
1360 : inline void setSARegId(uint32_t regId) { _saRegId = uint8_t(regId); }
1361 : inline void resetSARegId() { _saRegId = uint8_t(BaseReg::kIdBad); }
1362 :
1363 : inline FuncValue& arg(size_t argIndex, size_t valueIndex) noexcept {
1364 : ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
1365 : return _argPacks[argIndex][valueIndex];
1366 : }
1367 : inline const FuncValue& arg(size_t argIndex, size_t valueIndex) const noexcept {
1368 : ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
1369 : return _argPacks[argIndex][valueIndex];
1370 : }
1371 :
1372 : inline bool isAssigned(size_t argIndex, size_t valueIndex) const noexcept {
1373 : ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
1374 : return _argPacks[argIndex][valueIndex].isAssigned();
1375 : }
1376 :
1377 : inline void assignReg(size_t argIndex, const BaseReg& reg, TypeId typeId = TypeId::kVoid) noexcept {
1378 : ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
1379 : ASMJIT_ASSERT(reg.isPhysReg());
1380 : _argPacks[argIndex][0].initReg(reg.type(), reg.id(), typeId);
1381 : }
1382 :
1383 : inline void assignReg(size_t argIndex, RegType regType, uint32_t regId, TypeId typeId = TypeId::kVoid) noexcept {
1384 : ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
1385 : _argPacks[argIndex][0].initReg(regType, regId, typeId);
1386 : }
1387 :
1388 : inline void assignStack(size_t argIndex, int32_t offset, TypeId typeId = TypeId::kVoid) noexcept {
1389 : ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
1390 : _argPacks[argIndex][0].initStack(offset, typeId);
1391 : }
1392 :
1393 : inline void assignRegInPack(size_t argIndex, size_t valueIndex, const BaseReg& reg, TypeId typeId = TypeId::kVoid) noexcept {
1394 : ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
1395 : ASMJIT_ASSERT(reg.isPhysReg());
1396 : _argPacks[argIndex][valueIndex].initReg(reg.type(), reg.id(), typeId);
1397 : }
1398 :
1399 : inline void assignRegInPack(size_t argIndex, size_t valueIndex, RegType regType, uint32_t regId, TypeId typeId = TypeId::kVoid) noexcept {
1400 : ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
1401 : _argPacks[argIndex][valueIndex].initReg(regType, regId, typeId);
1402 0 : }
1403 :
1404 : inline void assignStackInPack(size_t argIndex, size_t valueIndex, int32_t offset, TypeId typeId = TypeId::kVoid) noexcept {
1405 : ASMJIT_ASSERT(argIndex < ASMJIT_ARRAY_SIZE(_argPacks));
1406 : _argPacks[argIndex][valueIndex].initStack(offset, typeId);
1407 : }
1408 :
1409 : // NOTE: All `assignAll()` methods are shortcuts to assign all arguments at once, however, since registers are
1410 : // passed all at once these initializers don't provide any way to pass TypeId and/or to keep any argument between
1411 : // the arguments passed unassigned.
1412 : inline void _assignAllInternal(size_t argIndex, const BaseReg& reg) noexcept {
1413 : assignReg(argIndex, reg);
1414 : }
1415 :
1416 : template<typename... Args>
1417 : inline void _assignAllInternal(size_t argIndex, const BaseReg& reg, Args&&... args) noexcept {
1418 : assignReg(argIndex, reg);
1419 : _assignAllInternal(argIndex + 1, std::forward<Args>(args)...);
1420 : }
1421 :
1422 : template<typename... Args>
1423 : inline void assignAll(Args&&... args) noexcept {
1424 : _assignAllInternal(0, std::forward<Args>(args)...);
1425 : }
1426 :
1427 : //! \}
1428 :
1429 : //! \name Utilities
1430 : //! \{
1431 :
1432 : //! Update `FuncFrame` based on function's arguments assignment.
1433 : //!
1434 : //! \note You MUST call this in orher to use `BaseEmitter::emitArgsAssignment()`, otherwise the FuncFrame would
1435 : //! not contain the information necessary to assign all arguments into the registers and/or stack specified.
1436 : ASMJIT_API Error updateFuncFrame(FuncFrame& frame) const noexcept;
1437 :
1438 : //! \}
1439 : };
1440 :
1441 : //! \}
1442 :
1443 : ASMJIT_END_NAMESPACE
1444 :
1445 : #endif // ASMJIT_CORE_FUNC_H_INCLUDED
1446 :
1447 :
1448 : #endif // __PLUMED_HAS_ASMJIT
|