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