Line data Source code
1 : #ifdef __PLUMED_HAS_ASMJIT
2 : // This file is part of AsmJit project <https://asmjit.com>
3 : //
4 : // See asmjit.h or LICENSE.md for license and copyright information
5 : // SPDX-License-Identifier: Zlib
6 :
7 : #ifndef ASMJIT_CORE_RALOCAL_P_H_INCLUDED
8 : #define ASMJIT_CORE_RALOCAL_P_H_INCLUDED
9 :
10 : #include "api-config.h"
11 : #ifndef ASMJIT_NO_COMPILER
12 :
13 : #include "raassignment_p.h"
14 : #include "radefs_p.h"
15 : #include "rapass_p.h"
16 : #include "support.h"
17 :
18 : ASMJIT_BEGIN_NAMESPACE
19 :
20 : //! \cond INTERNAL
21 : //! \addtogroup asmjit_ra
22 : //! \{
23 :
24 : //! Local register allocator.
25 : class RALocalAllocator {
26 : public:
27 : ASMJIT_NONCOPYABLE(RALocalAllocator)
28 :
29 : typedef RAAssignment::PhysToWorkMap PhysToWorkMap;
30 : typedef RAAssignment::WorkToPhysMap WorkToPhysMap;
31 :
32 : //! Link to `BaseRAPass`.
33 : BaseRAPass* _pass;
34 : //! Link to `BaseCompiler`.
35 : BaseCompiler* _cc;
36 :
37 : //! Architecture traits.
38 : const ArchTraits* _archTraits;
39 : //! Registers available to the allocator.
40 : RARegMask _availableRegs;
41 : //! Registers clobbered by the allocator.
42 : RARegMask _clobberedRegs;
43 :
44 : //! Register assignment (current).
45 : RAAssignment _curAssignment;
46 : //! Register assignment used temporarily during assignment switches.
47 : RAAssignment _tmpAssignment;
48 :
49 : //! Link to the current `RABlock`.
50 : RABlock* _block;
51 : //! InstNode.
52 : InstNode* _node;
53 : //! RA instruction.
54 : RAInst* _raInst;
55 :
56 : //! Count of all TiedReg's.
57 : uint32_t _tiedTotal;
58 : //! TiedReg's total counter.
59 : RARegCount _tiedCount;
60 :
61 : //! Temporary workToPhysMap that can be used freely by the allocator.
62 : WorkToPhysMap* _tmpWorkToPhysMap;
63 :
64 : //! \name Construction & Destruction
65 : //! \{
66 :
67 27492 : inline RALocalAllocator(BaseRAPass* pass) noexcept
68 27492 : : _pass(pass),
69 27492 : _cc(pass->cc()),
70 27492 : _archTraits(pass->_archTraits),
71 27492 : _availableRegs(pass->_availableRegs),
72 109968 : _clobberedRegs(),
73 27492 : _curAssignment(),
74 27492 : _block(nullptr),
75 27492 : _node(nullptr),
76 27492 : _raInst(nullptr),
77 27492 : _tiedTotal(),
78 137460 : _tiedCount() {}
79 :
80 : Error init() noexcept;
81 :
82 : //! \}
83 :
84 : //! \name Accessors
85 : //! \{
86 :
87 580836 : inline RAWorkReg* workRegById(uint32_t workId) const noexcept { return _pass->workRegById(workId); }
88 : inline PhysToWorkMap* physToWorkMap() const noexcept { return _curAssignment.physToWorkMap(); }
89 : inline WorkToPhysMap* workToPhysMap() const noexcept { return _curAssignment.workToPhysMap(); }
90 :
91 : //! Returns the currently processed block.
92 0 : inline RABlock* block() const noexcept { return _block; }
93 : //! Sets the currently processed block.
94 54984 : inline void setBlock(RABlock* block) noexcept { _block = block; }
95 :
96 : //! Returns the currently processed `InstNode`.
97 : inline InstNode* node() const noexcept { return _node; }
98 : //! Returns the currently processed `RAInst`.
99 : inline RAInst* raInst() const noexcept { return _raInst; }
100 :
101 : //! Returns all tied regs as `RATiedReg` array.
102 : inline RATiedReg* tiedRegs() const noexcept { return _raInst->tiedRegs(); }
103 : //! Returns tied registers grouped by the given `group`.
104 1327232 : inline RATiedReg* tiedRegs(RegGroup group) const noexcept { return _raInst->tiedRegs(group); }
105 :
106 : //! Returns count of all TiedRegs used by the instruction.
107 : inline uint32_t tiedCount() const noexcept { return _tiedTotal; }
108 : //! Returns count of TiedRegs used by the given register `group`.
109 : inline uint32_t tiedCount(RegGroup group) const noexcept { return _tiedCount.get(group); }
110 :
111 : inline bool isGroupUsed(RegGroup group) const noexcept { return _tiedCount[group] != 0; }
112 :
113 : //! \}
114 :
115 : //! \name Assignment
116 : //! \{
117 :
118 : Error makeInitialAssignment() noexcept;
119 :
120 : Error replaceAssignment(const PhysToWorkMap* physToWorkMap) noexcept;
121 :
122 : //! Switch to the given assignment by reassigning all register and emitting code that reassigns them.
123 : //! This is always used to switch to a previously stored assignment.
124 : //!
125 : //! If `tryMode` is true then the final assignment doesn't have to be exactly same as specified by `dstPhysToWorkMap`
126 : //! and `dstWorkToPhysMap`. This mode is only used before conditional jumps that already have assignment to generate
127 : //! a code sequence that is always executed regardless of the flow.
128 : Error switchToAssignment(PhysToWorkMap* dstPhysToWorkMap, const ZoneBitVector& liveIn, bool dstReadOnly, bool tryMode) noexcept;
129 :
130 27492 : inline Error spillRegsBeforeEntry(RABlock* block) noexcept {
131 27492 : return spillScratchGpRegsBeforeEntry(block->entryScratchGpRegs());
132 : }
133 :
134 : Error spillScratchGpRegsBeforeEntry(uint32_t scratchRegs) noexcept;
135 :
136 : //! \}
137 :
138 : //! \name Allocation
139 : //! \{
140 :
141 : Error allocInst(InstNode* node) noexcept;
142 : Error spillAfterAllocation(InstNode* node) noexcept;
143 :
144 : Error allocBranch(InstNode* node, RABlock* target, RABlock* cont) noexcept;
145 : Error allocJumpTable(InstNode* node, const RABlocks& targets, RABlock* cont) noexcept;
146 :
147 : //! \}
148 :
149 : //! \name Decision Making
150 : //! \{
151 :
152 : enum CostModel : uint32_t {
153 : kCostOfFrequency = 1048576,
154 : kCostOfDirtyFlag = kCostOfFrequency / 4
155 : };
156 :
157 : inline uint32_t costByFrequency(float freq) const noexcept {
158 117536 : return uint32_t(int32_t(freq * float(kCostOfFrequency)));
159 : }
160 :
161 117536 : inline uint32_t calculateSpillCost(RegGroup group, uint32_t workId, uint32_t assignedId) const noexcept {
162 : RAWorkReg* workReg = workRegById(workId);
163 : uint32_t cost = costByFrequency(workReg->liveStats().freq());
164 :
165 117536 : if (_curAssignment.isPhysDirty(group, assignedId))
166 117536 : cost += kCostOfDirtyFlag;
167 :
168 117536 : return cost;
169 : }
170 :
171 : //! Decides on register assignment.
172 : uint32_t decideOnAssignment(RegGroup group, uint32_t workId, uint32_t assignedId, RegMask allocableRegs) const noexcept;
173 :
174 : //! Decides on whether to MOVE or SPILL the given WorkReg, because it's allocated in a physical register that have
175 : //! to be used by another WorkReg.
176 : //!
177 : //! The function must return either `RAAssignment::kPhysNone`, which means that the WorkReg of `workId` should be
178 : //! spilled, or a valid physical register ID, which means that the register should be moved to that physical register
179 : //! instead.
180 : uint32_t decideOnReassignment(RegGroup group, uint32_t workId, uint32_t assignedId, RegMask allocableRegs) const noexcept;
181 :
182 : //! Decides on best spill given a register mask `spillableRegs`
183 : uint32_t decideOnSpillFor(RegGroup group, uint32_t workId, RegMask spillableRegs, uint32_t* spillWorkId) const noexcept;
184 :
185 : //! \}
186 :
187 : //! \name Emit
188 : //! \{
189 :
190 : //! Emits a move between a destination and source register, and fixes the
191 : //! register assignment.
192 1785 : inline Error onMoveReg(RegGroup group, uint32_t workId, uint32_t dstPhysId, uint32_t srcPhysId) noexcept {
193 1785 : if (dstPhysId == srcPhysId) return kErrorOk;
194 1785 : _curAssignment.reassign(group, workId, dstPhysId, srcPhysId);
195 1785 : return _pass->emitMove(workId, dstPhysId, srcPhysId);
196 : }
197 :
198 : //! Emits a swap between two physical registers and fixes their assignment.
199 : //!
200 : //! \note Target must support this operation otherwise this would ASSERT.
201 0 : inline Error onSwapReg(RegGroup group, uint32_t aWorkId, uint32_t aPhysId, uint32_t bWorkId, uint32_t bPhysId) noexcept {
202 0 : _curAssignment.swap(group, aWorkId, aPhysId, bWorkId, bPhysId);
203 0 : return _pass->emitSwap(aWorkId, aPhysId, bWorkId, bPhysId);
204 : }
205 :
206 : //! Emits a load from [VirtReg/WorkReg]'s spill slot to a physical register
207 : //! and makes it assigned and clean.
208 17638 : inline Error onLoadReg(RegGroup group, uint32_t workId, uint32_t physId) noexcept {
209 17638 : _curAssignment.assign(group, workId, physId, RAAssignment::kClean);
210 17638 : return _pass->emitLoad(workId, physId);
211 : }
212 :
213 : //! Emits a save a physical register to a [VirtReg/WorkReg]'s spill slot,
214 : //! keeps it assigned, and makes it clean.
215 30798 : inline Error onSaveReg(RegGroup group, uint32_t workId, uint32_t physId) noexcept {
216 : ASMJIT_ASSERT(_curAssignment.workToPhysId(group, workId) == physId);
217 : ASMJIT_ASSERT(_curAssignment.physToWorkId(group, physId) == workId);
218 :
219 : _curAssignment.makeClean(group, workId, physId);
220 30798 : return _pass->emitSave(workId, physId);
221 : }
222 :
223 : //! Assigns a register, the content of it is undefined at this point.
224 : inline Error onAssignReg(RegGroup group, uint32_t workId, uint32_t physId, bool dirty) noexcept {
225 276738 : _curAssignment.assign(group, workId, physId, dirty);
226 : return kErrorOk;
227 : }
228 :
229 : //! Spills a variable/register, saves the content to the memory-home if modified.
230 35846 : inline Error onSpillReg(RegGroup group, uint32_t workId, uint32_t physId) noexcept {
231 35846 : if (_curAssignment.isPhysDirty(group, physId))
232 30798 : ASMJIT_PROPAGATE(onSaveReg(group, workId, physId));
233 35846 : return onKillReg(group, workId, physId);
234 : }
235 :
236 : inline Error onDirtyReg(RegGroup group, uint32_t workId, uint32_t physId) noexcept {
237 : _curAssignment.makeDirty(group, workId, physId);
238 : return kErrorOk;
239 : }
240 :
241 : inline Error onKillReg(RegGroup group, uint32_t workId, uint32_t physId) noexcept {
242 294376 : _curAssignment.unassign(group, workId, physId);
243 : return kErrorOk;
244 : }
245 :
246 : //! \}
247 : };
248 :
249 : //! \}
250 : //! \endcond
251 :
252 : ASMJIT_END_NAMESPACE
253 :
254 : #endif // !ASMJIT_NO_COMPILER
255 : #endif // ASMJIT_CORE_RALOCAL_P_H_INCLUDED
256 :
257 : #endif // __PLUMED_HAS_ASMJIT
|