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

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