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