1 /*
2 * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ark_inlining.h"
17 #include "llvm_compiler_options.h"
18
19 #include <llvm/Analysis/InlineAdvisor.h>
20 #include <llvm/Analysis/ReplayInlineAdvisor.h>
21
22 #include <llvm/IR/InstIterator.h>
23 #include <llvm/IR/Instructions.h>
24 #include <llvm/IR/IntrinsicInst.h>
25 #include <llvm/Demangle/Demangle.h>
26
27 #include <vector>
28
29 namespace ark::llvmbackend::passes {
30
ShouldInsert(const ark::llvmbackend::LLVMCompilerOptions * options)31 bool InlinePrepare::ShouldInsert(const ark::llvmbackend::LLVMCompilerOptions *options)
32 {
33 return options->inlining;
34 }
35
Create(LLVMArkInterface * arkInterface,const ark::llvmbackend::LLVMCompilerOptions * options)36 InlinePrepare InlinePrepare::Create([[maybe_unused]] LLVMArkInterface *arkInterface,
37 const ark::llvmbackend::LLVMCompilerOptions *options)
38 {
39 static constexpr int INLINING_THRESHOLD = 500;
40 auto inlineParams = llvm::getInlineParams(INLINING_THRESHOLD);
41 inlineParams.AllowRecursiveCall = options->recursiveInlining;
42 return InlinePrepare(inlineParams);
43 }
44
run(llvm::Module & module,llvm::ModuleAnalysisManager & moduleAm)45 llvm::PreservedAnalyses InlinePrepare::run(llvm::Module &module, llvm::ModuleAnalysisManager &moduleAm)
46 {
47 auto &advisorResult = moduleAm.getResult<llvm::InlineAdvisorAnalysis>(module);
48 if (!advisorResult.tryCreate(
49 inlineParams_, llvm::InliningAdvisorMode::Default, {}, // CC-OFF(G.FMT.06-CPP) project code style
50 llvm::InlineContext {llvm::ThinOrFullLTOPhase::None, llvm::InlinePass::ModuleInliner})) {
51 module.getContext().emitError("Could not setup Inlining Advisor for the requested mode and/or options");
52 }
53 return llvm::PreservedAnalyses::all();
54 }
55
ShouldInsert(const ark::llvmbackend::LLVMCompilerOptions * options)56 bool IrtocInlineChecker::ShouldInsert(const ark::llvmbackend::LLVMCompilerOptions *options)
57 {
58 return options->doIrtocInline;
59 }
60
CheckShouldInline(llvm::CallBase * callBase)61 void IrtocInlineChecker::CheckShouldInline(llvm::CallBase *callBase)
62 {
63 using llvm::StringRef;
64 if (!callBase->hasFnAttr(llvm::Attribute::AlwaysInline)) {
65 return;
66 }
67 auto caller = callBase->getCaller();
68 auto callee = callBase->getCalledFunction();
69 std::string msg = "Unknown reason";
70 if (callBase->hasFnAttr("inline-remark")) {
71 msg = callBase->getFnAttr("inline-remark").getValueAsString();
72 }
73
74 auto demCallerName = llvm::demangle(std::string(caller->getName()));
75 if (callee == nullptr) {
76 llvm::report_fatal_error(llvm::Twine("Can't inline with alwaysinline attr 'nullptr") + "' into '" +
77 demCallerName + " due to " + msg + "'");
78 return;
79 }
80 auto demCalleeName = llvm::demangle(std::string(callee->getName()));
81 #ifdef __SANITIZE_THREAD__
82 // The functions from EXCLUSIONS are come from panda runtime (array-inl.h and class.h)
83 // These function are recursive (Thay are optimized by tail recursive in normal way
84 // but not if PANDA_ENABLE_THREAD_SANITIZER)
85 static constexpr std::array EXCLUSIONS = {StringRef("ark::coretypes::Array::CreateMultiDimensionalArray"),
86 StringRef("ark::Class::IsAssignableFrom(ark::Class const*)")};
87 if (std::find_if(EXCLUSIONS.cbegin(), EXCLUSIONS.cend(), [demCalleeName](StringRef pat) {
88 return demCalleeName.find(pat) != std::string::npos;
89 }) == EXCLUSIONS.cend()) {
90 llvm::report_fatal_error(llvm::Twine("Can't inline with alwaysinline attr '") + demCalleeName + "' into '" +
91 demCallerName + "' due to '" + msg + "'");
92 }
93 #else
94 llvm::report_fatal_error(llvm::Twine("Can't inline with alwaysinline attr '") + demCalleeName + "' into '" +
95 demCallerName + "' due to '" + msg + "'");
96 #endif
97 }
98
run(llvm::LazyCallGraph::SCC & component,llvm::CGSCCAnalysisManager &,llvm::LazyCallGraph &,llvm::CGSCCUpdateResult &)99 llvm::PreservedAnalyses IrtocInlineChecker::run(llvm::LazyCallGraph::SCC &component,
100 llvm::CGSCCAnalysisManager & /*unused*/,
101 llvm::LazyCallGraph & /*unused*/, llvm::CGSCCUpdateResult & /*unused*/)
102 {
103 for (const auto &node : component) {
104 auto &func = node.getFunction();
105 if (func.isDeclaration()) {
106 continue;
107 }
108
109 for (llvm::Instruction &inst : llvm::instructions(func)) {
110 auto *callBase = llvm::dyn_cast<llvm::CallBase>(&inst);
111 if (callBase == nullptr || llvm::isa<llvm::IntrinsicInst>(&inst)) {
112 continue;
113 }
114 llvm::Function *callee = callBase->getCalledFunction();
115 if (callee == nullptr || callee->isDeclaration()) {
116 continue;
117 }
118 CheckShouldInline(callBase);
119 }
120 }
121
122 return llvm::PreservedAnalyses::all();
123 }
124
125 } // namespace ark::llvmbackend::passes
126