LCOV - code coverage report
Current view: top level - asmjit - emithelper.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage (other modules) Lines: 13 84 15.5 %
Date: 2026-11-05 10:19:06 Functions: 1 1 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 "emithelper_p.h"
      10             : #include "formatter.h"
      11             : #include "funcargscontext_p.h"
      12             : #include "radefs_p.h"
      13             : 
      14             : // Can be used for debugging...
      15             : // #define ASMJIT_DUMP_ARGS_ASSIGNMENT
      16             : 
      17             : ASMJIT_BEGIN_NAMESPACE
      18             : 
      19             : // BaseEmitHelper - Formatting
      20             : // ===========================
      21             : 
      22             : #ifdef ASMJIT_DUMP_ARGS_ASSIGNMENT
      23             : static void dumpFuncValue(String& sb, Arch arch, const FuncValue& value) noexcept {
      24             :   Formatter::formatTypeId(sb, value.typeId());
      25             :   sb.append('@');
      26             : 
      27             :   if (value.isIndirect())
      28             :     sb.append('[');
      29             : 
      30             :   if (value.isReg())
      31             :     Formatter::formatRegister(sb, 0, nullptr, arch, value.regType(), value.regId());
      32             :   else if (value.isStack())
      33             :     sb.appendFormat("[%d]", value.stackOffset());
      34             :   else
      35             :     sb.append("<none>");
      36             : 
      37             :   if (value.isIndirect())
      38             :     sb.append(']');
      39             : }
      40             : 
      41             : static void dumpAssignment(String& sb, const FuncArgsContext& ctx) noexcept {
      42             :   typedef FuncArgsContext::Var Var;
      43             : 
      44             :   Arch arch = ctx.arch();
      45             :   uint32_t varCount = ctx.varCount();
      46             : 
      47             :   for (uint32_t i = 0; i < varCount; i++) {
      48             :     const Var& var = ctx.var(i);
      49             :     const FuncValue& dst = var.out;
      50             :     const FuncValue& cur = var.cur;
      51             : 
      52             :     sb.appendFormat("Var%u: ", i);
      53             :     dumpFuncValue(sb, arch, dst);
      54             :     sb.append(" <- ");
      55             :     dumpFuncValue(sb, arch, cur);
      56             : 
      57             :     if (var.isDone())
      58             :       sb.append(" {Done}");
      59             : 
      60             :     sb.append('\n');
      61             :   }
      62             : }
      63             : #endif
      64             : 
      65             : // BaseEmitHelper - EmitArgsAssignment
      66             : // ===================================
      67             : 
      68       27492 : ASMJIT_FAVOR_SIZE Error BaseEmitHelper::emitArgsAssignment(const FuncFrame& frame, const FuncArgsAssignment& args) {
      69             :   typedef FuncArgsContext::Var Var;
      70             :   typedef FuncArgsContext::WorkData WorkData;
      71             : 
      72             :   enum WorkFlags : uint32_t {
      73             :     kWorkNone      = 0x00,
      74             :     kWorkDidSome   = 0x01,
      75             :     kWorkPending   = 0x02,
      76             :     kWorkPostponed = 0x04
      77             :   };
      78             : 
      79             :   Arch arch = frame.arch();
      80             :   const ArchTraits& archTraits = ArchTraits::byArch(arch);
      81             : 
      82       27492 :   RAConstraints constraints;
      83       27492 :   FuncArgsContext ctx;
      84             : 
      85       27492 :   ASMJIT_PROPAGATE(constraints.init(arch));
      86       27492 :   ASMJIT_PROPAGATE(ctx.initWorkData(frame, args, &constraints));
      87             : 
      88             : #ifdef ASMJIT_DUMP_ARGS_ASSIGNMENT
      89             :   {
      90             :     String sb;
      91             :     dumpAssignment(sb, ctx);
      92             :     printf("%s\n", sb.data());
      93             :   }
      94             : #endif
      95             : 
      96             :   auto& workData = ctx._workData;
      97       27492 :   uint32_t varCount = ctx._varCount;
      98       27492 :   uint32_t saVarId = ctx._saVarId;
      99             : 
     100       27492 :   BaseReg sp = BaseReg(_emitter->_gpSignature, archTraits.spRegId());
     101             :   BaseReg sa = sp;
     102             : 
     103       27492 :   if (frame.hasDynamicAlignment()) {
     104           0 :     if (frame.hasPreservedFP())
     105             :       sa.setId(archTraits.fpRegId());
     106             :     else
     107           0 :       sa.setId(saVarId < varCount ? ctx._vars[saVarId].cur.regId() : frame.saRegId());
     108             :   }
     109             : 
     110             :   // Register to stack and stack to stack moves must be first as now we have
     111             :   // the biggest chance of having as many as possible unassigned registers.
     112             : 
     113       27492 :   if (ctx._stackDstMask) {
     114             :     // Base address of all arguments passed by stack.
     115           0 :     BaseMem baseArgPtr(sa, int32_t(frame.saOffset(sa.id())));
     116           0 :     BaseMem baseStackPtr(sp, 0);
     117             : 
     118           0 :     for (uint32_t varId = 0; varId < varCount; varId++) {
     119           0 :       Var& var = ctx._vars[varId];
     120             : 
     121           0 :       if (!var.out.isStack())
     122           0 :         continue;
     123             : 
     124             :       FuncValue& cur = var.cur;
     125             :       FuncValue& out = var.out;
     126             : 
     127             :       ASMJIT_ASSERT(cur.isReg() || cur.isStack());
     128           0 :       BaseReg reg;
     129             : 
     130           0 :       BaseMem dstStackPtr = baseStackPtr.cloneAdjusted(out.stackOffset());
     131           0 :       BaseMem srcStackPtr = baseArgPtr.cloneAdjusted(cur.stackOffset());
     132             : 
     133           0 :       if (cur.isIndirect()) {
     134           0 :         if (cur.isStack()) {
     135             :           // TODO: Indirect stack.
     136           0 :           return DebugUtils::errored(kErrorInvalidAssignment);
     137             :         }
     138             :         else {
     139             :           srcStackPtr.setBaseId(cur.regId());
     140             :         }
     141             :       }
     142             : 
     143           0 :       if (cur.isReg() && !cur.isIndirect()) {
     144             :         WorkData& wd = workData[archTraits.regTypeToGroup(cur.regType())];
     145             :         uint32_t regId = cur.regId();
     146             : 
     147             :         reg.setSignatureAndId(archTraits.regTypeToSignature(cur.regType()), regId);
     148           0 :         wd.unassign(varId, regId);
     149             :       }
     150             :       else {
     151             :         // Stack to reg move - tricky since we move stack to stack we can decide which register to use. In general
     152             :         // we follow the rule that IntToInt moves will use GP regs with possibility to signature or zero extend,
     153             :         // and all other moves will either use GP or VEC regs depending on the size of the move.
     154           0 :         OperandSignature signature = getSuitableRegForMemToMemMove(arch, out.typeId(), cur.typeId());
     155           0 :         if (ASMJIT_UNLIKELY(!signature.isValid()))
     156             :           return DebugUtils::errored(kErrorInvalidState);
     157             : 
     158             :         WorkData& wd = workData[signature.regGroup()];
     159             :         RegMask availableRegs = wd.availableRegs();
     160           0 :         if (ASMJIT_UNLIKELY(!availableRegs))
     161             :           return DebugUtils::errored(kErrorInvalidState);
     162             : 
     163             :         uint32_t availableId = Support::ctz(availableRegs);
     164             :         reg.setSignatureAndId(signature, availableId);
     165             : 
     166           0 :         ASMJIT_PROPAGATE(emitArgMove(reg, out.typeId(), srcStackPtr, cur.typeId()));
     167             :       }
     168             : 
     169           0 :       if (cur.isIndirect() && cur.isReg())
     170           0 :         workData[RegGroup::kGp].unassign(varId, cur.regId());
     171             : 
     172             :       // Register to stack move.
     173           0 :       ASMJIT_PROPAGATE(emitRegMove(dstStackPtr, reg, cur.typeId()));
     174             :       var.markDone();
     175             :     }
     176             :   }
     177             : 
     178             :   // Shuffle all registers that are currently assigned accordingly to target assignment.
     179             : 
     180             :   uint32_t workFlags = kWorkNone;
     181             :   for (;;) {
     182       27492 :     for (uint32_t varId = 0; varId < varCount; varId++) {
     183           0 :       Var& var = ctx._vars[varId];
     184           0 :       if (var.isDone() || !var.cur.isReg())
     185             :         continue;
     186             : 
     187           0 :       FuncValue& cur = var.cur;
     188             :       FuncValue& out = var.out;
     189             : 
     190             :       RegGroup curGroup = archTraits.regTypeToGroup(cur.regType());
     191             :       RegGroup outGroup = archTraits.regTypeToGroup(out.regType());
     192             : 
     193             :       uint32_t curId = cur.regId();
     194             :       uint32_t outId = out.regId();
     195             : 
     196           0 :       if (curGroup != outGroup) {
     197             :         // TODO: Conversion is not supported.
     198             :         return DebugUtils::errored(kErrorInvalidAssignment);
     199             :       }
     200             :       else {
     201             :         WorkData& wd = workData[outGroup];
     202           0 :         if (!wd.isAssigned(outId)) {
     203           0 : EmitMove:
     204           0 :           ASMJIT_PROPAGATE(
     205             :             emitArgMove(
     206             :               BaseReg(archTraits.regTypeToSignature(out.regType()), outId), out.typeId(),
     207             :               BaseReg(archTraits.regTypeToSignature(cur.regType()), curId), cur.typeId()));
     208             : 
     209           0 :           wd.reassign(varId, outId, curId);
     210           0 :           cur.initReg(out.regType(), outId, out.typeId());
     211             : 
     212           0 :           if (outId == out.regId())
     213             :             var.markDone();
     214           0 :           workFlags |= kWorkDidSome | kWorkPending;
     215             :         }
     216             :         else {
     217           0 :           uint32_t altId = wd._physToVarId[outId];
     218           0 :           Var& altVar = ctx._vars[altId];
     219             : 
     220           0 :           if (!altVar.out.isInitialized() || (altVar.out.isReg() && altVar.out.regId() == curId)) {
     221             :             // Only few architectures provide swap operations, and only for few register groups.
     222           0 :             if (archTraits.hasInstRegSwap(curGroup)) {
     223           0 :               RegType highestType = Support::max(cur.regType(), altVar.cur.regType());
     224           0 :               if (Support::isBetween(highestType, RegType::kGp8Lo, RegType::kGp16))
     225           0 :                 highestType = RegType::kGp32;
     226             : 
     227           0 :               OperandSignature signature = archTraits.regTypeToSignature(highestType);
     228           0 :               ASMJIT_PROPAGATE(
     229             :                 emitRegSwap(BaseReg(signature, outId), BaseReg(signature, curId)));
     230             : 
     231             :               wd.swap(varId, curId, altId, outId);
     232           0 :               cur.setRegId(outId);
     233             :               var.markDone();
     234           0 :               altVar.cur.setRegId(curId);
     235             : 
     236           0 :               if (altVar.out.isInitialized())
     237             :                 altVar.markDone();
     238           0 :               workFlags |= kWorkDidSome;
     239             :             }
     240             :             else {
     241             :               // If there is a scratch register it can be used to perform the swap.
     242             :               RegMask availableRegs = wd.availableRegs();
     243           0 :               if (availableRegs) {
     244             :                 RegMask inOutRegs = wd.dstRegs();
     245           0 :                 if (availableRegs & ~inOutRegs)
     246             :                   availableRegs &= ~inOutRegs;
     247             :                 outId = Support::ctz(availableRegs);
     248           0 :                 goto EmitMove;
     249             :               }
     250             :               else {
     251           0 :                 workFlags |= kWorkPending;
     252             :               }
     253             :             }
     254             :           }
     255             :           else {
     256           0 :             workFlags |= kWorkPending;
     257             :           }
     258             :         }
     259             :       }
     260             :     }
     261             : 
     262       27492 :     if (!(workFlags & kWorkPending))
     263             :       break;
     264             : 
     265             :     // If we did nothing twice it means that something is really broken.
     266           0 :     if ((workFlags & (kWorkDidSome | kWorkPostponed)) == kWorkPostponed)
     267             :       return DebugUtils::errored(kErrorInvalidState);
     268             : 
     269           0 :     workFlags = (workFlags & kWorkDidSome) ? kWorkNone : kWorkPostponed;
     270             :   }
     271             : 
     272             :   // Load arguments passed by stack into registers. This is pretty simple and
     273             :   // it never requires multiple iterations like the previous phase.
     274             : 
     275       27492 :   if (ctx._hasStackSrc) {
     276             :     uint32_t iterCount = 1;
     277           0 :     if (frame.hasDynamicAlignment() && !frame.hasPreservedFP())
     278           0 :       sa.setId(saVarId < varCount ? ctx._vars[saVarId].cur.regId() : frame.saRegId());
     279             : 
     280             :     // Base address of all arguments passed by stack.
     281           0 :     BaseMem baseArgPtr(sa, int32_t(frame.saOffset(sa.id())));
     282             : 
     283           0 :     for (uint32_t iter = 0; iter < iterCount; iter++) {
     284           0 :       for (uint32_t varId = 0; varId < varCount; varId++) {
     285           0 :         Var& var = ctx._vars[varId];
     286           0 :         if (var.isDone())
     287           0 :           continue;
     288             : 
     289           0 :         if (var.cur.isStack()) {
     290             :           ASMJIT_ASSERT(var.out.isReg());
     291             : 
     292             :           uint32_t outId = var.out.regId();
     293             :           RegType outType = var.out.regType();
     294             : 
     295             :           RegGroup group = archTraits.regTypeToGroup(outType);
     296             :           WorkData& wd = workData[group];
     297             : 
     298           0 :           if (outId == sa.id() && group == RegGroup::kGp) {
     299             :             // This register will be processed last as we still need `saRegId`.
     300           0 :             if (iterCount == 1) {
     301             :               iterCount++;
     302           0 :               continue;
     303             :             }
     304           0 :             wd.unassign(wd._physToVarId[outId], outId);
     305             :           }
     306             : 
     307             :           BaseReg dstReg = BaseReg(archTraits.regTypeToSignature(outType), outId);
     308           0 :           BaseMem srcMem = baseArgPtr.cloneAdjusted(var.cur.stackOffset());
     309             : 
     310           0 :           ASMJIT_PROPAGATE(emitArgMove(
     311             :             dstReg, var.out.typeId(),
     312             :             srcMem, var.cur.typeId()));
     313             : 
     314           0 :           wd.assign(varId, outId);
     315           0 :           var.cur.initReg(outType, outId, var.cur.typeId(), FuncValue::kFlagIsDone);
     316             :         }
     317             :       }
     318             :     }
     319             :   }
     320             : 
     321             :   return kErrorOk;
     322             : }
     323             : 
     324             : ASMJIT_END_NAMESPACE
     325             : 
     326             : #endif // __PLUMED_HAS_ASMJIT

Generated by: LCOV version 1.16