Expose IRGen API to add the default IR attributes to a function definition.

I've also made a stab at imposing some more order on where and how we add
attributes; this part should be NFC.  I wasn't sure whether the CUDA use
case for libdevice should propagate CPU/features attributes, so there's a
bit of unnecessary duplication.
This commit is contained in:
John McCall 2020-05-16 14:44:54 -04:00
parent 49c9a68d7f
commit 32870a84d9
5 changed files with 128 additions and 55 deletions

View File

@ -28,6 +28,7 @@
#include "clang/CodeGen/CGFunctionInfo.h"
namespace llvm {
class AttrBuilder;
class Constant;
class DataLayout;
class Module;
@ -86,6 +87,25 @@ llvm::Type *convertTypeForMemory(CodeGenModule &CGM, QualType T);
unsigned getLLVMFieldNumber(CodeGenModule &CGM,
const RecordDecl *RD, const FieldDecl *FD);
/// Given the language and code-generation options that Clang was configured
/// with, set the default LLVM IR attributes for a function definition.
/// The attributes set here are mostly global target-configuration and
/// pipeline-configuration options like the target CPU, variant stack
/// rules, whether to optimize for size, and so on. This is useful for
/// frontends (such as Swift) that generally intend to interoperate with
/// C code and rely on Clang's target configuration logic.
///
/// As a general rule, this function assumes that meaningful attributes
/// haven't already been added to the builder. It won't intentionally
/// displace any existing attributes, but it also won't check to avoid
/// overwriting them. Callers should generally apply customizations after
/// making this call.
///
/// This function assumes that the caller is not defining a function that
/// requires special no-builtin treatment.
void addDefaultFunctionDefinitionAttributes(CodeGenModule &CGM,
llvm::AttrBuilder &attrs);
/// Returns the default constructor for a C struct with non-trivially copyable
/// fields, generating it if necessary. The returned function uses the `cdecl`
/// calling convention, returns void, and takes a single argument that is a

View File

@ -1700,8 +1700,9 @@ static void AddAttributesFromFunctionProtoType(ASTContext &Ctx,
FuncAttrs.addAttribute(llvm::Attribute::NoUnwind);
}
void CodeGenModule::ConstructDefaultFnAttrList(StringRef Name, bool HasOptnone,
bool AttrOnCallSite,
void CodeGenModule::getDefaultFunctionAttributes(StringRef Name,
bool HasOptnone,
bool AttrOnCallSite,
llvm::AttrBuilder &FuncAttrs) {
// OptimizeNoneAttr takes precedence over -Os or -Oz. No warning needed.
if (!HasOptnone) {
@ -1796,6 +1797,8 @@ void CodeGenModule::ConstructDefaultFnAttrList(StringRef Name, bool HasOptnone,
FuncAttrs.addAttribute("stackrealign");
if (CodeGenOpts.Backchain)
FuncAttrs.addAttribute("backchain");
if (CodeGenOpts.EnableSegmentedStacks)
FuncAttrs.addAttribute("split-stack");
if (CodeGenOpts.SpeculativeLoadHardening)
FuncAttrs.addAttribute(llvm::Attribute::SpeculativeLoadHardening);
@ -1822,13 +1825,21 @@ void CodeGenModule::ConstructDefaultFnAttrList(StringRef Name, bool HasOptnone,
}
}
void CodeGenModule::AddDefaultFnAttrs(llvm::Function &F) {
void CodeGenModule::addDefaultFunctionDefinitionAttributes(llvm::Function &F) {
llvm::AttrBuilder FuncAttrs;
ConstructDefaultFnAttrList(F.getName(), F.hasOptNone(),
/* AttrOnCallSite = */ false, FuncAttrs);
getDefaultFunctionAttributes(F.getName(), F.hasOptNone(),
/* AttrOnCallSite = */ false, FuncAttrs);
// TODO: call GetCPUAndFeaturesAttributes?
F.addAttributes(llvm::AttributeList::FunctionIndex, FuncAttrs);
}
void CodeGenModule::addDefaultFunctionDefinitionAttributes(
llvm::AttrBuilder &attrs) {
getDefaultFunctionAttributes(/*function name*/ "", /*optnone*/ false,
/*for call*/ false, attrs);
GetCPUAndFeaturesAttributes(GlobalDecl(), attrs);
}
static void addNoBuiltinAttributes(llvm::AttrBuilder &FuncAttrs,
const LangOptions &LangOpts,
const NoBuiltinAttr *NBA = nullptr) {
@ -1865,29 +1876,49 @@ static void addNoBuiltinAttributes(llvm::AttrBuilder &FuncAttrs,
llvm::for_each(NBA->builtinNames(), AddNoBuiltinAttr);
}
/// Construct the IR attribute list of a function or call.
///
/// When adding an attribute, please consider where it should be handled:
///
/// - getDefaultFunctionAttributes is for attributes that are essentially
/// part of the global target configuration (but perhaps can be
/// overridden on a per-function basis). Adding attributes there
/// will cause them to also be set in frontends that build on Clang's
/// target-configuration logic, as well as for code defined in library
/// modules such as CUDA's libdevice.
///
/// - ConstructAttributeList builds on top of getDefaultFunctionAttributes
/// and adds declaration-specific, convention-specific, and
/// frontend-specific logic. The last is of particular importance:
/// attributes that restrict how the frontend generates code must be
/// added here rather than getDefaultFunctionAttributes.
///
void CodeGenModule::ConstructAttributeList(
StringRef Name, const CGFunctionInfo &FI, CGCalleeInfo CalleeInfo,
llvm::AttributeList &AttrList, unsigned &CallingConv, bool AttrOnCallSite) {
llvm::AttrBuilder FuncAttrs;
llvm::AttrBuilder RetAttrs;
// Collect function IR attributes from the CC lowering.
// We'll collect the paramete and result attributes later.
CallingConv = FI.getEffectiveCallingConvention();
if (FI.isNoReturn())
FuncAttrs.addAttribute(llvm::Attribute::NoReturn);
if (FI.isCmseNSCall())
FuncAttrs.addAttribute("cmse_nonsecure_call");
// If we have information about the function prototype, we can learn
// attributes from there.
// Collect function IR attributes from the callee prototype if we have one.
AddAttributesFromFunctionProtoType(getContext(), FuncAttrs,
CalleeInfo.getCalleeFunctionProtoType());
const Decl *TargetDecl = CalleeInfo.getCalleeDecl().getDecl();
bool HasOptnone = false;
// The NoBuiltinAttr attached to a TargetDecl (only allowed on FunctionDecls).
// The NoBuiltinAttr attached to the target FunctionDecl.
const NoBuiltinAttr *NBA = nullptr;
// Collect function IR attributes based on declaration-specific
// information.
// FIXME: handle sseregparm someday...
if (TargetDecl) {
if (TargetDecl->hasAttr<ReturnsTwiceAttr>())
@ -1953,6 +1984,21 @@ void CodeGenModule::ConstructAttributeList(
FuncAttrs.addAllocSizeAttr(AllocSize->getElemSizeParam().getLLVMIndex(),
NumElemsParam);
}
if (TargetDecl->hasAttr<OpenCLKernelAttr>()) {
if (getLangOpts().OpenCLVersion <= 120) {
// OpenCL v1.2 Work groups are always uniform
FuncAttrs.addAttribute("uniform-work-group-size", "true");
} else {
// OpenCL v2.0 Work groups may be whether uniform or not.
// '-cl-uniform-work-group-size' compile option gets a hint
// to the compiler that the global work-size be a multiple of
// the work-group size specified to clEnqueueNDRangeKernel
// (i.e. work groups are uniform).
FuncAttrs.addAttribute("uniform-work-group-size",
llvm::toStringRef(CodeGenOpts.UniformWGSize));
}
}
}
// Attach "no-builtins" attributes to:
@ -1963,71 +2009,68 @@ void CodeGenModule::ConstructAttributeList(
// * FunctionDecl attributes: __attribute__((no_builtin(...)))
addNoBuiltinAttributes(FuncAttrs, getLangOpts(), NBA);
ConstructDefaultFnAttrList(Name, HasOptnone, AttrOnCallSite, FuncAttrs);
// Collect function IR attributes based on global settiings.
getDefaultFunctionAttributes(Name, HasOptnone, AttrOnCallSite, FuncAttrs);
// This must run after constructing the default function attribute list
// to ensure that the speculative load hardening attribute is removed
// in the case where the -mspeculative-load-hardening flag was passed.
// Override some default IR attributes based on declaration-specific
// information.
if (TargetDecl) {
if (TargetDecl->hasAttr<NoSpeculativeLoadHardeningAttr>())
FuncAttrs.removeAttribute(llvm::Attribute::SpeculativeLoadHardening);
if (TargetDecl->hasAttr<SpeculativeLoadHardeningAttr>())
FuncAttrs.addAttribute(llvm::Attribute::SpeculativeLoadHardening);
}
if (TargetDecl->hasAttr<NoSplitStackAttr>())
FuncAttrs.removeAttribute("split-stack");
if (CodeGenOpts.EnableSegmentedStacks &&
!(TargetDecl && TargetDecl->hasAttr<NoSplitStackAttr>()))
FuncAttrs.addAttribute("split-stack");
// Add NonLazyBind attribute to function declarations when -fno-plt
// is used.
if (TargetDecl && CodeGenOpts.NoPLT) {
if (auto *Fn = dyn_cast<FunctionDecl>(TargetDecl)) {
if (!Fn->isDefined() && !AttrOnCallSite) {
FuncAttrs.addAttribute(llvm::Attribute::NonLazyBind);
// Add NonLazyBind attribute to function declarations when -fno-plt
// is used.
// FIXME: what if we just haven't processed the function definition
// yet, or if it's an external definition like C99 inline?
if (CodeGenOpts.NoPLT) {
if (auto *Fn = dyn_cast<FunctionDecl>(TargetDecl)) {
if (!Fn->isDefined() && !AttrOnCallSite) {
FuncAttrs.addAttribute(llvm::Attribute::NonLazyBind);
}
}
}
}
if (TargetDecl && TargetDecl->hasAttr<OpenCLKernelAttr>()) {
if (getLangOpts().OpenCLVersion <= 120) {
// OpenCL v1.2 Work groups are always uniform
FuncAttrs.addAttribute("uniform-work-group-size", "true");
} else {
// OpenCL v2.0 Work groups may be whether uniform or not.
// '-cl-uniform-work-group-size' compile option gets a hint
// to the compiler that the global work-size be a multiple of
// the work-group size specified to clEnqueueNDRangeKernel
// (i.e. work groups are uniform).
FuncAttrs.addAttribute("uniform-work-group-size",
llvm::toStringRef(CodeGenOpts.UniformWGSize));
}
}
// Collect non-call-site function IR attributes from declaration-specific
// information.
if (!AttrOnCallSite) {
if (TargetDecl && TargetDecl->hasAttr<CmseNSEntryAttr>())
FuncAttrs.addAttribute("cmse_nonsecure_entry");
bool DisableTailCalls = false;
// Whether tail calls are enabled.
auto shouldDisableTailCalls = [&] {
// Should this be honored in getDefaultFunctionAttributes?
if (CodeGenOpts.DisableTailCalls)
return true;
if (!TargetDecl)
return false;
if (CodeGenOpts.DisableTailCalls)
DisableTailCalls = true;
else if (TargetDecl) {
if (TargetDecl->hasAttr<DisableTailCallsAttr>() ||
TargetDecl->hasAttr<AnyX86InterruptAttr>())
DisableTailCalls = true;
else if (CodeGenOpts.NoEscapingBlockTailCalls) {
return true;
if (CodeGenOpts.NoEscapingBlockTailCalls) {
if (const auto *BD = dyn_cast<BlockDecl>(TargetDecl))
if (!BD->doesNotEscape())
DisableTailCalls = true;
return true;
}
}
return false;
};
FuncAttrs.addAttribute("disable-tail-calls",
llvm::toStringRef(DisableTailCalls));
llvm::toStringRef(shouldDisableTailCalls()));
// CPU/feature overrides. addDefaultFunctionDefinitionAttributes
// handles these separately to set them based on the global defaults.
GetCPUAndFeaturesAttributes(CalleeInfo.getCalleeDecl(), FuncAttrs);
}
// Collect attributes from arguments and return values.
ClangToLLVMArgMapping IRFunctionArgs(getContext(), FI);
QualType RetTy = FI.getReturnType();

View File

@ -25,6 +25,11 @@
using namespace clang;
using namespace CodeGen;
void CodeGen::addDefaultFunctionDefinitionAttributes(CodeGenModule &CGM,
llvm::AttrBuilder &attrs) {
CGM.addDefaultFunctionDefinitionAttributes(attrs);
}
const CGFunctionInfo &
CodeGen::arrangeObjCMessageSendSignature(CodeGenModule &CGM,
const ObjCMethodDecl *MD,

View File

@ -246,7 +246,7 @@ namespace clang {
for (auto &LM : LinkModules) {
if (LM.PropagateAttrs)
for (Function &F : *LM.Module)
Gen->CGM().AddDefaultFnAttrs(F);
Gen->CGM().addDefaultFunctionDefinitionAttributes(F);
CurLinkModule = LM.Module.get();

View File

@ -1170,7 +1170,11 @@ public:
/// on the function more conservative. But it's unsafe to call this on a
/// function which relies on particular fast-math attributes for correctness.
/// It's up to you to ensure that this is safe.
void AddDefaultFnAttrs(llvm::Function &F);
void addDefaultFunctionDefinitionAttributes(llvm::Function &F);
/// Like the overload taking a `Function &`, but intended specifically
/// for frontends that want to build on Clang's target-configuration logic.
void addDefaultFunctionDefinitionAttributes(llvm::AttrBuilder &attrs);
StringRef getMangledName(GlobalDecl GD);
StringRef getBlockMangledName(GlobalDecl GD, const BlockDecl *BD);
@ -1532,11 +1536,12 @@ private:
/// function.
void SimplifyPersonality();
/// Helper function for ConstructAttributeList and AddDefaultFnAttrs.
/// Constructs an AttrList for a function with the given properties.
void ConstructDefaultFnAttrList(StringRef Name, bool HasOptnone,
bool AttrOnCallSite,
llvm::AttrBuilder &FuncAttrs);
/// Helper function for ConstructAttributeList and
/// addDefaultFunctionDefinitionAttributes. Builds a set of function
/// attributes to add to a function with the given properties.
void getDefaultFunctionAttributes(StringRef Name, bool HasOptnone,
bool AttrOnCallSite,
llvm::AttrBuilder &FuncAttrs);
llvm::Metadata *CreateMetadataIdentifierImpl(QualType T, MetadataTypeMap &Map,
StringRef Suffix);