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 | |
58 | QT_BEGIN_NAMESPACE |
59 | namespace QV4 { |
60 | namespace JIT { |
61 | |
62 | Q_LOGGING_CATEGORY(lcAsm, "qt.v4.asm" ) |
63 | |
64 | namespace { |
65 | class QIODevicePrintStream: public FilePrintStream |
66 | { |
67 | Q_DISABLE_COPY(QIODevicePrintStream) |
68 | |
69 | public: |
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 | |
92 | private: |
93 | QIODevice *dest; |
94 | QByteArray buf; |
95 | }; |
96 | } // anonymous namespace |
97 | |
98 | static 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 | |
124 | JIT::PlatformAssemblerCommon::~PlatformAssemblerCommon() |
125 | {} |
126 | |
127 | void 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 | |
168 | void 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 | |
181 | void PlatformAssemblerCommon::storeInstructionPointer(int instructionOffset) |
182 | { |
183 | Address addr(CppStackFrameRegister, offsetof(QV4::CppStackFrame, instructionPointer)); |
184 | store32(imm: TrustedImm32(instructionOffset), address: addr); |
185 | } |
186 | |
187 | PlatformAssemblerCommon::Address PlatformAssemblerCommon::argStackAddress(int arg) |
188 | { |
189 | int offset = arg - ArgInRegCount; |
190 | Q_ASSERT(offset >= 0); |
191 | return Address(StackPointerRegister, offset * PointerSize); |
192 | } |
193 | |
194 | void 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 | |
204 | void JIT::PlatformAssemblerCommon::pushAccumulatorAsArg(int arg) |
205 | { |
206 | passAccumulatorAsArg_internal(arg, doPush: true); |
207 | } |
208 | |
209 | void 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 | |
222 | void 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 | |
237 | void 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 | |
251 | void PlatformAssemblerCommon::passJSSlotAsArg(int reg, int arg) |
252 | { |
253 | Address addr(JSStackFrameRegister, reg * int(sizeof(QV4::Value))); |
254 | passAddressAsArg(addr, arg); |
255 | } |
256 | |
257 | void 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 | |
272 | void 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 | |
285 | void 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 | |
298 | void 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 | |
311 | void 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 | |
324 | void 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 | |
331 | void 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 | |
342 | void 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 | |
353 | JSC::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 | |
363 | void 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 | |
374 | QT_END_NAMESPACE |
375 | |
376 | #endif // QT_CONFIG(qml_jit) |
377 | |