1/****************************************************************************
2* Copyright (C) 2014-2015 Intel Corporation.   All Rights Reserved.
3*
4* Permission is hereby granted, free of charge, to any person obtaining a
5* copy of this software and associated documentation files (the "Software"),
6* to deal in the Software without restriction, including without limitation
7* the rights to use, copy, modify, merge, publish, distribute, sublicense,
8* and/or sell copies of the Software, and to permit persons to whom the
9* Software is furnished to do so, subject to the following conditions:
10*
11* The above copyright notice and this permission notice (including the next
12* paragraph) shall be included in all copies or substantial portions of the
13* Software.
14*
15* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21* IN THE SOFTWARE.
22*
23* @file JitManager.cpp
24*
25* @brief Implementation if the Jit Manager.
26*
27* Notes:
28*
29******************************************************************************/
30#if defined(_WIN32)
31#pragma warning(disable: 4800 4146 4244 4267 4355 4996)
32#endif
33
34#include "jit_api.h"
35#include "JitManager.h"
36#include "fetch_jit.h"
37
38#pragma push_macro("DEBUG")
39#undef DEBUG
40
41#if defined(_WIN32)
42#include "llvm/ADT/Triple.h"
43#endif
44#include "llvm/IR/Function.h"
45
46#include "llvm/Support/MemoryBuffer.h"
47#include "llvm/Support/SourceMgr.h"
48
49#include "llvm/Analysis/CFGPrinter.h"
50#include "llvm/IRReader/IRReader.h"
51#include "llvm/Target/TargetMachine.h"
52#include "llvm/Support/FormattedStream.h"
53
54#if LLVM_USE_INTEL_JITEVENTS
55#include "llvm/ExecutionEngine/JITEventListener.h"
56#endif
57
58#pragma pop_macro("DEBUG")
59
60#include "core/state.h"
61
62#include "state_llvm.h"
63
64#include <sstream>
65#if defined(_WIN32)
66#include <psapi.h>
67#include <cstring>
68
69#define INTEL_OUTPUT_DIR "c:\\Intel"
70#define SWR_OUTPUT_DIR INTEL_OUTPUT_DIR "\\SWR"
71#define JITTER_OUTPUT_DIR SWR_OUTPUT_DIR "\\Jitter"
72#endif
73
74using namespace llvm;
75using namespace SwrJit;
76
77//////////////////////////////////////////////////////////////////////////
78/// @brief Contructor for JitManager.
79/// @param simdWidth - SIMD width to be used in generated program.
80JitManager::JitManager(uint32_t simdWidth, const char *arch, const char* core)
81    : mContext(), mBuilder(mContext), mIsModuleFinalized(true), mJitNumber(0), mVWidth(simdWidth), mArch(arch)
82{
83    InitializeNativeTarget();
84    InitializeNativeTargetAsmPrinter();
85    InitializeNativeTargetDisassembler();
86
87    TargetOptions    tOpts;
88    tOpts.AllowFPOpFusion = FPOpFusion::Fast;
89    tOpts.NoInfsFPMath = false;
90    tOpts.NoNaNsFPMath = false;
91    tOpts.UnsafeFPMath = true;
92#if defined(_DEBUG)
93#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 7
94    tOpts.NoFramePointerElim = true;
95#endif
96#endif
97
98    //tOpts.PrintMachineCode    = true;
99
100    mCore = std::string(core);
101    std::transform(mCore.begin(), mCore.end(), mCore.begin(), ::tolower);
102
103    std::stringstream fnName("JitModule", std::ios_base::in | std::ios_base::out | std::ios_base::ate);
104    fnName << mJitNumber++;
105    std::unique_ptr<Module> newModule(new Module(fnName.str(), mContext));
106    mpCurrentModule = newModule.get();
107
108    auto &&EB = EngineBuilder(std::move(newModule));
109    EB.setTargetOptions(tOpts);
110    EB.setOptLevel(CodeGenOpt::Aggressive);
111
112    StringRef hostCPUName;
113
114    hostCPUName = sys::getHostCPUName();
115
116    EB.setMCPU(hostCPUName);
117
118#if defined(_WIN32)
119    // Needed for MCJIT on windows
120    Triple hostTriple(sys::getProcessTriple());
121    hostTriple.setObjectFormat(Triple::ELF);
122    mpCurrentModule->setTargetTriple(hostTriple.getTriple());
123#endif // _WIN32
124
125    mpExec = EB.create();
126
127#if LLVM_USE_INTEL_JITEVENTS
128    JITEventListener *vTune = JITEventListener::createIntelJITEventListener();
129    mpExec->RegisterJITEventListener(vTune);
130#endif
131
132    mFP32Ty = Type::getFloatTy(mContext);   // float type
133    mInt8Ty = Type::getInt8Ty(mContext);
134    mInt32Ty = Type::getInt32Ty(mContext);   // int type
135    mInt64Ty = Type::getInt64Ty(mContext);   // int type
136    mV4FP32Ty = StructType::get(mContext, std::vector<Type*>(4, mFP32Ty), false); // vector4 float type (represented as structure)
137    mV4Int32Ty = StructType::get(mContext, std::vector<Type*>(4, mInt32Ty), false); // vector4 int type
138
139    // fetch function signature
140    // typedef void(__cdecl *PFN_FETCH_FUNC)(SWR_FETCH_CONTEXT& fetchInfo, simdvertex& out);
141    std::vector<Type*> fsArgs;
142    fsArgs.push_back(PointerType::get(Gen_SWR_FETCH_CONTEXT(this), 0));
143    fsArgs.push_back(PointerType::get(Gen_simdvertex(this), 0));
144
145    mFetchShaderTy = FunctionType::get(Type::getVoidTy(mContext), fsArgs, false);
146
147    mSimtFP32Ty = VectorType::get(mFP32Ty, mVWidth);
148    mSimtInt32Ty = VectorType::get(mInt32Ty, mVWidth);
149
150    mSimdVectorTy = StructType::get(mContext, std::vector<Type*>(4, mSimtFP32Ty), false);
151    mSimdVectorInt32Ty = StructType::get(mContext, std::vector<Type*>(4, mSimtInt32Ty), false);
152
153#if defined(_WIN32)
154    // explicitly instantiate used symbols from potentially staticly linked libs
155    sys::DynamicLibrary::AddSymbol("exp2f", &exp2f);
156    sys::DynamicLibrary::AddSymbol("log2f", &log2f);
157    sys::DynamicLibrary::AddSymbol("sinf", &sinf);
158    sys::DynamicLibrary::AddSymbol("cosf", &cosf);
159    sys::DynamicLibrary::AddSymbol("powf", &powf);
160#endif
161
162#if defined(_WIN32)
163    if (KNOB_DUMP_SHADER_IR)
164    {
165        CreateDirectory(INTEL_OUTPUT_DIR, NULL);
166        CreateDirectory(SWR_OUTPUT_DIR, NULL);
167        CreateDirectory(JITTER_OUTPUT_DIR, NULL);
168    }
169#endif
170}
171
172//////////////////////////////////////////////////////////////////////////
173/// @brief Create new LLVM module.
174void JitManager::SetupNewModule()
175{
176    SWR_ASSERT(mIsModuleFinalized == true && "Current module is not finalized!");
177
178    std::stringstream fnName("JitModule", std::ios_base::in | std::ios_base::out | std::ios_base::ate);
179    fnName << mJitNumber++;
180    std::unique_ptr<Module> newModule(new Module(fnName.str(), mContext));
181    mpCurrentModule = newModule.get();
182#if defined(_WIN32)
183    // Needed for MCJIT on windows
184    Triple hostTriple(sys::getProcessTriple());
185    hostTriple.setObjectFormat(Triple::ELF);
186    newModule->setTargetTriple(hostTriple.getTriple());
187#endif // _WIN32
188
189    mpExec->addModule(std::move(newModule));
190    mIsModuleFinalized = false;
191}
192
193//////////////////////////////////////////////////////////////////////////
194/// @brief Create new LLVM module from IR.
195bool JitManager::SetupModuleFromIR(const uint8_t *pIR)
196{
197    std::unique_ptr<MemoryBuffer> pMem = MemoryBuffer::getMemBuffer(StringRef((const char*)pIR), "");
198
199    SMDiagnostic Err;
200    std::unique_ptr<Module> newModule = parseIR(pMem.get()->getMemBufferRef(), Err, mContext);
201
202    SWR_REL_ASSERT(
203        !(newModule == nullptr),
204        "Parse failed!\n"
205        "%s", Err.getMessage().data());
206    if (newModule == nullptr)
207    {
208        return false;
209    }
210
211#if HAVE_LLVM == 0x307
212    // llvm-3.7 has mismatched setDataLyout/getDataLayout APIs
213    newModule->setDataLayout(*mpExec->getDataLayout());
214#else
215    newModule->setDataLayout(mpExec->getDataLayout());
216#endif
217
218    mpCurrentModule = newModule.get();
219#if defined(_WIN32)
220    // Needed for MCJIT on windows
221    Triple hostTriple(sys::getProcessTriple());
222    hostTriple.setObjectFormat(Triple::ELF);
223    newModule->setTargetTriple(hostTriple.getTriple());
224#endif // _WIN32
225
226    mpExec->addModule(std::move(newModule));
227    mIsModuleFinalized = false;
228
229    return true;
230}
231
232//////////////////////////////////////////////////////////////////////////
233/// @brief Dump function x86 assembly to file.
234/// @note This should only be called after the module has been jitted to x86 and the
235///       module will not be further accessed.
236void JitManager::DumpAsm(Function* pFunction, const char* fileName)
237{
238    if (KNOB_DUMP_SHADER_IR)
239    {
240
241#if defined(_WIN32)
242        DWORD pid = GetCurrentProcessId();
243        TCHAR procname[MAX_PATH];
244        GetModuleFileName(NULL, procname, MAX_PATH);
245        const char* pBaseName = strrchr(procname, '\\');
246        std::stringstream outDir;
247        outDir << JITTER_OUTPUT_DIR << pBaseName << "_" << pid << std::ends;
248        CreateDirectory(outDir.str().c_str(), NULL);
249#endif
250
251        std::error_code EC;
252        Module* pModule = pFunction->getParent();
253        const char *funcName = pFunction->getName().data();
254        char fName[256];
255#if defined(_WIN32)
256        sprintf(fName, "%s\\%s.%s.asm", outDir.str().c_str(), funcName, fileName);
257#else
258        sprintf(fName, "%s.%s.asm", funcName, fileName);
259#endif
260
261#if HAVE_LLVM == 0x306
262        raw_fd_ostream fd(fName, EC, llvm::sys::fs::F_None);
263        formatted_raw_ostream filestream(fd);
264#else
265        raw_fd_ostream filestream(fName, EC, llvm::sys::fs::F_None);
266#endif
267
268        legacy::PassManager* pMPasses = new legacy::PassManager();
269        auto* pTarget = mpExec->getTargetMachine();
270        pTarget->Options.MCOptions.AsmVerbose = true;
271        pTarget->addPassesToEmitFile(*pMPasses, filestream, TargetMachine::CGFT_AssemblyFile);
272        pMPasses->run(*pModule);
273        delete pMPasses;
274        pTarget->Options.MCOptions.AsmVerbose = false;
275    }
276}
277
278//////////////////////////////////////////////////////////////////////////
279/// @brief Dump function to file.
280void JitManager::DumpToFile(Function *f, const char *fileName)
281{
282    if (KNOB_DUMP_SHADER_IR)
283    {
284#if defined(_WIN32)
285        DWORD pid = GetCurrentProcessId();
286        TCHAR procname[MAX_PATH];
287        GetModuleFileName(NULL, procname, MAX_PATH);
288        const char* pBaseName = strrchr(procname, '\\');
289        std::stringstream outDir;
290        outDir << JITTER_OUTPUT_DIR << pBaseName << "_" << pid << std::ends;
291        CreateDirectory(outDir.str().c_str(), NULL);
292#endif
293
294        std::error_code EC;
295        const char *funcName = f->getName().data();
296        char fName[256];
297#if defined(_WIN32)
298        sprintf(fName, "%s\\%s.%s.ll", outDir.str().c_str(), funcName, fileName);
299#else
300        sprintf(fName, "%s.%s.ll", funcName, fileName);
301#endif
302        raw_fd_ostream fd(fName, EC, llvm::sys::fs::F_None);
303        Module* pModule = f->getParent();
304        pModule->print(fd, nullptr);
305
306#if defined(_WIN32)
307        sprintf(fName, "%s\\cfg.%s.%s.dot", outDir.str().c_str(), funcName, fileName);
308#else
309        sprintf(fName, "cfg.%s.%s.dot", funcName, fileName);
310#endif
311        fd.flush();
312
313        raw_fd_ostream fd_cfg(fName, EC, llvm::sys::fs::F_Text);
314        WriteGraph(fd_cfg, (const Function*)f);
315
316        fd_cfg.flush();
317    }
318}
319
320extern "C"
321{
322    bool g_DllActive = true;
323
324    //////////////////////////////////////////////////////////////////////////
325    /// @brief Create JIT context.
326    /// @param simdWidth - SIMD width to be used in generated program.
327    HANDLE JITCALL JitCreateContext(uint32_t targetSimdWidth, const char* arch, const char* core)
328    {
329        return new JitManager(targetSimdWidth, arch, core);
330    }
331
332    //////////////////////////////////////////////////////////////////////////
333    /// @brief Destroy JIT context.
334    void JITCALL JitDestroyContext(HANDLE hJitContext)
335    {
336        if (g_DllActive)
337        {
338            delete reinterpret_cast<JitManager*>(hJitContext);
339        }
340    }
341}
342