Skip to content
Open
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
7 changes: 7 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1984,6 +1984,13 @@ def Flatten : InheritableAttr {
let SimpleHandler = 1;
}

def FlattenDeep : InheritableAttr {
let Spellings = [Clang<"flatten_deep">];
let Subjects = SubjectList<[Function], ErrorDiag>;
let Args = [UnsignedArgument<"MaxDepth">];
let Documentation = [FlattenDeepDocs];
}

def Format : InheritableAttr {
let Spellings = [GCC<"format">];
let Args = [IdentifierArgument<"Type">, IntArgument<"FormatIdx">,
Expand Down
23 changes: 23 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -4032,6 +4032,29 @@ callee is unavailable or if the callee has the ``noinline`` attribute.
}];
}

def FlattenDeepDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
The ``flatten_deep`` attribute causes calls within the attributed function and
their transitive callees to be inlined up to a specified depth, unless it is
impossible to do so (for example if the body of the callee is unavailable or if
the callee has the ``noinline`` attribute).

This attribute takes a single unsigned integer argument representing the maximum
depth of the call tree to inline. For example, ``__attribute__((flatten_deep(3)))``
will inline all calls within the function, then inline all calls within those
inlined functions (depth 2), and then inline all calls within those functions
(depth 3).

.. code-block:: c++

__attribute__((flatten_deep(3)))
void process_data() {
// All calls up to 3 levels deep in the call tree will be inlined
}
}];
}

def FormatDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2749,6 +2749,15 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
B.addAttribute("aarch64_new_zt0");
}

// Handle flatten_deep attribute for depth-based inlining
if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
if (const FlattenDeepAttr *FDA = FD->getAttr<FlattenDeepAttr>()) {
// Add the flatten_deep attribute with the max depth value as a typed int
// attribute
B.addRawIntAttr(llvm::Attribute::FlattenDeep, FDA->getMaxDepth());
}
}

// Track whether we need to add the optnone LLVM attribute,
// starting with the default for this optimization level.
bool ShouldAddOptNone =
Expand Down
21 changes: 21 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3695,6 +3695,24 @@ static void handleInitPriorityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(::new (S.Context) InitPriorityAttr(S.Context, AL, prioritynum));
}

static void handleFlattenDeepAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
Expr *E = AL.getArgAsExpr(0);
uint32_t maxDepth;
if (!S.checkUInt32Argument(AL, E, maxDepth)) {
AL.setInvalid();
return;
}

if (maxDepth == 0) {
S.Diag(AL.getLoc(), diag::err_attribute_argument_is_zero)
<< AL << E->getSourceRange();
AL.setInvalid();
return;
}

D->addAttr(::new (S.Context) FlattenDeepAttr(S.Context, AL, maxDepth));
}

