1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <bitset>
18 #include <iterator>
19
20 #include "berberis/backend/x86_64/context_liveness_analyzer.h"
21 #include "berberis/backend/x86_64/machine_ir.h"
22 #include "berberis/backend/x86_64/machine_ir_analysis.h"
23 #include "berberis/backend/x86_64/machine_ir_opt.h"
24 #include "berberis/backend/x86_64/vreg_bit_set.h"
25 #include "berberis/base/arena_vector.h"
26
27 namespace berberis::x86_64 {
28
29 // TODO(b/232598137): Move more code in this file into the anonymous namespace.
30 namespace {
31
32 // Wrapper function for VRegBitSet, to ensure safe behavior of hardware registers - in particular,
33 // all hardware registers are considered to be used (and thus not dead). Hardware registers are not
34 // optimized, for efficiency's sake.
35 class RegUsageBitSet {
36 public:
RegUsageBitSet(const MachineIR * machine_ir)37 RegUsageBitSet(const MachineIR* machine_ir)
38 : reg_set_(machine_ir->NumVReg(), machine_ir->arena()) {}
39
Set(MachineReg reg)40 void Set(MachineReg reg) {
41 if (reg.IsVReg()) {
42 reg_set_.Set(reg);
43 }
44 }
45
Reset(MachineReg reg)46 void Reset(MachineReg reg) {
47 if (reg.IsVReg()) {
48 reg_set_.Reset(reg);
49 }
50 }
51
operator [](MachineReg reg) const52 bool operator[](MachineReg reg) const {
53 if (reg.IsVReg()) {
54 return reg_set_[reg];
55 } else {
56 return true;
57 }
58 }
59
Clear()60 void Clear() { reg_set_.Clear(); }
61
62 private:
63 VRegBitSet reg_set_;
64 };
65
AreResultsUsed(const MachineInsn * insn,const RegUsageBitSet & is_reg_used)66 bool AreResultsUsed(const MachineInsn* insn, const RegUsageBitSet& is_reg_used) {
67 for (int i = 0; i < insn->NumRegOperands(); ++i) {
68 if (insn->RegKindAt(i).IsDef() && is_reg_used[insn->RegAt(i)]) {
69 return true;
70 }
71 }
72 return false;
73 }
74
SetInsnResultsUnused(const MachineInsn * insn,RegUsageBitSet & is_reg_used)75 void SetInsnResultsUnused(const MachineInsn* insn, RegUsageBitSet& is_reg_used) {
76 for (int i = 0; i < insn->NumRegOperands(); ++i) {
77 if (insn->RegKindAt(i).IsDef()) {
78 is_reg_used.Reset(insn->RegAt(i));
79 }
80 }
81 }
82
SetInsnArgumentsUsed(const MachineInsn * insn,RegUsageBitSet & is_reg_used)83 void SetInsnArgumentsUsed(const MachineInsn* insn, RegUsageBitSet& is_reg_used) {
84 for (int i = 0; i < insn->NumRegOperands(); ++i) {
85 if (insn->RegKindAt(i).IsUse()) {
86 is_reg_used.Set(insn->RegAt(i));
87 }
88 }
89 }
90
91 } // namespace
92
RemoveDeadCode(MachineIR * machine_ir)93 void RemoveDeadCode(MachineIR* machine_ir) {
94 RegUsageBitSet is_reg_used(machine_ir);
95
96 for (auto* bb : machine_ir->bb_list()) {
97 is_reg_used.Clear();
98
99 for (auto vreg : bb->live_out()) {
100 is_reg_used.Set(vreg);
101 }
102
103 // Go from end to begin removing all unused instructions.
104 for (auto insn_it = bb->insn_list().rbegin(); insn_it != bb->insn_list().rend();) {
105 MachineInsn* insn = *insn_it++;
106
107 if (!insn->has_side_effects() && !AreResultsUsed(insn, is_reg_used)) {
108 // Note non trivial way in which reverse_iterator is erased.
109 insn_it = MachineInsnList::reverse_iterator(bb->insn_list().erase(insn_it.base()));
110 SetInsnResultsUnused(insn, is_reg_used);
111 continue;
112 }
113
114 SetInsnResultsUnused(insn, is_reg_used);
115 SetInsnArgumentsUsed(insn, is_reg_used);
116 } // For insn in bb
117 } // For bb in IR
118 }
119
ChangeBranchTarget(MachineBasicBlock * bb,MachineBasicBlock * old_dst,MachineBasicBlock * new_dst)120 void ChangeBranchTarget(MachineBasicBlock* bb,
121 MachineBasicBlock* old_dst,
122 MachineBasicBlock* new_dst) {
123 CHECK_GT(bb->insn_list().size(), 0);
124 auto last_insn = bb->insn_list().back();
125
126 // The branch instruction can either be PseudoCondBranch or PseudoBranch.
127 // When removing critical edges, the branch instruction is PseudoBranch if
128 // and only if bb has an outedge to a recovery block.
129 if (last_insn->opcode() == kMachineOpPseudoBranch) {
130 auto insn = static_cast<PseudoBranch*>(last_insn);
131 CHECK_EQ(insn->then_bb(), old_dst);
132 insn->set_then_bb(new_dst);
133 return;
134 }
135
136 CHECK(last_insn->opcode() == kMachineOpPseudoCondBranch);
137 auto insn = static_cast<PseudoCondBranch*>(last_insn);
138 if (insn->then_bb() == old_dst) {
139 insn->set_then_bb(new_dst);
140 } else if (insn->else_bb() == old_dst) {
141 insn->set_else_bb(new_dst);
142 }
143 }
144
InsertNodeOnEdge(MachineIR * ir,MachineEdge * edge,int in_edge_index)145 void InsertNodeOnEdge(MachineIR* ir, MachineEdge* edge, int in_edge_index) {
146 MachineBasicBlock* pred_bb = edge->src();
147 MachineBasicBlock* succ_bb = edge->dst();
148 MachineBasicBlock* new_bb = ir->NewBasicBlock();
149 ir->bb_list().push_back(new_bb);
150
151 // Create a new edge between new_bb and bb.
152 MachineEdge* new_edge = NewInArena<MachineEdge>(ir->arena(), ir->arena(), new_bb, succ_bb);
153 new_bb->out_edges().push_back(new_edge);
154 succ_bb->in_edges()[in_edge_index] = new_edge;
155
156 // Reuse edge but change dst to new_bb.
157 edge->set_dst(new_bb);
158 new_bb->in_edges().push_back(edge);
159
160 ChangeBranchTarget(pred_bb, succ_bb, new_bb);
161 new_bb->insn_list().push_back(ir->NewInsn<PseudoBranch>(succ_bb));
162 }
163
RemoveCriticalEdges(MachineIR * machine_ir)164 void RemoveCriticalEdges(MachineIR* machine_ir) {
165 for (auto bb : machine_ir->bb_list()) {
166 if (bb->in_edges().size() < 2) {
167 continue;
168 }
169 for (size_t i = 0; i < bb->in_edges().size(); i++) {
170 MachineEdge* edge = bb->in_edges()[i];
171 MachineBasicBlock* pred_bb = edge->src();
172 if (pred_bb->out_edges().size() >= 2) {
173 // This is a critical edge!
174 InsertNodeOnEdge(machine_ir, edge, i);
175 }
176 }
177 }
178 }
179
RemovePutIfDead(const ContextLivenessAnalyzer * analyzer,MachineBasicBlock * bb,MachineInsnList::reverse_iterator insn_it,const std::bitset<sizeof (CPUState)> seen_get)180 MachineInsnList::reverse_iterator RemovePutIfDead(const ContextLivenessAnalyzer* analyzer,
181 MachineBasicBlock* bb,
182 MachineInsnList::reverse_iterator insn_it,
183 const std::bitset<sizeof(CPUState)> seen_get) {
184 CHECK_NE(bb->out_edges().size(), 0);
185 auto* insn = AsMachineInsnX86_64(*insn_it);
186 auto disp = insn->disp();
187
188 if (seen_get.test(disp)) {
189 return ++insn_it;
190 }
191
192 for (auto out_edge : bb->out_edges()) {
193 auto dst = out_edge->dst();
194 if (analyzer->IsLiveIn(dst, disp)) {
195 return ++insn_it;
196 }
197 }
198
199 // The instruction writes to dead context.
200 auto forward_it = --(insn_it.base());
201 auto next_it = bb->insn_list().erase(forward_it);
202 std::reverse_iterator<MachineInsnList::iterator> rev_it(next_it);
203 return rev_it;
204 }
205
RemoveRedundantPut(MachineIR * ir)206 void RemoveRedundantPut(MachineIR* ir) {
207 ContextLivenessAnalyzer analyzer(ir);
208 analyzer.Init();
209 std::bitset<sizeof(CPUState)> seen_get;
210 for (auto* bb : ir->bb_list()) {
211 if (bb->out_edges().size() == 0) {
212 // We are only looking for PUTs that are dead due to other PUTs in
213 // sucessor basic blocks, because RemoveLocalGuestContextAccesses ensures
214 // that there is at most one PUT to each guest reg in a basic block.
215 continue;
216 }
217
218 seen_get.reset();
219 for (auto insn_it = bb->insn_list().rbegin(); insn_it != bb->insn_list().rend();) {
220 auto* insn = AsMachineInsnX86_64(*insn_it);
221 if (insn->IsCPUStatePut()) {
222 insn_it = RemovePutIfDead(&analyzer, bb, insn_it, seen_get);
223 } else {
224 if (insn->IsCPUStateGet()) {
225 seen_get.set(insn->disp(), true);
226 }
227 insn_it++;
228 }
229 }
230 }
231 }
232
IsForwarderBlock(MachineBasicBlock * bb)233 bool IsForwarderBlock(MachineBasicBlock* bb) {
234 if (bb->insn_list().size() != 1) {
235 return false;
236 }
237
238 // Don't remove the entry block.
239 if (bb->in_edges().size() == 0) {
240 return false;
241 }
242
243 // Don't remove self-loop. We don't need to check for loops formed by
244 // a sequence of forwarders since we can remove all of them but the
245 // last one, which will be excluded by this condition.
246 if (bb->out_edges().size() == 1 && bb->out_edges()[0]->dst() == bb) {
247 return false;
248 }
249
250 const MachineInsn* last_insn = bb->insn_list().back();
251 return last_insn->opcode() == PseudoBranch::kOpcode;
252 }
253
UnlinkForwarderBlock(MachineBasicBlock * bb)254 void UnlinkForwarderBlock(MachineBasicBlock* bb) {
255 CHECK_EQ(bb->out_edges().size(), 1);
256 auto dst = bb->out_edges()[0]->dst();
257 for (auto edge : bb->in_edges()) {
258 edge->set_dst(dst);
259 dst->in_edges().push_back(edge);
260 ChangeBranchTarget(edge->src(), bb, dst);
261 }
262
263 auto* edge = bb->out_edges()[0];
264 auto dst_in_edge_it = std::find(dst->in_edges().begin(), dst->in_edges().end(), edge);
265 CHECK(dst_in_edge_it != dst->in_edges().end());
266 dst->in_edges().erase(dst_in_edge_it);
267 }
268
RemoveForwarderBlocks(MachineIR * ir)269 void RemoveForwarderBlocks(MachineIR* ir) {
270 for (auto bb_it = ir->bb_list().begin(); bb_it != ir->bb_list().end();) {
271 auto* bb = *bb_it;
272 if (!IsForwarderBlock(bb)) {
273 bb_it++;
274 continue;
275 }
276
277 UnlinkForwarderBlock(bb);
278 bb_it = ir->bb_list().erase(bb_it);
279 }
280 }
281
ReorderBasicBlocksInReversePostOrder(MachineIR * ir)282 void ReorderBasicBlocksInReversePostOrder(MachineIR* ir) {
283 ir->bb_list() = GetReversePostOrderBBList(ir);
284 ir->set_bb_order(MachineIR::BasicBlockOrder::kReversePostOrder);
285 }
286
287 } // namespace berberis::x86_64
288