156 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements a function which calls the Generic Delta pass in order10// 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/calls26/// 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 the36 // convergent attribute if a function uses convergencectrl bundles. As a37 // 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 eliminating40 // convergent if there are no convergent token uses in a function.41 HasControlledConvergence = any_of(42 ArrayRef<Intrinsic::ID>{Intrinsic::experimental_convergence_anchor,43 Intrinsic::experimental_convergence_entry,44 Intrinsic::experimental_convergence_loop},45 [&M](Intrinsic::ID ID) {46 return Intrinsic::getDeclarationIfExists(&M, 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(Context, 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(F.getAttributes()));69 }70 71 void visitCallBase(CallBase &CB) {72 CB.setAttributes(visitAttributeList(CB.getAttributes()));73 }74 75 AttributeSet visitAttributeIndex(AttributeList AL, unsigned Index) {76 AttrBuilder AttributesToPreserve(Context);77 visitAttributeSet(AL.getAttributes(Index), AttributesToPreserve);78 79 if (AttributesToPreserve.attrs().empty())80 return {};81 return AttributeSet::get(Context, AttributesToPreserve);82 }83 84 AttributeList visitAttributeList(AttributeList AL) {85 SmallVector<std::pair<unsigned, AttributeSet>> NewAttrList;86 NewAttrList.reserve(AL.getNumAttrSets());87 88 for (unsigned SetIdx : AL.indexes()) {89 if (SetIdx == AttributeList::FunctionIndex)90 continue;91 92 AttributeSet AttrSet = visitAttributeIndex(AL, SetIdx);93 if (AttrSet.hasAttributes())94 NewAttrList.emplace_back(SetIdx, AttrSet);95 }96 97 // FIXME: It's ridiculous that indexes() doesn't give us the correct order98 // for contructing a new AttributeList. Special case the function index so99 // we don't have to sort.100 AttributeSet FnAttrSet =101 visitAttributeIndex(AL, AttributeList::FunctionIndex);102 if (FnAttrSet.hasAttributes())103 NewAttrList.emplace_back(AttributeList::FunctionIndex, FnAttrSet);104 105 return AttributeList::get(Context, NewAttrList);106 }107 108 void visitAttributeSet(const AttributeSet &AS, AttrBuilder &AttrsToPreserve) {109 // Optnone requires noinline, so removing noinline requires removing the110 // pair.111 Attribute NoInline = AS.getAttribute(Attribute::NoInline);112 bool RemoveNoInline = false;113 if (NoInline.isValid()) {114 RemoveNoInline = !O.shouldKeep();115 if (!RemoveNoInline)116 AttrsToPreserve.addAttribute(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 the129 // 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 in136 // 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} // namespace150 151/// Removes out-of-chunk attributes from module.152void llvm::reduceAttributesDeltaPass(Oracle &O, ReducerWorkItem &WorkItem) {153 AttributeRemapper R(O, WorkItem.getModule());154 R.visit(WorkItem.getModule());155}156