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