Loading clang/docs/ReleaseNotes.rst +0 −10 Original line number Diff line number Diff line Loading @@ -311,16 +311,6 @@ Attribute Changes in Clang foreign language personality with a given function. Note that this does not perform any ABI validation for the personality routine. - The ``__attribute__((flatten))`` attribute behavior has changed to match GCC. Previously, Clang only inlined direct callees of the attributed function. Now, all calls are inlined transitively, including calls introduced by inlining. Calls that cannot be inlined are left as-is: this includes callees marked ``noinline``, callees with incompatible ABI attributes (e.g. SME), callees without a visible definition, and recursive calls where a function already appears in the inlining chain. Flatten also works across ThinLTO module boundaries when callee definitions are available. - The :doc:`ThreadSafetyAnalysis` attributes ``guarded_by`` and ``pt_guarded_by`` now accept multiple capability arguments with refined access semantics: *writing* requires all listed capabilities to be held Loading clang/lib/CodeGen/CGCall.cpp +11 −0 Original line number Diff line number Diff line Loading @@ -5992,6 +5992,17 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, // Apply some call-site-specific attributes. // TODO: work this into building the attribute set. // Apply always_inline to all calls within flatten functions. // FIXME: should this really take priority over __try, below? if (CurCodeDecl && CurCodeDecl->hasAttr<FlattenAttr>() && !InNoInlineAttributedStmt && !(TargetDecl && TargetDecl->hasAttr<NoInlineAttr>()) && !CGM.getTargetCodeGenInfo().wouldInliningViolateFunctionCallABI( CallerDecl, CalleeDecl)) { Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::AlwaysInline); } // Disable inlining inside SEH __try blocks. if (isSEHTryScope()) { Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::NoInline); Loading clang/lib/CodeGen/CodeGenModule.cpp +0 −3 Original line number Diff line number Diff line Loading @@ -2975,9 +2975,6 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, if (CodeGenOpts.DisableOutlining || D->hasAttr<NoOutlineAttr>()) B.addAttribute(llvm::Attribute::NoOutline); if (D->hasAttr<FlattenAttr>()) B.addAttribute(llvm::Attribute::Flatten); F->addFnAttrs(B); llvm::MaybeAlign ExplicitAlignment; Loading clang/test/CodeGen/AArch64/sme-inline-callees-streaming-attrs.c +34 −81 Original line number Diff line number Diff line // RUN: %clang_cc1 -triple aarch64-none-linux-gnu -emit-llvm -target-feature +sme -target-feature +sme2 %s -DUSE_FLATTEN -o - | FileCheck %s --check-prefix=CHECK-FLATTEN // RUN: %clang_cc1 -triple aarch64-none-linux-gnu -emit-llvm -target-feature +sme -target-feature +sme2 %s -DUSE_ALWAYS_INLINE_STMT -o - | FileCheck %s --check-prefix=CHECK-ALWAYS-INLINE // RUN: %clang_cc1 -triple aarch64-none-linux-gnu -emit-llvm -target-feature +sme -target-feature +sme2 %s -DUSE_FLATTEN -o - | FileCheck %s // RUN: %clang_cc1 -triple aarch64-none-linux-gnu -emit-llvm -target-feature +sme -target-feature +sme2 %s -DUSE_ALWAYS_INLINE_STMT -o - | FileCheck %s // REQUIRES: aarch64-registered-target Loading Loading @@ -31,26 +31,14 @@ void caller(void) { STMT_ATTR fn_streaming_new_za(); STMT_ATTR fn_streaming_new_zt0(); } // For flatten: fn() and fn_streaming_compatible() are inlined, streaming functions // are blocked by TTI (non-streaming caller), new_za/new_zt0 are always blocked. // CHECK-FLATTEN-LABEL: void @caller() // CHECK-FLATTEN-NEXT: entry: // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @fn_streaming // CHECK-FLATTEN-NEXT: call void @fn_locally_streaming // CHECK-FLATTEN-NEXT: call void @fn_streaming_new_za // CHECK-FLATTEN-NEXT: call void @fn_streaming_new_zt0 // For always_inline: Clang's wouldInliningViolateFunctionCallABI controls. // CHECK-ALWAYS-INLINE-LABEL: void @caller() // CHECK-ALWAYS-INLINE-NEXT: entry: // CHECK-ALWAYS-INLINE-NEXT: call void @was_inlined // CHECK-ALWAYS-INLINE-NEXT: call void @was_inlined // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming // CHECK-ALWAYS-INLINE-NEXT: call void @fn_locally_streaming // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming_new_za // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming_new_zt0 // CHECK-LABEL: void @caller() // CHECK-NEXT: entry: // CHECK-NEXT: call void @was_inlined // CHECK-NEXT: call void @was_inlined // CHECK-NEXT: call void @fn_streaming // CHECK-NEXT: call void @fn_locally_streaming // CHECK-NEXT: call void @fn_streaming_new_za // CHECK-NEXT: call void @fn_streaming_new_zt0 FN_ATTR void caller_streaming_compatible(void) __arm_streaming_compatible { STMT_ATTR fn(); Loading @@ -60,26 +48,14 @@ FN_ATTR void caller_streaming_compatible(void) __arm_streaming_compatible { STMT_ATTR fn_streaming_new_za(); STMT_ATTR fn_streaming_new_zt0(); } // For flatten: TTI allows inlining fn(), fn_streaming_compatible(), fn_streaming(), // fn_locally_streaming() because they don't have incompatible ops. Only new_za/new_zt0 blocked. // CHECK-FLATTEN-LABEL: void @caller_streaming_compatible() // CHECK-FLATTEN-NEXT: entry: // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @fn_streaming_new_za // CHECK-FLATTEN-NEXT: call void @fn_streaming_new_zt0 // For always_inline: Clang blocks fn() (streaming-compatible caller, non-streaming callee). // CHECK-ALWAYS-INLINE-LABEL: void @caller_streaming_compatible() // CHECK-ALWAYS-INLINE-NEXT: entry: // CHECK-ALWAYS-INLINE-NEXT: call void @fn // CHECK-ALWAYS-INLINE-NEXT: call void @was_inlined // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming // CHECK-ALWAYS-INLINE-NEXT: call void @fn_locally_streaming // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming_new_za // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming_new_zt0 // CHECK-LABEL: void @caller_streaming_compatible() // CHECK-NEXT: entry: // CHECK-NEXT: call void @fn // CHECK-NEXT: call void @was_inlined // CHECK-NEXT: call void @fn_streaming // CHECK-NEXT: call void @fn_locally_streaming // CHECK-NEXT: call void @fn_streaming_new_za // CHECK-NEXT: call void @fn_streaming_new_zt0 FN_ATTR void caller_streaming(void) __arm_streaming { STMT_ATTR fn(); Loading @@ -89,26 +65,14 @@ FN_ATTR void caller_streaming(void) __arm_streaming { STMT_ATTR fn_streaming_new_za(); STMT_ATTR fn_streaming_new_zt0(); } // For flatten: TTI allows all except new_za/new_zt0. fn() is inlined because // streaming caller can execute non-streaming callee's code (no incompatible ops). // CHECK-FLATTEN-LABEL: void @caller_streaming() // CHECK-FLATTEN-NEXT: entry: // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @fn_streaming_new_za // CHECK-FLATTEN-NEXT: call void @fn_streaming_new_zt0 // For always_inline: Clang blocks fn() (streaming caller, non-streaming callee). // CHECK-ALWAYS-INLINE-LABEL: void @caller_streaming() // CHECK-ALWAYS-INLINE-NEXT: entry: // CHECK-ALWAYS-INLINE-NEXT: call void @fn // CHECK-ALWAYS-INLINE-NEXT: call void @was_inlined // CHECK-ALWAYS-INLINE-NEXT: call void @was_inlined // CHECK-ALWAYS-INLINE-NEXT: call void @was_inlined // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming_new_za // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming_new_zt0 // CHECK-LABEL: void @caller_streaming() // CHECK-NEXT: entry: // CHECK-NEXT: call void @fn // CHECK-NEXT: call void @was_inlined // CHECK-NEXT: call void @was_inlined // CHECK-NEXT: call void @was_inlined // CHECK-NEXT: call void @fn_streaming_new_za // CHECK-NEXT: call void @fn_streaming_new_zt0 FN_ATTR __arm_locally_streaming void caller_locally_streaming(void) { Loading @@ -119,22 +83,11 @@ void caller_locally_streaming(void) { STMT_ATTR fn_streaming_new_za(); STMT_ATTR fn_streaming_new_zt0(); } // For flatten: Similar to caller_streaming - TTI allows all except new_za/new_zt0. // CHECK-FLATTEN-LABEL: void @caller_locally_streaming() // CHECK-FLATTEN-NEXT: entry: // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @fn_streaming_new_za // CHECK-FLATTEN-NEXT: call void @fn_streaming_new_zt0 // For always_inline: Clang blocks fn(). // CHECK-ALWAYS-INLINE-LABEL: void @caller_locally_streaming() // CHECK-ALWAYS-INLINE-NEXT: entry: // CHECK-ALWAYS-INLINE-NEXT: call void @fn // CHECK-ALWAYS-INLINE-NEXT: call void @was_inlined // CHECK-ALWAYS-INLINE-NEXT: call void @was_inlined // CHECK-ALWAYS-INLINE-NEXT: call void @was_inlined // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming_new_za // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming_new_zt0 // CHECK-LABEL: void @caller_locally_streaming() // CHECK-NEXT: entry: // CHECK-NEXT: call void @fn // CHECK-NEXT: call void @was_inlined // CHECK-NEXT: call void @was_inlined // CHECK-NEXT: call void @was_inlined // CHECK-NEXT: call void @fn_streaming_new_za // CHECK-NEXT: call void @fn_streaming_new_zt0 clang/test/CodeGen/flatten.c +10 −18 Original line number Diff line number Diff line // RUN: %clang_cc1 -triple=x86_64-linux-gnu %s -emit-llvm -o - | FileCheck %s // External functions to provide side effects that prevent trivial elimination. void external_f(void); void external_h(void); void f(void) {} void f(void) { external_f(); } __attribute__((noinline)) void ni(void) {} void h(void) { external_h(); f(); } // CHECK-LABEL: define{{.*}} void @g() // CHECK-SAME: [[FLATTEN_ATTR:#[0-9]+]] __attribute__((flatten)) // CHECK: define{{.*}} void @g() void g(void) { // Flatten recursively inlines: g -> h -> f, so neither call remains. // Only the leaf external() call should survive. // CHECK-NOT: call {{.*}} @h // CHECK-NOT: call {{.*}} @f // CHECK: call {{.*}} @external_h // CHECK: call {{.*}} @external_f h(); f(); // CHECK: call {{.*}} @ni ni(); } // CHECK: attributes [[FLATTEN_ATTR]] = {{{.*}}flatten{{.*}}} void h(void) { // CHECK: call {{.*}} @f f(); } Loading
clang/docs/ReleaseNotes.rst +0 −10 Original line number Diff line number Diff line Loading @@ -311,16 +311,6 @@ Attribute Changes in Clang foreign language personality with a given function. Note that this does not perform any ABI validation for the personality routine. - The ``__attribute__((flatten))`` attribute behavior has changed to match GCC. Previously, Clang only inlined direct callees of the attributed function. Now, all calls are inlined transitively, including calls introduced by inlining. Calls that cannot be inlined are left as-is: this includes callees marked ``noinline``, callees with incompatible ABI attributes (e.g. SME), callees without a visible definition, and recursive calls where a function already appears in the inlining chain. Flatten also works across ThinLTO module boundaries when callee definitions are available. - The :doc:`ThreadSafetyAnalysis` attributes ``guarded_by`` and ``pt_guarded_by`` now accept multiple capability arguments with refined access semantics: *writing* requires all listed capabilities to be held Loading
clang/lib/CodeGen/CGCall.cpp +11 −0 Original line number Diff line number Diff line Loading @@ -5992,6 +5992,17 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, // Apply some call-site-specific attributes. // TODO: work this into building the attribute set. // Apply always_inline to all calls within flatten functions. // FIXME: should this really take priority over __try, below? if (CurCodeDecl && CurCodeDecl->hasAttr<FlattenAttr>() && !InNoInlineAttributedStmt && !(TargetDecl && TargetDecl->hasAttr<NoInlineAttr>()) && !CGM.getTargetCodeGenInfo().wouldInliningViolateFunctionCallABI( CallerDecl, CalleeDecl)) { Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::AlwaysInline); } // Disable inlining inside SEH __try blocks. if (isSEHTryScope()) { Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::NoInline); Loading
clang/lib/CodeGen/CodeGenModule.cpp +0 −3 Original line number Diff line number Diff line Loading @@ -2975,9 +2975,6 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, if (CodeGenOpts.DisableOutlining || D->hasAttr<NoOutlineAttr>()) B.addAttribute(llvm::Attribute::NoOutline); if (D->hasAttr<FlattenAttr>()) B.addAttribute(llvm::Attribute::Flatten); F->addFnAttrs(B); llvm::MaybeAlign ExplicitAlignment; Loading
clang/test/CodeGen/AArch64/sme-inline-callees-streaming-attrs.c +34 −81 Original line number Diff line number Diff line // RUN: %clang_cc1 -triple aarch64-none-linux-gnu -emit-llvm -target-feature +sme -target-feature +sme2 %s -DUSE_FLATTEN -o - | FileCheck %s --check-prefix=CHECK-FLATTEN // RUN: %clang_cc1 -triple aarch64-none-linux-gnu -emit-llvm -target-feature +sme -target-feature +sme2 %s -DUSE_ALWAYS_INLINE_STMT -o - | FileCheck %s --check-prefix=CHECK-ALWAYS-INLINE // RUN: %clang_cc1 -triple aarch64-none-linux-gnu -emit-llvm -target-feature +sme -target-feature +sme2 %s -DUSE_FLATTEN -o - | FileCheck %s // RUN: %clang_cc1 -triple aarch64-none-linux-gnu -emit-llvm -target-feature +sme -target-feature +sme2 %s -DUSE_ALWAYS_INLINE_STMT -o - | FileCheck %s // REQUIRES: aarch64-registered-target Loading Loading @@ -31,26 +31,14 @@ void caller(void) { STMT_ATTR fn_streaming_new_za(); STMT_ATTR fn_streaming_new_zt0(); } // For flatten: fn() and fn_streaming_compatible() are inlined, streaming functions // are blocked by TTI (non-streaming caller), new_za/new_zt0 are always blocked. // CHECK-FLATTEN-LABEL: void @caller() // CHECK-FLATTEN-NEXT: entry: // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @fn_streaming // CHECK-FLATTEN-NEXT: call void @fn_locally_streaming // CHECK-FLATTEN-NEXT: call void @fn_streaming_new_za // CHECK-FLATTEN-NEXT: call void @fn_streaming_new_zt0 // For always_inline: Clang's wouldInliningViolateFunctionCallABI controls. // CHECK-ALWAYS-INLINE-LABEL: void @caller() // CHECK-ALWAYS-INLINE-NEXT: entry: // CHECK-ALWAYS-INLINE-NEXT: call void @was_inlined // CHECK-ALWAYS-INLINE-NEXT: call void @was_inlined // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming // CHECK-ALWAYS-INLINE-NEXT: call void @fn_locally_streaming // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming_new_za // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming_new_zt0 // CHECK-LABEL: void @caller() // CHECK-NEXT: entry: // CHECK-NEXT: call void @was_inlined // CHECK-NEXT: call void @was_inlined // CHECK-NEXT: call void @fn_streaming // CHECK-NEXT: call void @fn_locally_streaming // CHECK-NEXT: call void @fn_streaming_new_za // CHECK-NEXT: call void @fn_streaming_new_zt0 FN_ATTR void caller_streaming_compatible(void) __arm_streaming_compatible { STMT_ATTR fn(); Loading @@ -60,26 +48,14 @@ FN_ATTR void caller_streaming_compatible(void) __arm_streaming_compatible { STMT_ATTR fn_streaming_new_za(); STMT_ATTR fn_streaming_new_zt0(); } // For flatten: TTI allows inlining fn(), fn_streaming_compatible(), fn_streaming(), // fn_locally_streaming() because they don't have incompatible ops. Only new_za/new_zt0 blocked. // CHECK-FLATTEN-LABEL: void @caller_streaming_compatible() // CHECK-FLATTEN-NEXT: entry: // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @fn_streaming_new_za // CHECK-FLATTEN-NEXT: call void @fn_streaming_new_zt0 // For always_inline: Clang blocks fn() (streaming-compatible caller, non-streaming callee). // CHECK-ALWAYS-INLINE-LABEL: void @caller_streaming_compatible() // CHECK-ALWAYS-INLINE-NEXT: entry: // CHECK-ALWAYS-INLINE-NEXT: call void @fn // CHECK-ALWAYS-INLINE-NEXT: call void @was_inlined // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming // CHECK-ALWAYS-INLINE-NEXT: call void @fn_locally_streaming // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming_new_za // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming_new_zt0 // CHECK-LABEL: void @caller_streaming_compatible() // CHECK-NEXT: entry: // CHECK-NEXT: call void @fn // CHECK-NEXT: call void @was_inlined // CHECK-NEXT: call void @fn_streaming // CHECK-NEXT: call void @fn_locally_streaming // CHECK-NEXT: call void @fn_streaming_new_za // CHECK-NEXT: call void @fn_streaming_new_zt0 FN_ATTR void caller_streaming(void) __arm_streaming { STMT_ATTR fn(); Loading @@ -89,26 +65,14 @@ FN_ATTR void caller_streaming(void) __arm_streaming { STMT_ATTR fn_streaming_new_za(); STMT_ATTR fn_streaming_new_zt0(); } // For flatten: TTI allows all except new_za/new_zt0. fn() is inlined because // streaming caller can execute non-streaming callee's code (no incompatible ops). // CHECK-FLATTEN-LABEL: void @caller_streaming() // CHECK-FLATTEN-NEXT: entry: // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @fn_streaming_new_za // CHECK-FLATTEN-NEXT: call void @fn_streaming_new_zt0 // For always_inline: Clang blocks fn() (streaming caller, non-streaming callee). // CHECK-ALWAYS-INLINE-LABEL: void @caller_streaming() // CHECK-ALWAYS-INLINE-NEXT: entry: // CHECK-ALWAYS-INLINE-NEXT: call void @fn // CHECK-ALWAYS-INLINE-NEXT: call void @was_inlined // CHECK-ALWAYS-INLINE-NEXT: call void @was_inlined // CHECK-ALWAYS-INLINE-NEXT: call void @was_inlined // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming_new_za // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming_new_zt0 // CHECK-LABEL: void @caller_streaming() // CHECK-NEXT: entry: // CHECK-NEXT: call void @fn // CHECK-NEXT: call void @was_inlined // CHECK-NEXT: call void @was_inlined // CHECK-NEXT: call void @was_inlined // CHECK-NEXT: call void @fn_streaming_new_za // CHECK-NEXT: call void @fn_streaming_new_zt0 FN_ATTR __arm_locally_streaming void caller_locally_streaming(void) { Loading @@ -119,22 +83,11 @@ void caller_locally_streaming(void) { STMT_ATTR fn_streaming_new_za(); STMT_ATTR fn_streaming_new_zt0(); } // For flatten: Similar to caller_streaming - TTI allows all except new_za/new_zt0. // CHECK-FLATTEN-LABEL: void @caller_locally_streaming() // CHECK-FLATTEN-NEXT: entry: // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @was_inlined // CHECK-FLATTEN-NEXT: call void @fn_streaming_new_za // CHECK-FLATTEN-NEXT: call void @fn_streaming_new_zt0 // For always_inline: Clang blocks fn(). // CHECK-ALWAYS-INLINE-LABEL: void @caller_locally_streaming() // CHECK-ALWAYS-INLINE-NEXT: entry: // CHECK-ALWAYS-INLINE-NEXT: call void @fn // CHECK-ALWAYS-INLINE-NEXT: call void @was_inlined // CHECK-ALWAYS-INLINE-NEXT: call void @was_inlined // CHECK-ALWAYS-INLINE-NEXT: call void @was_inlined // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming_new_za // CHECK-ALWAYS-INLINE-NEXT: call void @fn_streaming_new_zt0 // CHECK-LABEL: void @caller_locally_streaming() // CHECK-NEXT: entry: // CHECK-NEXT: call void @fn // CHECK-NEXT: call void @was_inlined // CHECK-NEXT: call void @was_inlined // CHECK-NEXT: call void @was_inlined // CHECK-NEXT: call void @fn_streaming_new_za // CHECK-NEXT: call void @fn_streaming_new_zt0
clang/test/CodeGen/flatten.c +10 −18 Original line number Diff line number Diff line // RUN: %clang_cc1 -triple=x86_64-linux-gnu %s -emit-llvm -o - | FileCheck %s // External functions to provide side effects that prevent trivial elimination. void external_f(void); void external_h(void); void f(void) {} void f(void) { external_f(); } __attribute__((noinline)) void ni(void) {} void h(void) { external_h(); f(); } // CHECK-LABEL: define{{.*}} void @g() // CHECK-SAME: [[FLATTEN_ATTR:#[0-9]+]] __attribute__((flatten)) // CHECK: define{{.*}} void @g() void g(void) { // Flatten recursively inlines: g -> h -> f, so neither call remains. // Only the leaf external() call should survive. // CHECK-NOT: call {{.*}} @h // CHECK-NOT: call {{.*}} @f // CHECK: call {{.*}} @external_h // CHECK: call {{.*}} @external_f h(); f(); // CHECK: call {{.*}} @ni ni(); } // CHECK: attributes [[FLATTEN_ATTR]] = {{{.*}}flatten{{.*}}} void h(void) { // CHECK: call {{.*}} @f f(); }