1 //===- ForceFunctionAttrs.cpp - Force function attrs for debugging --------===// 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 #include "llvm/Transforms/IPO/ForceFunctionAttrs.h" 10 #include "llvm/IR/Function.h" 11 #include "llvm/IR/Module.h" 12 #include "llvm/Support/CommandLine.h" 13 #include "llvm/Support/Debug.h" 14 #include "llvm/Support/LineIterator.h" 15 #include "llvm/Support/MemoryBuffer.h" 16 #include "llvm/Support/raw_ostream.h" 17 using namespace llvm; 18 19 #define DEBUG_TYPE "forceattrs" 20 21 static cl::list<std::string> ForceAttributes( 22 "force-attribute", cl::Hidden, 23 cl::desc( 24 "Add an attribute to a function. This can be a " 25 "pair of 'function-name:attribute-name', to apply an attribute to a " 26 "specific function. For " 27 "example -force-attribute=foo:noinline. Specifying only an attribute " 28 "will apply the attribute to every function in the module. This " 29 "option can be specified multiple times.")); 30 31 static cl::list<std::string> ForceRemoveAttributes( 32 "force-remove-attribute", cl::Hidden, 33 cl::desc("Remove an attribute from a function. This can be a " 34 "pair of 'function-name:attribute-name' to remove an attribute " 35 "from a specific function. For " 36 "example -force-remove-attribute=foo:noinline. Specifying only an " 37 "attribute will remove the attribute from all functions in the " 38 "module. This " 39 "option can be specified multiple times.")); 40 41 static cl::opt<std::string> CSVFilePath( 42 "forceattrs-csv-path", cl::Hidden, 43 cl::desc( 44 "Path to CSV file containing lines of function names and attributes to " 45 "add to them in the form of `f1,attr1` or `f2,attr2=str`.")); 46 47 /// If F has any forced attributes given on the command line, add them. 48 /// If F has any forced remove attributes given on the command line, remove 49 /// them. When both force and force-remove are given to a function, the latter 50 /// takes precedence. 51 static void forceAttributes(Function &F) { 52 auto ParseFunctionAndAttr = [&](StringRef S) { 53 StringRef AttributeText; 54 if (S.contains(':')) { 55 auto KV = StringRef(S).split(':'); 56 if (KV.first != F.getName()) 57 return Attribute::None; 58 AttributeText = KV.second; 59 } else { 60 AttributeText = S; 61 } 62 auto Kind = Attribute::getAttrKindFromName(AttributeText); 63 if (Kind == Attribute::None || !Attribute::canUseAsFnAttr(Kind)) { 64 LLVM_DEBUG(dbgs() << "ForcedAttribute: " << AttributeText 65 << " unknown or not a function attribute!\n"); 66 } 67 return Kind; 68 }; 69 70 for (const auto &S : ForceAttributes) { 71 auto Kind = ParseFunctionAndAttr(S); 72 if (Kind == Attribute::None || F.hasFnAttribute(Kind)) 73 continue; 74 F.addFnAttr(Kind); 75 } 76 77 for (const auto &S : ForceRemoveAttributes) { 78 auto Kind = ParseFunctionAndAttr(S); 79 if (Kind == Attribute::None || !F.hasFnAttribute(Kind)) 80 continue; 81 F.removeFnAttr(Kind); 82 } 83 } 84 85 static bool hasForceAttributes() { 86 return !ForceAttributes.empty() || !ForceRemoveAttributes.empty(); 87 } 88 89 PreservedAnalyses ForceFunctionAttrsPass::run(Module &M, 90 ModuleAnalysisManager &) { 91 bool Changed = false; 92 if (!CSVFilePath.empty()) { 93 auto BufferOrError = MemoryBuffer::getFileOrSTDIN(CSVFilePath); 94 if (!BufferOrError) 95 report_fatal_error("Cannot open CSV file."); 96 StringRef Buffer = BufferOrError.get()->getBuffer(); 97 auto MemoryBuffer = MemoryBuffer::getMemBuffer(Buffer); 98 line_iterator It(*MemoryBuffer); 99 for (; !It.is_at_end(); ++It) { 100 auto SplitPair = It->split(','); 101 if (SplitPair.second.empty()) 102 continue; 103 Function *Func = M.getFunction(SplitPair.first); 104 if (Func) { 105 if (Func->isDeclaration()) 106 continue; 107 auto SecondSplitPair = SplitPair.second.split('='); 108 if (!SecondSplitPair.second.empty()) { 109 Func->addFnAttr(SecondSplitPair.first, SecondSplitPair.second); 110 Changed = true; 111 } else { 112 auto AttrKind = Attribute::getAttrKindFromName(SplitPair.second); 113 if (AttrKind != Attribute::None && 114 Attribute::canUseAsFnAttr(AttrKind)) { 115 // TODO: There could be string attributes without a value, we should 116 // support those, too. 117 Func->addFnAttr(AttrKind); 118 Changed = true; 119 } else 120 errs() << "Cannot add " << SplitPair.second 121 << " as an attribute name.\n"; 122 } 123 } else { 124 errs() << "Function in CSV file at line " << It.line_number() 125 << " does not exist.\n"; 126 // TODO: `report_fatal_error at end of pass for missing functions. 127 continue; 128 } 129 } 130 } 131 if (hasForceAttributes()) { 132 for (Function &F : M.functions()) 133 forceAttributes(F); 134 Changed = true; 135 } 136 // Just conservatively invalidate analyses if we've made any changes, this 137 // isn't likely to be important. 138 return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); 139 } 140