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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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