1//===- InferAlignment.cpp -------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Infer alignment for load, stores and other memory operations based on
10// trailing zero known bits information.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/Transforms/Scalar/InferAlignment.h"
15#include "llvm/ADT/APInt.h"
16#include "llvm/ADT/STLFunctionalExtras.h"
17#include "llvm/ADT/ScopedHashTable.h"
18#include "llvm/Analysis/AssumptionCache.h"
19#include "llvm/Analysis/ValueTracking.h"
20#include "llvm/IR/Instruction.h"
21#include "llvm/IR/Instructions.h"
22#include "llvm/IR/IntrinsicInst.h"
23#include "llvm/IR/PatternMatch.h"
24#include "llvm/Support/KnownBits.h"
25#include "llvm/Transforms/Scalar.h"
26#include "llvm/Transforms/Utils/Local.h"
27
28using namespace llvm;
29using namespace llvm::PatternMatch;
30
31static bool tryToImproveAlign(
32 const DataLayout &DL, Instruction *I,
33 function_ref<Align(Value *PtrOp, Align OldAlign, Align PrefAlign)> Fn) {
34
35 if (auto *PtrOp = getLoadStorePointerOperand(V: I)) {
36 Align OldAlign = getLoadStoreAlignment(I);
37 Align PrefAlign = DL.getPrefTypeAlign(Ty: getLoadStoreType(I));
38
39 Align NewAlign = Fn(PtrOp, OldAlign, PrefAlign);
40 if (NewAlign > OldAlign) {
41 setLoadStoreAlignment(I, NewAlign);
42 return true;
43 }
44 }
45
46 Value *PtrOp;
47 const APInt *Const;
48 if (match(V: I, P: m_And(L: m_PtrToIntOrAddr(Op: m_Value(V&: PtrOp)), R: m_APInt(Res&: Const)))) {
49 Align ActualAlign = Fn(PtrOp, Align(1), Align(1));
50 if (Const->ult(RHS: ActualAlign.value())) {
51 I->replaceAllUsesWith(V: Constant::getNullValue(Ty: I->getType()));
52 return true;
53 }
54 if (Const->uge(
55 RHS: APInt::getBitsSetFrom(numBits: Const->getBitWidth(), loBit: Log2(A: ActualAlign)))) {
56 I->replaceAllUsesWith(V: I->getOperand(i: 0));
57 return true;
58 }
59 }
60 if (match(V: I, P: m_Trunc(Op: m_PtrToIntOrAddr(Op: m_Value(V&: PtrOp))))) {
61 Align ActualAlign = Fn(PtrOp, Align(1), Align(1));
62 if (Log2(A: ActualAlign) >= I->getType()->getScalarSizeInBits()) {
63 I->replaceAllUsesWith(V: Constant::getNullValue(Ty: I->getType()));
64 return true;
65 }
66 }
67
68 IntrinsicInst *II = dyn_cast<IntrinsicInst>(Val: I);
69 if (!II)
70 return false;
71
72 if (!isa<MemIntrinsic>(Val: II) &&
73 II->getIntrinsicID() != Intrinsic::masked_load &&
74 II->getIntrinsicID() != Intrinsic::masked_store)
75 return false;
76
77 bool Changed = false;
78 for (unsigned ArgNo = 0; ArgNo != II->arg_size(); ++ArgNo) {
79 Value *Arg = II->getArgOperand(i: ArgNo);
80 if (!Arg->getType()->isPointerTy())
81 continue;
82
83 Align OldAlign = II->getParamAlign(ArgNo).valueOrOne();
84 Align NewAlign = Fn(Arg, OldAlign, Align(1));
85 if (NewAlign <= OldAlign)
86 continue;
87
88 II->addParamAttr(ArgNo,
89 Attr: Attribute::getWithAlignment(Context&: II->getContext(), Alignment: NewAlign));
90 Changed = true;
91 }
92 return Changed;
93}
94
95using ScopedHT =
96 ScopedHashTable<Value *, Align, DenseMapInfo<Value *>, BumpPtrAllocator>;
97struct AlignmentScope {
98 // If BB is nullptr, the BB is processed.
99 BasicBlock *BB;
100 DomTreeNode::const_iterator Iter;
101 DomTreeNode::const_iterator End;
102 ScopedHT::ScopeTy Scope;
103
104 AlignmentScope(DomTreeNode *N, ScopedHT &Table)
105 : BB(N->getBlock()), Iter(N->begin()), End(N->end()), Scope(Table) {}
106};
107
108bool inferAlignment(Function &F, AssumptionCache &AC, DominatorTree &DT) {
109 const DataLayout &DL = F.getDataLayout();
110 bool Changed = false;
111
112 // Enforce preferred type alignment if possible. We do this as a separate
113 // pass first, because it may improve the alignments we infer below.
114 for (BasicBlock &BB : F) {
115 for (Instruction &I : BB) {
116 Changed |= tryToImproveAlign(
117 DL, I: &I, Fn: [&](Value *PtrOp, Align OldAlign, Align PrefAlign) {
118 if (PrefAlign > OldAlign)
119 return std::max(a: OldAlign,
120 b: tryEnforceAlignment(V: PtrOp, PrefAlign, DL));
121 return OldAlign;
122 });
123 }
124 }
125
126 // Compute alignment from known bits.
127 auto InferFromKnownBits = [&](Instruction &I, Value *PtrOp) {
128 KnownBits Known = computeKnownBits(V: PtrOp, DL, AC: &AC, CxtI: &I, DT: &DT);
129 unsigned TrailZ =
130 std::min(a: Known.countMinTrailingZeros(), b: +Value::MaxAlignmentExponent);
131 return Align(1ull << std::min(a: Known.getBitWidth() - 1, b: TrailZ));
132 };
133
134 // Propagate alignment between loads and stores that originate from the
135 // same base pointer.
136 ScopedHT BestBasePointerAligns;
137 auto InferFromBasePointer = [&](Value *PtrOp, Align LoadStoreAlign) {
138 APInt OffsetFromBase(DL.getIndexTypeSizeInBits(Ty: PtrOp->getType()), 0);
139 PtrOp = PtrOp->stripAndAccumulateConstantOffsets(DL, Offset&: OffsetFromBase, AllowNonInbounds: true);
140 // Derive the base pointer alignment from the load/store alignment
141 // and the offset from the base pointer.
142 Align BasePointerAlign =
143 commonAlignment(A: LoadStoreAlign, Offset: OffsetFromBase.getLimitedValue());
144
145 if (auto BestAlign = BestBasePointerAligns.lookup(Key: PtrOp);
146 BestAlign != Align()) {
147 // If the stored base pointer alignment is better than the
148 // base pointer alignment we derived, we may be able to use it
149 // to improve the load/store alignment. If not, store the
150 // improved base pointer alignment for future iterations.
151 if (BestAlign > BasePointerAlign) {
152 Align BetterLoadStoreAlign =
153 commonAlignment(A: BestAlign, Offset: OffsetFromBase.getLimitedValue());
154 return BetterLoadStoreAlign;
155 }
156 }
157
158 BestBasePointerAligns.insert(Key: PtrOp, Val: BasePointerAlign);
159 return LoadStoreAlign;
160 };
161
162 // AlignmentScope is unmovable.
163 std::list<AlignmentScope> Stack;
164 Stack.emplace_back(args: DT.getRootNode(), args&: BestBasePointerAligns);
165 while (!Stack.empty()) {
166 AlignmentScope &Top = Stack.back();
167 if (Top.BB) {
168 for (Instruction &I : *Top.BB) {
169 Changed |= tryToImproveAlign(
170 DL, I: &I, Fn: [&](Value *PtrOp, Align OldAlign, Align PrefAlign) {
171 return std::max(a: InferFromKnownBits(I, PtrOp),
172 b: InferFromBasePointer(PtrOp, OldAlign));
173 });
174 }
175 Top.BB = nullptr;
176 }
177
178 if (Top.Iter != Top.End)
179 Stack.emplace_back(args: *Top.Iter++, args&: BestBasePointerAligns);
180 else
181 Stack.pop_back();
182 }
183
184 return Changed;
185}
186
187PreservedAnalyses InferAlignmentPass::run(Function &F,
188 FunctionAnalysisManager &AM) {
189 AssumptionCache &AC = AM.getResult<AssumptionAnalysis>(IR&: F);
190 DominatorTree &DT = AM.getResult<DominatorTreeAnalysis>(IR&: F);
191 inferAlignment(F, AC, DT);
192 // Changes to alignment shouldn't invalidated analyses.
193 return PreservedAnalyses::all();
194}
195