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 //////////////////////////////////////////////////////////////////////////
44 class JitInstructionSet : public InstructionSet
45 {
46 public:
JitInstructionSet(const char * requestedIsa)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 
AVX2(void)71     bool AVX2(void) { return bForceAVX ? 0 : InstructionSet::AVX2(); }
AVX512F(void)72     bool AVX512F(void) { return (bForceAVX | bForceAVX2) ? 0 : InstructionSet::AVX512F(); }
AVX512ER(void)73     bool AVX512ER(void) { return (bForceAVX | bForceAVX2) ? 0 : InstructionSet::AVX512ER(); }
BMI2(void)74     bool BMI2(void) { return bForceAVX ? 0 : InstructionSet::BMI2(); }
75 
76 private:
77     bool        bForceAVX    = false;
78     bool        bForceAVX2   = false;
79     bool        bForceAVX512 = false;
80     std::string isaRequest;
81 };
82 
83 struct JitLLVMContext : llvm::LLVMContext
84 {
85 };
86 
87 //////////////////////////////////////////////////////////////////////////
88 /// JitCache
89 //////////////////////////////////////////////////////////////////////////
90 struct JitManager; // Forward Decl
91 class JitCache : public llvm::ObjectCache
92 {
93 public:
94     /// constructor
95     JitCache();
~JitCache()96     virtual ~JitCache() {}
97 
Init(JitManager * pJitMgr,const llvm::StringRef & cpu,llvm::CodeGenOpt::Level level)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 
GetModuleCacheDir()113     const char* GetModuleCacheDir() { return mModuleCacheDir.c_str(); }
114 
115 private:
116     std::string                 mCpu;
117     llvm::SmallString<MAX_PATH> mCacheDir;
118     llvm::SmallString<MAX_PATH> mModuleCacheDir;
119     uint32_t                    mCurrentModuleCRC = 0;
120     JitManager*                 mpJitMgr          = nullptr;
121     llvm::CodeGenOpt::Level     mOptLevel         = llvm::CodeGenOpt::None;
122 
123     /// Calculate actual directory where module will be cached.
124     /// This is always a subdirectory of mCacheDir.  Full absolute
125     /// path name will be stored in mCurrentModuleCacheDir
126     void CalcModuleCacheDir();
127 };
128 
129 //////////////////////////////////////////////////////////////////////////
130 /// JitManager
131 //////////////////////////////////////////////////////////////////////////
132 struct JitManager
133 {
134     JitManager(uint32_t w, const char* arch, const char* core);
~JitManagerJitManager135     ~JitManager()
136     {
137         for (auto* pExec : mvExecEngines)
138         {
139             delete pExec;
140         }
141     }
142 
143     JitLLVMContext                      mContext; ///< LLVM compiler
144     llvm::IRBuilder<>                   mBuilder; ///< LLVM IR Builder
145     llvm::ExecutionEngine*              mpExec;
146     std::vector<llvm::ExecutionEngine*> mvExecEngines;
147     JitCache                            mCache;
148     llvm::StringRef                     mHostCpuName;
149     llvm::CodeGenOpt::Level             mOptLevel;
150 
151     // Need to be rebuilt after a JIT and before building new IR
152     llvm::Module* mpCurrentModule;
153     bool          mIsModuleFinalized;
154     uint32_t      mJitNumber;
155 
156     uint32_t mVWidth;
157 
158     bool mUsingAVX512 = false;
159 
160     // fetch shader types
161     llvm::FunctionType* mFetchShaderTy;
162 
163     JitInstructionSet mArch;
164 
165     // Debugging support
166     std::unordered_map<llvm::StructType*, llvm::DIType*> mDebugStructMap;
167 
168     void CreateExecEngine(std::unique_ptr<llvm::Module> M);
169     void SetupNewModule();
170 
171     void               DumpAsm(llvm::Function* pFunction, const char* fileName);
172     static void        DumpToFile(llvm::Function* f, const char* fileName);
173     static void        DumpToFile(llvm::Module*                   M,
174                                   const char*                     fileName,
175                                   llvm::AssemblyAnnotationWriter* annotater = nullptr);
176     static std::string GetOutputDir();
177 
178     // Debugging support methods
179     llvm::DIType* GetDebugType(llvm::Type* pTy);
180     llvm::DIType* GetDebugIntegerType(llvm::Type* pTy);
181     llvm::DIType* GetDebugArrayType(llvm::Type* pTy);
182     llvm::DIType* GetDebugVectorType(llvm::Type* pTy);
183     llvm::DIType* GetDebugFunctionType(llvm::Type* pTy);
184 
GetDebugStructTypeJitManager185     llvm::DIType* GetDebugStructType(llvm::Type* pType)
186     {
187         llvm::StructType* pStructTy = llvm::cast<llvm::StructType>(pType);
188         if (mDebugStructMap.find(pStructTy) == mDebugStructMap.end())
189         {
190             return nullptr;
191         }
192         return mDebugStructMap[pStructTy];
193     }
194 
195     llvm::DIType*
196     CreateDebugStructType(llvm::StructType*                                    pType,
197                           const std::string&                                   name,
198                           llvm::DIFile*                                        pFile,
199                           uint32_t                                             lineNum,
200                           const std::vector<std::pair<std::string, uint32_t>>& members);
201 };
202 
203 class InterleaveAssemblyAnnotater : public llvm::AssemblyAnnotationWriter
204 {
205 public:
206     void                     emitInstructionAnnot(const llvm::Instruction*     pInst,
207                                                   llvm::formatted_raw_ostream& OS) override;
208     std::vector<std::string> mAssembly;
209 
210 private:
211     uint32_t mCurrentLineNo = 0;
212 };
213