LCOV - code coverage report
Current view: top level - asmjit - func.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage (other modules) Lines: 84 96 87.5 %
Date: 2026-11-05 10:19:06 Functions: 5 5 100.0 %

          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             : #include "api-build_p.h"
       8             : #include "archtraits.h"
       9             : #include "func.h"
      10             : #include "operand.h"
      11             : #include "type.h"
      12             : #include "funcargscontext_p.h"
      13             : 
      14             : #if !defined(ASMJIT_NO_X86)
      15             :   #include "x86func_p.h"
      16             : #endif
      17             : 
      18             : #if !defined(ASMJIT_NO_AARCH64)
      19             :   #include "a64func_p.h"
      20             : #endif
      21             : 
      22             : ASMJIT_BEGIN_NAMESPACE
      23             : 
      24             : // CallConv - Init & Reset
      25             : // =======================
      26             : 
      27       37131 : ASMJIT_FAVOR_SIZE Error CallConv::init(CallConvId ccId, const Environment& environment) noexcept {
      28       37131 :   reset();
      29             : 
      30             : #if !defined(ASMJIT_NO_X86)
      31       37131 :   if (environment.isFamilyX86())
      32       37131 :     return x86::FuncInternal::initCallConv(*this, ccId, environment);
      33             : #endif
      34             : 
      35             : #if !defined(ASMJIT_NO_AARCH64)
      36           0 :   if (environment.isFamilyAArch64())
      37           0 :     return a64::FuncInternal::initCallConv(*this, ccId, environment);
      38             : #endif
      39             : 
      40             :   return DebugUtils::errored(kErrorInvalidArgument);
      41             : }
      42             : 
      43             : // FuncDetail - Init / Reset
      44             : // =========================
      45             : 
      46       37131 : ASMJIT_FAVOR_SIZE Error FuncDetail::init(const FuncSignature& signature, const Environment& environment) noexcept {
      47             :   CallConvId ccId = signature.callConvId();
      48             :   uint32_t argCount = signature.argCount();
      49             : 
      50       37131 :   if (ASMJIT_UNLIKELY(argCount > Globals::kMaxFuncArgs))
      51             :     return DebugUtils::errored(kErrorInvalidArgument);
      52             : 
      53       37131 :   CallConv& cc = _callConv;
      54       37131 :   ASMJIT_PROPAGATE(cc.init(ccId, environment));
      55             : 
      56             :   uint32_t registerSize = Environment::registerSizeFromArch(cc.arch());
      57             :   uint32_t deabstractDelta = TypeUtils::deabstractDeltaOfSize(registerSize);
      58             : 
      59             :   const TypeId* signatureArgs = signature.args();
      60       47004 :   for (uint32_t argIndex = 0; argIndex < argCount; argIndex++) {
      61             :     FuncValuePack& argPack = _args[argIndex];
      62        9873 :     argPack[0].initTypeId(TypeUtils::deabstract(signatureArgs[argIndex], deabstractDelta));
      63             :   }
      64             : 
      65       37131 :   _argCount = uint8_t(argCount);
      66       37131 :   _vaIndex = uint8_t(signature.vaIndex());
      67             : 
      68             :   TypeId ret = signature.ret();
      69       37131 :   if (ret != TypeId::kVoid)
      70       37131 :     _rets[0].initTypeId(TypeUtils::deabstract(ret, deabstractDelta));
      71             : 
      72             : #if !defined(ASMJIT_NO_X86)
      73       37131 :   if (environment.isFamilyX86())
      74       37131 :     return x86::FuncInternal::initFuncDetail(*this, signature, registerSize);
      75             : #endif
      76             : 
      77             : #if !defined(ASMJIT_NO_AARCH64)
      78           0 :   if (environment.isFamilyAArch64())
      79           0 :     return a64::FuncInternal::initFuncDetail(*this, signature, registerSize);
      80             : #endif
      81             : 
      82             :   // We should never bubble here as if `cc.init()` succeeded then there has to be an implementation for the current
      83             :   // architecture. However, stay safe.
      84             :   return DebugUtils::errored(kErrorInvalidArgument);
      85             : }
      86             : 
      87             : // FuncFrame - Init
      88             : // ================
      89             : 
      90       27492 : ASMJIT_FAVOR_SIZE Error FuncFrame::init(const FuncDetail& func) noexcept {
      91             :   Arch arch = func.callConv().arch();
      92       27492 :   if (!Environment::isValidArch(arch))
      93             :     return DebugUtils::errored(kErrorInvalidArch);
      94             : 
      95             :   const ArchTraits& archTraits = ArchTraits::byArch(arch);
      96             : 
      97             :   // Initializing FuncFrame means making a copy of some properties of `func`. Properties like `_localStackSize` will
      98             :   // be set by the user before the frame is finalized.
      99       27492 :   reset();
     100             : 
     101       27492 :   _arch = arch;
     102       27492 :   _spRegId = uint8_t(archTraits.spRegId());
     103       27492 :   _saRegId = uint8_t(BaseReg::kIdBad);
     104             : 
     105             :   uint32_t naturalStackAlignment = func.callConv().naturalStackAlignment();
     106             :   uint32_t minDynamicAlignment = Support::max<uint32_t>(naturalStackAlignment, 16);
     107             : 
     108       27492 :   if (minDynamicAlignment == naturalStackAlignment)
     109       27492 :     minDynamicAlignment <<= 1;
     110             : 
     111       27492 :   _naturalStackAlignment = uint8_t(naturalStackAlignment);
     112       27492 :   _minDynamicAlignment = uint8_t(minDynamicAlignment);
     113       27492 :   _redZoneSize = uint8_t(func.redZoneSize());
     114       27492 :   _spillZoneSize = uint8_t(func.spillZoneSize());
     115       27492 :   _finalStackAlignment = uint8_t(_naturalStackAlignment);
     116             : 
     117       27492 :   if (func.hasFlag(CallConvFlags::kCalleePopsStack)) {
     118           0 :     _calleeStackCleanup = uint16_t(func.argStackSize());
     119             :   }
     120             : 
     121             :   // Initial masks of dirty and preserved registers.
     122      137460 :   for (RegGroup group : RegGroupVirtValues{}) {
     123      109968 :     _dirtyRegs[group] = func.usedRegs(group);
     124      109968 :     _preservedRegs[group] = func.preservedRegs(group);
     125             :   }
     126             : 
     127             :   // Exclude stack pointer - this register is never included in saved GP regs.
     128       27492 :   _preservedRegs[RegGroup::kGp] &= ~Support::bitMask(archTraits.spRegId());
     129             : 
     130             :   // The size and alignment of save/restore area of registers for each virtual register group
     131       27492 :   _saveRestoreRegSize = func.callConv()._saveRestoreRegSize;
     132       27492 :   _saveRestoreAlignment = func.callConv()._saveRestoreAlignment;
     133             : 
     134       27492 :   return kErrorOk;
     135             : }
     136             : 
     137             : // FuncFrame - Finalize
     138             : // ====================
     139             : 
     140       27492 : ASMJIT_FAVOR_SIZE Error FuncFrame::finalize() noexcept {
     141       27492 :   if (!Environment::isValidArch(arch()))
     142             :     return DebugUtils::errored(kErrorInvalidArch);
     143             : 
     144             :   const ArchTraits& archTraits = ArchTraits::byArch(arch());
     145             : 
     146       27492 :   uint32_t registerSize = _saveRestoreRegSize[RegGroup::kGp];
     147       27492 :   uint32_t vectorSize = _saveRestoreRegSize[RegGroup::kVec];
     148       27492 :   uint32_t returnAddressSize = archTraits.hasLinkReg() ? 0u : registerSize;
     149             : 
     150             :   // The final stack alignment must be updated accordingly to call and local stack alignments.
     151       27492 :   uint32_t stackAlignment = _finalStackAlignment;
     152             :   ASMJIT_ASSERT(stackAlignment == Support::max(_naturalStackAlignment,
     153             :                                                _callStackAlignment,
     154             :                                                _localStackAlignment));
     155             : 
     156             :   bool hasFP = hasPreservedFP();
     157             :   bool hasDA = hasDynamicAlignment();
     158             : 
     159             :   uint32_t kSp = archTraits.spRegId();
     160             :   uint32_t kFp = archTraits.fpRegId();
     161             :   uint32_t kLr = archTraits.linkRegId();
     162             : 
     163             :   // Make frame pointer dirty if the function uses it.
     164       27492 :   if (hasFP) {
     165           0 :     _dirtyRegs[RegGroup::kGp] |= Support::bitMask(kFp);
     166             : 
     167             :     // Currently required by ARM, if this works differently across architectures we would have to generalize most
     168             :     // likely in CallConv.
     169           0 :     if (kLr != BaseReg::kIdBad)
     170           0 :       _dirtyRegs[RegGroup::kGp] |= Support::bitMask(kLr);
     171             :   }
     172             : 
     173             :   // These two are identical if the function doesn't align its stack dynamically.
     174       27492 :   uint32_t saRegId = _saRegId;
     175       27492 :   if (saRegId == BaseReg::kIdBad)
     176             :     saRegId = kSp;
     177             : 
     178             :   // Fix stack arguments base-register from SP to FP in case it was not picked before and the function performs
     179             :   // dynamic stack alignment.
     180       27492 :   if (hasDA && saRegId == kSp)
     181             :     saRegId = kFp;
     182             : 
     183             :   // Mark as dirty any register but SP if used as SA pointer.
     184       27492 :   if (saRegId != kSp)
     185           0 :     _dirtyRegs[RegGroup::kGp] |= Support::bitMask(saRegId);
     186             : 
     187       27492 :   _spRegId = uint8_t(kSp);
     188       27492 :   _saRegId = uint8_t(saRegId);
     189             : 
     190             :   // Setup stack size used to save preserved registers.
     191       27492 :   uint32_t saveRestoreSizes[2] {};
     192      137460 :   for (RegGroup group : RegGroupVirtValues{})
     193      109968 :     saveRestoreSizes[size_t(!archTraits.hasInstPushPop(group))]
     194      109968 :       += Support::alignUp(Support::popcnt(savedRegs(group)) * saveRestoreRegSize(group), saveRestoreAlignment(group));
     195             : 
     196       27492 :   _pushPopSaveSize  = uint16_t(saveRestoreSizes[0]);
     197       27492 :   _extraRegSaveSize = uint16_t(saveRestoreSizes[1]);
     198             : 
     199             :   uint32_t v = 0;                            // The beginning of the stack frame relative to SP after prolog.
     200             :   v += callStackSize();                      // Count 'callStackSize'      <- This is used to call functions.
     201             :   v  = Support::alignUp(v, stackAlignment);  // Align to function's stack alignment.
     202             : 
     203       27492 :   _localStackOffset = v;                     // Store 'localStackOffset'   <- Function's local stack starts here.
     204       27492 :   v += localStackSize();                     // Count 'localStackSize'     <- Function's local stack ends here.
     205             : 
     206             :   // If the function's stack must be aligned, calculate the alignment necessary to store vector registers, and set
     207             :   // `FuncAttributes::kAlignedVecSR` to inform PEI that it can use instructions that perform aligned stores/loads.
     208       27492 :   if (stackAlignment >= vectorSize && _extraRegSaveSize) {
     209             :     addAttributes(FuncAttributes::kAlignedVecSR);
     210             :     v = Support::alignUp(v, vectorSize);     // Align 'extraRegSaveOffset'.
     211             :   }
     212             : 
     213       27492 :   _extraRegSaveOffset = v;                   // Store 'extraRegSaveOffset' <- Non-GP save/restore starts here.
     214       27492 :   v += _extraRegSaveSize;                    // Count 'extraRegSaveSize'   <- Non-GP save/restore ends here.
     215             : 
     216             :   // Calculate if dynamic alignment (DA) slot (stored as offset relative to SP) is required and its offset.
     217       27492 :   if (hasDA && !hasFP) {
     218           0 :     _daOffset = v;                           // Store 'daOffset'           <- DA pointer would be stored here.
     219           0 :     v += registerSize;                       // Count 'daOffset'.
     220             :   }
     221             :   else {
     222       27492 :     _daOffset = FuncFrame::kTagInvalidOffset;
     223             :   }
     224             : 
     225             :   // Link Register
     226             :   // -------------
     227             :   //
     228             :   // The stack is aligned after the function call as the return address is stored in a link register. Some
     229             :   // architectures may require to always have aligned stack after PUSH/POP operation, which is represented
     230             :   // by ArchTraits::stackAlignmentConstraint().
     231             :   //
     232             :   // No Link Register (X86/X64)
     233             :   // --------------------------
     234             :   //
     235             :   // The return address should be stored after GP save/restore regs. It has the same size as `registerSize`
     236             :   // (basically the native register/pointer size). We don't adjust it now as `v` now contains the exact size
     237             :   // that the function requires to adjust (call frame + stack frame, vec stack size). The stack (if we consider
     238             :   // this size) is misaligned now, as it's always aligned before the function call - when `call()` is executed
     239             :   // it pushes the current EIP|RIP onto the stack, and misaligns it by 12 or 8 bytes (depending on the
     240             :   // architecture). So count number of bytes needed to align it up to the function's CallFrame (the beginning).
     241       27492 :   if (v || hasFuncCalls() || !returnAddressSize)
     242        4990 :     v += Support::alignUpDiff(v + pushPopSaveSize() + returnAddressSize, stackAlignment);
     243             : 
     244       27492 :   _pushPopSaveOffset = v;                    // Store 'pushPopSaveOffset'  <- Function's push/pop save/restore starts here.
     245       27492 :   _stackAdjustment = v;                      // Store 'stackAdjustment'    <- SA used by 'add SP, SA' and 'sub SP, SA'.
     246       27492 :   v += _pushPopSaveSize;                     // Count 'pushPopSaveSize'    <- Function's push/pop save/restore ends here.
     247       27492 :   _finalStackSize = v;                       // Store 'finalStackSize'     <- Final stack used by the function.
     248             : 
     249       27492 :   if (!archTraits.hasLinkReg())
     250       27492 :     v += registerSize;                       // Count 'ReturnAddress'      <- As CALL pushes onto stack.
     251             : 
     252             :   // If the function performs dynamic stack alignment then the stack-adjustment must be aligned.
     253       27492 :   if (hasDA)
     254           0 :     _stackAdjustment = Support::alignUp(_stackAdjustment, stackAlignment);
     255             : 
     256             :   // Calculate where the function arguments start relative to SP.
     257       27492 :   _saOffsetFromSP = hasDA ? FuncFrame::kTagInvalidOffset : v;
     258             : 
     259             :   // Calculate where the function arguments start relative to FP or user-provided register.
     260       27492 :   _saOffsetFromSA = hasFP ? returnAddressSize + registerSize      // Return address + frame pointer.
     261             :                           : returnAddressSize + _pushPopSaveSize; // Return address + all push/pop regs.
     262             : 
     263       27492 :   return kErrorOk;
     264             : }
     265             : 
     266             : // FuncArgsAssignment - UpdateFuncFrame
     267             : // ====================================
     268             : 
     269       27492 : ASMJIT_FAVOR_SIZE Error FuncArgsAssignment::updateFuncFrame(FuncFrame& frame) const noexcept {
     270             :   Arch arch = frame.arch();
     271             :   const FuncDetail* func = funcDetail();
     272             : 
     273       27492 :   if (!func)
     274             :     return DebugUtils::errored(kErrorInvalidState);
     275             : 
     276       27492 :   RAConstraints constraints;
     277       27492 :   ASMJIT_PROPAGATE(constraints.init(arch));
     278             : 
     279       27492 :   FuncArgsContext ctx;
     280       27492 :   ASMJIT_PROPAGATE(ctx.initWorkData(frame, *this, &constraints));
     281       27492 :   ASMJIT_PROPAGATE(ctx.markDstRegsDirty(frame));
     282       27492 :   ASMJIT_PROPAGATE(ctx.markScratchRegs(frame));
     283       27492 :   ASMJIT_PROPAGATE(ctx.markStackArgsReg(frame));
     284             :   return kErrorOk;
     285             : }
     286             : 
     287             : ASMJIT_END_NAMESPACE
     288             : 
     289             : #endif // __PLUMED_HAS_ASMJIT

Generated by: LCOV version 1.16