• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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