ErrorAttr *Sema::mergeErrorAttr(Decl *D, const AttributeCommonInfo &CI,
StringRef NewUserDiagnostic) {
if (const auto *EA = D->getAttr<ErrorAttr>()) {
Expand Down Expand Up @@ -7236,6 +7254,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_Format:
handleFormatAttr(S, D, AL);
break;
case ParsedAttr::AT_FlattenDeep:
handleFlattenDeepAttr(S, D, AL);
break;
case ParsedAttr::AT_FormatMatches:
handleFormatMatchesAttr(S, D, AL);
break;
Expand Down
10 changes: 10 additions & 0 deletions clang/test/CodeGen/attr-flatten-deep-cg.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s

// CHECK-LABEL: define {{.*}} @test1
// CHECK-SAME: #[[ATTR1:[0-9]+]]
__attribute__((flatten_deep(3)))
void test1() {
}

// Verify the attribute is present in the attribute groups
// CHECK-DAG: attributes #[[ATTR1]] = { {{.*}}flatten_deep=3{{.*}} }
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
// CHECK-NEXT: ExternalSourceSymbol ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable))
// CHECK-NEXT: FlagEnum (SubjectMatchRule_enum)
// CHECK-NEXT: Flatten (SubjectMatchRule_function)
// CHECK-NEXT: FlattenDeep (SubjectMatchRule_function)
// CHECK-NEXT: FunctionReturnThunks (SubjectMatchRule_function)
// CHECK-NEXT: GNUInline (SubjectMatchRule_function)
// CHECK-NEXT: HIPManaged (SubjectMatchRule_variable)
Expand Down
14 changes: 14 additions & 0 deletions clang/test/Sema/attr-flatten-deep.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s

// Test basic usage - valid
__attribute__((flatten_deep(3)))
void test_valid() {
}

// Test attribute on non-function - should error
__attribute__((flatten_deep(3))) int x; // expected-error {{'flatten_deep' attribute only applies to functions}}

// Test depth = 0 - should error (depth must be >= 1)
__attribute__((flatten_deep(0))) // expected-error {{'flatten_deep' attribute must be greater than 0}}
void test_depth_zero() {
}
1 change: 1 addition & 0 deletions llvm/include/llvm/AsmParser/LLParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ namespace llvm {
bool AllowParens = false);
bool parseOptionalCodeModel(CodeModel::Model &model);
bool parseOptionalDerefAttrBytes(lltok::Kind AttrKind, uint64_t &Bytes);
bool parseOptionalFlattenDeepDepth(lltok::Kind AttrKind, uint64_t &Depth);
bool parseOptionalUWTableKind(UWTableKind &Kind);
bool parseAllocKind(AllocFnKind &Kind);
std::optional<MemoryEffects> parseMemoryAttr();
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/Bitcode/LLVMBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,7 @@ enum AttributeKindCodes {
ATTR_KIND_CAPTURES = 102,
ATTR_KIND_DEAD_ON_RETURN = 103,
ATTR_KIND_SANITIZE_ALLOC_TOKEN = 104,
ATTR_KIND_FLATTEN_DEEP = 105,
};

enum ComdatSelectionKindCodes {
Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/IR/Attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1246,6 +1246,10 @@ class AttrBuilder {
/// form used internally in Attribute.
LLVM_ABI AttrBuilder &addDereferenceableOrNullAttr(uint64_t Bytes);

/// This turns the flatten_deep depth into the form used internally in
/// Attribute.
LLVM_ABI AttrBuilder &addFlattenDeepAttr(uint64_t Depth);

/// This turns one (or two) ints into the form used internally in Attribute.
LLVM_ABI AttrBuilder &
addAllocSizeAttr(unsigned ElemSizeArg,
Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/IR/Attributes.td
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ def NoImplicitFloat : EnumAttr<"noimplicitfloat", IntersectPreserve, [FnAttr]>;
/// inline=never.
def NoInline : EnumAttr<"noinline", IntersectPreserve, [FnAttr]>;

/// Inline calls recursively up to specified depth.
def FlattenDeep : IntAttr<"flatten_deep", IntersectPreserve, [FnAttr]>;

/// Function is called early and/or often, so lazy binding isn't worthwhile.
def NonLazyBind : EnumAttr<"nonlazybind", IntersectPreserve, [FnAttr]>;

Expand Down
40 changes: 40 additions & 0 deletions llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1620,6 +1620,22 @@ bool LLParser::parseEnumAttribute(Attribute::AttrKind Attr, AttrBuilder &B,
B.addDereferenceableOrNullAttr(Bytes);
return false;
}
case Attribute::FlattenDeep: {
uint64_t Depth = 0;
if (InAttrGroup) {
Lex.Lex();
LocTy DepthLoc = Lex.getLoc();
if (parseToken(lltok::equal, "expected '=' here") || parseUInt64(Depth))
return true;
if (!Depth)
return error(DepthLoc, "flatten_deep depth must be non-zero");
} else {
if (parseOptionalFlattenDeepDepth(lltok::kw_flatten_deep, Depth))
return true;
}
B.addFlattenDeepAttr(Depth);
return false;
}
case Attribute::UWTable: {
UWTableKind Kind;
if (parseOptionalUWTableKind(Kind))
Expand Down Expand Up @@ -2494,6 +2510,30 @@ bool LLParser::parseOptionalDerefAttrBytes(lltok::Kind AttrKind,
return false;
}

/// parseOptionalFlattenDeepDepth
/// ::= /* empty */
/// ::= 'flatten_deep' '(' 4 ')'
bool LLParser::parseOptionalFlattenDeepDepth(lltok::Kind AttrKind,
uint64_t &Depth) {
assert(AttrKind == lltok::kw_flatten_deep && "contract!");

Depth = 0;
if (!EatIfPresent(AttrKind))
return false;
LocTy ParenLoc = Lex.getLoc();
if (!EatIfPresent(lltok::lparen))
return error(ParenLoc, "expected '('");
LocTy DepthLoc = Lex.getLoc();
if (parseUInt64(Depth))
return true;
ParenLoc = Lex.getLoc();
if (!EatIfPresent(lltok::rparen))
return error(ParenLoc, "expected ')'");
if (!Depth)
return error(DepthLoc, "flatten_deep depth must be non-zero");
return false;
}

bool LLParser::parseOptionalUWTableKind(UWTableKind &Kind) {
Lex.Lex();
Kind = UWTableKind::Default;
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2077,6 +2077,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
return Attribute::DisableSanitizerInstrumentation;
case bitc::ATTR_KIND_ELEMENTTYPE:
return Attribute::ElementType;
case bitc::ATTR_KIND_FLATTEN_DEEP:
return Attribute::FlattenDeep;
case bitc::ATTR_KIND_FNRETTHUNK_EXTERN:
return Attribute::FnRetThunkExtern;
case bitc::ATTR_KIND_INLINE_HINT:
Expand Down Expand Up @@ -2391,6 +2393,8 @@ Error BitcodeReader::parseAttributeGroupBlock() {
B.addDereferenceableAttr(Record[++i]);
else if (Kind == Attribute::DereferenceableOrNull)
B.addDereferenceableOrNullAttr(Record[++i]);
else if (Kind == Attribute::FlattenDeep)
B.addFlattenDeepAttr(Record[++i]);
else if (Kind == Attribute::AllocSize)
B.addAllocSizeAttrFromRawRepr(Record[++i]);
else if (Kind == Attribute::VScaleRange)
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
return bitc::ATTR_KIND_CAPTURES;
case Attribute::DeadOnReturn:
return bitc::ATTR_KIND_DEAD_ON_RETURN;
case Attribute::FlattenDeep:
return bitc::ATTR_KIND_FLATTEN_DEEP;
case Attribute::EndAttrKinds:
llvm_unreachable("Can not encode end-attribute kinds marker.");
case Attribute::None:
Expand Down
10 changes: 10 additions & 0 deletions llvm/lib/IR/Attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,9 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
if (hasAttribute(Attribute::DereferenceableOrNull))
return AttrWithBytesToString("dereferenceable_or_null");

if (hasAttribute(Attribute::FlattenDeep))
return AttrWithBytesToString("flatten_deep");

if (hasAttribute(Attribute::AllocSize)) {
unsigned ElemSize;
std::optional<unsigned> NumElems;
Expand Down Expand Up @@ -2206,6 +2209,13 @@ AttrBuilder &AttrBuilder::addDereferenceableOrNullAttr(uint64_t Bytes) {
return addRawIntAttr(Attribute::DereferenceableOrNull, Bytes);
}

AttrBuilder &AttrBuilder::addFlattenDeepAttr(uint64_t Depth) {
if (Depth == 0)
return *this;

return addRawIntAttr(Attribute::FlattenDeep, Depth);
}

AttrBuilder &
AttrBuilder::addAllocSizeAttr(unsigned ElemSize,
const std::optional<unsigned> &NumElems) {
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Transforms/Utils/CodeExtractor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,7 @@ Function *CodeExtractor::constructFunctionDeclaration(
continue;
// Those attributes should be safe to propagate to the extracted function.
case Attribute::AlwaysInline:
case Attribute::FlattenDeep:
case Attribute::Cold:
case Attribute::DisableSanitizerInstrumentation:
case Attribute::FnRetThunkExtern:
Expand Down
32 changes: 32 additions & 0 deletions llvm/test/Bitcode/attributes-flatten-deep.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
; RUN: llvm-as < %s | llvm-dis | FileCheck %s
; RUN: verify-uselistorder %s

; Test that flatten_deep attribute with integer values is properly handled
; in both attribute groups (flatten_deep=N syntax) and inline (flatten_deep(N) syntax)

; Test inline syntax
; CHECK: define void @test_inline() #0
define void @test_inline() flatten_deep(5) {
ret void
}

; Test attribute group alone
; CHECK: define void @test_group_alone() #1
define void @test_group_alone() #1 {
ret void
}

; Test attribute group with other attributes
; CHECK: define void @test_group_combined() #2
define void @test_group_combined() #2 {
ret void
}

; CHECK: attributes #0 = { flatten_deep=5 }
attributes #0 = { flatten_deep=5 }

; CHECK: attributes #1 = { flatten_deep=3 }
attributes #1 = { flatten_deep=3 }

; CHECK: attributes #2 = { noinline nounwind flatten_deep=7 }
attributes #2 = { noinline nounwind flatten_deep=7 }