• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "transforms/passes/insert_safepoints.h"
17 
18 #include "llvm_ark_interface.h"
19 #include "llvm_compiler_options.h"
20 #include "transforms/gc_utils.h"
21 #include "transforms/transform_utils.h"
22 
23 #include <llvm/IR/Function.h>
24 #include <llvm/IR/Instructions.h>
25 #include <llvm/IR/LegacyPassManager.h>
26 #include <llvm/Pass.h>
27 #include <llvm/Support/CommandLine.h>
28 #include <llvm/Transforms/Scalar.h>
29 #include <llvm/Transforms/Utils/Cloning.h>
30 
31 #define DEBUG_TYPE "insert-safepoints"
32 
33 using llvm::BasicBlock;
34 using llvm::CallInst;
35 using llvm::Function;
36 using llvm::Instruction;
37 
38 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
39 static llvm::cl::opt<uint32_t> g_safepointOnEntryLimit("isp-on-entry-limit", llvm::cl::Hidden, llvm::cl::init(0));
40 
InsertInlinedPoll(Function * poll,Instruction * point,bool afterPoint=false)41 static void InsertInlinedPoll(Function *poll, Instruction *point, bool afterPoint = false)
42 {
43     CallInst *call;
44     if (afterPoint) {
45         call = llvm::CallInst::Create(poll);
46         call->insertAfter(point);
47     } else {
48         call = llvm::CallInst::Create(poll, "", point);
49     }
50     llvm::InlineFunctionInfo info;
51     // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
52     [[maybe_unused]] bool status = InlineFunction(*call, info).isSuccess();
53     ASSERT(status && "Inlining of gc.safepoint_poll must succeed");
54 }
55 
56 /// Insert an inlined poll if a function size is greater than g_safepointOnEntryLimit
InsertSafepointOnEntry(Function & func,Function * poll)57 static bool InsertSafepointOnEntry(Function &func, Function *poll)
58 {
59     uint32_t count = 0;
60     bool spOnEntry = false;
61     for (BasicBlock &block : func) {
62         count += block.size();
63         if (count > g_safepointOnEntryLimit) {
64             spOnEntry = true;
65             break;
66         }
67     }
68     if (spOnEntry) {
69         auto insertInst = func.getEntryBlock().getFirstNonPHI();
70         // Skip allocas so LLVM can merge them into prologue
71         while (llvm::isa<llvm::AllocaInst>(insertInst)) {
72             insertInst = insertInst->getNextNode();
73         }
74         InsertInlinedPoll(poll, insertInst);
75     }
76     return spOnEntry;
77 }
78 
79 /// Insert inlined polls after instructions having attribute needs-extra-safepoint
InsertSafepointAfterIntrinsics(Function & func,Function * poll)80 static bool InsertSafepointAfterIntrinsics(Function &func, Function *poll)
81 {
82     std::vector<CallInst *> calls;
83     for (auto &block : func) {
84         for (auto &inst : block) {
85             auto call = llvm::dyn_cast<CallInst>(&inst);
86             if (call != nullptr && call->hasFnAttr("needs-extra-safepoint")) {
87                 calls.push_back(call);
88             }
89         }
90     }
91     for (auto call : calls) {
92         InsertInlinedPoll(poll, call, true);
93     }
94     return !calls.empty();
95 }
96 
97 /// Run PlaceSafepoint using legacy pass manager as it hasn't been adapted to a new pass manager
RunLegacyInserter(Function & func)98 static bool RunLegacyInserter(Function &func)
99 {
100     llvm::legacy::FunctionPassManager manager(func.getParent());
101     manager.add(llvm::createPlaceSafepointsPass());
102 
103     manager.doInitialization();
104     auto changed = manager.run(func);
105     manager.doFinalization();
106     return changed;
107 }
108 
109 namespace ark::llvmbackend::passes {
110 
ShouldInsert(const ark::llvmbackend::LLVMCompilerOptions * options)111 bool InsertSafepoints::ShouldInsert(const ark::llvmbackend::LLVMCompilerOptions *options)
112 {
113     return options->useSafepoint;
114 }
115 
run(llvm::Function & function,llvm::FunctionAnalysisManager &)116 llvm::PreservedAnalyses InsertSafepoints::run(llvm::Function &function,
117                                               llvm::FunctionAnalysisManager & /*analysisManager*/)
118 {
119     if (!gc_utils::IsGcFunction(function) || gc_utils::IsFunctionSupplemental(function)) {
120         return llvm::PreservedAnalyses::all();
121     }
122 
123     auto poll = function.getParent()->getFunction(ark::llvmbackend::LLVMArkInterface::GC_SAFEPOINT_POLL_NAME);
124 
125     auto changed = InsertSafepointOnEntry(function, poll);
126     // It is a rare case when we need extra safe points. So optimize it by this way.
127     if (function.hasFnAttribute("needs-extra-safepoint")) {
128         changed |= InsertSafepointAfterIntrinsics(function, poll);
129     }
130     changed |= RunLegacyInserter(function);
131     return changed ? llvm::PreservedAnalyses::none() : llvm::PreservedAnalyses::all();
132 }
133 
134 }  // namespace ark::llvmbackend::passes
135