Each lambda that can be converted to a plain function pointer has a thunk generated for it, which invokes the body of the lambda function.
When a section attribute is added to a lambda function, it only applies to the body of the lambda function, and not the thunk. When a lambda is only ever used by converting it to a function pointer, the body of the lambda is inlined into this thunk. As a result, the section attribute is effectively ignored: the function it applied to is gone, and the thunk does not have the section attribute applied to it either. This patch checks if a section attribute is present on a lambda, and applies it to the thunk. The motivation for this change is embedded devices where most code is executed from flash, but code which must execute while the device is being reprogrammed can be moved to RAM by placing it in a different section. This patch was tested with bootstrapping on x86-64 under WSL, and the newly added test was also run on 32-bit ARM. gcc/cp/ChangeLog: * lambda.cc (maybe_add_lambda_conv_op): Don't ignore section attributes on lambda functions which are converted to plain function pointers. gcc/testsuite/ChangeLog: * g++.dg/ext/attr-section-lambda.C: New test. Signed-off-by: Campbell Suter <z...@znix.xyz> --- gcc/cp/lambda.cc | 7 ++++ .../g++.dg/ext/attr-section-lambda.C | 42 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 gcc/testsuite/g++.dg/ext/attr-section-lambda.C diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc index d8a15d97d..e46ecbdf2 100644 --- a/gcc/cp/lambda.cc +++ b/gcc/cp/lambda.cc @@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see #include "gimplify.h" #include "target.h" #include "decl.h" +#include "attribs.h" /* Constructor for a lambda expression. */ @@ -1376,6 +1377,12 @@ maybe_add_lambda_conv_op (tree type) if (generic_lambda_p) fn = add_inherited_template_parms (fn, DECL_TI_TEMPLATE (callop)); + if (tree a = lookup_attribute ("section", DECL_ATTRIBUTES (callop))) + { + DECL_ATTRIBUTES (fn) = attr_chainon (DECL_ATTRIBUTES (fn), a); + set_decl_section_name (fn, callop); + } + if (flag_sanitize & SANITIZE_NULL) /* Don't UBsan this function; we're deliberately calling op() with a null object argument. */ diff --git a/gcc/testsuite/g++.dg/ext/attr-section-lambda.C b/gcc/testsuite/g++.dg/ext/attr-section-lambda.C new file mode 100644 index 000000000..4202ea943 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attr-section-lambda.C @@ -0,0 +1,42 @@ +// Make sure that attributes apply to lambda functions properly, and aren't +// broken by inlining with their static thunks. +// +// { dg-do compile { target { c++11 && named_sections } } } +// { dg-options "-O2" } + + +extern int i; + +void func_ptr_consumer (int (*)()); + +void __attribute__((section(".outer_regular"))) +test_func_regular () +{ + func_ptr_consumer ([]() __attribute__((section(".lambda_regular"))) { + return i; + }); +} + +template<typename T> +void __attribute__((section(".outer_template"))) __attribute__((noinline)) +test_func_generic () +{ + func_ptr_consumer ([]() __attribute__((section(".lambda_template"))) { + return i + T::a; + }); +} + +struct +A +{ + static constexpr int a = 0xaabbccdd; +}; + +template void test_func_generic<A>(); + +// Since we've enabled optimisations, the _FUN thunk will have absorbed the +// lambda bodies due to inlining. +// Thus if these sections exist, they must be from the thunk - and thus the +// thunk has it's section set. +// { dg-final { scan-assembler {\.(section|csect)[ \t]+"?\.lambda_regular} } } +// { dg-final { scan-assembler {\.(section|csect)[ \t]+"?\.lambda_template} } } -- 2.34.1