• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 "aarch64_fixshortbranch.h"
17 
18 namespace maplebe {
CalculateAlignRange(const BB & bb,uint32 addr) const19 uint32 AArch64FixShortBranch::CalculateAlignRange(const BB &bb, uint32 addr) const
20 {
21     if (addr == 0) {
22         return addr;
23     }
24     uint32 alignPower = bb.GetAlignPower();
25     /*
26      * The algorithm can avoid the problem that alignment causes conditional branch out of range in two stages.
27      * 1. asm:  .mpl -> .s
28      *          The pseudo-instruction [.p2align 5] is 12B.
29      *          kAlignPseudoSize = 12 / 4 = 3
30      * 2. link: .s -> .o
31      *          The pseudo-instruction will be expanded to nop.
32      *      eg. .p2align 5
33      *          alignPower = 5, alignValue = 2^5 = 32
34      *          range = (32 - ((addr - 1) * 4) % 32) / 4 - 1
35      *
36      * =======> max[range, kAlignPseudoSize]
37      */
38     uint32 range = ((1U << alignPower) - (((addr - 1) * kInsnSize) & ((1U << alignPower) - 1))) / kInsnSize - 1;
39     return range > kAlignPseudoSize ? range : kAlignPseudoSize;
40 }
41 
SetInsnId() const42 void AArch64FixShortBranch::SetInsnId() const
43 {
44     uint32 i = 0;
45     AArch64CGFunc *aarch64CGFunc = static_cast<AArch64CGFunc *>(cgFunc);
46     FOR_ALL_BB(bb, aarch64CGFunc)
47     {
48         FOR_BB_INSNS(insn, bb)
49         {
50             if (!insn->IsMachineInstruction()) {
51                 continue;
52             }
53             i += insn->GetAtomicNum();
54             insn->SetId(i);
55             if (insn->GetMachineOpcode() == MOP_adrp_ldr && CGOptions::IsLazyBinding() &&
56                 !cgFunc->GetCG()->IsLibcore()) {
57                 /* For 1 additional EmitLazyBindingRoutine in lazybinding
58                  * see function AArch64Insn::Emit in file aarch64_insn.cpp
59                  */
60                 ++i;
61             }
62         }
63     }
64 }
65 
CalculateIfBBNum() const66 uint32 AArch64FixShortBranch::CalculateIfBBNum() const
67 {
68     uint32 ifBBCount = 0;
69     FOR_ALL_BB(bb, cgFunc)
70     {
71         if (bb->GetKind() != BB::kBBIf) {
72             ifBBCount++;
73         }
74     }
75     return ifBBCount;
76 }
77 
PatchLongBranch()78 void AArch64FixShortBranch::PatchLongBranch()
79 {
80     AArch64CGFunc *aarch64CGFunc = static_cast<AArch64CGFunc *>(cgFunc);
81     SetInsnId();
82     uint32 ifBBCount = CalculateIfBBNum();
83     for (BB *bb = aarch64CGFunc->GetFirstBB(); bb != nullptr; bb = bb->GetNext()) {
84         if (bb->GetKind() != BB::kBBIf) {
85             continue;
86         }
87         Insn *insn = bb->GetLastMachineInsn();
88         while (insn != nullptr && insn->IsImmaterialInsn()) {
89             insn = insn->GetPrev();
90         }
91         if (insn == nullptr || !insn->IsCondBranch()) {
92             continue;
93         }
94         LabelIdx tbbLabelIdx = aarch64CGFunc->GetLabelInInsn(*insn);
95         // when we change condbr to condbr and b, we will have more insns
96         // in case the insn num will cause distance check not calculating right
97         // we assume that each if bb will be changed(which is the worst case).
98         if (ifBBCount <= AArch64Abi::kMaxInstrForCondBr &&
99             aarch64CGFunc->DistanceCheck(*bb, tbbLabelIdx, insn->GetId(), AArch64Abi::kMaxInstrForCondBr - ifBBCount)) {
100             continue;
101         }
102         aarch64CGFunc->InsertJumpPad(insn);
103     }
104 }
105 /*
106  * TBZ/TBNZ instruction is generated under -O2, these branch instructions only have a range of +/-32KB.
107  * If the branch target is not reachable, we split tbz/tbnz into combination of ubfx and cbz/cbnz, which
108  * will clobber one extra register. With LSRA under -O2, we can use one of the reserved registers R16 for
109  * that purpose. To save compile time, we do this change when there are more than 32KB / 4 instructions
110  * in the function.
111  */
FixShortBranches() const112 void AArch64FixShortBranch::FixShortBranches() const
113 {
114     AArch64CGFunc *aarch64CGFunc = static_cast<AArch64CGFunc *>(cgFunc);
115     bool change = false;
116     do {
117         change = false;
118         SetInsnId();
119         for (auto *bb = aarch64CGFunc->GetFirstBB(); bb != nullptr && !change; bb = bb->GetNext()) {
120             /* Do a backward scan searching for short branches */
121             for (auto *insn = bb->GetLastInsn(); insn != nullptr && !change; insn = insn->GetPrev()) {
122                 if (!insn->IsMachineInstruction()) {
123                     continue;
124                 }
125                 MOperator thisMop = insn->GetMachineOpcode();
126                 if (thisMop != MOP_wtbz && thisMop != MOP_wtbnz && thisMop != MOP_xtbz && thisMop != MOP_xtbnz) {
127                     continue;
128                 }
129                 LabelOperand &label = static_cast<LabelOperand &>(insn->GetOperand(kInsnThirdOpnd));
130                 /*  should not be commented out after bug fix */
131                 if (aarch64CGFunc->DistanceCheck(*bb, label.GetLabelIndex(), insn->GetId(),
132                                                  AArch64Abi::kMaxInstrForTbnz)) {
133                     continue;
134                 }
135                 auto &reg = static_cast<RegOperand &>(insn->GetOperand(kInsnFirstOpnd));
136                 ImmOperand &bitSize = aarch64CGFunc->CreateImmOperand(1, k8BitSize, false);
137                 auto &bitPos = static_cast<ImmOperand &>(insn->GetOperand(kInsnSecondOpnd));
138                 MOperator ubfxOp = MOP_undef;
139                 MOperator cbOp = MOP_undef;
140                 switch (thisMop) {
141                     case MOP_wtbz:
142                         ubfxOp = MOP_wubfxrri5i5;
143                         cbOp = MOP_wcbz;
144                         break;
145                     case MOP_wtbnz:
146                         ubfxOp = MOP_wubfxrri5i5;
147                         cbOp = MOP_wcbnz;
148                         break;
149                     case MOP_xtbz:
150                         ubfxOp = MOP_xubfxrri6i6;
151                         cbOp = MOP_xcbz;
152                         break;
153                     case MOP_xtbnz:
154                         ubfxOp = MOP_xubfxrri6i6;
155                         cbOp = MOP_xcbnz;
156                         break;
157                     default:
158                         CHECK_FATAL_FALSE("must be");
159                         break;
160                 }
161                 RegOperand &tmp = aarch64CGFunc->GetOrCreatePhysicalRegisterOperand(
162                     R16, (ubfxOp == MOP_wubfxrri5i5) ? k32BitSize : k64BitSize, kRegTyInt);
163                 (void)bb->InsertInsnAfter(*insn, cgFunc->GetInsnBuilder()->BuildInsn(cbOp, tmp, label));
164                 (void)bb->InsertInsnAfter(*insn,
165                                           cgFunc->GetInsnBuilder()->BuildInsn(ubfxOp, tmp, reg, bitPos, bitSize));
166                 bb->RemoveInsn(*insn);
167                 change = true;
168             }
169         }
170     } while (change);
171 }
172 
CheckFunctionSize(uint32 maxSize) const173 bool AArch64FixShortBranch::CheckFunctionSize(uint32 maxSize) const
174 {
175     uint32 firstInsnId = 0;
176     uint32 lastInsnId = UINT32_MAX;
177     bool findLast = false;
178     bool findFirst = false;
179 
180     for (auto *bb = cgFunc->GetLastBB(); bb != nullptr && !findLast; bb = bb->GetPrev()) {
181         for (auto *insn = bb->GetLastInsn(); insn != nullptr && !findLast; insn = insn->GetPrev()) {
182             if (!insn->IsMachineInstruction() || insn->IsImmaterialInsn()) {
183                 continue;
184             }
185             findLast = true;
186             lastInsnId = insn->GetId();
187             break;
188         }
189     }
190 
191     for (auto *bb = cgFunc->GetFirstBB(); bb != nullptr && !findFirst; bb = bb->GetNext()) {
192         for (auto *insn = bb->GetFirstInsn(); insn != nullptr && !findFirst; insn = insn->GetNext()) {
193             if (!insn->IsMachineInstruction() || insn->IsImmaterialInsn()) {
194                 continue;
195             }
196             findFirst = true;
197             firstInsnId = insn->GetId();
198             break;
199         }
200     }
201     return (lastInsnId - firstInsnId + 1) <= maxSize;
202 }
203 
204 // when func size >= kMaxInstrForLdr
205 // ldr R1, .L.4__5
206 // .L_x: ...
207 // =>
208 // adrp    x1, .L.4__5
209 // add     x1, x1, :lo12:.L.4__5
210 // ldr     x1, [x1]
FixLdr()211 void AArch64FixShortBranch::FixLdr()
212 {
213     AArch64CGFunc *aarch64CGFunc = static_cast<AArch64CGFunc *>(cgFunc);
214     SetInsnId();
215     if (CheckFunctionSize(AArch64Abi::kMaxInstrForLdr)) {
216         return;
217     }
218     FOR_ALL_BB(bb, cgFunc)
219     {
220         FOR_BB_INSNS(insn, bb)
221         {
222             if (!insn->IsMachineInstruction()) {
223                 continue;
224             }
225             if (insn->GetMachineOpcode() == MOP_xldli && insn->GetOperand(kInsnSecondOpnd).IsLabelOpnd()) {
226                 // ldr -> adrp + add
227                 auto &regOpnd = static_cast<RegOperand &>(insn->GetOperand(kInsnFirstOpnd));
228                 auto &labelOpnd = static_cast<LabelOperand &>(insn->GetOperand(kInsnSecondOpnd));
229                 Operand &immOpnd = aarch64CGFunc->CreateImmOperand(labelOpnd.GetLabelIndex(), k64BitSize, false);
230                 insn->SetOperand(kInsnSecondOpnd, immOpnd);
231                 insn->SetMOP(AArch64CG::kMd[MOP_adrp_label]);
232                 // ldr x1, [x1]
233                 MemOperand *newDest = aarch64CGFunc->CreateMemOperand(
234                     k64BitSize, regOpnd, aarch64CGFunc->CreateImmOperand(0, k32BitSize, false), false);
235                 auto *newRegOpnd = static_cast<RegOperand *>(regOpnd.Clone(*aarch64CGFunc->GetMemoryPool()));
236                 Insn &ldrInsn = aarch64CGFunc->GetInsnBuilder()->BuildInsn(MOP_xldr, *newRegOpnd, *newDest);
237                 (void)bb->InsertInsnAfter(*insn, ldrInsn);
238             }
239         }
240     }
241 }
242 
PhaseRun(maplebe::CGFunc & f)243 bool CgFixShortBranch::PhaseRun(maplebe::CGFunc &f)
244 {
245     auto *fixShortBranch = GetPhaseAllocator()->New<AArch64FixShortBranch>(&f);
246     CHECK_FATAL(fixShortBranch != nullptr, "AArch64FixShortBranch instance create failure");
247     fixShortBranch->FixShortBranches();
248 
249     // fix ldr would cause insn num increasing, do ldr fix first.
250     fixShortBranch->FixLdr();
251     fixShortBranch->PatchLongBranch();
252     return false;
253 }
254 MAPLE_TRANSFORM_PHASE_REGISTER(CgFixShortBranch, fixshortbranch)
255 } /* namespace maplebe */
256