From bf136379521d543c54d5c0ce377471ff76dce35e Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 6 Jun 2018 16:29:13 +0200 Subject: V4: Split PlatformAssemblerCommon (and base classes) in its own file This makes it easier to re-use them later on, without inheriting all extra stuff that the baseline JIT needs. Change-Id: I9368b16017b8b9d99f8c005a5b47ec9f9ed09fb0 Reviewed-by: Simon Hausmann --- src/qml/jit/qv4assemblercommon.cpp | 343 +++++++++++++++++++++++++++++++++++++ 1 file changed, 343 insertions(+) create mode 100644 src/qml/jit/qv4assemblercommon.cpp (limited to 'src/qml/jit/qv4assemblercommon.cpp') diff --git a/src/qml/jit/qv4assemblercommon.cpp b/src/qml/jit/qv4assemblercommon.cpp new file mode 100644 index 0000000000..79a8237878 --- /dev/null +++ b/src/qml/jit/qv4assemblercommon.cpp @@ -0,0 +1,343 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "qv4engine_p.h" +#include "qv4assemblercommon_p.h" +#include +#include + +#include +#include +#include + +#undef ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES + +#ifdef V4_ENABLE_JIT + +QT_BEGIN_NAMESPACE +namespace QV4 { +namespace JIT { + +namespace { +class QIODevicePrintStream: public FilePrintStream +{ + Q_DISABLE_COPY(QIODevicePrintStream) + +public: + explicit QIODevicePrintStream(QIODevice *dest) + : FilePrintStream(nullptr) + , dest(dest) + , buf(4096, '0') + { + Q_ASSERT(dest); + } + + ~QIODevicePrintStream() + {} + + void vprintf(const char* format, va_list argList) WTF_ATTRIBUTE_PRINTF(2, 0) + { + const int written = qvsnprintf(buf.data(), buf.size(), format, argList); + if (written > 0) + dest->write(buf.constData(), written); + memset(buf.data(), 0, qMin(written, buf.size())); + } + + void flush() + {} + +private: + QIODevice *dest; + QByteArray buf; +}; +} // anonymous namespace + +static void printDisassembledOutputWithCalls(QByteArray processedOutput, + const QHash& functions) +{ + for (QHash::ConstIterator it = functions.begin(), end = functions.end(); + it != end; ++it) { + const QByteArray ptrString = "0x" + QByteArray::number(quintptr(it.key()), 16); + int idx = 0; + while (idx >= 0) { + idx = processedOutput.indexOf(ptrString, idx); + if (idx < 0) + break; + idx = processedOutput.indexOf('\n', idx); + if (idx < 0) + break; + processedOutput = processedOutput.insert(idx, QByteArrayLiteral(" ; ") + it.value()); + } + } + + qDebug("%s", processedOutput.constData()); +} + +static QByteArray functionName(Function *function) +{ + QByteArray name = function->name()->toQString().toUtf8(); + if (name.isEmpty()) { + name = QByteArray::number(reinterpret_cast(function), 16); + name.prepend("QV4::Function(0x"); + name.append(')'); + } + return name; +} + +JIT::PlatformAssemblerCommon::~PlatformAssemblerCommon() +{} + +void PlatformAssemblerCommon::link(Function *function) +{ + for (const auto &jumpTarget : jumpsToLink) + jumpTarget.jump.linkTo(labelForOffset[jumpTarget.offset], this); + + JSC::JSGlobalData dummy(function->internalClass->engine->executableAllocator); + JSC::LinkBuffer linkBuffer(dummy, this, nullptr); + + for (const auto &ehTarget : ehTargets) { + auto targetLabel = labelForOffset.value(ehTarget.offset); + linkBuffer.patch(ehTarget.label, linkBuffer.locationOf(targetLabel)); + } + + JSC::MacroAssemblerCodeRef codeRef; + + static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM"); + if (showCode) { + QBuffer buf; + buf.open(QIODevice::WriteOnly); + WTF::setDataFile(new QIODevicePrintStream(&buf)); + + QByteArray name = functionName(function); + codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.constData()); + + WTF::setDataFile(stderr); + printDisassembledOutputWithCalls(buf.data(), functions); + } else { + codeRef = linkBuffer.finalizeCodeWithoutDisassembly(); + } + + function->codeRef = new JSC::MacroAssemblerCodeRef(codeRef); + function->jittedCode = reinterpret_cast(function->codeRef->code().executableAddress()); + + // This implements writing of JIT'd addresses so that perf can find the + // symbol names. + // + // Perf expects the mapping to be in a certain place and have certain + // content, for more information, see: + // https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt + static bool doProfile = !qEnvironmentVariableIsEmpty("QV4_PROFILE_WRITE_PERF_MAP"); + if (Q_UNLIKELY(doProfile)) { + static QFile perfMapFile(QString::fromLatin1("/tmp/perf-%1.map") + .arg(QCoreApplication::applicationPid())); + static const bool isOpen = perfMapFile.open(QIODevice::WriteOnly); + if (!isOpen) { + qWarning("QV4::JIT::Assembler: Cannot write perf map file."); + doProfile = false; + } else { + perfMapFile.write(QByteArray::number(reinterpret_cast( + codeRef.code().executableAddress()), 16)); + perfMapFile.putChar(' '); + perfMapFile.write(QByteArray::number(static_cast(codeRef.size()), 16)); + perfMapFile.putChar(' '); + perfMapFile.write(functionName(function)); + perfMapFile.putChar('\n'); + perfMapFile.flush(); + } + } +} + +void PlatformAssemblerCommon::prepareCallWithArgCount(int argc) +{ +#ifndef QT_NO_DEBUG + Q_ASSERT(remainingArgcForCall == NoCall); + remainingArgcForCall = argc; +#endif + + if (argc > ArgInRegCount) { + argcOnStackForCall = int(WTF::roundUpToMultipleOf(16, size_t(argc - ArgInRegCount) * PointerSize)); + subPtr(TrustedImm32(argcOnStackForCall), StackPointerRegister); + } +} + +void PlatformAssemblerCommon::storeInstructionPointer(int instructionOffset) +{ + Address addr(CppStackFrameRegister, offsetof(QV4::CppStackFrame, instructionPointer)); + store32(TrustedImm32(instructionOffset), addr); +} + +PlatformAssemblerCommon::Address PlatformAssemblerCommon::argStackAddress(int arg) +{ + int offset = arg - ArgInRegCount; + Q_ASSERT(offset >= 0); + return Address(StackPointerRegister, offset * PointerSize); +} + +void PlatformAssemblerCommon::passAccumulatorAsArg(int arg) +{ +#ifndef QT_NO_DEBUG + Q_ASSERT(arg < remainingArgcForCall); + --remainingArgcForCall; +#endif + + passAccumulatorAsArg_internal(arg, false); +} + +void JIT::PlatformAssemblerCommon::pushAccumulatorAsArg(int arg) +{ + passAccumulatorAsArg_internal(arg, true); +} + +void PlatformAssemblerCommon::passAccumulatorAsArg_internal(int arg, bool doPush) +{ + if (arg < ArgInRegCount) { + addPtr(TrustedImm32(offsetof(CallData, accumulator)), JSStackFrameRegister, registerForArg(arg)); + } else { + addPtr(TrustedImm32(offsetof(CallData, accumulator)), JSStackFrameRegister, ScratchRegister); + if (doPush) + push(ScratchRegister); + else + storePtr(ScratchRegister, argStackAddress(arg)); + } +} + +void PlatformAssemblerCommon::passFunctionAsArg(int arg) +{ +#ifndef QT_NO_DEBUG + Q_ASSERT(arg < remainingArgcForCall); + --remainingArgcForCall; +#endif + + if (arg < ArgInRegCount) { + loadFunctionPtr(registerForArg(arg)); + } else { + loadFunctionPtr(ScratchRegister); + storePtr(ScratchRegister, argStackAddress(arg)); + } +} + +void PlatformAssemblerCommon::passEngineAsArg(int arg) +{ +#ifndef QT_NO_DEBUG + Q_ASSERT(arg < remainingArgcForCall); + --remainingArgcForCall; +#endif + + if (arg < ArgInRegCount) { + move(EngineRegister, registerForArg(arg)); + } else { + storePtr(EngineRegister, argStackAddress(arg)); + } +} + +void PlatformAssemblerCommon::passJSSlotAsArg(int reg, int arg) +{ + Address addr(JSStackFrameRegister, reg * int(sizeof(QV4::Value))); + passAddressAsArg(addr, arg); +} + +void JIT::PlatformAssemblerCommon::passAddressAsArg(Address addr, int arg) +{ +#ifndef QT_NO_DEBUG + Q_ASSERT(arg < remainingArgcForCall); + --remainingArgcForCall; +#endif + + if (arg < ArgInRegCount) { + addPtr(TrustedImm32(addr.offset), addr.base, registerForArg(arg)); + } else { + addPtr(TrustedImm32(addr.offset), addr.base, ScratchRegister); + storePtr(ScratchRegister, argStackAddress(arg)); + } +} + +void PlatformAssemblerCommon::passCppFrameAsArg(int arg) +{ +#ifndef QT_NO_DEBUG + Q_ASSERT(arg < remainingArgcForCall); + --remainingArgcForCall; +#endif + + if (arg < ArgInRegCount) + move(CppStackFrameRegister, registerForArg(arg)); + else + store32(CppStackFrameRegister, argStackAddress(arg)); +} + +void PlatformAssemblerCommon::passInt32AsArg(int value, int arg) +{ +#ifndef QT_NO_DEBUG + Q_ASSERT(arg < remainingArgcForCall); + --remainingArgcForCall; +#endif + + if (arg < ArgInRegCount) + move(TrustedImm32(value), registerForArg(arg)); + else + store32(TrustedImm32(value), argStackAddress(arg)); +} + +void PlatformAssemblerCommon::callRuntime(const char *functionName, const void *funcPtr) +{ +#ifndef QT_NO_DEBUG + Q_ASSERT(remainingArgcForCall == 0); + remainingArgcForCall = NoCall; +#endif + callRuntimeUnchecked(functionName, funcPtr); + if (argcOnStackForCall > 0) { + addPtr(TrustedImm32(argcOnStackForCall), StackPointerRegister); + argcOnStackForCall = 0; + } +} + +void JIT::PlatformAssemblerCommon::callRuntimeUnchecked(const char *functionName, const void *funcPtr) +{ + functions.insert(funcPtr, functionName); + callAbsolute(funcPtr); +} + +} // JIT namespace +} // QV4 namepsace + +QT_END_NAMESPACE + +#endif // V4_ENABLE_JIT -- cgit v1.2.3