Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
4#include <QBuffer>
5#include <QFile>
6#include <QLoggingCategory>
7
8#include "qv4engine_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
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 printed = qvsnprintf(buf.data(), buf.size(), format, argList);
48 Q_ASSERT(printed <= buf.size());
49
50 qint64 written = 0;
51 while (written < printed) {
52 const qint64 result = dest->write(buf.constData() + written, printed - written);
53 if (result < 0)
54 break;
55 written += result;
56 }
57
58 Q_ASSERT(written <= buf.size());
59 Q_ASSERT(written >= 0);
60 memset(buf.data(), 0, size_t(written));
61 }
62
63 void flush() override
64 {}
65
66private:
67 QIODevice *dest;
69};
70} // anonymous namespace
71
72static void printDisassembledOutputWithCalls(QByteArray processedOutput,
73 const QHash<const void*, const char*>& functions)
74{
75 const auto symbols = Runtime::symbolTable();
76 const QByteArray padding(" ; ");
77 for (auto it = functions.begin(), end = functions.end(); it != end; ++it) {
78 const QByteArray ptrString = "0x" + QByteArray::number(quintptr(it.key()), 16);
79 int idx = 0;
80 while (idx >= 0) {
81 idx = processedOutput.indexOf(ptrString, idx);
82 if (idx < 0)
83 break;
84 idx = processedOutput.indexOf('\n', idx);
85 if (idx < 0)
86 break;
87 const char *functionName = it.value();
88 processedOutput = processedOutput.insert(
89 idx, QByteArray(padding + QByteArray(
90 functionName ? functionName : symbols[it.key()])));
91 }
92 }
93
94 auto lines = processedOutput.split('\n');
95 for (const auto &line : lines)
96 qCDebug(lcAsm, "%s", line.constData());
97}
98
99JIT::PlatformAssemblerCommon::~PlatformAssemblerCommon()
100{}
101
102void PlatformAssemblerCommon::link(Function *function, const char *jitKind)
103{
104 for (const auto &jumpTarget : jumpsToLink)
105 jumpTarget.jump.linkTo(labelForOffset[jumpTarget.offset], this);
106
107 JSC::JSGlobalData dummy(function->internalClass->engine->executableAllocator);
108 JSC::LinkBuffer<MacroAssembler> linkBuffer(dummy, this, nullptr);
109
110 for (const auto &ehTarget : ehTargets) {
111 auto targetLabel = labelForOffset.value(ehTarget.offset);
112 linkBuffer.patch(ehTarget.label, linkBuffer.locationOf(targetLabel));
113 }
114
115 JSC::MacroAssemblerCodeRef codeRef;
116
117 static const bool showCode = lcAsm().isDebugEnabled();
118 if (showCode) {
119 QBuffer buf;
121 WTF::setDataFile(new QIODevicePrintStream(&buf));
122
123 // We use debugAddress here because it's actually for debugging and hidden behind an
124 // environment variable.
125 const QByteArray name = Function::prettyName(function, linkBuffer.debugAddress()).toUtf8();
126 codeRef = linkBuffer.finalizeCodeWithDisassembly(jitKind, name.constData());
127
128 WTF::setDataFile(stderr);
129 printDisassembledOutputWithCalls(buf.data(), functions);
130 } else {
131 codeRef = linkBuffer.finalizeCodeWithoutDisassembly();
132 }
133
134 function->codeRef = new JSC::MacroAssemblerCodeRef(codeRef);
135 function->jittedCode = reinterpret_cast<Function::JittedCode>(function->codeRef->code().executableAddress());
136
137 generateFunctionTable(function, &codeRef);
138
139 if (Q_UNLIKELY(!linkBuffer.makeExecutable()))
140 function->jittedCode = nullptr; // The function is not executable, but the coderef exists.
141}
142
143void PlatformAssemblerCommon::prepareCallWithArgCount(int argc)
144{
145#ifndef QT_NO_DEBUG
146 Q_ASSERT(remainingArgcForCall == NoCall);
147 remainingArgcForCall = argc;
148#endif
149
150 if (argc > ArgInRegCount) {
151 argcOnStackForCall = int(WTF::roundUpToMultipleOf(16, size_t(argc - ArgInRegCount) * PointerSize));
152 subPtr(TrustedImm32(argcOnStackForCall), StackPointerRegister);
153 }
154}
155
156void PlatformAssemblerCommon::storeInstructionPointer(int instructionOffset)
157{
158 Address addr(CppStackFrameRegister, offsetof(QV4::JSTypesStackFrame, instructionPointer));
159 store32(TrustedImm32(instructionOffset), addr);
160}
161
162PlatformAssemblerCommon::Address PlatformAssemblerCommon::argStackAddress(int arg)
163{
164 int offset = arg - ArgInRegCount;
165 Q_ASSERT(offset >= 0);
166 return Address(StackPointerRegister, offset * PointerSize);
167}
168
169void PlatformAssemblerCommon::passAccumulatorAsArg(int arg)
170{
171#ifndef QT_NO_DEBUG
172 Q_ASSERT(arg < remainingArgcForCall);
173 --remainingArgcForCall;
174#endif
175
176 passAccumulatorAsArg_internal(arg, false);
177}
178
179void JIT::PlatformAssemblerCommon::pushAccumulatorAsArg(int arg)
180{
181 passAccumulatorAsArg_internal(arg, true);
182}
183
184void PlatformAssemblerCommon::passAccumulatorAsArg_internal(int arg, bool doPush)
185{
186 if (arg < ArgInRegCount) {
187 addPtr(TrustedImm32(offsetof(CallData, accumulator)), JSStackFrameRegister, registerForArg(arg));
188 } else {
189 addPtr(TrustedImm32(offsetof(CallData, accumulator)), JSStackFrameRegister, ScratchRegister);
190 if (doPush)
191 push(ScratchRegister);
192 else
193 storePtr(ScratchRegister, argStackAddress(arg));
194 }
195}
196
197void PlatformAssemblerCommon::passFunctionAsArg(int arg)
198{
199#ifndef QT_NO_DEBUG
200 Q_ASSERT(arg < remainingArgcForCall);
201 --remainingArgcForCall;
202#endif
203
204 if (arg < ArgInRegCount) {
205 loadFunctionPtr(registerForArg(arg));
206 } else {
207 loadFunctionPtr(ScratchRegister);
208 storePtr(ScratchRegister, argStackAddress(arg));
209 }
210}
211
212void PlatformAssemblerCommon::passEngineAsArg(int arg)
213{
214#ifndef QT_NO_DEBUG
215 Q_ASSERT(arg < remainingArgcForCall);
216 --remainingArgcForCall;
217#endif
218
219 if (arg < ArgInRegCount) {
220 move(EngineRegister, registerForArg(arg));
221 } else {
222 storePtr(EngineRegister, argStackAddress(arg));
223 }
224}
225
226void PlatformAssemblerCommon::passJSSlotAsArg(int reg, int arg)
227{
228 Address addr(JSStackFrameRegister, reg * int(sizeof(QV4::Value)));
229 passAddressAsArg(addr, arg);
230}
231
232void JIT::PlatformAssemblerCommon::passAddressAsArg(Address addr, int arg)
233{
234#ifndef QT_NO_DEBUG
235 Q_ASSERT(arg < remainingArgcForCall);
236 --remainingArgcForCall;
237#endif
238
239 if (arg < ArgInRegCount) {
240 addPtr(TrustedImm32(addr.offset), addr.base, registerForArg(arg));
241 } else {
242 addPtr(TrustedImm32(addr.offset), addr.base, ScratchRegister);
243 storePtr(ScratchRegister, argStackAddress(arg));
244 }
245}
246
247void PlatformAssemblerCommon::passCppFrameAsArg(int arg)
248{
249#ifndef QT_NO_DEBUG
250 Q_ASSERT(arg < remainingArgcForCall);
251 --remainingArgcForCall;
252#endif
253
254 if (arg < ArgInRegCount)
255 move(CppStackFrameRegister, registerForArg(arg));
256 else
257 store32(CppStackFrameRegister, argStackAddress(arg));
258}
259
260void PlatformAssemblerCommon::passInt32AsArg(int value, int arg)
261{
262#ifndef QT_NO_DEBUG
263 Q_ASSERT(arg < remainingArgcForCall);
264 --remainingArgcForCall;
265#endif
266
267 if (arg < ArgInRegCount)
268 move(TrustedImm32(value), registerForArg(arg));
269 else
270 store32(TrustedImm32(value), argStackAddress(arg));
271}
272
273void JIT::PlatformAssemblerCommon::passPointerAsArg(void *ptr, int arg)
274{
275#ifndef QT_NO_DEBUG
276 Q_ASSERT(arg < remainingArgcForCall);
277 --remainingArgcForCall;
278#endif
279
280 if (arg < ArgInRegCount)
281 move(TrustedImmPtr(ptr), registerForArg(arg));
282 else
283 storePtr(TrustedImmPtr(ptr), argStackAddress(arg));
284}
285
286void PlatformAssemblerCommon::callRuntime(const void *funcPtr, const char *functionName)
287{
288#ifndef QT_NO_DEBUG
289 Q_ASSERT(remainingArgcForCall == 0);
290 remainingArgcForCall = NoCall;
291#endif
292 callRuntimeUnchecked(funcPtr, functionName);
293 if (argcOnStackForCall > 0) {
294 addPtr(TrustedImm32(argcOnStackForCall), StackPointerRegister);
295 argcOnStackForCall = 0;
296 }
297}
298
299void PlatformAssemblerCommon::callRuntimeUnchecked(const void *funcPtr, const char *functionName)
300{
301 Q_ASSERT(functionName || Runtime::symbolTable().contains(funcPtr));
302 functions.insert(funcPtr, functionName);
303 callAbsolute(funcPtr);
304}
305
306void PlatformAssemblerCommon::tailCallRuntime(const void *funcPtr, const char *functionName)
307{
308 Q_ASSERT(functionName || Runtime::symbolTable().contains(funcPtr));
309 functions.insert(funcPtr, functionName);
310 setTailCallArg(EngineRegister, 1);
311 setTailCallArg(CppStackFrameRegister, 0);
312 freeStackSpace();
313 generatePlatformFunctionExit(/*tailCall =*/ true);
314 jumpAbsolute(funcPtr);
315}
316
317void PlatformAssemblerCommon::setTailCallArg(RegisterID src, int arg)
318{
319 if (arg < ArgInRegCount) {
320 move(src, registerForArg(arg));
321 } else {
322 // We never write to the incoming arguments space on the stack, and the tail call runtime
323 // method has the same signature as the jitted function, so it is safe for us to just reuse
324 // the arguments that we got in.
325 }
326}
327
328JSC::MacroAssemblerBase::Address PlatformAssemblerCommon::jsAlloca(int slotCount)
329{
330 Address jsStackTopAddr(EngineRegister, offsetof(EngineBase, jsStackTop));
331 RegisterID jsStackTop = AccumulatorRegisterValue;
332 loadPtr(jsStackTopAddr, jsStackTop);
333 addPtr(TrustedImm32(sizeof(Value) * slotCount), jsStackTop);
334 storePtr(jsStackTop, jsStackTopAddr);
335 return Address(jsStackTop, 0);
336}
337
338void PlatformAssemblerCommon::storeInt32AsValue(int srcInt, Address destAddr)
339{
340 store32(TrustedImm32(srcInt),
341 Address(destAddr.base, destAddr.offset + QV4::Value::valueOffset()));
342 store32(TrustedImm32(int(QV4::Value::ValueTypeInternal::Integer)),
343 Address(destAddr.base, destAddr.offset + QV4::Value::tagOffset()));
344}
345
346} // JIT namespace
347} // QV4 namepsace
348
350
351#endif // QT_CONFIG(qml_jit)
\inmodule QtCore \reentrant
Definition qbuffer.h:16
\inmodule QtCore
Definition qbytearray.h:57
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
\inmodule QtCore \reentrant
Definition qiodevice.h:34
iterator insert(const T &value)
Definition qset.h:155
#define this
Definition dialogs.cpp:9
QSet< QString >::iterator it
Combined button and popup list for selecting options.
void generateFunctionTable(Function *function, JSC::MacroAssemblerCodeRef *codeRef)
QTextStream & flush(QTextStream &stream)
Calls QTextStream::flush() on stream and returns stream.
Q_CORE_EXPORT int qvsnprintf(char *str, size_t n, const char *fmt, va_list ap)
#define Q_UNLIKELY(x)
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
static void jump(QtMsgType t, const QMessageLogContext &context, const QString &m)
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
static ControlElement< T > * ptr(QWidget *widget)
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLuint64 key
GLuint GLuint end
GLenum src
GLenum GLuint GLenum GLsizei const GLchar * buf
GLenum GLuint GLintptr offset
GLuint name
GLint GLsizei GLsizei GLenum format
GLenum const void * addr
GLuint64EXT * result
[6]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int void * arg
size_t quintptr
Definition qtypes.h:167
long long qint64
Definition qtypes.h:60
QObject::connect nullptr
stack push(command1)
static QString prettyName(const Function *function, const void *address)
ReturnedValue(* JittedCode)(CppStackFrame *, ExecutionEngine *)
static QHash< const void *, const char * > symbolTable()
static int tagOffset()
static int valueOffset()