• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 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 "berberis/backend/x86_64/read_flags_optimizer.h"
18 
19 #include <optional>
20 
21 #include "berberis/backend/common/machine_ir.h"
22 #include "berberis/backend/x86_64/machine_ir.h"
23 #include "berberis/base/algorithm.h"
24 #include "berberis/base/arena_vector.h"
25 
26 namespace berberis::x86_64 {
27 
28 // Reads range of instructions to see if any of the registers in regs is used.
29 // Will also insert new registers into regs if we encounter PSEUDO_COPY.
30 // Returns true iff we reach the end without encountering any uses of regs.
CheckRegsUnusedWithinInsnRange(MachineInsnList::iterator insn_it,MachineInsnList::iterator end,ArenaVector<MachineReg> & regs)31 bool CheckRegsUnusedWithinInsnRange(MachineInsnList::iterator insn_it,
32                                     MachineInsnList::iterator end,
33                                     ArenaVector<MachineReg>& regs) {
34   for (; insn_it != end; ++insn_it) {
35     for (auto i = 0; i < (*insn_it)->NumRegOperands(); i++) {
36       if (Contains(regs, (*insn_it)->RegAt(i))) {
37         if (AsMachineInsnX86_64(*insn_it)->opcode() != kMachineOpPseudoCopy || i != 1) {
38           return false;
39         }
40         regs.push_back((*insn_it)->RegAt(0));
41       }
42     }
43   }
44   return true;
45 }
46 
47 // Checks if a successor node meets requirements for read flags optimization
48 // Requirements:
49 // * must be exit node or not use registers
50 // * only one in_edge - guarantees register comes from the readflags node
51 // * any registers from regs can only be live_in to the post loop nodes
52 // * nothing from regs used in node
53 // * Postloop node connected to this node must meet same post loop node as
54 //   original node with readflags instruction
55 //
56 // Returns true iff this node doesn't stop us from using the optimization.
CheckSuccessorNode(Loop * loop,MachineBasicBlock * bb,ArenaVector<MachineReg> & regs)57 bool CheckSuccessorNode(Loop* loop, MachineBasicBlock* bb, ArenaVector<MachineReg>& regs) {
58   // If the node doesn't actually use any of regs we can just skip it.
59   if (!RegsLiveInBasicBlock(bb, regs)) {
60     return true;
61   }
62 
63   // To simplify things we only allow one in_edge.
64   if (bb->in_edges().size() != 1) {
65     return false;
66   }
67 
68   MachineEdge* postloop_edge;
69   MachineEdge* loop_edge;
70   // Nodes have at most 2 out_edges so if this is a successor node there can be
71   // at most one postloop edge.
72   for (auto edge : bb->out_edges()) {
73     if (Contains(*loop, edge->dst())) {
74       loop_edge = edge;
75     } else {
76       // There should only be one exit edge.
77       CHECK_EQ(postloop_edge, nullptr);
78       postloop_edge = edge;
79     }
80   }
81   // Check if exit node.
82   if (postloop_edge == nullptr) {
83     return false;
84   }
85   CHECK(loop_edge);
86 
87   // Check regs not used in node. Note this can add additional elements into regs.
88   if (!CheckRegsUnusedWithinInsnRange(bb->insn_list().begin(), bb->insn_list().end(), regs)) {
89     return false;
90   }
91   // Check if regs found in live_in of other loop nodes.
92   // Must be done after CheckRegsUnusedWithinInsnRange in case we added new registers to regs.
93   if (RegsLiveInBasicBlock(loop_edge->dst(), regs)) {
94     return false;
95   }
96   // Check post loop nodes.
97   return CheckPostLoopNode(postloop_edge->dst(), regs);
98 }
99 
100 // Checks if this post loop node meets requirements for the read flags
101 // optimization.
102 // Requirements:
103 // * the node must have only one in_edge - this guarantees the register is coming
104 // from the readflags
105 // * nothing in regs should be in live_out
CheckPostLoopNode(MachineBasicBlock * bb,const ArenaVector<MachineReg> & regs)106 bool CheckPostLoopNode(MachineBasicBlock* bb, const ArenaVector<MachineReg>& regs) {
107   // If the node doesn't actually use any of regs we can just skip it.
108   if (!RegsLiveInBasicBlock(bb, regs)) {
109     return true;
110   }
111 
112   // Check that there's only one in_edge.
113   if (bb->in_edges().size() != 1) {
114     return false;
115   }
116   // Check that it's not live_out.
117   for (auto r : bb->live_out()) {
118     if (Contains(regs, r)) {
119       return false;
120     }
121   }
122   return true;
123 }
124 
125 // Checks if anything in regs is in bb->live_in().
RegsLiveInBasicBlock(MachineBasicBlock * bb,const ArenaVector<MachineReg> & regs)126 bool RegsLiveInBasicBlock(MachineBasicBlock* bb, const ArenaVector<MachineReg>& regs) {
127   for (auto r : bb->live_in()) {
128     if (Contains(regs, r)) {
129       return true;
130     }
131   }
132   return false;
133 }
134 
135 template <typename T>
CopyInstruction(MachineIR * machine_ir,MachineInsn * insn)136 MachineInsn* CopyInstruction(MachineIR* machine_ir, MachineInsn* insn) {
137   return machine_ir->NewInsn<T>(*static_cast<T*>(insn));
138 }
139 
GetInsnGen(MachineOpcode opcode)140 std::optional<InsnGenerator> GetInsnGen(MachineOpcode opcode) {
141   switch (opcode) {
142     case kMachineOpAddqRegReg:
143       return CopyInstruction<AddqRegReg>;
144     case kMachineOpPseudoReadFlags:
145       return CopyInstruction<PseudoReadFlags>;
146     case kMachineOpCmplRegImm:
147       return CopyInstruction<CmplRegImm>;
148     case kMachineOpCmplRegReg:
149       return CopyInstruction<CmpqRegReg>;
150     case kMachineOpCmpqRegImm:
151       return CopyInstruction<CmpqRegImm>;
152     case kMachineOpCmpqRegReg:
153       return CopyInstruction<CmpqRegReg>;
154     case kMachineOpSublRegImm:
155       return CopyInstruction<SublRegImm>;
156     case kMachineOpSublRegReg:
157       return CopyInstruction<SublRegReg>;
158     case kMachineOpSubqRegImm:
159       return CopyInstruction<SubqRegImm>;
160     case kMachineOpSubqRegReg:
161       return CopyInstruction<SubqRegReg>;
162     default:
163       return std::nullopt;
164   }
165 }
166 
167 // Finds the instruction which sets a flag register.
168 // insn_it should point to one past the element we first want to check
169 // (typically it should point to the readflags instruction).
FindFlagSettingInsn(MachineInsnList::iterator insn_it,MachineInsnList::iterator begin,MachineReg reg)170 std::optional<MachineInsnList::iterator> FindFlagSettingInsn(MachineInsnList::iterator insn_it,
171                                                              MachineInsnList::iterator begin,
172                                                              MachineReg reg) {
173   while (insn_it != begin) {
174     insn_it--;
175     for (int i = 0; i < (*insn_it)->NumRegOperands(); i++) {
176       if ((*insn_it)->RegAt(i) == reg && (*insn_it)->RegKindAt(i).IsDef()) {
177         return insn_it;
178       }
179     }
180   }
181   return std::nullopt;
182 }
183 
184 }  // namespace berberis::x86_64
185