Skip to content

[DNM] Expose IRGen API to add the default IR attributes to a function definition #1247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions clang/include/clang/CodeGen/CodeGenABITypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "clang/CodeGen/CGFunctionInfo.h"

namespace llvm {
class AttrBuilder;
class Constant;
class DataLayout;
class Module;
Expand Down Expand Up @@ -102,6 +103,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);

/// Compute a stable hash of the given string.
///
/// The exact algorithm is the little-endian interpretation of the
Expand Down
140 changes: 92 additions & 48 deletions clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1702,8 +1702,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) {
Expand Down Expand Up @@ -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.PointerAuth.ReturnAddresses)
FuncAttrs.addAttribute("ptrauth-returns");
if (CodeGenOpts.PointerAuth.FunctionPointers)
Expand Down Expand Up @@ -1834,13 +1837,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) {
Expand Down Expand Up @@ -1877,26 +1888,47 @@ 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 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>())
Expand Down Expand Up @@ -1955,6 +1987,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:
Expand All @@ -1965,68 +2012,65 @@ 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 (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);
if (TargetDecl->hasAttr<NoSplitStackAttr>())
FuncAttrs.removeAttribute("split-stack");

// 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) {
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();
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/CodeGen/CodeGenABITypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,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,
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/CodeGenAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,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();

Expand Down
17 changes: 11 additions & 6 deletions clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -1219,7 +1219,11 @@ class CodeGenModule : public CodeGenTypeCache {
/// 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);
Expand Down Expand Up @@ -1574,11 +1578,12 @@ class CodeGenModule : public CodeGenTypeCache {

void destroyConstantSignedPointerCaches();

/// 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);
Expand Down