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