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.h
24 *
25 * @brief JitManager contains the LLVM data structures used for JIT generation
26 *
27 * Notes:
28 *
29 ******************************************************************************/
30#pragma once
31
32#include "jit_pch.hpp"
33#include "common/isa.hpp"
34#include <llvm/IR/AssemblyAnnotationWriter.h>
35
36
37//////////////////////////////////////////////////////////////////////////
38/// JitInstructionSet
39/// @brief Subclass of InstructionSet that allows users to override
40/// the reporting of support for certain ISA features.  This allows capping
41/// the jitted code to a certain feature level, e.g. jit AVX level code on
42/// a platform that supports AVX2.
43//////////////////////////////////////////////////////////////////////////
44class JitInstructionSet : public InstructionSet
45{
46public:
47    JitInstructionSet(const char* requestedIsa) : isaRequest(requestedIsa)
48    {
49        std::transform(isaRequest.begin(), isaRequest.end(), isaRequest.begin(), ::tolower);
50
51        if (isaRequest == "avx")
52        {
53            bForceAVX    = true;
54            bForceAVX2   = false;
55            bForceAVX512 = false;
56        }
57        else if (isaRequest == "avx2")
58        {
59            bForceAVX    = false;
60            bForceAVX2   = true;
61            bForceAVX512 = false;
62        }
63        else if (isaRequest == "avx512")
64        {
65            bForceAVX    = false;
66            bForceAVX2   = false;
67            bForceAVX512 = true;
68        }
69    };
70
71    bool AVX2(void) { return bForceAVX ? 0 : InstructionSet::AVX2(); }
72    bool AVX512F(void) { return (bForceAVX | bForceAVX2) ? 0 : InstructionSet::AVX512F(); }
73    bool AVX512ER(void) { return (bForceAVX | bForceAVX2) ? 0 : InstructionSet::AVX512ER(); }
74    bool BMI2(void) { return bForceAVX ? 0 : InstructionSet::BMI2(); }
75
76private:
77    bool        bForceAVX    = false;
78    bool        bForceAVX2   = false;
79    bool        bForceAVX512 = false;
80    std::string isaRequest;
81};
82
83struct JitLLVMContext : llvm::LLVMContext
84{
85};
86
87//////////////////////////////////////////////////////////////////////////
88/// JitCache
89//////////////////////////////////////////////////////////////////////////
90struct JitManager; // Forward Decl
91class JitCache : public llvm::ObjectCache
92{
93public:
94    /// constructor
95    JitCache();
96    virtual ~JitCache() {}
97
98    void Init(JitManager* pJitMgr, const llvm::StringRef& cpu, llvm::CodeGenOpt::Level level)
99    {
100        mCpu      = cpu.str();
101        mpJitMgr  = pJitMgr;
102        mOptLevel = level;
103    }
104
105    /// notifyObjectCompiled - Provides a pointer to compiled code for Module M.
106    void notifyObjectCompiled(const llvm::Module* M, llvm::MemoryBufferRef Obj) override;
107
108    /// Returns a pointer to a newly allocated MemoryBuffer that contains the
109    /// object which corresponds with Module M, or 0 if an object is not
110    /// available.
111    std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module* M) override;
112
113private:
114    std::string                 mCpu;
115    llvm::SmallString<MAX_PATH> mCacheDir;
116    llvm::SmallString<MAX_PATH> mModuleCacheDir;
117    uint32_t                    mCurrentModuleCRC = 0;
118    JitManager*                 mpJitMgr          = nullptr;
119    llvm::CodeGenOpt::Level     mOptLevel         = llvm::CodeGenOpt::None;
120
121    /// Calculate actual directory where module will be cached.
122    /// This is always a subdirectory of mCacheDir.  Full absolute
123    /// path name will be stored in mCurrentModuleCacheDir
124    void CalcModuleCacheDir();
125};
126
127//////////////////////////////////////////////////////////////////////////
128/// JitManager
129//////////////////////////////////////////////////////////////////////////
130struct JitManager
131{
132    JitManager(uint32_t w, const char* arch, const char* core);
133    ~JitManager()
134    {
135        for (auto* pExec : mvExecEngines)
136        {
137            delete pExec;
138        }
139    }
140
141    JitLLVMContext                      mContext; ///< LLVM compiler
142    llvm::IRBuilder<>                   mBuilder; ///< LLVM IR Builder
143    llvm::ExecutionEngine*              mpExec;
144    std::vector<llvm::ExecutionEngine*> mvExecEngines;
145    JitCache                            mCache;
146    llvm::StringRef                     mHostCpuName;
147    llvm::CodeGenOpt::Level             mOptLevel;
148
149    // Need to be rebuilt after a JIT and before building new IR
150    llvm::Module* mpCurrentModule;
151    bool          mIsModuleFinalized;
152    uint32_t      mJitNumber;
153
154    uint32_t mVWidth;
155
156    bool mUsingAVX512 = false;
157
158    // fetch shader types
159    llvm::FunctionType* mFetchShaderTy;
160
161    JitInstructionSet mArch;
162
163    // Debugging support
164    std::unordered_map<llvm::StructType*, llvm::DIType*> mDebugStructMap;
165
166    void CreateExecEngine(std::unique_ptr<llvm::Module> M);
167    void SetupNewModule();
168
169    void               DumpAsm(llvm::Function* pFunction, const char* fileName);
170    static void        DumpToFile(llvm::Function* f, const char* fileName);
171    static void        DumpToFile(llvm::Module*                   M,
172                                  const char*                     fileName,
173                                  llvm::AssemblyAnnotationWriter* annotater = nullptr);
174    static std::string GetOutputDir();
175
176    // Debugging support methods
177    llvm::DIType* GetDebugType(llvm::Type* pTy);
178    llvm::DIType* GetDebugIntegerType(llvm::Type* pTy);
179    llvm::DIType* GetDebugArrayType(llvm::Type* pTy);
180    llvm::DIType* GetDebugVectorType(llvm::Type* pTy);
181    llvm::DIType* GetDebugFunctionType(llvm::Type* pTy);
182
183    llvm::DIType* GetDebugStructType(llvm::Type* pType)
184    {
185        llvm::StructType* pStructTy = llvm::cast<llvm::StructType>(pType);
186        if (mDebugStructMap.find(pStructTy) == mDebugStructMap.end())
187        {
188            return nullptr;
189        }
190        return mDebugStructMap[pStructTy];
191    }
192
193    llvm::DIType*
194    CreateDebugStructType(llvm::StructType*                                    pType,
195                          const std::string&                                   name,
196                          llvm::DIFile*                                        pFile,
197                          uint32_t                                             lineNum,
198                          const std::vector<std::pair<std::string, uint32_t>>& members);
199};
200
201class InterleaveAssemblyAnnotater : public llvm::AssemblyAnnotationWriter
202{
203public:
204    void                     emitInstructionAnnot(const llvm::Instruction*     pInst,
205                                                  llvm::formatted_raw_ostream& OS) override;
206    std::vector<std::string> mAssembly;
207
208private:
209    uint32_t mCurrentLineNo = 0;
210};
211