LCOV - code coverage report
Current view: top level - asmjit - x86rapass.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage (other modules) Lines: 205 461 44.5 %
Date: 2026-11-05 10:19:06 Functions: 17 24 70.8 %

          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             : #if !defined(ASMJIT_NO_X86) && !defined(ASMJIT_NO_COMPILER)
       9             : 
      10             : #include "cpuinfo.h"
      11             : #include "support.h"
      12             : #include "type.h"
      13             : #include "x86assembler.h"
      14             : #include "x86compiler.h"
      15             : #include "x86instapi_p.h"
      16             : #include "x86instdb_p.h"
      17             : #include "x86emithelper_p.h"
      18             : #include "x86rapass_p.h"
      19             : 
      20             : ASMJIT_BEGIN_SUB_NAMESPACE(x86)
      21             : 
      22             : // x86::X86RAPass - Utilities
      23             : // ==========================
      24             : 
      25             : static ASMJIT_FORCE_INLINE uint64_t raImmMaskFromSize(uint32_t size) noexcept {
      26             :   ASMJIT_ASSERT(size > 0 && size < 256);
      27             :   static constexpr uint64_t masks[] = {
      28             :     0x00000000000000FFu, //   1
      29             :     0x000000000000FFFFu, //   2
      30             :     0x00000000FFFFFFFFu, //   4
      31             :     0xFFFFFFFFFFFFFFFFu, //   8
      32             :     0x0000000000000000u, //  16
      33             :     0x0000000000000000u, //  32
      34             :     0x0000000000000000u, //  64
      35             :     0x0000000000000000u, // 128
      36             :     0x0000000000000000u  // 256
      37             :   };
      38           0 :   return masks[Support::ctz(size)];
      39             : }
      40             : 
      41             : static const RegMask raConsecutiveLeadCountToRegMaskFilter[5] = {
      42             :   0xFFFFFFFFu, // [0] No consecutive.
      43             :   0x00000000u, // [1] Invalid, never used.
      44             :   0x55555555u, // [2] Even registers.
      45             :   0x00000000u, // [3] Invalid, never used.
      46             :   0x11111111u  // [4] Every fourth register.
      47             : };
      48             : 
      49             : static ASMJIT_FORCE_INLINE RATiedFlags raUseOutFlagsFromRWFlags(OpRWFlags rwFlags) noexcept {
      50             :   static constexpr RATiedFlags map[] = {
      51             :     RATiedFlags::kNone,
      52             :     RATiedFlags::kRead  | RATiedFlags::kUse,                       // kRead
      53             :     RATiedFlags::kWrite | RATiedFlags::kOut,                       // kWrite
      54             :     RATiedFlags::kRW    | RATiedFlags::kUse,                       // kRW
      55             :     RATiedFlags::kNone,
      56             :     RATiedFlags::kRead  | RATiedFlags::kUse | RATiedFlags::kUseRM, // kRead  | kRegMem
      57             :     RATiedFlags::kWrite | RATiedFlags::kOut | RATiedFlags::kOutRM, // kWrite | kRegMem
      58             :     RATiedFlags::kRW    | RATiedFlags::kUse | RATiedFlags::kUseRM  // kRW    | kRegMem
      59             :   };
      60             : 
      61      575407 :   return map[uint32_t(rwFlags & (OpRWFlags::kRW | OpRWFlags::kRegMem))];
      62             : }
      63             : 
      64             : static ASMJIT_FORCE_INLINE RATiedFlags raRegRwFlags(OpRWFlags flags) noexcept {
      65             :   return (RATiedFlags)raUseOutFlagsFromRWFlags(flags);
      66             : }
      67             : 
      68             : static ASMJIT_FORCE_INLINE RATiedFlags raMemBaseRwFlags(OpRWFlags flags) noexcept {
      69             :   constexpr uint32_t kShift = Support::ConstCTZ<uint32_t(OpRWFlags::kMemBaseRW)>::value;
      70       70974 :   return (RATiedFlags)raUseOutFlagsFromRWFlags(OpRWFlags(uint32_t(flags) >> kShift) & OpRWFlags::kRW);
      71             : }
      72             : 
      73             : static ASMJIT_FORCE_INLINE RATiedFlags raMemIndexRwFlags(OpRWFlags flags) noexcept {
      74             :   constexpr uint32_t kShift = Support::ConstCTZ<uint32_t(OpRWFlags::kMemIndexRW)>::value;
      75           0 :   return (RATiedFlags)raUseOutFlagsFromRWFlags(OpRWFlags(uint32_t(flags) >> kShift) & OpRWFlags::kRW);
      76             : }
      77             : 
      78             : // x86::RACFGBuilder
      79             : // =================
      80             : 
      81             : class RACFGBuilder : public RACFGBuilderT<RACFGBuilder> {
      82             : public:
      83             :   Arch _arch;
      84             :   bool _is64Bit;
      85             :   bool _avxEnabled;
      86             : 
      87       27492 :   inline RACFGBuilder(X86RAPass* pass) noexcept
      88       27492 :     : RACFGBuilderT<RACFGBuilder>(pass),
      89       27492 :       _arch(pass->cc()->arch()),
      90       27492 :       _is64Bit(pass->registerSize() == 8),
      91       27492 :       _avxEnabled(pass->avxEnabled()) {
      92       27492 :   }
      93             : 
      94       46958 :   inline Compiler* cc() const noexcept { return static_cast<Compiler*>(_cc); }
      95             : 
      96             :   inline uint32_t choose(uint32_t sseInst, uint32_t avxInst) const noexcept {
      97           0 :     return _avxEnabled ? avxInst : sseInst;
      98             :   }
      99             : 
     100             :   Error onInst(InstNode* inst, InstControlFlow& cf, RAInstBuilder& ib) noexcept;
     101             : 
     102             :   Error onBeforeInvoke(InvokeNode* invokeNode) noexcept;
     103             :   Error onInvoke(InvokeNode* invokeNode, RAInstBuilder& ib) noexcept;
     104             : 
     105             :   Error moveVecToPtr(InvokeNode* invokeNode, const FuncValue& arg, const Vec& src, BaseReg* out) noexcept;
     106             :   Error moveImmToRegArg(InvokeNode* invokeNode, const FuncValue& arg, const Imm& imm_, BaseReg* out) noexcept;
     107             :   Error moveImmToStackArg(InvokeNode* invokeNode, const FuncValue& arg, const Imm& imm_) noexcept;
     108             :   Error moveRegToStackArg(InvokeNode* invokeNode, const FuncValue& arg, const BaseReg& reg) noexcept;
     109             : 
     110             :   Error onBeforeRet(FuncRetNode* funcRet) noexcept;
     111             :   Error onRet(FuncRetNode* funcRet, RAInstBuilder& ib) noexcept;
     112             : };
     113             : 
     114             : // x86::RACFGBuilder - OnInst
     115             : // ==========================
     116             : 
     117      331808 : Error RACFGBuilder::onInst(InstNode* inst, InstControlFlow& cf, RAInstBuilder& ib) noexcept {
     118             :   InstRWInfo rwInfo;
     119             : 
     120             :   InstId instId = inst->id();
     121      331808 :   if (Inst::isDefinedId(instId)) {
     122             :     uint32_t opCount = inst->opCount();
     123             :     const Operand* opArray = inst->operands();
     124      304316 :     ASMJIT_PROPAGATE(InstInternal::queryRWInfo(_arch, inst->baseInst(), opArray, opCount, &rwInfo));
     125             : 
     126             :     const InstDB::InstInfo& instInfo = InstDB::infoById(instId);
     127             :     bool hasGpbHiConstraint = false;
     128             :     uint32_t singleRegOps = 0;
     129             : 
     130             :     // Copy instruction RW flags to instruction builder except kMovOp, which is propagated manually later.
     131             :     ib.addInstRWFlags(rwInfo.instFlags() & ~InstRWFlags::kMovOp);
     132             : 
     133             :     // Mask of all operand types used by the instruction - can be used as an optimization later.
     134             :     uint32_t opTypesMask = 0u;
     135             : 
     136      304316 :     if (opCount) {
     137             :       // The mask is for all registers, but we are mostly interested in AVX-512 registers at the moment. The mask
     138             :       // will be combined with all available registers of the Compiler at the end so we it never use more registers
     139             :       // than available.
     140             :       RegMask instructionAllowedRegs = 0xFFFFFFFFu;
     141             : 
     142             :       uint32_t consecutiveOffset = 0;
     143             :       uint32_t consecutiveLeadId = Globals::kInvalidId;
     144             :       uint32_t consecutiveParent = Globals::kInvalidId;
     145             : 
     146      304316 :       if (instInfo.isEvex()) {
     147             :         // EVEX instruction and VEX instructions that can be encoded with EVEX have the possibility to use 32 SIMD
     148             :         // registers (XMM/YMM/ZMM).
     149      206278 :         if (instInfo.isVex() && !instInfo.isEvexCompatible()) {
     150        1970 :           if (instInfo.isEvexKRegOnly()) {
     151             :             // EVEX encodable only if the first operand is K register (compare instructions).
     152        1970 :             if (!Reg::isKReg(opArray[0]))
     153             :               instructionAllowedRegs = 0xFFFFu;
     154             :           }
     155           0 :           else if (instInfo.isEvexTwoOpOnly()) {
     156             :             // EVEX encodable only if the instruction has two operands (gather instructions).
     157           0 :             if (opCount != 2)
     158             :               instructionAllowedRegs = 0xFFFFu;
     159             :           }
     160             :           else {
     161             :             instructionAllowedRegs = 0xFFFFu;
     162             :           }
     163             :         }
     164             :       }
     165       98038 :       else if (instInfo.isEvexTransformable()) {
     166             :         ib.addAggregatedFlags(RATiedFlags::kInst_IsTransformable);
     167             :       }
     168             :       else {
     169             :         // Not EVEX, restrict everything to [0-15] registers.
     170             :         instructionAllowedRegs = 0xFFFFu;
     171             :       }
     172             : 
     173     1040909 :       for (uint32_t i = 0; i < opCount; i++) {
     174      736593 :         const Operand& op = opArray[i];
     175             :         const OpRWInfo& opRwInfo = rwInfo.operand(i);
     176             : 
     177      736593 :         opTypesMask |= 1u << uint32_t(op.opType());
     178             : 
     179      736593 :         if (op.isReg()) {
     180             :           // Register Operand
     181             :           // ----------------
     182             :           const Reg& reg = op.as<Reg>();
     183             : 
     184             :           RATiedFlags flags = raRegRwFlags(opRwInfo.opFlags());
     185             :           RegMask allowedRegs = instructionAllowedRegs;
     186             : 
     187             :           // X86-specific constraints related to LO|HI general purpose registers. This is only required when the
     188             :           // register is part of the encoding. If the register is fixed we won't restrict anything as it doesn't
     189             :           // restrict encoding of other registers.
     190      575407 :           if (reg.isGpb() && !opRwInfo.hasOpFlag(OpRWFlags::kRegPhysId)) {
     191             :             flags |= RATiedFlags::kX86_Gpb;
     192           0 :             if (!_is64Bit) {
     193             :               // Restrict to first four - AL|AH|BL|BH|CL|CH|DL|DH. In 32-bit mode it's not possible to access
     194             :               // SIL|DIL, etc, so this is just enough.
     195             :               allowedRegs = 0x0Fu;
     196             :             }
     197             :             else {
     198             :               // If we encountered GPB-HI register the situation is much more complicated than in 32-bit mode.
     199             :               // We need to patch all registers to not use ID higher than 7 and all GPB-LO registers to not use
     200             :               // index higher than 3. Instead of doing the patching here we just set a flag and will do it later,
     201             :               // to not complicate this loop.
     202           0 :               if (reg.isGpbHi()) {
     203             :                 hasGpbHiConstraint = true;
     204             :                 allowedRegs = 0x0Fu;
     205             :               }
     206             :             }
     207             :           }
     208             : 
     209             :           uint32_t vIndex = Operand::virtIdToIndex(reg.id());
     210      575407 :           if (vIndex < Operand::kVirtIdCount) {
     211             :             RAWorkReg* workReg;
     212     1150814 :             ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(vIndex, &workReg));
     213             : 
     214             :             // Use RW instead of Write in case that not the whole register is overwritten. This is important
     215             :             // for liveness as we cannot kill a register that will be used. For example `mov al, 0xFF` is not
     216             :             // a write-only operation if user allocated the whole `rax` register.
     217      575407 :             if ((flags & RATiedFlags::kRW) == RATiedFlags::kWrite) {
     218      294583 :               if (workReg->regByteMask() & ~(opRwInfo.writeByteMask() | opRwInfo.extendByteMask())) {
     219             :                 // Not write-only operation.
     220             :                 flags = (flags & ~RATiedFlags::kOut) | (RATiedFlags::kRead | RATiedFlags::kUse);
     221             :               }
     222             :             }
     223             : 
     224             :             // Do not use RegMem flag if changing Reg to Mem requires additional CPU feature that may not be enabled.
     225      575407 :             if (rwInfo.rmFeature() && Support::test(flags, RATiedFlags::kUseRM | RATiedFlags::kOutRM)) {
     226             :               flags &= ~(RATiedFlags::kUseRM | RATiedFlags::kOutRM);
     227             :             }
     228             : 
     229      575407 :             RegGroup group = workReg->group();
     230      575407 :             RegMask useRegs = _pass->_availableRegs[group] & allowedRegs;
     231             :             RegMask outRegs = useRegs;
     232             : 
     233             :             uint32_t useId = BaseReg::kIdBad;
     234             :             uint32_t outId = BaseReg::kIdBad;
     235             : 
     236             :             uint32_t useRewriteMask = 0;
     237             :             uint32_t outRewriteMask = 0;
     238             : 
     239      575407 :             if (opRwInfo.consecutiveLeadCount()) {
     240             :               // There must be a single consecutive register lead, otherwise the RW data is invalid.
     241           0 :               if (consecutiveLeadId != Globals::kInvalidId)
     242             :                 return DebugUtils::errored(kErrorInvalidState);
     243             : 
     244             :               // A consecutive lead register cannot be used as a consecutive +1/+2/+3 register, the registers must be distinct.
     245           0 :               if (RATiedReg::consecutiveDataFromFlags(flags) != 0)
     246             :                 return DebugUtils::errored(kErrorNotConsecutiveRegs);
     247             : 
     248           0 :               flags |= RATiedFlags::kLeadConsecutive | RATiedReg::consecutiveDataToFlags(opRwInfo.consecutiveLeadCount() - 1);
     249             :               consecutiveLeadId = workReg->workId();
     250             : 
     251           0 :               RegMask filter = raConsecutiveLeadCountToRegMaskFilter[opRwInfo.consecutiveLeadCount()];
     252           0 :               if (Support::test(flags, RATiedFlags::kUse)) {
     253             :                 flags |= RATiedFlags::kUseConsecutive;
     254           0 :                 useRegs &= filter;
     255             :               }
     256             :               else {
     257             :                 flags |= RATiedFlags::kOutConsecutive;
     258           0 :                 outRegs &= filter;
     259             :               }
     260             :             }
     261             : 
     262             :             if (Support::test(flags, RATiedFlags::kUse)) {
     263      280824 :               useRewriteMask = Support::bitMask(inst->getRewriteIndex(&reg._baseId));
     264      280824 :               if (opRwInfo.hasOpFlag(OpRWFlags::kRegPhysId)) {
     265             :                 useId = opRwInfo.physId();
     266             :                 flags |= RATiedFlags::kUseFixed;
     267             :               }
     268      280824 :               else if (opRwInfo.hasOpFlag(OpRWFlags::kConsecutive)) {
     269           0 :                 if (consecutiveLeadId == Globals::kInvalidId)
     270             :                   return DebugUtils::errored(kErrorInvalidState);
     271             : 
     272           0 :                 if (consecutiveLeadId == workReg->workId())
     273             :                   return DebugUtils::errored(kErrorOverlappedRegs);
     274             : 
     275           0 :                 flags |= RATiedFlags::kUseConsecutive | RATiedReg::consecutiveDataToFlags(++consecutiveOffset);
     276             :               }
     277             :             }
     278             :             else {
     279      294583 :               outRewriteMask = Support::bitMask(inst->getRewriteIndex(&reg._baseId));
     280      294583 :               if (opRwInfo.hasOpFlag(OpRWFlags::kRegPhysId)) {
     281             :                 outId = opRwInfo.physId();
     282             :                 flags |= RATiedFlags::kOutFixed;
     283             :               }
     284      294583 :               else if (opRwInfo.hasOpFlag(OpRWFlags::kConsecutive)) {
     285           0 :                 if (consecutiveLeadId == Globals::kInvalidId)
     286             :                   return DebugUtils::errored(kErrorInvalidState);
     287             : 
     288           0 :                 if (consecutiveLeadId == workReg->workId())
     289             :                   return DebugUtils::errored(kErrorOverlappedRegs);
     290             : 
     291           0 :                 flags |= RATiedFlags::kOutConsecutive | RATiedReg::consecutiveDataToFlags(++consecutiveOffset);
     292             :               }
     293             :             }
     294             : 
     295      575407 :             ASMJIT_PROPAGATE(ib.add(workReg, flags, useRegs, useId, useRewriteMask, outRegs, outId, outRewriteMask, opRwInfo.rmSize(), consecutiveParent));
     296      575407 :             if (singleRegOps == i)
     297      575313 :               singleRegOps++;
     298             : 
     299      575407 :             if (Support::test(flags, RATiedFlags::kLeadConsecutive | RATiedFlags::kUseConsecutive | RATiedFlags::kOutConsecutive))
     300           0 :               consecutiveParent = workReg->workId();
     301             :           }
     302             :         }
     303      161186 :         else if (op.isMem()) {
     304             :           // Memory Operand
     305             :           // --------------
     306             :           const Mem& mem = op.as<Mem>();
     307             :           ib.addForbiddenFlags(RATiedFlags::kUseRM | RATiedFlags::kOutRM);
     308             : 
     309       70974 :           if (mem.isRegHome()) {
     310             :             RAWorkReg* workReg;
     311           0 :             ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(Operand::virtIdToIndex(mem.baseId()), &workReg));
     312           0 :             _pass->getOrCreateStackSlot(workReg);
     313             :           }
     314       70974 :           else if (mem.hasBaseReg()) {
     315             :             uint32_t vIndex = Operand::virtIdToIndex(mem.baseId());
     316       70974 :             if (vIndex < Operand::kVirtIdCount) {
     317             :               RAWorkReg* workReg;
     318      141948 :               ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(vIndex, &workReg));
     319             : 
     320             :               RATiedFlags flags = raMemBaseRwFlags(opRwInfo.opFlags());
     321       70974 :               RegGroup group = workReg->group();
     322       70974 :               RegMask inOutRegs = _pass->_availableRegs[group];
     323             : 
     324             :               uint32_t useId = BaseReg::kIdBad;
     325             :               uint32_t outId = BaseReg::kIdBad;
     326             : 
     327             :               uint32_t useRewriteMask = 0;
     328             :               uint32_t outRewriteMask = 0;
     329             : 
     330       70974 :               if (Support::test(flags, RATiedFlags::kUse)) {
     331       70974 :                 useRewriteMask = Support::bitMask(inst->getRewriteIndex(&mem._baseId));
     332       70974 :                 if (opRwInfo.hasOpFlag(OpRWFlags::kMemPhysId)) {
     333             :                   useId = opRwInfo.physId();
     334             :                   flags |= RATiedFlags::kUseFixed;
     335             :                 }
     336             :               }
     337             :               else {
     338           0 :                 outRewriteMask = Support::bitMask(inst->getRewriteIndex(&mem._baseId));
     339           0 :                 if (opRwInfo.hasOpFlag(OpRWFlags::kMemPhysId)) {
     340             :                   outId = opRwInfo.physId();
     341             :                   flags |= RATiedFlags::kOutFixed;
     342             :                 }
     343             :               }
     344             : 
     345       70974 :               ASMJIT_PROPAGATE(ib.add(workReg, flags, inOutRegs, useId, useRewriteMask, inOutRegs, outId, outRewriteMask));
     346             :             }
     347             :           }
     348             : 
     349       70974 :           if (mem.hasIndexReg()) {
     350             :             uint32_t vIndex = Operand::virtIdToIndex(mem.indexId());
     351           0 :             if (vIndex < Operand::kVirtIdCount) {
     352             :               RAWorkReg* workReg;
     353           0 :               ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(vIndex, &workReg));
     354             : 
     355             :               RATiedFlags flags = raMemIndexRwFlags(opRwInfo.opFlags());
     356           0 :               RegGroup group = workReg->group();
     357           0 :               RegMask inOutRegs = _pass->_availableRegs[group] & instructionAllowedRegs;
     358             : 
     359             :               // Index registers have never fixed id on X86/x64.
     360             :               const uint32_t useId = BaseReg::kIdBad;
     361             :               const uint32_t outId = BaseReg::kIdBad;
     362             : 
     363             :               uint32_t useRewriteMask = 0;
     364             :               uint32_t outRewriteMask = 0;
     365             : 
     366           0 :               if (Support::test(flags, RATiedFlags::kUse))
     367           0 :                 useRewriteMask = Support::bitMask(inst->getRewriteIndex(&mem._data[Operand::kDataMemIndexId]));
     368             :               else
     369           0 :                 outRewriteMask = Support::bitMask(inst->getRewriteIndex(&mem._data[Operand::kDataMemIndexId]));
     370             : 
     371           0 :               ASMJIT_PROPAGATE(ib.add(workReg, RATiedFlags::kUse | RATiedFlags::kRead, inOutRegs, useId, useRewriteMask, inOutRegs, outId, outRewriteMask));
     372             :             }
     373             :           }
     374             :         }
     375             :       }
     376             :     }
     377             : 
     378             :     // Handle extra operand (either REP {cx|ecx|rcx} or AVX-512 {k} selector).
     379      304316 :     if (inst->hasExtraReg()) {
     380             :       uint32_t vIndex = Operand::virtIdToIndex(inst->extraReg().id());
     381           0 :       if (vIndex < Operand::kVirtIdCount) {
     382             :         RAWorkReg* workReg;
     383           0 :         ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(vIndex, &workReg));
     384             : 
     385           0 :         RegGroup group = workReg->group();
     386           0 :         RegMask inOutRegs = _pass->_availableRegs[group];
     387             :         uint32_t rewriteMask = Support::bitMask(inst->getRewriteIndex(&inst->extraReg()._id));
     388             : 
     389           0 :         if (group == RegGroup::kX86_K) {
     390             :           // AVX-512 mask selector {k} register - read-only, allocable to any register except {k0}.
     391           0 :           ASMJIT_PROPAGATE(ib.add(workReg, RATiedFlags::kUse | RATiedFlags::kRead, inOutRegs, BaseReg::kIdBad, rewriteMask, inOutRegs, BaseReg::kIdBad, 0));
     392             :           singleRegOps = 0;
     393             :         }
     394             :         else {
     395             :           // REP {cx|ecx|rcx} register - read & write, allocable to {cx|ecx|rcx} only.
     396           0 :           ASMJIT_PROPAGATE(ib.add(workReg, RATiedFlags::kUse | RATiedFlags::kRW, inOutRegs, Gp::kIdCx, rewriteMask, inOutRegs, Gp::kIdBad, 0));
     397             :         }
     398             :       }
     399             :       else {
     400             :         RegGroup group = inst->extraReg().group();
     401           0 :         if (group == RegGroup::kX86_K && inst->extraReg().id() != 0)
     402             :           singleRegOps = 0;
     403             :       }
     404             :     }
     405             : 
     406             :     // If this instruction has move semantics then check whether it could be eliminated if all virtual registers
     407             :     // are allocated into the same register. Take into account the virtual size of the destination register as that's
     408             :     // more important than a physical register size in this case.
     409      304316 :     if (rwInfo.hasInstFlag(InstRWFlags::kMovOp) && !inst->hasExtraReg() && Support::bitTest(opTypesMask, uint32_t(OperandType::kReg))) {
     410             :       // AVX+ move instructions have 3 operand form - the first two operands must be the same to guarantee move semantics.
     411       30807 :       if (opCount == 2 || (opCount == 3 && opArray[0] == opArray[1])) {
     412             :         uint32_t vIndex = Operand::virtIdToIndex(opArray[0].as<Reg>().id());
     413           0 :         if (vIndex < Operand::kVirtIdCount) {
     414           0 :           const VirtReg* vReg = _cc->virtRegByIndex(vIndex);
     415             :           const OpRWInfo& opRwInfo = rwInfo.operand(0);
     416             : 
     417           0 :           uint64_t remainingByteMask = vReg->workReg()->regByteMask() & ~opRwInfo.writeByteMask();
     418           0 :           if (remainingByteMask == 0u || (remainingByteMask & opRwInfo.extendByteMask()) == 0)
     419             :             ib.addInstRWFlags(InstRWFlags::kMovOp);
     420             :         }
     421             :       }
     422             :     }
     423             : 
     424             :     // Handle X86 constraints.
     425      304316 :     if (hasGpbHiConstraint) {
     426           0 :       for (RATiedReg& tiedReg : ib) {
     427           0 :         RegMask filter = tiedReg.hasFlag(RATiedFlags::kX86_Gpb) ? 0x0Fu : 0xFFu;
     428           0 :         tiedReg._useRegMask &= filter;
     429           0 :         tiedReg._outRegMask &= filter;
     430             :       }
     431             :     }
     432             : 
     433      304316 :     if (ib.tiedRegCount() == 1) {
     434             :       // Handle special cases of some instructions where all operands share the same
     435             :       // register. In such case the single operand becomes read-only or write-only.
     436             :       InstSameRegHint sameRegHint = InstSameRegHint::kNone;
     437      116405 :       if (singleRegOps == opCount) {
     438             :         sameRegHint = instInfo.sameRegHint();
     439             :       }
     440       88236 :       else if (opCount == 2 && inst->op(1).isImm()) {
     441             :         // Handle some tricks used by X86 asm.
     442             :         const BaseReg& reg = inst->op(0).as<BaseReg>();
     443             :         const Imm& imm = inst->op(1).as<Imm>();
     444             : 
     445       88236 :         const RAWorkReg* workReg = _pass->workRegById(ib[0]->workId());
     446             :         uint32_t workRegSize = workReg->signature().size();
     447             : 
     448       88236 :         switch (inst->id()) {
     449           0 :           case Inst::kIdOr: {
     450             :             // Sets the value of the destination register to -1, previous content unused.
     451           0 :             if (reg.size() >= 4 || reg.size() >= workRegSize) {
     452           0 :               if (imm.value() == -1 || imm.valueAs<uint64_t>() == raImmMaskFromSize(reg.size()))
     453             :                 sameRegHint = InstSameRegHint::kWO;
     454             :             }
     455             :             ASMJIT_FALLTHROUGH;
     456             :           }
     457             : 
     458             :           case Inst::kIdAdd:
     459             :           case Inst::kIdAnd:
     460             :           case Inst::kIdRol:
     461             :           case Inst::kIdRor:
     462             :           case Inst::kIdSar:
     463             :           case Inst::kIdShl:
     464             :           case Inst::kIdShr:
     465             :           case Inst::kIdSub:
     466             :           case Inst::kIdXor: {
     467             :             // Updates [E|R]FLAGS without changing the content.
     468           0 :             if (reg.size() != 4 || reg.size() >= workRegSize) {
     469           0 :               if (imm.value() == 0)
     470             :                 sameRegHint = InstSameRegHint::kRO;
     471             :             }
     472             :             break;
     473             :           }
     474             :         }
     475             :       }
     476             : 
     477       28169 :       switch (sameRegHint) {
     478             :         case InstSameRegHint::kNone:
     479             :           break;
     480             :         case InstSameRegHint::kRO:
     481             :           ib[0]->makeReadOnly();
     482             :           break;
     483             :         case InstSameRegHint::kWO:
     484             :           ib[0]->makeWriteOnly();
     485             :           break;
     486             :       }
     487             :     }
     488             : 
     489      304316 :     cf = instInfo.controlFlow();
     490             :   }
     491             : 
     492             :   return kErrorOk;
     493             : }
     494             : 
     495             : // x86::RACFGBuilder - OnInvoke
     496             : // ============================
     497             : 
     498        9639 : Error RACFGBuilder::onBeforeInvoke(InvokeNode* invokeNode) noexcept {
     499             :   const FuncDetail& fd = invokeNode->detail();
     500             :   uint32_t argCount = invokeNode->argCount();
     501             : 
     502             :   cc()->_setCursor(invokeNode->prev());
     503             :   RegType nativeRegType = cc()->_gpSignature.regType();
     504             : 
     505       19512 :   for (uint32_t argIndex = 0; argIndex < argCount; argIndex++) {
     506        9873 :     const FuncValuePack& argPack = fd.argPack(argIndex);
     507       19746 :     for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
     508       19746 :       if (!argPack[valueIndex])
     509             :         break;
     510             : 
     511             :       const FuncValue& arg = argPack[valueIndex];
     512             :       const Operand& op = invokeNode->arg(argIndex, valueIndex);
     513             : 
     514        9873 :       if (op.isNone())
     515           0 :         continue;
     516             : 
     517        9873 :       if (op.isReg()) {
     518             :         const Reg& reg = op.as<Reg>();
     519             :         RAWorkReg* workReg;
     520       19370 :         ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(Operand::virtIdToIndex(reg.id()), &workReg));
     521             : 
     522        9685 :         if (arg.isReg()) {
     523        9685 :           RegGroup regGroup = workReg->group();
     524             :           RegGroup argGroup = Reg::groupOf(arg.regType());
     525             : 
     526        9685 :           if (arg.isIndirect()) {
     527           0 :             if (reg.isGp()) {
     528           0 :               if (reg.type() != nativeRegType)
     529           0 :                 return DebugUtils::errored(kErrorInvalidAssignment);
     530             :               // It's considered allocated if this is an indirect argument and the user used GP.
     531           0 :               continue;
     532             :             }
     533             : 
     534           0 :             BaseReg indirectReg;
     535           0 :             moveVecToPtr(invokeNode, arg, reg.as<Vec>(), &indirectReg);
     536           0 :             invokeNode->_args[argIndex][valueIndex] = indirectReg;
     537             :           }
     538             :           else {
     539        9685 :             if (regGroup != argGroup) {
     540             :               // TODO: Conversion is not supported.
     541             :               return DebugUtils::errored(kErrorInvalidAssignment);
     542             :             }
     543             :           }
     544             :         }
     545             :         else {
     546           0 :           if (arg.isIndirect()) {
     547           0 :             if (reg.isGp()) {
     548           0 :               if (reg.type() != nativeRegType)
     549           0 :                 return DebugUtils::errored(kErrorInvalidAssignment);
     550             : 
     551           0 :               ASMJIT_PROPAGATE(moveRegToStackArg(invokeNode, arg, reg));
     552           0 :               continue;
     553           0 :             }
     554             : 
     555           0 :             BaseReg indirectReg;
     556           0 :             moveVecToPtr(invokeNode, arg, reg.as<Vec>(), &indirectReg);
     557           0 :             ASMJIT_PROPAGATE(moveRegToStackArg(invokeNode, arg, indirectReg));
     558             :           }
     559             :           else {
     560           0 :             ASMJIT_PROPAGATE(moveRegToStackArg(invokeNode, arg, reg));
     561             :           }
     562             :         }
     563             :       }
     564         188 :       else if (op.isImm()) {
     565         188 :         if (arg.isReg()) {
     566         188 :           BaseReg reg;
     567         188 :           ASMJIT_PROPAGATE(moveImmToRegArg(invokeNode, arg, op.as<Imm>(), &reg));
     568         188 :           invokeNode->_args[argIndex][valueIndex] = reg;
     569             :         }
     570             :         else {
     571           0 :           ASMJIT_PROPAGATE(moveImmToStackArg(invokeNode, arg, op.as<Imm>()));
     572             :         }
     573             :       }
     574             :     }
     575             :   }
     576             : 
     577             :   cc()->_setCursor(invokeNode);
     578        9639 :   if (fd.hasFlag(CallConvFlags::kCalleePopsStack) && fd.argStackSize() != 0)
     579           0 :     ASMJIT_PROPAGATE(cc()->sub(cc()->zsp(), fd.argStackSize()));
     580             : 
     581        9639 :   if (fd.hasRet()) {
     582       19278 :     for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
     583       19278 :       const FuncValue& ret = fd.ret(valueIndex);
     584       19278 :       if (!ret)
     585             :         break;
     586             : 
     587             :       const Operand& op = invokeNode->ret(valueIndex);
     588        9639 :       if (op.isReg()) {
     589             :         const Reg& reg = op.as<Reg>();
     590             :         RAWorkReg* workReg;
     591       19278 :         ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(Operand::virtIdToIndex(reg.id()), &workReg));
     592             : 
     593        9639 :         if (ret.isReg()) {
     594        9639 :           if (ret.regType() == RegType::kX86_St) {
     595           0 :             if (workReg->group() != RegGroup::kVec)
     596           0 :               return DebugUtils::errored(kErrorInvalidAssignment);
     597             : 
     598             :             Reg dst(workReg->signature(), workReg->virtId());
     599           0 :             Mem mem;
     600             : 
     601             :             TypeId typeId = TypeUtils::scalarOf(workReg->typeId());
     602           0 :             if (ret.hasTypeId())
     603             :               typeId = ret.typeId();
     604             : 
     605           0 :             switch (typeId) {
     606           0 :               case TypeId::kFloat32:
     607           0 :                 ASMJIT_PROPAGATE(_pass->useTemporaryMem(mem, 4, 4));
     608             :                 mem.setSize(4);
     609           0 :                 ASMJIT_PROPAGATE(cc()->fstp(mem));
     610           0 :                 ASMJIT_PROPAGATE(cc()->emit(choose(Inst::kIdMovss, Inst::kIdVmovss), dst.as<Xmm>(), mem));
     611             :                 break;
     612             : 
     613           0 :               case TypeId::kFloat64:
     614           0 :                 ASMJIT_PROPAGATE(_pass->useTemporaryMem(mem, 8, 4));
     615             :                 mem.setSize(8);
     616           0 :                 ASMJIT_PROPAGATE(cc()->fstp(mem));
     617           0 :                 ASMJIT_PROPAGATE(cc()->emit(choose(Inst::kIdMovsd, Inst::kIdVmovsd), dst.as<Xmm>(), mem));
     618             :                 break;
     619             : 
     620             :               default:
     621             :                 return DebugUtils::errored(kErrorInvalidAssignment);
     622             :             }
     623             :           }
     624             :           else {
     625        9639 :             RegGroup regGroup = workReg->group();
     626             :             RegGroup retGroup = Reg::groupOf(ret.regType());
     627             : 
     628        9639 :             if (regGroup != retGroup) {
     629             :               // TODO: Conversion is not supported.
     630             :               return DebugUtils::errored(kErrorInvalidAssignment);
     631             :             }
     632             :           }
     633             :         }
     634             :       }
     635             :     }
     636             :   }
     637             : 
     638             :   // This block has function call(s).
     639        9639 :   _curBlock->addFlags(RABlockFlags::kHasFuncCalls);
     640        9639 :   _pass->func()->frame().addAttributes(FuncAttributes::kHasFuncCalls);
     641        9639 :   _pass->func()->frame().updateCallStackSize(fd.argStackSize());
     642             : 
     643        9639 :   return kErrorOk;
     644             : }
     645             : 
     646        9639 : Error RACFGBuilder::onInvoke(InvokeNode* invokeNode, RAInstBuilder& ib) noexcept {
     647             :   uint32_t argCount = invokeNode->argCount();
     648             :   const FuncDetail& fd = invokeNode->detail();
     649             : 
     650       19512 :   for (uint32_t argIndex = 0; argIndex < argCount; argIndex++) {
     651        9873 :     const FuncValuePack& argPack = fd.argPack(argIndex);
     652       49365 :     for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
     653       39492 :       if (!argPack[valueIndex])
     654       29619 :         continue;
     655             : 
     656             :       const FuncValue& arg = argPack[valueIndex];
     657             :       const Operand& op = invokeNode->arg(argIndex, valueIndex);
     658             : 
     659        9873 :       if (op.isNone())
     660           0 :         continue;
     661             : 
     662        9873 :       if (op.isReg()) {
     663             :         const Reg& reg = op.as<Reg>();
     664             :         RAWorkReg* workReg;
     665       19746 :         ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(Operand::virtIdToIndex(reg.id()), &workReg));
     666             : 
     667        9873 :         if (arg.isIndirect()) {
     668           0 :           RegGroup regGroup = workReg->group();
     669           0 :           if (regGroup != RegGroup::kGp)
     670             :             return DebugUtils::errored(kErrorInvalidState);
     671           0 :           ASMJIT_PROPAGATE(ib.addCallArg(workReg, arg.regId()));
     672             :         }
     673        9873 :         else if (arg.isReg()) {
     674        9873 :           RegGroup regGroup = workReg->group();
     675             :           RegGroup argGroup = Reg::groupOf(arg.regType());
     676             : 
     677        9873 :           if (regGroup == argGroup) {
     678        9873 :             ASMJIT_PROPAGATE(ib.addCallArg(workReg, arg.regId()));
     679             :           }
     680             :         }
     681             :       }
     682             :     }
     683             :   }
     684             : 
     685       19278 :   for (uint32_t retIndex = 0; retIndex < Globals::kMaxValuePack; retIndex++) {
     686       19278 :     const FuncValue& ret = fd.ret(retIndex);
     687       19278 :     if (!ret)
     688             :       break;
     689             : 
     690             :     // Not handled here...
     691             :     const Operand& op = invokeNode->ret(retIndex);
     692        9639 :     if (ret.regType() == RegType::kX86_St)
     693           0 :       continue;
     694             : 
     695        9639 :     if (op.isReg()) {
     696             :       const Reg& reg = op.as<Reg>();
     697             :       RAWorkReg* workReg;
     698       19278 :       ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(Operand::virtIdToIndex(reg.id()), &workReg));
     699             : 
     700        9639 :       if (ret.isReg()) {
     701        9639 :         RegGroup regGroup = workReg->group();
     702             :         RegGroup retGroup = Reg::groupOf(ret.regType());
     703             : 
     704        9639 :         if (regGroup == retGroup) {
     705        9639 :           ASMJIT_PROPAGATE(ib.addCallRet(workReg, ret.regId()));
     706             :         }
     707             :       }
     708             :       else {
     709             :         return DebugUtils::errored(kErrorInvalidAssignment);
     710             :       }
     711             :     }
     712             :   }
     713             : 
     714             :   // Setup clobbered registers.
     715       48195 :   for (RegGroup group : RegGroupVirtValues{})
     716       38556 :     ib._clobbered[group] = Support::lsbMask<RegMask>(_pass->_physRegCount[group]) & ~fd.preservedRegs(group);
     717             : 
     718             :   return kErrorOk;
     719             : }
     720             : 
     721             : // x86::RACFGBuilder - MoveVecToPtr
     722             : // ================================
     723             : 
     724             : static inline OperandSignature x86VecRegSignatureBySize(uint32_t size) noexcept {
     725           0 :   return OperandSignature{size >= 64 ? uint32_t(Zmm::kSignature) :
     726           0 :                           size >= 32 ? uint32_t(Ymm::kSignature) : uint32_t(Xmm::kSignature)};
     727             : }
     728             : 
     729           0 : Error RACFGBuilder::moveVecToPtr(InvokeNode* invokeNode, const FuncValue& arg, const Vec& src, BaseReg* out) noexcept {
     730             :   DebugUtils::unused(invokeNode);
     731             :   ASMJIT_ASSERT(arg.isReg());
     732             : 
     733             :   uint32_t argSize = TypeUtils::sizeOf(arg.typeId());
     734           0 :   if (argSize == 0)
     735             :     return DebugUtils::errored(kErrorInvalidState);
     736             : 
     737             :   if (argSize < 16)
     738             :     argSize = 16;
     739             : 
     740           0 :   uint32_t argStackOffset = Support::alignUp(invokeNode->detail()._argStackSize, argSize);
     741           0 :   _funcNode->frame().updateCallStackAlignment(argSize);
     742           0 :   invokeNode->detail()._argStackSize = argStackOffset + argSize;
     743             : 
     744             :   Vec vecReg(x86VecRegSignatureBySize(argSize), src.id());
     745           0 :   Mem vecPtr = ptr(_pass->_sp.as<Gp>(), int32_t(argStackOffset));
     746             : 
     747             :   uint32_t vMovInstId = choose(Inst::kIdMovaps, Inst::kIdVmovaps);
     748           0 :   if (argSize > 16)
     749             :     vMovInstId = Inst::kIdVmovaps;
     750             : 
     751           0 :   ASMJIT_PROPAGATE(cc()->_newReg(out, ArchTraits::byArch(cc()->arch()).regTypeToTypeId(cc()->_gpSignature.regType()), nullptr));
     752             : 
     753             :   VirtReg* vReg = cc()->virtRegById(out->id());
     754             :   vReg->setWeight(BaseRAPass::kCallArgWeight);
     755             : 
     756           0 :   ASMJIT_PROPAGATE(cc()->lea(out->as<Gp>(), vecPtr));
     757           0 :   ASMJIT_PROPAGATE(cc()->emit(vMovInstId, ptr(out->as<Gp>()), vecReg));
     758             : 
     759           0 :   if (arg.isStack()) {
     760           0 :     Mem stackPtr = ptr(_pass->_sp.as<Gp>(), arg.stackOffset());
     761           0 :     ASMJIT_PROPAGATE(cc()->mov(stackPtr, out->as<Gp>()));
     762             :   }
     763             : 
     764             :   return kErrorOk;
     765             : }
     766             : 
     767             : // x86::RACFGBuilder - MoveImmToRegArg
     768             : // ===================================
     769             : 
     770         188 : Error RACFGBuilder::moveImmToRegArg(InvokeNode* invokeNode, const FuncValue& arg, const Imm& imm_, BaseReg* out) noexcept {
     771             :   DebugUtils::unused(invokeNode);
     772             :   ASMJIT_ASSERT(arg.isReg());
     773             : 
     774             :   Imm imm(imm_);
     775             :   TypeId rTypeId = TypeId::kUInt32;
     776             : 
     777         188 :   switch (arg.typeId()) {
     778           0 :     case TypeId::kInt8: imm.signExtend8Bits(); goto MovU32;
     779           0 :     case TypeId::kUInt8: imm.zeroExtend8Bits(); goto MovU32;
     780           0 :     case TypeId::kInt16: imm.signExtend16Bits(); goto MovU32;
     781           0 :     case TypeId::kUInt16: imm.zeroExtend16Bits(); goto MovU32;
     782             : 
     783             :     case TypeId::kInt32:
     784             :     case TypeId::kUInt32:
     785           0 : MovU32:
     786             :       imm.zeroExtend32Bits();
     787             :       break;
     788             : 
     789             :     case TypeId::kInt64:
     790             :     case TypeId::kUInt64:
     791             :       // Moving to GPD automatically zero extends in 64-bit mode.
     792             :       if (imm.isUInt32()) {
     793             :         imm.zeroExtend32Bits();
     794             :         break;
     795             :       }
     796             : 
     797             :       rTypeId = TypeId::kUInt64;
     798             :       break;
     799             : 
     800             :     default:
     801             :       return DebugUtils::errored(kErrorInvalidAssignment);
     802             :   }
     803             : 
     804         188 :   ASMJIT_PROPAGATE(cc()->_newReg(out, rTypeId, nullptr));
     805             :   cc()->virtRegById(out->id())->setWeight(BaseRAPass::kCallArgWeight);
     806             : 
     807         188 :   return cc()->mov(out->as<x86::Gp>(), imm);
     808             : }
     809             : 
     810             : // x86::RACFGBuilder - MoveImmToStackArg
     811             : // =====================================
     812             : 
     813           0 : Error RACFGBuilder::moveImmToStackArg(InvokeNode* invokeNode, const FuncValue& arg, const Imm& imm_) noexcept {
     814             :   DebugUtils::unused(invokeNode);
     815             :   ASMJIT_ASSERT(arg.isStack());
     816             : 
     817           0 :   Mem stackPtr = ptr(_pass->_sp.as<Gp>(), arg.stackOffset());
     818           0 :   Imm imm[2];
     819             : 
     820             :   stackPtr.setSize(4);
     821             :   imm[0] = imm_;
     822             :   uint32_t nMovs = 0;
     823             : 
     824             :   // One stack entry has the same size as the native register size. That means that if we want to move a 32-bit
     825             :   // integer on the stack in 64-bit mode, we need to extend it to a 64-bit integer first. In 32-bit mode, pushing
     826             :   // a 64-bit on stack is done in two steps by pushing low and high parts separately.
     827           0 :   switch (arg.typeId()) {
     828           0 :     case TypeId::kInt8: imm[0].signExtend8Bits(); goto MovU32;
     829           0 :     case TypeId::kUInt8: imm[0].zeroExtend8Bits(); goto MovU32;
     830           0 :     case TypeId::kInt16: imm[0].signExtend16Bits(); goto MovU32;
     831           0 :     case TypeId::kUInt16: imm[0].zeroExtend16Bits(); goto MovU32;
     832             : 
     833             :     case TypeId::kInt32:
     834             :     case TypeId::kUInt32:
     835             :     case TypeId::kFloat32:
     836           0 : MovU32:
     837             :       imm[0].zeroExtend32Bits();
     838             :       nMovs = 1;
     839           0 :       break;
     840             : 
     841           0 :     case TypeId::kInt64:
     842             :     case TypeId::kUInt64:
     843             :     case TypeId::kFloat64:
     844             :     case TypeId::kMmx32:
     845             :     case TypeId::kMmx64:
     846           0 :       if (_is64Bit && imm[0].isInt32()) {
     847             :         stackPtr.setSize(8);
     848             :         nMovs = 1;
     849           0 :         break;
     850             :       }
     851             : 
     852             :       imm[1].setValue(imm[0].uint32Hi());
     853             :       imm[0].zeroExtend32Bits();
     854             :       nMovs = 2;
     855           0 :       break;
     856             : 
     857             :     default:
     858             :       return DebugUtils::errored(kErrorInvalidAssignment);
     859             :   }
     860             : 
     861           0 :   for (uint32_t i = 0; i < nMovs; i++) {
     862           0 :     ASMJIT_PROPAGATE(cc()->mov(stackPtr, imm[i]));
     863             :     stackPtr.addOffsetLo32(int32_t(stackPtr.size()));
     864             :   }
     865             : 
     866             :   return kErrorOk;
     867             : }
     868             : 
     869             : // x86::RACFGBuilder - MoveRegToStackArg
     870             : // =====================================
     871             : 
     872           0 : Error RACFGBuilder::moveRegToStackArg(InvokeNode* invokeNode, const FuncValue& arg, const BaseReg& reg) noexcept {
     873             :   DebugUtils::unused(invokeNode);
     874             :   ASMJIT_ASSERT(arg.isStack());
     875             : 
     876           0 :   Mem stackPtr = ptr(_pass->_sp.as<Gp>(), arg.stackOffset());
     877           0 :   Reg r0, r1;
     878             : 
     879             :   VirtReg* vr = cc()->virtRegById(reg.id());
     880             :   uint32_t registerSize = cc()->registerSize();
     881             :   InstId instId = 0;
     882             : 
     883             :   TypeId dstTypeId = arg.typeId();
     884             :   TypeId srcTypeId = vr->typeId();
     885             : 
     886           0 :   switch (dstTypeId) {
     887             :     case TypeId::kInt64:
     888             :     case TypeId::kUInt64:
     889             :       // Extend BYTE->QWORD (GP).
     890             :       if (TypeUtils::isGp8(srcTypeId)) {
     891             :         r1.setRegT<RegType::kX86_GpbLo>(reg.id());
     892             : 
     893           0 :         instId = (dstTypeId == TypeId::kInt64 && srcTypeId == TypeId::kInt8) ? Inst::kIdMovsx : Inst::kIdMovzx;
     894           0 :         goto ExtendMovGpXQ;
     895             :       }
     896             : 
     897             :       // Extend WORD->QWORD (GP).
     898             :       if (TypeUtils::isGp16(srcTypeId)) {
     899             :         r1.setRegT<RegType::kX86_Gpw>(reg.id());
     900             : 
     901           0 :         instId = (dstTypeId == TypeId::kInt64 && srcTypeId == TypeId::kInt16) ? Inst::kIdMovsx : Inst::kIdMovzx;
     902           0 :         goto ExtendMovGpXQ;
     903             :       }
     904             : 
     905             :       // Extend DWORD->QWORD (GP).
     906             :       if (TypeUtils::isGp32(srcTypeId)) {
     907             :         r1.setRegT<RegType::kX86_Gpd>(reg.id());
     908             : 
     909             :         instId = Inst::kIdMovsxd;
     910           0 :         if (dstTypeId == TypeId::kInt64 && srcTypeId == TypeId::kInt32)
     911           0 :           goto ExtendMovGpXQ;
     912             :         else
     913           0 :           goto ZeroExtendGpDQ;
     914             :       }
     915             : 
     916             :       // Move QWORD (GP).
     917           0 :       if (TypeUtils::isGp64(srcTypeId)) goto MovGpQ;
     918           0 :       if (TypeUtils::isMmx(srcTypeId)) goto MovMmQ;
     919           0 :       if (TypeUtils::isVec(srcTypeId)) goto MovXmmQ;
     920             :       break;
     921             : 
     922             :     case TypeId::kInt32:
     923             :     case TypeId::kUInt32:
     924             :     case TypeId::kInt16:
     925             :     case TypeId::kUInt16:
     926             :       // DWORD <- WORD (Zero|Sign Extend).
     927             :       if (TypeUtils::isGp16(srcTypeId)) {
     928           0 :         bool isDstSigned = dstTypeId == TypeId::kInt16 || dstTypeId == TypeId::kInt32;
     929           0 :         bool isSrcSigned = srcTypeId == TypeId::kInt8  || srcTypeId == TypeId::kInt16;
     930             : 
     931             :         r1.setRegT<RegType::kX86_Gpw>(reg.id());
     932           0 :         instId = isDstSigned && isSrcSigned ? Inst::kIdMovsx : Inst::kIdMovzx;
     933           0 :         goto ExtendMovGpD;
     934             :       }
     935             : 
     936             :       // DWORD <- BYTE (Zero|Sign Extend).
     937             :       if (TypeUtils::isGp8(srcTypeId)) {
     938           0 :         bool isDstSigned = dstTypeId == TypeId::kInt16 || dstTypeId == TypeId::kInt32;
     939           0 :         bool isSrcSigned = srcTypeId == TypeId::kInt8  || srcTypeId == TypeId::kInt16;
     940             : 
     941             :         r1.setRegT<RegType::kX86_GpbLo>(reg.id());
     942           0 :         instId = isDstSigned && isSrcSigned ? Inst::kIdMovsx : Inst::kIdMovzx;
     943           0 :         goto ExtendMovGpD;
     944             :       }
     945             :       ASMJIT_FALLTHROUGH;
     946             : 
     947             :     case TypeId::kInt8:
     948             :     case TypeId::kUInt8:
     949           0 :       if (TypeUtils::isInt(srcTypeId)) goto MovGpD;
     950           0 :       if (TypeUtils::isMmx(srcTypeId)) goto MovMmD;
     951           0 :       if (TypeUtils::isVec(srcTypeId)) goto MovXmmD;
     952             :       break;
     953             : 
     954             :     case TypeId::kMmx32:
     955             :     case TypeId::kMmx64:
     956             :       // Extend BYTE->QWORD (GP).
     957             :       if (TypeUtils::isGp8(srcTypeId)) {
     958             :         r1.setRegT<RegType::kX86_GpbLo>(reg.id());
     959             : 
     960             :         instId = Inst::kIdMovzx;
     961           0 :         goto ExtendMovGpXQ;
     962             :       }
     963             : 
     964             :       // Extend WORD->QWORD (GP).
     965             :       if (TypeUtils::isGp16(srcTypeId)) {
     966             :         r1.setRegT<RegType::kX86_Gpw>(reg.id());
     967             : 
     968             :         instId = Inst::kIdMovzx;
     969           0 :         goto ExtendMovGpXQ;
     970             :       }
     971             : 
     972           0 :       if (TypeUtils::isGp32(srcTypeId)) goto ExtendMovGpDQ;
     973           0 :       if (TypeUtils::isGp64(srcTypeId)) goto MovGpQ;
     974           0 :       if (TypeUtils::isMmx(srcTypeId)) goto MovMmQ;
     975           0 :       if (TypeUtils::isVec(srcTypeId)) goto MovXmmQ;
     976             :       break;
     977             : 
     978             :     case TypeId::kFloat32:
     979             :     case TypeId::kFloat32x1:
     980           0 :       if (TypeUtils::isVec(srcTypeId)) goto MovXmmD;
     981             :       break;
     982             : 
     983             :     case TypeId::kFloat64:
     984             :     case TypeId::kFloat64x1:
     985           0 :       if (TypeUtils::isVec(srcTypeId)) goto MovXmmQ;
     986             :       break;
     987             : 
     988             :     default:
     989           0 :       if (TypeUtils::isVec(dstTypeId) && reg.as<Reg>().isVec()) {
     990             :         stackPtr.setSize(TypeUtils::sizeOf(dstTypeId));
     991             :         uint32_t vMovInstId = choose(Inst::kIdMovaps, Inst::kIdVmovaps);
     992             : 
     993             :         if (TypeUtils::isVec128(dstTypeId))
     994             :           r0.setRegT<RegType::kX86_Xmm>(reg.id());
     995             :         else if (TypeUtils::isVec256(dstTypeId))
     996             :           r0.setRegT<RegType::kX86_Ymm>(reg.id());
     997             :         else if (TypeUtils::isVec512(dstTypeId))
     998             :           r0.setRegT<RegType::kX86_Zmm>(reg.id());
     999             :         else
    1000             :           break;
    1001             : 
    1002           0 :         return cc()->emit(vMovInstId, stackPtr, r0);
    1003             :       }
    1004             :       break;
    1005             :   }
    1006             :   return DebugUtils::errored(kErrorInvalidAssignment);
    1007             : 
    1008             :   // Extend+Move Gp.
    1009           0 : ExtendMovGpD:
    1010             :   stackPtr.setSize(4);
    1011             :   r0.setRegT<RegType::kX86_Gpd>(reg.id());
    1012             : 
    1013           0 :   ASMJIT_PROPAGATE(cc()->emit(instId, r0, r1));
    1014           0 :   ASMJIT_PROPAGATE(cc()->emit(Inst::kIdMov, stackPtr, r0));
    1015             :   return kErrorOk;
    1016             : 
    1017           0 : ExtendMovGpXQ:
    1018           0 :   if (registerSize == 8) {
    1019             :     stackPtr.setSize(8);
    1020             :     r0.setRegT<RegType::kX86_Gpq>(reg.id());
    1021             : 
    1022           0 :     ASMJIT_PROPAGATE(cc()->emit(instId, r0, r1));
    1023           0 :     ASMJIT_PROPAGATE(cc()->emit(Inst::kIdMov, stackPtr, r0));
    1024             :   }
    1025             :   else {
    1026             :     stackPtr.setSize(4);
    1027             :     r0.setRegT<RegType::kX86_Gpd>(reg.id());
    1028             : 
    1029           0 :     ASMJIT_PROPAGATE(cc()->emit(instId, r0, r1));
    1030             : 
    1031           0 : ExtendMovGpDQ:
    1032           0 :     ASMJIT_PROPAGATE(cc()->emit(Inst::kIdMov, stackPtr, r0));
    1033             :     stackPtr.addOffsetLo32(4);
    1034           0 :     ASMJIT_PROPAGATE(cc()->emit(Inst::kIdAnd, stackPtr, 0));
    1035             :   }
    1036             :   return kErrorOk;
    1037             : 
    1038             : ZeroExtendGpDQ:
    1039             :   stackPtr.setSize(4);
    1040             :   r0.setRegT<RegType::kX86_Gpd>(reg.id());
    1041           0 :   goto ExtendMovGpDQ;
    1042             : 
    1043             : MovGpD:
    1044             :   stackPtr.setSize(4);
    1045             :   r0.setRegT<RegType::kX86_Gpd>(reg.id());
    1046           0 :   return cc()->emit(Inst::kIdMov, stackPtr, r0);
    1047             : 
    1048           0 : MovGpQ:
    1049             :   stackPtr.setSize(8);
    1050             :   r0.setRegT<RegType::kX86_Gpq>(reg.id());
    1051           0 :   return cc()->emit(Inst::kIdMov, stackPtr, r0);
    1052             : 
    1053             : MovMmD:
    1054             :   stackPtr.setSize(4);
    1055             :   r0.setRegT<RegType::kX86_Mm>(reg.id());
    1056           0 :   return cc()->emit(choose(Inst::kIdMovd, Inst::kIdVmovd), stackPtr, r0);
    1057             : 
    1058           0 : MovMmQ:
    1059             :   stackPtr.setSize(8);
    1060             :   r0.setRegT<RegType::kX86_Mm>(reg.id());
    1061           0 :   return cc()->emit(choose(Inst::kIdMovq, Inst::kIdVmovq), stackPtr, r0);
    1062             : 
    1063           0 : MovXmmD:
    1064             :   stackPtr.setSize(4);
    1065             :   r0.setRegT<RegType::kX86_Xmm>(reg.id());
    1066           0 :   return cc()->emit(choose(Inst::kIdMovss, Inst::kIdVmovss), stackPtr, r0);
    1067             : 
    1068           0 : MovXmmQ:
    1069             :   stackPtr.setSize(8);
    1070             :   r0.setRegT<RegType::kX86_Xmm>(reg.id());
    1071           0 :   return cc()->emit(choose(Inst::kIdMovlps, Inst::kIdVmovlps), stackPtr, r0);
    1072             : }
    1073             : 
    1074             : // x86::RACFGBuilder - OnReg
    1075             : // =========================
    1076             : 
    1077       27492 : Error RACFGBuilder::onBeforeRet(FuncRetNode* funcRet) noexcept {
    1078       27492 :   const FuncDetail& funcDetail = _pass->func()->detail();
    1079             :   const Operand* opArray = funcRet->operands();
    1080             :   uint32_t opCount = funcRet->opCount();
    1081             : 
    1082             :   cc()->_setCursor(funcRet->prev());
    1083             : 
    1084       54984 :   for (uint32_t i = 0; i < opCount; i++) {
    1085       27492 :     const Operand& op = opArray[i];
    1086             :     const FuncValue& ret = funcDetail.ret(i);
    1087             : 
    1088       27492 :     if (!op.isReg())
    1089           0 :       continue;
    1090             : 
    1091       27492 :     if (ret.regType() == RegType::kX86_St) {
    1092             :       const Reg& reg = op.as<Reg>();
    1093             :       uint32_t vIndex = Operand::virtIdToIndex(reg.id());
    1094             : 
    1095           0 :       if (vIndex < Operand::kVirtIdCount) {
    1096             :         RAWorkReg* workReg;
    1097           0 :         ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(vIndex, &workReg));
    1098             : 
    1099           0 :         if (workReg->group() != RegGroup::kVec)
    1100             :           return DebugUtils::errored(kErrorInvalidAssignment);
    1101             : 
    1102             :         Reg src(workReg->signature(), workReg->virtId());
    1103           0 :         Mem mem;
    1104             : 
    1105             :         TypeId typeId = TypeUtils::scalarOf(workReg->typeId());
    1106           0 :         if (ret.hasTypeId())
    1107             :           typeId = ret.typeId();
    1108             : 
    1109           0 :         switch (typeId) {
    1110           0 :           case TypeId::kFloat32:
    1111           0 :             ASMJIT_PROPAGATE(_pass->useTemporaryMem(mem, 4, 4));
    1112             :             mem.setSize(4);
    1113           0 :             ASMJIT_PROPAGATE(cc()->emit(choose(Inst::kIdMovss, Inst::kIdVmovss), mem, src.as<Xmm>()));
    1114           0 :             ASMJIT_PROPAGATE(cc()->fld(mem));
    1115             :             break;
    1116             : 
    1117           0 :           case TypeId::kFloat64:
    1118           0 :             ASMJIT_PROPAGATE(_pass->useTemporaryMem(mem, 8, 4));
    1119             :             mem.setSize(8);
    1120           0 :             ASMJIT_PROPAGATE(cc()->emit(choose(Inst::kIdMovsd, Inst::kIdVmovsd), mem, src.as<Xmm>()));
    1121           0 :             ASMJIT_PROPAGATE(cc()->fld(mem));
    1122             :             break;
    1123             : 
    1124             :           default:
    1125             :             return DebugUtils::errored(kErrorInvalidAssignment);
    1126             :         }
    1127             :       }
    1128             :     }
    1129             :   }
    1130             : 
    1131             :   return kErrorOk;
    1132             : }
    1133             : 
    1134       27492 : Error RACFGBuilder::onRet(FuncRetNode* funcRet, RAInstBuilder& ib) noexcept {
    1135       27492 :   const FuncDetail& funcDetail = _pass->func()->detail();
    1136             :   const Operand* opArray = funcRet->operands();
    1137             :   uint32_t opCount = funcRet->opCount();
    1138             : 
    1139       54984 :   for (uint32_t i = 0; i < opCount; i++) {
    1140       27492 :     const Operand& op = opArray[i];
    1141       27492 :     if (op.isNone()) continue;
    1142             : 
    1143             :     const FuncValue& ret = funcDetail.ret(i);
    1144       27492 :     if (ASMJIT_UNLIKELY(!ret.isReg()))
    1145             :       return DebugUtils::errored(kErrorInvalidAssignment);
    1146             : 
    1147             :     // Not handled here...
    1148       27492 :     if (ret.regType() == RegType::kX86_St)
    1149           0 :       continue;
    1150             : 
    1151       27492 :     if (op.isReg()) {
    1152             :       // Register return value.
    1153             :       const Reg& reg = op.as<Reg>();
    1154             :       uint32_t vIndex = Operand::virtIdToIndex(reg.id());
    1155             : 
    1156       27492 :       if (vIndex < Operand::kVirtIdCount) {
    1157             :         RAWorkReg* workReg;
    1158       54984 :         ASMJIT_PROPAGATE(_pass->virtIndexAsWorkReg(vIndex, &workReg));
    1159             : 
    1160       27492 :         RegGroup group = workReg->group();
    1161       27492 :         RegMask inOutRegs = _pass->_availableRegs[group];
    1162       27492 :         ASMJIT_PROPAGATE(ib.add(workReg, RATiedFlags::kUse | RATiedFlags::kRead, inOutRegs, ret.regId(), 0, inOutRegs, BaseReg::kIdBad, 0));
    1163             :       }
    1164             :     }
    1165             :     else {
    1166             :       return DebugUtils::errored(kErrorInvalidAssignment);
    1167             :     }
    1168             :   }
    1169             : 
    1170             :   return kErrorOk;
    1171             : }
    1172             : 
    1173             : // x86::X86RAPass - Construction & Destruction
    1174             : // ===========================================
    1175             : 
    1176       27492 : X86RAPass::X86RAPass() noexcept
    1177       27492 :   : BaseRAPass() { _iEmitHelper = &_emitHelper; }
    1178       27492 : X86RAPass::~X86RAPass() noexcept {}
    1179             : 
    1180             : // x86::X86RAPass - OnInit & OnDone
    1181             : // ================================
    1182             : 
    1183       27492 : void X86RAPass::onInit() noexcept {
    1184             :   Arch arch = cc()->arch();
    1185       27492 :   uint32_t baseRegCount = Environment::is32Bit(arch) ? 8u : 16u;
    1186             :   uint32_t simdRegCount = baseRegCount;
    1187             : 
    1188       27492 :   if (Environment::is64Bit(arch) && _func->frame().isAvx512Enabled())
    1189             :     simdRegCount = 32u;
    1190             : 
    1191       27492 :   bool avxEnabled = _func->frame().isAvxEnabled();
    1192             :   bool avx512Enabled = _func->frame().isAvx512Enabled();
    1193             : 
    1194       27492 :   _emitHelper._emitter = _cb;
    1195       27492 :   _emitHelper._avxEnabled = avxEnabled || avx512Enabled;
    1196       27492 :   _emitHelper._avx512Enabled = avx512Enabled;
    1197             : 
    1198       27492 :   _archTraits = &ArchTraits::byArch(arch);
    1199             :   _physRegCount.set(RegGroup::kGp, baseRegCount);
    1200             :   _physRegCount.set(RegGroup::kVec, simdRegCount);
    1201             :   _physRegCount.set(RegGroup::kX86_K, 8);
    1202             :   _physRegCount.set(RegGroup::kX86_MM, 8);
    1203       27492 :   _buildPhysIndex();
    1204             : 
    1205       27492 :   _availableRegCount = _physRegCount;
    1206       27492 :   _availableRegs[RegGroup::kGp] = Support::lsbMask<RegMask>(_physRegCount.get(RegGroup::kGp));
    1207       27492 :   _availableRegs[RegGroup::kVec] = Support::lsbMask<RegMask>(_physRegCount.get(RegGroup::kVec));
    1208       27492 :   _availableRegs[RegGroup::kX86_K] = Support::lsbMask<RegMask>(_physRegCount.get(RegGroup::kX86_K)) ^ 1u;
    1209       27492 :   _availableRegs[RegGroup::kX86_MM] = Support::lsbMask<RegMask>(_physRegCount.get(RegGroup::kX86_MM));
    1210             : 
    1211       27492 :   _scratchRegIndexes[0] = uint8_t(Gp::kIdCx);
    1212       27492 :   _scratchRegIndexes[1] = uint8_t(baseRegCount - 1);
    1213             : 
    1214             :   // The architecture specific setup makes implicitly all registers available. So
    1215             :   // make unavailable all registers that are special and cannot be used in general.
    1216             :   bool hasFP = _func->frame().hasPreservedFP();
    1217             : 
    1218             :   makeUnavailable(RegGroup::kGp, Gp::kIdSp);            // ESP|RSP used as a stack-pointer (SP).
    1219       27492 :   if (hasFP) makeUnavailable(RegGroup::kGp, Gp::kIdBp); // EBP|RBP used as a frame-pointer (FP).
    1220             : 
    1221       27492 :   _sp = cc()->zsp();
    1222       27492 :   _fp = cc()->zbp();
    1223       27492 : }
    1224             : 
    1225       27492 : void X86RAPass::onDone() noexcept {}
    1226             : 
    1227             : // x86::X86RAPass - BuildCFG
    1228             : // =========================
    1229             : 
    1230       27492 : Error X86RAPass::buildCFG() noexcept {
    1231       27492 :   return RACFGBuilder(this).run();
    1232             : }
    1233             : 
    1234             : // x86::X86RAPass - Rewrite
    1235             : // ========================
    1236             : 
    1237           0 : static InstId transformVexToEvex(InstId instId) {
    1238           0 :   switch (instId) {
    1239             :     case Inst::kIdVbroadcastf128: return Inst::kIdVbroadcastf32x4;
    1240           0 :     case Inst::kIdVbroadcasti128: return Inst::kIdVbroadcasti32x4;
    1241           0 :     case Inst::kIdVextractf128: return Inst::kIdVextractf32x4;
    1242           0 :     case Inst::kIdVextracti128: return Inst::kIdVextracti32x4;
    1243           0 :     case Inst::kIdVinsertf128: return Inst::kIdVinsertf32x4;
    1244           0 :     case Inst::kIdVinserti128: return Inst::kIdVinserti32x4;
    1245           0 :     case Inst::kIdVmovdqa: return Inst::kIdVmovdqa32;
    1246           0 :     case Inst::kIdVmovdqu: return Inst::kIdVmovdqu32;
    1247           0 :     case Inst::kIdVpand: return Inst::kIdVpandd;
    1248           0 :     case Inst::kIdVpandn: return Inst::kIdVpandnd;
    1249           0 :     case Inst::kIdVpor: return Inst::kIdVpord;
    1250           0 :     case Inst::kIdVpxor: return Inst::kIdVpxord;
    1251           0 :     case Inst::kIdVroundpd: return Inst::kIdVrndscalepd;
    1252           0 :     case Inst::kIdVroundps: return Inst::kIdVrndscaleps;
    1253           0 :     case Inst::kIdVroundsd: return Inst::kIdVrndscalesd;
    1254           0 :     case Inst::kIdVroundss: return Inst::kIdVrndscaless;
    1255             : 
    1256           0 :     default:
    1257             :       // This should never happen as only transformable instructions should go this path.
    1258             :       ASMJIT_ASSERT(false);
    1259           0 :       return 0;
    1260             :   }
    1261             : }
    1262             : 
    1263       27492 : ASMJIT_FAVOR_SPEED Error X86RAPass::_rewrite(BaseNode* first, BaseNode* stop) noexcept {
    1264             :   uint32_t virtCount = cc()->_vRegArray.size();
    1265             : 
    1266             :   BaseNode* node = first;
    1267      529469 :   while (node != stop) {
    1268             :     BaseNode* next = node->next();
    1269      501977 :     if (node->isInst()) {
    1270             :       InstNode* inst = node->as<InstNode>();
    1271             :       RAInst* raInst = node->passData<RAInst>();
    1272             : 
    1273             :       Operand* operands = inst->operands();
    1274             :       uint32_t opCount = inst->opCount();
    1275             :       uint32_t maxRegId = 0;
    1276             : 
    1277             :       uint32_t i;
    1278             : 
    1279             :       // Rewrite virtual registers into physical registers.
    1280      419501 :       if (raInst) {
    1281             :         // This data is allocated by Zone passed to `runOnFunction()`, which will be reset after the RA pass finishes.
    1282             :         // So reset this data to prevent having a dead pointer after the RA pass is complete.
    1283             :         node->resetPassData();
    1284             : 
    1285             :         // If the instruction contains pass data (raInst) then it was a subject for register allocation and must be
    1286             :         // rewritten to use physical regs.
    1287             :         RATiedReg* tiedRegs = raInst->tiedRegs();
    1288             :         uint32_t tiedCount = raInst->tiedCount();
    1289             : 
    1290      946262 :         for (i = 0; i < tiedCount; i++) {
    1291      614454 :           RATiedReg* tiedReg = &tiedRegs[i];
    1292             : 
    1293             :           Support::BitWordIterator<uint32_t> useIt(tiedReg->useRewriteMask());
    1294             :           uint32_t useId = tiedReg->useId();
    1295      934197 :           while (useIt.hasNext()) {
    1296             :             maxRegId = Support::max(maxRegId, useId);
    1297             :             inst->rewriteIdAtIndex(useIt.next(), useId);
    1298             :           }
    1299             : 
    1300             :           Support::BitWordIterator<uint32_t> outIt(tiedReg->outRewriteMask());
    1301             :           uint32_t outId = tiedReg->outId();
    1302      918397 :           while (outIt.hasNext()) {
    1303             :             maxRegId = Support::max(maxRegId, outId);
    1304             :             inst->rewriteIdAtIndex(outIt.next(), outId);
    1305             :           }
    1306             :         }
    1307             : 
    1308             :         // Transform VEX instruction to EVEX when necessary.
    1309      331808 :         if (raInst->isTransformable()) {
    1310           6 :           if (maxRegId > 15) {
    1311           0 :             inst->setId(transformVexToEvex(inst->id()));
    1312             :           }
    1313             :         }
    1314             : 
    1315             :         // Remove moves that do not do anything.
    1316             :         //
    1317             :         // Usually these moves are inserted during code generation and originally they used different registers. If RA
    1318             :         // allocated these into the same register such redundant mov would appear.
    1319      331808 :         if (raInst->hasInstRWFlag(InstRWFlags::kMovOp) && !inst->hasExtraReg()) {
    1320           0 :           if (inst->opCount() == 2) {
    1321           0 :             if (inst->op(0) == inst->op(1)) {
    1322           0 :               cc()->removeNode(node);
    1323           0 :               goto Next;
    1324             :             }
    1325             :           }
    1326             :         }
    1327             : 
    1328      331808 :         if (ASMJIT_UNLIKELY(node->type() != NodeType::kInst)) {
    1329             :           // FuncRet terminates the flow, it must either be removed if the exit label is next to it (optimization) or
    1330             :           // patched to an architecture dependent jump instruction that jumps to the function's exit before the epilog.
    1331       37131 :           if (node->type() == NodeType::kFuncRet) {
    1332             :             RABlock* block = raInst->block();
    1333       27492 :             if (!isNextTo(node, _func->exitNode())) {
    1334             :               cc()->_setCursor(node->prev());
    1335           0 :               ASMJIT_PROPAGATE(emitJump(_func->exitNode()->label()));
    1336             :             }
    1337             : 
    1338             :             BaseNode* prev = node->prev();
    1339       27492 :             cc()->removeNode(node);
    1340             :             block->setLast(prev);
    1341             :           }
    1342             :         }
    1343             :       }
    1344             : 
    1345             :       // Rewrite stack slot addresses.
    1346     1303988 :       for (i = 0; i < opCount; i++) {
    1347      884487 :         Operand& op = operands[i];
    1348      884487 :         if (op.isMem()) {
    1349             :           BaseMem& mem = op.as<BaseMem>();
    1350      142105 :           if (mem.isRegHome()) {
    1351             :             uint32_t virtIndex = Operand::virtIdToIndex(mem.baseId());
    1352       71131 :             if (ASMJIT_UNLIKELY(virtIndex >= virtCount))
    1353             :               return DebugUtils::errored(kErrorInvalidVirtId);
    1354             : 
    1355             :             VirtReg* virtReg = cc()->virtRegByIndex(virtIndex);
    1356             :             RAWorkReg* workReg = virtReg->workReg();
    1357             :             ASMJIT_ASSERT(workReg != nullptr);
    1358             : 
    1359             :             RAStackSlot* slot = workReg->stackSlot();
    1360             :             int32_t offset = slot->offset();
    1361             : 
    1362             :             mem._setBase(_sp.type(), slot->baseRegId());
    1363             :             mem.clearRegHome();
    1364             :             mem.addOffsetLo32(offset);
    1365             :           }
    1366             :         }
    1367             :       }
    1368             :     }
    1369             : 
    1370       82476 : Next:
    1371             :     node = next;
    1372             :   }
    1373             : 
    1374             :   return kErrorOk;
    1375             : }
    1376             : 
    1377             : // x86::X86RAPass - OnEmit
    1378             : // =======================
    1379             : 
    1380        1785 : Error X86RAPass::emitMove(uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept {
    1381             :   RAWorkReg* wReg = workRegById(workId);
    1382             :   BaseReg dst(wReg->signature(), dstPhysId);
    1383             :   BaseReg src(wReg->signature(), srcPhysId);
    1384             : 
    1385             :   const char* comment = nullptr;
    1386             : 
    1387             : #ifndef ASMJIT_NO_LOGGING
    1388        1785 :   if (hasDiagnosticOption(DiagnosticOptions::kRAAnnotate)) {
    1389           0 :     _tmpString.assignFormat("<MOVE> %s", workRegById(workId)->name());
    1390             :     comment = _tmpString.data();
    1391             :   }
    1392             : #endif
    1393             : 
    1394        1785 :   return _emitHelper.emitRegMove(dst, src, wReg->typeId(), comment);
    1395             : }
    1396             : 
    1397           0 : Error X86RAPass::emitSwap(uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept {
    1398             :   RAWorkReg* waReg = workRegById(aWorkId);
    1399             :   RAWorkReg* wbReg = workRegById(bWorkId);
    1400             : 
    1401             :   bool is64Bit = Support::max(waReg->typeId(), wbReg->typeId()) >= TypeId::kInt64;
    1402           0 :   OperandSignature sign = is64Bit ? OperandSignature{RegTraits<RegType::kX86_Gpq>::kSignature}
    1403             :                                   : OperandSignature{RegTraits<RegType::kX86_Gpd>::kSignature};
    1404             : 
    1405             : #ifndef ASMJIT_NO_LOGGING
    1406           0 :   if (hasDiagnosticOption(DiagnosticOptions::kRAAnnotate)) {
    1407           0 :     _tmpString.assignFormat("<SWAP> %s, %s", waReg->name(), wbReg->name());
    1408             :     cc()->setInlineComment(_tmpString.data());
    1409             :   }
    1410             : #endif
    1411             : 
    1412           0 :   return cc()->emit(Inst::kIdXchg, Reg(sign, aPhysId), Reg(sign, bPhysId));
    1413             : }
    1414             : 
    1415       17638 : Error X86RAPass::emitLoad(uint32_t workId, uint32_t dstPhysId) noexcept {
    1416             :   RAWorkReg* wReg = workRegById(workId);
    1417             :   BaseReg dstReg(wReg->signature(), dstPhysId);
    1418       17638 :   BaseMem srcMem(workRegAsMem(wReg));
    1419             : 
    1420             :   const char* comment = nullptr;
    1421             : 
    1422             : #ifndef ASMJIT_NO_LOGGING
    1423       17638 :   if (hasDiagnosticOption(DiagnosticOptions::kRAAnnotate)) {
    1424           0 :     _tmpString.assignFormat("<LOAD> %s", workRegById(workId)->name());
    1425             :     comment = _tmpString.data();
    1426             :   }
    1427             : #endif
    1428             : 
    1429       17638 :   return _emitHelper.emitRegMove(dstReg, srcMem, wReg->typeId(), comment);
    1430             : }
    1431             : 
    1432       30798 : Error X86RAPass::emitSave(uint32_t workId, uint32_t srcPhysId) noexcept {
    1433             :   RAWorkReg* wReg = workRegById(workId);
    1434       30798 :   BaseMem dstMem(workRegAsMem(wReg));
    1435             :   BaseReg srcReg(wReg->signature(), srcPhysId);
    1436             : 
    1437             :   const char* comment = nullptr;
    1438             : 
    1439             : #ifndef ASMJIT_NO_LOGGING
    1440       30798 :   if (hasDiagnosticOption(DiagnosticOptions::kRAAnnotate)) {
    1441           0 :     _tmpString.assignFormat("<SAVE> %s", workRegById(workId)->name());
    1442             :     comment = _tmpString.data();
    1443             :   }
    1444             : #endif
    1445             : 
    1446       30798 :   return _emitHelper.emitRegMove(dstMem, srcReg, wReg->typeId(), comment);
    1447             : }
    1448             : 
    1449           0 : Error X86RAPass::emitJump(const Label& label) noexcept {
    1450           0 :   return cc()->jmp(label);
    1451             : }
    1452             : 
    1453        9639 : Error X86RAPass::emitPreCall(InvokeNode* invokeNode) noexcept {
    1454        9639 :   if (invokeNode->detail().hasVarArgs() && cc()->is64Bit()) {
    1455             :     const FuncDetail& fd = invokeNode->detail();
    1456             :     uint32_t argCount = invokeNode->argCount();
    1457             : 
    1458           0 :     switch (invokeNode->detail().callConv().id()) {
    1459             :       case CallConvId::kX64SystemV: {
    1460             :         // AL register contains the number of arguments passed in XMM register(s).
    1461             :         uint32_t n = 0;
    1462           0 :         for (uint32_t argIndex = 0; argIndex < argCount; argIndex++) {
    1463           0 :           const FuncValuePack& argPack = fd.argPack(argIndex);
    1464           0 :           for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
    1465           0 :             const FuncValue& arg = argPack[valueIndex];
    1466           0 :             if (!arg)
    1467             :               break;
    1468             : 
    1469           0 :             if (arg.isReg() && Reg::groupOf(arg.regType()) == RegGroup::kVec)
    1470           0 :               n++;
    1471             :           }
    1472             :         }
    1473             : 
    1474           0 :         if (!n)
    1475           0 :           ASMJIT_PROPAGATE(cc()->xor_(eax, eax));
    1476             :         else
    1477           0 :           ASMJIT_PROPAGATE(cc()->mov(eax, n));
    1478             :         break;
    1479             :       }
    1480             : 
    1481             :       case CallConvId::kX64Windows: {
    1482             :         // Each double-precision argument passed in XMM must be also passed in GP.
    1483           0 :         for (uint32_t argIndex = 0; argIndex < argCount; argIndex++) {
    1484           0 :           const FuncValuePack& argPack = fd.argPack(argIndex);
    1485           0 :           for (uint32_t valueIndex = 0; valueIndex < Globals::kMaxValuePack; valueIndex++) {
    1486           0 :             const FuncValue& arg = argPack[valueIndex];
    1487           0 :             if (!arg)
    1488             :               break;
    1489             : 
    1490           0 :             if (arg.isReg() && Reg::groupOf(arg.regType()) == RegGroup::kVec) {
    1491           0 :               Gp dst = gpq(fd.callConv().passedOrder(RegGroup::kGp)[argIndex]);
    1492             :               Xmm src = xmm(arg.regId());
    1493           0 :               ASMJIT_PROPAGATE(cc()->emit(choose(Inst::kIdMovq, Inst::kIdVmovq), dst, src));
    1494             :             }
    1495             :           }
    1496             :         }
    1497             :         break;
    1498             :       }
    1499             : 
    1500             :       default:
    1501             :         return DebugUtils::errored(kErrorInvalidState);
    1502             :     }
    1503             :   }
    1504             : 
    1505             :   return kErrorOk;
    1506             : }
    1507             : 
    1508             : ASMJIT_END_SUB_NAMESPACE
    1509             : 
    1510             : #endif // !ASMJIT_NO_X86 && !ASMJIT_NO_COMPILER
    1511             : 
    1512             : #endif // __PLUMED_HAS_ASMJIT

Generated by: LCOV version 1.16