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