1 | //===- ReduceAttributes.cpp - Specialized Delta Pass ----------------------===// |
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 | // This file implements a function which calls the Generic Delta pass in order |
10 | // to reduce uninteresting attributes. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "ReduceAttributes.h" |
15 | #include "Delta.h" |
16 | #include "TestRunner.h" |
17 | #include "llvm/ADT/ArrayRef.h" |
18 | #include "llvm/ADT/STLExtras.h" |
19 | #include "llvm/ADT/Sequence.h" |
20 | #include "llvm/ADT/SmallVector.h" |
21 | #include "llvm/IR/Attributes.h" |
22 | #include "llvm/IR/Function.h" |
23 | #include "llvm/IR/GlobalVariable.h" |
24 | #include "llvm/IR/InstVisitor.h" |
25 | #include "llvm/IR/InstrTypes.h" |
26 | #include "llvm/IR/Intrinsics.h" |
27 | #include "llvm/IR/Module.h" |
28 | #include "llvm/Support/raw_ostream.h" |
29 | #include <algorithm> |
30 | #include <cassert> |
31 | #include <iterator> |
32 | #include <utility> |
33 | |
34 | namespace llvm { |
35 | class LLVMContext; |
36 | } // namespace llvm |
37 | |
38 | using namespace llvm; |
39 | |
40 | namespace { |
41 | |
42 | /// Given ChunksToKeep, produce a map of global variables/functions/calls |
43 | /// and indexes of attributes to be preserved for each of them. |
44 | class AttributeRemapper : public InstVisitor<AttributeRemapper> { |
45 | Oracle &O; |
46 | LLVMContext &Context; |
47 | |
48 | public: |
49 | AttributeRemapper(Oracle &O, LLVMContext &C) : O(O), Context(C) {} |
50 | |
51 | void visitModule(Module &M) { |
52 | for (GlobalVariable &GV : M.globals()) |
53 | visitGlobalVariable(GV); |
54 | } |
55 | |
56 | void visitGlobalVariable(GlobalVariable &GV) { |
57 | // Global variables only have one attribute set. |
58 | AttributeSet AS = GV.getAttributes(); |
59 | if (AS.hasAttributes()) { |
60 | AttrBuilder AttrsToPreserve(Context); |
61 | visitAttributeSet(AS, AttrsToPreserve); |
62 | GV.setAttributes(AttributeSet::get(C&: Context, B: AttrsToPreserve)); |
63 | } |
64 | } |
65 | |
66 | void visitFunction(Function &F) { |
67 | // We can neither add nor remove attributes from intrinsics. |
68 | if (F.getIntrinsicID() == Intrinsic::not_intrinsic) |
69 | F.setAttributes(visitAttributeList(AL: F.getAttributes())); |
70 | } |
71 | |
72 | void visitCallBase(CallBase &CB) { |
73 | CB.setAttributes(visitAttributeList(AL: CB.getAttributes())); |
74 | } |
75 | |
76 | AttributeSet visitAttributeIndex(AttributeList AL, unsigned Index) { |
77 | AttrBuilder AttributesToPreserve(Context); |
78 | visitAttributeSet(AS: AL.getAttributes(Index), AttrsToPreserve&: AttributesToPreserve); |
79 | |
80 | if (AttributesToPreserve.attrs().empty()) |
81 | return {}; |
82 | return AttributeSet::get(C&: Context, B: AttributesToPreserve); |
83 | } |
84 | |
85 | AttributeList visitAttributeList(AttributeList AL) { |
86 | SmallVector<std::pair<unsigned, AttributeSet>> NewAttrList; |
87 | NewAttrList.reserve(N: AL.getNumAttrSets()); |
88 | |
89 | for (unsigned SetIdx : AL.indexes()) { |
90 | if (SetIdx == AttributeList::FunctionIndex) |
91 | continue; |
92 | |
93 | AttributeSet AttrSet = visitAttributeIndex(AL, Index: SetIdx); |
94 | if (AttrSet.hasAttributes()) |
95 | NewAttrList.emplace_back(Args&: SetIdx, Args&: AttrSet); |
96 | } |
97 | |
98 | // FIXME: It's ridiculous that indexes() doesn't give us the correct order |
99 | // for contructing a new AttributeList. Special case the function index so |
100 | // we don't have to sort. |
101 | AttributeSet FnAttrSet = |
102 | visitAttributeIndex(AL, Index: AttributeList::FunctionIndex); |
103 | if (FnAttrSet.hasAttributes()) |
104 | NewAttrList.emplace_back(Args: AttributeList::FunctionIndex, Args&: FnAttrSet); |
105 | |
106 | return AttributeList::get(C&: Context, Attrs: NewAttrList); |
107 | } |
108 | |
109 | void visitAttributeSet(const AttributeSet &AS, AttrBuilder &AttrsToPreserve) { |
110 | // Optnone requires noinline, so removing noinline requires removing the |
111 | // pair. |
112 | Attribute NoInline = AS.getAttribute(Kind: Attribute::NoInline); |
113 | bool RemoveNoInline = false; |
114 | if (NoInline.isValid()) { |
115 | RemoveNoInline = !O.shouldKeep(); |
116 | if (!RemoveNoInline) |
117 | AttrsToPreserve.addAttribute(A: NoInline); |
118 | } |
119 | |
120 | for (Attribute A : AS) { |
121 | if (A.isEnumAttribute()) { |
122 | Attribute::AttrKind Kind = A.getKindAsEnum(); |
123 | if (Kind == Attribute::NoInline) |
124 | continue; |
125 | |
126 | if (RemoveNoInline && Kind == Attribute::OptimizeNone) |
127 | continue; |
128 | |
129 | // TODO: Could only remove this if there are no constrained calls in the |
130 | // function. |
131 | if (Kind == Attribute::StrictFP) { |
132 | AttrsToPreserve.addAttribute(A); |
133 | continue; |
134 | } |
135 | } |
136 | |
137 | if (O.shouldKeep()) |
138 | AttrsToPreserve.addAttribute(A); |
139 | } |
140 | } |
141 | }; |
142 | |
143 | } // namespace |
144 | |
145 | /// Removes out-of-chunk attributes from module. |
146 | static void (Oracle &O, ReducerWorkItem &WorkItem) { |
147 | AttributeRemapper R(O, WorkItem.getContext()); |
148 | R.visit(M&: WorkItem.getModule()); |
149 | } |
150 | |
151 | void llvm::reduceAttributesDeltaPass(TestRunner &Test) { |
152 | runDeltaPass(Test, ExtractChunksFromModule: extractAttributesFromModule, Message: "Reducing Attributes" ); |
153 | } |
154 | |