1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QBuffer>
5#include <QFile>
6#include <QLoggingCategory>
7
8#include "qv4engine_p.h"
9#include "qv4assemblercommon_p.h"
10#include <private/qv4function_p.h>
11#include <private/qv4functiontable_p.h>
12#include <private/qv4runtime_p.h>
13
14#include <assembler/MacroAssemblerCodeRef.h>
15#include <assembler/LinkBuffer.h>
16#include <WTFStubs.h>
17
18#if QT_CONFIG(qml_jit)
19
20#undef ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES
21
22QT_BEGIN_NAMESPACE
23namespace QV4 {
24namespace JIT {
25
26Q_LOGGING_CATEGORY(lcAsm, "qt.qml.v4.asm")
27
28namespace {
29class QIODevicePrintStream: public FilePrintStream
30{
31 Q_DISABLE_COPY(QIODevicePrintStream)
32
33public:
34 explicit QIODevicePrintStream(QIODevice *dest)
35 : FilePrintStream(nullptr)
36 , dest(dest)
37 , buf(4096, '0')
38 {
39 Q_ASSERT(dest);
40 }
41
42 ~QIODevicePrintStream()
43 {}
44
45 void vprintf(const char* format, va_list argList) override WTF_ATTRIBUTE_PRINTF(2, 0)
46 {
47 const int written = qvsnprintf(str: buf.data(), n: buf.size(), fmt: format, ap: argList);
48 if (written > 0)
49 dest->write(data: buf.constData(), len: written);
50 memset(s: buf.data(), c: 0, n: qMin(a: written, b: buf.size()));
51 }
52
53 void flush() override
54 {}
55
56private:
57 QIODevice *dest;
58 QByteArray buf;
59};
60} // anonymous namespace
61
62static void printDisassembledOutputWithCalls(QByteArray processedOutput,
63 const QHash<const void*, const char*>& functions)
64{
65 const auto symbols = Runtime::symbolTable();
66 const QByteArray padding(" ; ");
67 for (auto it = functions.begin(), end = functions.end(); it != end; ++it) {
68 const QByteArray ptrString = "0x" + QByteArray::number(quintptr(it.key()), base: 16);
69 int idx = 0;
70 while (idx >= 0) {
71 idx = processedOutput.indexOf(bv: ptrString, from: idx);
72 if (idx < 0)
73 break;
74 idx = processedOutput.indexOf(c: '\n', from: idx);
75 if (idx < 0)
76 break;
77 const char *functionName = it.value();
78 processedOutput = processedOutput.insert(
79 i: idx, data: QByteArray(padding + QByteArray(
80 functionName ? functionName : symbols[it.key()])));
81 }
82 }
83
84 auto lines = processedOutput.split(sep: '\n');
85 for (const auto &line : lines)
86 qCDebug(lcAsm, "%s", line.constData());
87}
88
89JIT::PlatformAssemblerCommon::~PlatformAssemblerCommon()
90{}
91
92void PlatformAssemblerCommon::link(Function *function, const char *jitKind)
93{
94 for (const auto &jumpTarget : jumpsToLink)
95 jumpTarget.jump.linkTo(label: labelForOffset[jumpTarget.offset], masm: this);
96
97 JSC::JSGlobalData dummy(function->internalClass->engine->executableAllocator);
98 JSC::LinkBuffer<MacroAssembler> linkBuffer(dummy, this, nullptr);
99
100 for (const auto &ehTarget : ehTargets) {
101 auto targetLabel = labelForOffset.value(key: ehTarget.offset);
102 linkBuffer.patch(label: ehTarget.label, value: linkBuffer.locationOf(label: targetLabel));
103 }
104
105 JSC::MacroAssemblerCodeRef codeRef;
106
107 static const bool showCode = lcAsm().isDebugEnabled();
108 if (showCode) {
109 QBuffer buf;
110 buf.open(openMode: QIODevice::WriteOnly);
111 WTF::setDataFile(new QIODevicePrintStream(&buf));
112
113 // We use debugAddress here because it's actually for debugging and hidden behind an
114 // environment variable.
115 const QByteArray name = Function::prettyName(function, address: linkBuffer.debugAddress()).toUtf8();
116 codeRef = linkBuffer.finalizeCodeWithDisassembly(jitKind, func: name.constData());
117
118 WTF::setDataFile(stderr);
119 printDisassembledOutputWithCalls(processedOutput: buf.data(), functions);
120 } else {
121 codeRef = linkBuffer.finalizeCodeWithoutDisassembly();
122 }
123
124 function->codeRef = new JSC::MacroAssemblerCodeRef(codeRef);
125 function->jittedCode = reinterpret_cast<Function::JittedCode>(function->codeRef->code().executableAddress());
126
127 generateFunctionTable(function, codeRef: &codeRef);
128
129 if (Q_UNLIKELY(!linkBuffer.makeExecutable()))
130 function->jittedCode = nullptr; // The function is not executable, but the coderef exists.
131}
132
133void PlatformAssemblerCommon::prepareCallWithArgCount(int argc)
134{
135#ifndef QT_NO_DEBUG
136 Q_ASSERT(remainingArgcForCall == NoCall);
137 remainingArgcForCall = argc;
138#endif
139
140 if (argc > ArgInRegCount) {
141 argcOnStackForCall = int(WTF::roundUpToMultipleOf(divisor: 16, x: size_t(argc - ArgInRegCount) * PointerSize));
142 subPtr(imm: TrustedImm32(argcOnStackForCall), dest: StackPointerRegister);
143 }
144}
145
146void PlatformAssemblerCommon::storeInstructionPointer(int instructionOffset)
147{
148 Address addr(CppStackFrameRegister, offsetof(QV4::JSTypesStackFrame, instructionPointer));
149 store32(imm: TrustedImm32(instructionOffset), address: addr);
150}
151
152PlatformAssemblerCommon::Address PlatformAssemblerCommon::argStackAddress(int arg)
153{
154 int offset = arg - ArgInRegCount;
155 Q_ASSERT(offset >= 0);
156 return Address(StackPointerRegister, offset * PointerSize);
157}
158
159void PlatformAssemblerCommon::passAccumulatorAsArg(int arg)
160{
161#ifndef QT_NO_DEBUG
162 Q_ASSERT(arg < remainingArgcForCall);
163 --remainingArgcForCall;
164#endif
165
166 passAccumulatorAsArg_internal(arg, doPush: false);
167}
168
169void JIT::PlatformAssemblerCommon::pushAccumulatorAsArg(int arg)
170{
171 passAccumulatorAsArg_internal(arg, doPush: true);
172}
173
174void PlatformAssemblerCommon::passAccumulatorAsArg_internal(int arg, bool doPush)
175{
176 if (arg < ArgInRegCount) {
177 addPtr(imm: TrustedImm32(offsetof(CallData, accumulator)), src: JSStackFrameRegister, dest: registerForArg(arg));
178 } else {
179 addPtr(imm: TrustedImm32(offsetof(CallData, accumulator)), src: JSStackFrameRegister, dest: ScratchRegister);
180 if (doPush)
181 push(src: ScratchRegister);
182 else
183 storePtr(src: ScratchRegister, address: argStackAddress(arg));
184 }
185}
186
187void PlatformAssemblerCommon::passFunctionAsArg(int arg)
188{
189#ifndef QT_NO_DEBUG
190 Q_ASSERT(arg < remainingArgcForCall);
191 --remainingArgcForCall;
192#endif
193
194 if (arg < ArgInRegCount) {
195 loadFunctionPtr(target: registerForArg(arg));
196 } else {
197 loadFunctionPtr(target: ScratchRegister);
198 storePtr(src: ScratchRegister, address: argStackAddress(arg));
199 }
200}
201
202void PlatformAssemblerCommon::passEngineAsArg(int arg)
203{
204#ifndef QT_NO_DEBUG
205 Q_ASSERT(arg < remainingArgcForCall);
206 --remainingArgcForCall;
207#endif
208
209 if (arg < ArgInRegCount) {
210 move(src: EngineRegister, dest: registerForArg(arg));
211 } else {
212 storePtr(src: EngineRegister, address: argStackAddress(arg));
213 }
214}
215
216void PlatformAssemblerCommon::passJSSlotAsArg(int reg, int arg)
217{
218 Address addr(JSStackFrameRegister, reg * int(sizeof(QV4::Value)));
219 passAddressAsArg(addr, arg);
220}
221
222void JIT::PlatformAssemblerCommon::passAddressAsArg(Address addr, int arg)
223{
224#ifndef QT_NO_DEBUG
225 Q_ASSERT(arg < remainingArgcForCall);
226 --remainingArgcForCall;
227#endif
228
229 if (arg < ArgInRegCount) {
230 addPtr(imm: TrustedImm32(addr.offset), src: addr.base, dest: registerForArg(arg));
231 } else {
232 addPtr(imm: TrustedImm32(addr.offset), src: addr.base, dest: ScratchRegister);
233 storePtr(src: ScratchRegister, address: argStackAddress(arg));
234 }
235}
236
237void PlatformAssemblerCommon::passCppFrameAsArg(int arg)
238{
239#ifndef QT_NO_DEBUG
240 Q_ASSERT(arg < remainingArgcForCall);
241 --remainingArgcForCall;
242#endif
243
244 if (arg < ArgInRegCount)
245 move(src: CppStackFrameRegister, dest: registerForArg(arg));
246 else
247 store32(src: CppStackFrameRegister, address: argStackAddress(arg));
248}
249
250void PlatformAssemblerCommon::passInt32AsArg(int value, int arg)
251{
252#ifndef QT_NO_DEBUG
253 Q_ASSERT(arg < remainingArgcForCall);
254 --remainingArgcForCall;
255#endif
256
257 if (arg < ArgInRegCount)
258 move(imm: TrustedImm32(value), dest: registerForArg(arg));
259 else
260 store32(imm: TrustedImm32(value), address: argStackAddress(arg));
261}
262
263void JIT::PlatformAssemblerCommon::passPointerAsArg(void *ptr, int arg)
264{
265#ifndef QT_NO_DEBUG
266 Q_ASSERT(arg < remainingArgcForCall);
267 --remainingArgcForCall;
268#endif
269
270 if (arg < ArgInRegCount)
271 move(imm: TrustedImmPtr(ptr), dest: registerForArg(arg));
272 else
273 storePtr(imm: TrustedImmPtr(ptr), address: argStackAddress(arg));
274}
275
276void PlatformAssemblerCommon::callRuntime(const void *funcPtr, const char *functionName)
277{
278#ifndef QT_NO_DEBUG
279 Q_ASSERT(remainingArgcForCall == 0);
280 remainingArgcForCall = NoCall;
281#endif
282 callRuntimeUnchecked(funcPtr, functionName);
283 if (argcOnStackForCall > 0) {
284 addPtr(imm: TrustedImm32(argcOnStackForCall), srcDest: StackPointerRegister);
285 argcOnStackForCall = 0;
286 }
287}
288
289void PlatformAssemblerCommon::callRuntimeUnchecked(const void *funcPtr, const char *functionName)
290{
291 Q_ASSERT(functionName || Runtime::symbolTable().contains(funcPtr));
292 functions.insert(key: funcPtr, value: functionName);
293 callAbsolute(funcPtr);
294}
295
296void PlatformAssemblerCommon::tailCallRuntime(const void *funcPtr, const char *functionName)
297{
298 Q_ASSERT(functionName || Runtime::symbolTable().contains(funcPtr));
299 functions.insert(key: funcPtr, value: functionName);
300 setTailCallArg(src: EngineRegister, arg: 1);
301 setTailCallArg(src: CppStackFrameRegister, arg: 0);
302 freeStackSpace();
303 generatePlatformFunctionExit(/*tailCall =*/ true);
304 jumpAbsolute(funcPtr);
305}
306
307void PlatformAssemblerCommon::setTailCallArg(RegisterID src, int arg)
308{
309 if (arg < ArgInRegCount) {
310 move(src, dest: registerForArg(arg));
311 } else {
312 // We never write to the incoming arguments space on the stack, and the tail call runtime
313 // method has the same signature as the jitted function, so it is safe for us to just reuse
314 // the arguments that we got in.
315 }
316}
317
318JSC::MacroAssemblerBase::Address PlatformAssemblerCommon::jsAlloca(int slotCount)
319{
320 Address jsStackTopAddr(EngineRegister, offsetof(EngineBase, jsStackTop));
321 RegisterID jsStackTop = AccumulatorRegisterValue;
322 loadPtr(address: jsStackTopAddr, dest: jsStackTop);
323 addPtr(imm: TrustedImm32(sizeof(Value) * slotCount), srcDest: jsStackTop);
324 storePtr(src: jsStackTop, address: jsStackTopAddr);
325 return Address(jsStackTop, 0);
326}
327
328void PlatformAssemblerCommon::storeInt32AsValue(int srcInt, Address destAddr)
329{
330 store32(imm: TrustedImm32(srcInt),
331 address: Address(destAddr.base, destAddr.offset + QV4::Value::valueOffset()));
332 store32(imm: TrustedImm32(int(QV4::Value::ValueTypeInternal::Integer)),
333 address: Address(destAddr.base, destAddr.offset + QV4::Value::tagOffset()));
334}
335
336} // JIT namespace
337} // QV4 namepsace
338
339QT_END_NAMESPACE
340
341#endif // QT_CONFIG(qml_jit)
342

source code of qtdeclarative/src/qml/jit/qv4assemblercommon.cpp