1 /*========================== begin_copyright_notice ============================
2 
3 Copyright (C) 2017-2021 Intel Corporation
4 
5 SPDX-License-Identifier: MIT
6 
7 ============================= end_copyright_notice ===========================*/
8 
9 #include "Compiler/CodeGenPublic.h"
10 #include "Compiler/CISACodeGen/PassTimer.hpp"
11 #include "Compiler/CISACodeGen/TimeStatsCounter.h"
12 #include "common/Stats.hpp"
13 #include "common/debug/Dump.hpp"
14 #include "common/shaderOverride.hpp"
15 #include "common/IntrinsicAnnotator.hpp"
16 #include "common/LLVMUtils.h"
17 
18 #include "common/LLVMWarningsPush.hpp"
19 #include <llvm/IRReader/IRReader.h>
20 #include <llvm/Support/SourceMgr.h>
21 #include <llvmWrapper/ADT/StringRef.h>
22 #include "common/LLVMWarningsPop.hpp"
23 
24 using namespace IGC;
25 using namespace IGC::Debug;
26 using namespace llvm;
27 
getPassToggles(std::bitset<1024> & toggles)28 bool getPassToggles(std::bitset<1024>& toggles)
29 {
30     const char* passToggles = IGC_GET_REGKEYSTRING(DisablePassToggles);
31     if (passToggles != nullptr && strlen(passToggles) > 0)
32     {
33         std::string szBin;
34         std::string szHexLL;
35         unsigned int len = 0;
36         unsigned long long x = 0;
37         std::string szHex = passToggles;
38         for (size_t i = 0; i < szHex.size(); i += 16)
39         {
40             szHexLL = szHex.substr(i, 16);
41             len = szHexLL.size() * 4;
42             x = std::stoull(szHexLL, nullptr, 16);
43             szBin += std::bitset<64>(x).to_string().substr(64 - len, len);
44         }
45 
46         toggles = std::bitset<1024>(szBin);
47         return true;
48     }
49 
50     return false;
51 }
52 
createFlushPass(llvm::Pass * pass,Dump & dump)53 llvm::Pass* createFlushPass(llvm::Pass* pass, Dump& dump)
54 {
55     // Choose an appropriate pass to preserve the original order of pass execution in the pass manager
56     switch (pass->getPassKind())
57     {
58     case PT_Function:
59         return new FunctionFlushDumpPass(dump);
60     case PT_Module:
61         return new ModuleFlushDumpPass(dump);
62     case PT_Region:
63     case PT_Loop:
64     case PT_CallGraphSCC:
65         // flushing for analysis passes is not required
66         break;
67     case PT_PassManager:
68     default:
69         // internal pass managers are not considered in the context
70         break;
71     }
72     return nullptr;
73 }
74 
add(Pass * P)75 void IGCPassManager::add(Pass *P)
76 {
77     //check only once
78     static bool checkedToggles = false;
79     static bool hasToggles = false;
80     static std::bitset<1024> toggles;
81     if (!checkedToggles)
82     {
83         checkedToggles = true;
84         hasToggles = getPassToggles(toggles);
85     }
86     if (hasToggles && m_pContext->m_numPasses < 1024 && toggles[m_pContext->m_numPasses])
87     {
88         errs() << "Skipping pass: '" << P->getPassName() << "\n";
89         m_pContext->m_numPasses++;
90         return;
91     }
92 
93     if (IGC_IS_FLAG_ENABLED(ShaderDisableOptPassesAfter)
94             && m_pContext->m_numPasses > IGC_GET_FLAG_VALUE(ShaderDisableOptPassesAfter)
95             && m_name == "OPT") {
96         errs() << "Skipping optimization pass: '" << P->getPassName()
97                << "' (threshold: " << IGC_GET_FLAG_VALUE(ShaderDisableOptPassesAfter) << ").\n";
98         return;
99     }
100 
101     if (isPrintBefore(P))
102     {
103         addPrintPass(P, true);
104     }
105 
106     if (IGC_REGKEY_OR_FLAG_ENABLED(DumpTimeStatsPerPass, TIME_STATS_PER_PASS))
107     {
108         PassManager::add(createTimeStatsIGCPass(m_pContext, m_name + '_' + std::string(P->getPassName()), STATS_COUNTER_START));
109     }
110 
111     PassManager::add(P);
112 
113     if (IGC_REGKEY_OR_FLAG_ENABLED(DumpTimeStatsPerPass, TIME_STATS_PER_PASS))
114     {
115         PassManager::add(createTimeStatsIGCPass(m_pContext, m_name + '_' + std::string(P->getPassName()), STATS_COUNTER_END));
116     }
117 
118     if (isPrintAfter(P))
119     {
120         addPrintPass(P, false);
121     }
122 }
123 
124 // List: a comma/semicolon-separated list of pass names.
125 //    N: a pass name
126 // return true if N is in List.
isInList(const StringRef & N,const StringRef & List) const127 bool IGCPassManager::isInList(const StringRef& N, const StringRef& List) const
128 {
129     StringRef Separators(",;");
130     size_t startPos = 0;
131     while (startPos != StringRef::npos)
132     {
133         size_t endPos = List.find_first_of(Separators, startPos);
134         size_t len = (endPos != StringRef::npos ? endPos - startPos : endPos);
135         StringRef Name = List.substr(startPos, len);
136         if (IGCLLVM::equals_insensitive(Name,N))
137         {
138             return true;
139         }
140         startPos = (endPos != StringRef::npos ? endPos + 1 : StringRef::npos);
141     }
142     return false;
143 }
144 
isPrintBefore(Pass * P)145 bool IGCPassManager::isPrintBefore(Pass* P)
146 {
147     if (IGC_IS_FLAG_ENABLED(PrintBefore))
148     {
149         // PrintBefore=N0,N1,N2  : comma-separate list of pass names
150         //                         or pass command args registered in passInfo.
151         StringRef  passNameList(IGC_GET_REGKEYSTRING(PrintBefore));
152         StringRef PN = P->getPassName();
153         if (IGCLLVM::equals_insensitive(passNameList, "all") || isInList(PN, passNameList))
154             return true;
155 
156         // further check passInfo
157         if (const PassInfo* PI = Pass::lookupPassInfo(P->getPassID()))
158         {
159             return isInList(PI->getPassArgument(), passNameList);
160         }
161     }
162     return false;
163 }
164 
isPrintAfter(Pass * P)165 bool IGCPassManager::isPrintAfter(Pass* P)
166 {
167     if (IGC_IS_FLAG_ENABLED(ShaderDumpEnableAll))
168     {
169         return true;
170     }
171     if (IGC_IS_FLAG_ENABLED(PrintAfter))
172     {
173         // PrintAfter=N0,N1,N2  : comma-separate list of pass names or
174         //                         or pass command args registered in passInfo.
175         StringRef  passNameList(IGC_GET_REGKEYSTRING(PrintAfter));
176         StringRef PN = P->getPassName();
177         if (IGCLLVM::equals_insensitive(passNameList, "all") || isInList(PN, passNameList))
178             return true;
179 
180         // further check passInfo
181         if (const PassInfo* PI = Pass::lookupPassInfo(P->getPassID()))
182         {
183             return isInList(PI->getPassArgument(), passNameList);
184         }
185     }
186     return false;
187 }
188 
addPrintPass(Pass * P,bool isBefore)189 void IGCPassManager::addPrintPass(Pass* P, bool isBefore)
190 {
191     std::string passName =
192         m_name + (isBefore ? "_before_" : "_after_") + std::string(P->getPassName());
193     auto name =
194         IGC::Debug::DumpName(IGC::Debug::GetShaderOutputName())
195         .Type(m_pContext->type)
196         .Hash(m_pContext->hash)
197         .Pass(passName, m_pContext->m_numPasses++)
198         .StagedInfo(m_pContext)
199         .Extension("ll");
200 
201     if (!name.allow())
202         return;
203 
204     // The dump object needs to be on the Heap because it owns the stream, and the stream
205     // is taken by reference into the printer pass. If the Dump object had been on the
206     // stack, then that reference would go bad as soon as we exit this scope, and then
207     // the printer pass would access an invalid pointer later on when we call PassManager::run()
208     m_irDumps.emplace_front(name, IGC::Debug::DumpType::PASS_IR_TEXT);
209     PassManager::add(P->createPrinterPass(m_irDumps.front().stream(), ""));
210 
211     llvm::Pass* flushPass = createFlushPass(P, m_irDumps.front());
212     if (nullptr != flushPass)
213     {
214         PassManager::add(flushPass);
215     }
216 }
217 
DumpLLVMIR(IGC::CodeGenContext * pContext,const char * dumpName)218 void DumpLLVMIR(IGC::CodeGenContext* pContext, const char* dumpName)
219 {
220     SetCurrentDebugHash(pContext->hash.asmHash);
221 
222     if (IGC_IS_FLAG_ENABLED(DumpLLVMIR))
223     {
224         pContext->getMetaDataUtils()->save(*pContext->getLLVMContext());
225         serialize(*(pContext->getModuleMetaData()), pContext->getModule());
226         using namespace IGC::Debug;
227         auto name =
228             DumpName(IGC::Debug::GetShaderOutputName())
229             .Hash(pContext->hash)
230             .Type(pContext->type)
231             .Pass(dumpName)
232             .Retry(pContext->m_retryManager.GetRetryId())
233             .Extension("ll");
234         auto new_annotator = IntrinsicAnnotator();
235         auto annotator = (pContext->annotater != nullptr) ? pContext->annotater : &new_annotator;
236         DumpLLVMIRText(
237             pContext->getModule(),
238             name,
239             annotator);
240     }
241     if (IGC_IS_FLAG_ENABLED(ShaderOverride))
242     {
243         auto name =
244             DumpName(IGC::Debug::GetShaderOutputName())
245             .Hash(pContext->hash)
246             .Type(pContext->type)
247             .Pass(dumpName)
248             .Extension("ll");
249         SMDiagnostic Err;
250         std::string fileName = name.overridePath();
251         FILE* fp = fopen(fileName.c_str(), "r");
252         if (fp != nullptr)
253         {
254             fclose(fp);
255             errs() << "Override shader: " << fileName << "\n";
256             Module* mod = parseIRFile(fileName, Err, *pContext->getLLVMContext()).release();
257             if (mod)
258             {
259                 pContext->deleteModule();
260                 pContext->setModule(mod);
261                 deserialize(*(pContext->getModuleMetaData()), mod);
262                 appendToShaderOverrideLogFile(fileName, "OVERRIDEN: ");
263             }
264             else
265             {
266                 std::stringstream ss;
267                 ss << "Parse IR failed.\n";
268                 ss << Err.getLineNo() << ": "
269                     << Err.getLineContents().str() << "\n"
270                     << Err.getMessage().str() << "\n";
271 
272                 std::string str = ss.str();
273                 errs() << str;
274                 appendToShaderOverrideLogFile(fileName, str.c_str());
275             }
276         }
277     }
278 }
279