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