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
34namespace llvm {
35class LLVMContext;
36} // namespace llvm
37
38using namespace llvm;
39
40namespace {
41
42/// Given ChunksToKeep, produce a map of global variables/functions/calls
43/// and indexes of attributes to be preserved for each of them.
44class AttributeRemapper : public InstVisitor<AttributeRemapper> {
45 Oracle &O;
46 LLVMContext &Context;
47
48public:
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.
146static void extractAttributesFromModule(Oracle &O, ReducerWorkItem &WorkItem) {
147 AttributeRemapper R(O, WorkItem.getContext());
148 R.visit(M&: WorkItem.getModule());
149}
150
151void llvm::reduceAttributesDeltaPass(TestRunner &Test) {
152 runDeltaPass(Test, ExtractChunksFromModule: extractAttributesFromModule, Message: "Reducing Attributes");
153}
154