mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
Merge branch 'feature/lto-take-2' into 'master'
feat(build): add options to enable link-time optimization (LTO) Closes IDF-71 and IDFGH-17838 See merge request espressif/esp-idf!50002
This commit is contained in:
+37
-1
@@ -291,7 +291,22 @@ if(CONFIG_ESP_SYSTEM_USE_FRAME_POINTER)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
list(APPEND link_options "-fno-lto")
|
if(CONFIG_COMPILER_LTO_LINKTIME AND NOT BOOTLOADER_BUILD AND NOT ESP_TEE_BUILD)
|
||||||
|
list(APPEND link_options "-flto=auto")
|
||||||
|
|
||||||
|
if(CONFIG_APP_REPRODUCIBLE_BUILD)
|
||||||
|
# LTO generates code at link time, where the path remapping applied to
|
||||||
|
# compile_options doesn't take effect, so pass it to the linker as well.
|
||||||
|
# -save-temps keeps LTRANS objects out of $TMPDIR, and a pinned random
|
||||||
|
# seed makes LTO bytecode byte-identical. See the commit message for
|
||||||
|
# details.
|
||||||
|
list(APPEND link_options ${prefix_map_compile_options})
|
||||||
|
list(APPEND link_options "-save-temps")
|
||||||
|
list(APPEND compile_options "-frandom-seed=1")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
list(APPEND compile_options "-fno-lto")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(CONFIG_IDF_TARGET_LINUX AND CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
|
if(CONFIG_IDF_TARGET_LINUX AND CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
|
||||||
# Not all versions of the MacOS linker support the -warn_commons flag.
|
# Not all versions of the MacOS linker support the -warn_commons flag.
|
||||||
@@ -383,6 +398,27 @@ foreach(component_target ${build_component_targets})
|
|||||||
set(__idf_component_context 0)
|
set(__idf_component_context 0)
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
if(CONFIG_COMPILER_LTO_COMPILETIME AND NOT BOOTLOADER_BUILD AND NOT ESP_TEE_BUILD)
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/tools/cmake/lto.cmake")
|
||||||
|
idf_build_get_property(build_components BUILD_COMPONENTS)
|
||||||
|
# Components whose object code is placed by a linker fragment (their own or
|
||||||
|
# another component's) must not be compiled with LTO, otherwise the placement
|
||||||
|
# stops matching. See tools/cmake/lto.cmake for details.
|
||||||
|
__lto_collect_fragment_placed_components(lto_placed_components ${build_components})
|
||||||
|
# For each component, enable LTO unless it has its own linker fragments, is
|
||||||
|
# placed by some fragment, has opted out via NO_LTO, or is not a static library.
|
||||||
|
foreach(component_name ${build_components})
|
||||||
|
idf_component_get_property(ldfragment ${component_name} LDFRAGMENTS)
|
||||||
|
idf_component_get_property(no_lto ${component_name} NO_LTO)
|
||||||
|
idf_component_get_property(component_lib ${component_name} COMPONENT_LIB)
|
||||||
|
get_target_property(type ${component_lib} TYPE)
|
||||||
|
if(NOT ldfragment AND NOT no_lto AND NOT component_name IN_LIST lto_placed_components
|
||||||
|
AND type STREQUAL "STATIC_LIBRARY")
|
||||||
|
target_compile_options(${component_lib} PRIVATE -flto=auto)
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
# Run component validation checks after all components have been processed
|
# Run component validation checks after all components have been processed
|
||||||
# Only run validation for the main project, not subprojects like bootloader
|
# Only run validation for the main project, not subprojects like bootloader
|
||||||
idf_build_get_property(bootloader_build BOOTLOADER_BUILD)
|
idf_build_get_property(bootloader_build BOOTLOADER_BUILD)
|
||||||
|
|||||||
@@ -787,6 +787,47 @@ mainmenu "Espressif IoT Development Framework Configuration"
|
|||||||
default "gcc" if COMPILER_RT_LIB_GCCLIB
|
default "gcc" if COMPILER_RT_LIB_GCCLIB
|
||||||
default "" if COMPILER_RT_LIB_HOST
|
default "" if COMPILER_RT_LIB_HOST
|
||||||
|
|
||||||
|
config COMPILER_LTO_LINKTIME
|
||||||
|
bool "Enable LTO at link time"
|
||||||
|
default n
|
||||||
|
# LTO for host builds is probably not needed at all
|
||||||
|
# LTO with Clang needs LLD (IDF-8286)
|
||||||
|
# LTO works with APP_REPRODUCIBLE_BUILD (see top-level CMakeLists.txt)
|
||||||
|
depends on !IDF_TARGET_LINUX && !IDF_TOOLCHAIN_CLANG
|
||||||
|
help
|
||||||
|
If enabled, ESP-IDF build system will tell the linker to use LTO
|
||||||
|
(Link Time Optimization). This option may increase linking time but
|
||||||
|
will also produce smaller binaries.
|
||||||
|
Note that to see some code size reduction, you should also enable LTO
|
||||||
|
at compile time for some components.
|
||||||
|
|
||||||
|
You can do this either selectively for specific components, or use
|
||||||
|
the option CONFIG_COMPILER_LTO_COMPILETIME below to enable
|
||||||
|
it for most of the components by default.
|
||||||
|
|
||||||
|
Note that LTO can also increase task stack usage, because inlining
|
||||||
|
across translation units produces fewer but larger stack frames. After
|
||||||
|
enabling LTO, check the stack high water mark of your tasks and raise the
|
||||||
|
stack sizes of the affected tasks if necessary.
|
||||||
|
|
||||||
|
config COMPILER_LTO_COMPILETIME
|
||||||
|
bool "Enable LTO by default at compile time"
|
||||||
|
depends on COMPILER_LTO_LINKTIME
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
If enabled, ESP-IDF build system will compile most of the components with
|
||||||
|
LTO (link time optimizations) enabled.
|
||||||
|
|
||||||
|
Components which have link-time sections placement requirements will not
|
||||||
|
have LTO enabled. This includes components which specify linker fragment
|
||||||
|
files, as well as components whose object code is placed by a linker
|
||||||
|
fragment of another component.
|
||||||
|
|
||||||
|
Note that enabling LTO for all components does not always reduce the
|
||||||
|
binary size, and in some cases can increase it. For a more predictable
|
||||||
|
result, consider enabling LTO selectively for specific components by
|
||||||
|
adding the -flto=auto compile option to them.
|
||||||
|
|
||||||
choice COMPILER_ORPHAN_SECTIONS
|
choice COMPILER_ORPHAN_SECTIONS
|
||||||
prompt "Orphan sections handling"
|
prompt "Orphan sections handling"
|
||||||
default COMPILER_ORPHAN_SECTIONS_ERROR
|
default COMPILER_ORPHAN_SECTIONS_ERROR
|
||||||
|
|||||||
@@ -92,6 +92,10 @@ idf_build_set_property(IDF_COMPONENT_OPTIONAL_REQUIRES_MODE DEFERRED)
|
|||||||
# (CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_*) set below, not the app-level
|
# (CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_*) set below, not the app-level
|
||||||
# CONFIG_COMPILER_OPTIMIZATION_* defaults.
|
# CONFIG_COMPILER_OPTIMIZATION_* defaults.
|
||||||
idf_build_set_property(SET_COMPILER_OPTIMIZATION NO)
|
idf_build_set_property(SET_COMPILER_OPTIMIZATION NO)
|
||||||
|
# The bootloader relies on linker-script section placement matched by object
|
||||||
|
# file name, which LTO does not preserve, so it must never be compiled or linked
|
||||||
|
# with LTO regardless of the application's CONFIG_COMPILER_LTO_* options.
|
||||||
|
idf_build_set_property(SET_COMPILER_LTO NO)
|
||||||
|
|
||||||
# Perform internal IDF project initialisation
|
# Perform internal IDF project initialisation
|
||||||
idf_project_init()
|
idf_project_init()
|
||||||
|
|||||||
@@ -239,3 +239,7 @@ if(BOOTLOADER_BUILD)
|
|||||||
# linker will ignore this structure as it has no other files depending on it.
|
# linker will ignore this structure as it has no other files depending on it.
|
||||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u esp_bootloader_desc")
|
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u esp_bootloader_desc")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Disable LTO for bootloader_support: it relies on linker script placements that
|
||||||
|
# depend on object file names, which LTO does not preserve.
|
||||||
|
idf_component_set_property(${COMPONENT_NAME} NO_LTO 1)
|
||||||
|
|||||||
@@ -90,7 +90,17 @@ extern "C" {
|
|||||||
|
|
||||||
// Forces a string into DRAM instead of flash
|
// Forces a string into DRAM instead of flash
|
||||||
// Use as esp_rom_printf(DRAM_STR("Hello world!\n"));
|
// Use as esp_rom_printf(DRAM_STR("Hello world!\n"));
|
||||||
#define DRAM_STR(str) (__extension__({static const DRAM_ATTR char __c[] = (str); (const char *)&__c;}))
|
// The string is placed into a uniquely-named variable in a uniquely-named
|
||||||
|
// subsection of .dram1. Both names are derived from a single __COUNTER__ value.
|
||||||
|
// This avoids "section type conflict" errors under LTO, where the local static
|
||||||
|
// variables generated by DRAM_STR in different translation units would otherwise
|
||||||
|
// share the same name and the same DRAM_ATTR section name after the units are merged.
|
||||||
|
#define DRAM_STR(str) _DRAM_STR_IMPL(str, __COUNTER__)
|
||||||
|
#define _DRAM_STR_COMBINE(x, y) x ## y
|
||||||
|
#define _DRAM_STR_IMPL(str, counter) \
|
||||||
|
_DRAM_STR_IMPL2(str, _DRAM_STR_COMBINE(__c, counter), _SECTION_ATTR_IMPL(".dram1.str", counter))
|
||||||
|
#define _DRAM_STR_IMPL2(str, varname, attr) \
|
||||||
|
(__extension__({static const attr char varname[] = (str); (const char *)&varname;}))
|
||||||
|
|
||||||
#if CONFIG_SOC_RTC_FAST_MEM_SUPPORTED || CONFIG_SOC_RTC_SLOW_MEM_SUPPORTED
|
#if CONFIG_SOC_RTC_FAST_MEM_SUPPORTED || CONFIG_SOC_RTC_SLOW_MEM_SUPPORTED
|
||||||
// Forces data into RTC memory. See "docs/deep-sleep-stub.rst"
|
// Forces data into RTC memory. See "docs/deep-sleep-stub.rst"
|
||||||
|
|||||||
@@ -79,6 +79,12 @@ The following configuration options reduces the final binary size of almost any
|
|||||||
.. list::
|
.. list::
|
||||||
|
|
||||||
- Set :ref:`CONFIG_COMPILER_OPTIMIZATION` to ``Optimize for size (-Os)``. In some cases, ``Optimize for performance (-O2)`` will also reduce the binary size compared to the default. Note that if your code contains C or C++ Undefined Behavior then increasing the compiler optimization level may expose bugs that otherwise do not happen.
|
- Set :ref:`CONFIG_COMPILER_OPTIMIZATION` to ``Optimize for size (-Os)``. In some cases, ``Optimize for performance (-O2)`` will also reduce the binary size compared to the default. Note that if your code contains C or C++ Undefined Behavior then increasing the compiler optimization level may expose bugs that otherwise do not happen.
|
||||||
|
- Set :ref:`CONFIG_COMPILER_LTO_LINKTIME` to enable link-time optimization, and enable LTO at compile time for some of the components (either by setting :ref:`CONFIG_COMPILER_LTO_COMPILETIME`, or by adding the ``-flto=auto`` compile option to specific components). When used together with optimization for size, this option can reduce the binary size. ``idf.py size``, ``idf.py size-components``, and ``idf.py size-files`` all report accurate sizes for LTO-enabled builds. Note that enabling LTO has a few downsides:
|
||||||
|
|
||||||
|
- Enabling LTO increases the build time.
|
||||||
|
- Enabling LTO can increase the stack usage of your tasks. Inlining across translation units replaces several small stack frames with fewer but larger ones, so the peak stack depth along a call path may grow. The effect is usually small, but it can be significant for applications with deep call chains that span many LTO-optimized components. After enabling LTO, check the stack high water mark of your tasks (for example, with :cpp:func:`uxTaskGetStackHighWaterMark`) and raise the stack sizes of the affected tasks if necessary.
|
||||||
|
- LTO must not be enabled for components with custom :doc:`linker fragments <../linker-script-generation>`. This is because LTO merges the code from multiple object files into one, so the linker fragment is not able to control the placement of individual functions and variables.
|
||||||
|
- Enabling LTO may make the code more difficult to debug.
|
||||||
- Reduce the compiled-in log output by lowering the app :ref:`CONFIG_LOG_DEFAULT_LEVEL`. If the :ref:`CONFIG_LOG_MAXIMUM_LEVEL` is changed from the default then this setting controls the binary size instead. Reducing compiled-in logging reduces the number of strings in the binary, and also the code size of the calls to logging functions.
|
- Reduce the compiled-in log output by lowering the app :ref:`CONFIG_LOG_DEFAULT_LEVEL`. If the :ref:`CONFIG_LOG_MAXIMUM_LEVEL` is changed from the default then this setting controls the binary size instead. Reducing compiled-in logging reduces the number of strings in the binary, and also the code size of the calls to logging functions.
|
||||||
- If your application does not require dynamic log level changes and you do not need to control logs per module using tags, consider disabling :ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL` and changing :ref:`CONFIG_LOG_TAG_LEVEL_IMPL`. It reduces IRAM usage by approximately 260 bytes, DRAM usage by approximately 264 bytes, and flash usage by approximately 1 KB compared to the default option, it also speeds up logging.
|
- If your application does not require dynamic log level changes and you do not need to control logs per module using tags, consider disabling :ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL` and changing :ref:`CONFIG_LOG_TAG_LEVEL_IMPL`. It reduces IRAM usage by approximately 260 bytes, DRAM usage by approximately 264 bytes, and flash usage by approximately 1 KB compared to the default option, it also speeds up logging.
|
||||||
- Set the :ref:`CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL` to ``Silent``. This avoids compiling in a dedicated assertion string and source file name for each assert that may fail. It is still possible to find the failed assert in the code by looking at the memory address where the assertion failed.
|
- Set the :ref:`CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL` to ``Silent``. This avoids compiling in a dedicated assertion string and source file name for each assert that may fail. It is still possible to find the failed assert in the code by looking at the memory address where the assertion failed.
|
||||||
|
|||||||
@@ -79,6 +79,12 @@ ESP-IDF 构建系统会编译项目和 ESP-IDF 中所有源文件,但只有程
|
|||||||
.. list::
|
.. list::
|
||||||
|
|
||||||
- 将 :ref:`CONFIG_COMPILER_OPTIMIZATION` 设置为 ``Optimize for size (-Os)``。在某些情况下,相较于默认设置, ``Optimize for size (-Os)`` 也可以减小二进制文件的大小。请注意,若代码包含 C 或 C++ 的未定义行为,提高编译器优化级别可能会暴露出原本不存在的错误。
|
- 将 :ref:`CONFIG_COMPILER_OPTIMIZATION` 设置为 ``Optimize for size (-Os)``。在某些情况下,相较于默认设置, ``Optimize for size (-Os)`` 也可以减小二进制文件的大小。请注意,若代码包含 C 或 C++ 的未定义行为,提高编译器优化级别可能会暴露出原本不存在的错误。
|
||||||
|
- 设置 :ref:`CONFIG_COMPILER_LTO_LINKTIME` 以启用链接时优化 (LTO),并为部分组件启用编译时 LTO(既可以设置 :ref:`CONFIG_COMPILER_LTO_COMPILETIME`,也可以为特定组件添加 ``-flto=auto`` 编译选项)。与优化大小 (``-Os``) 一同使用时,该选项可以减小二进制文件的大小。``idf.py size``、``idf.py size-components`` 和 ``idf.py size-files`` 均可为启用 LTO 的构建报告准确的大小。请注意,启用 LTO 有以下几点不足:
|
||||||
|
|
||||||
|
- 启用 LTO 会增加构建时间。
|
||||||
|
- 启用 LTO 可能会增加任务的栈使用量。跨翻译单元的内联会用更少但更大的栈帧替代多个较小的栈帧,因此某条调用路径上的峰值栈深度可能增加。该影响通常较小,但对于调用链较深、且跨越多个经 LTO 优化组件的应用程序,影响可能较为明显。启用 LTO 后,请检查任务的栈高水位(例如使用 :cpp:func:`uxTaskGetStackHighWaterMark`),并在必要时增大受影响任务的栈大小。
|
||||||
|
- 不得为使用自定义 :doc:`链接器片段 <../linker-script-generation>` 的组件启用 LTO。这是因为 LTO 会将多个目标文件中的代码合并为一个,导致链接器片段无法控制单个函数和变量的放置位置。
|
||||||
|
- 启用 LTO 可能会使代码更难调试。
|
||||||
- 通过降低应用程序的 :ref:`CONFIG_LOG_DEFAULT_LEVEL` ,可以减少编译时的日志输出。如果改变 :ref:`CONFIG_LOG_MAXIMUM_LEVEL` 的默认选项,则可以控制二进制文件的大小。减少编译时的日志输出可以减少二进制文件中的字符串数量,并减小调用日志函数的代码大小。
|
- 通过降低应用程序的 :ref:`CONFIG_LOG_DEFAULT_LEVEL` ,可以减少编译时的日志输出。如果改变 :ref:`CONFIG_LOG_MAXIMUM_LEVEL` 的默认选项,则可以控制二进制文件的大小。减少编译时的日志输出可以减少二进制文件中的字符串数量,并减小调用日志函数的代码大小。
|
||||||
- 如果应用程序不需要动态更改日志级别,并且不需要使用标签来控制每个模块的日志,建议禁用 :ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL` 并更改 :ref:`CONFIG_LOG_TAG_LEVEL_IMPL`。与默认选项相比,这可以节约大概 260 字节的 IRAM、264 字节的 DRAM、以及 1 KB 的 flash,同时还可以加快日志记录的速度。
|
- 如果应用程序不需要动态更改日志级别,并且不需要使用标签来控制每个模块的日志,建议禁用 :ref:`CONFIG_LOG_DYNAMIC_LEVEL_CONTROL` 并更改 :ref:`CONFIG_LOG_TAG_LEVEL_IMPL`。与默认选项相比,这可以节约大概 260 字节的 IRAM、264 字节的 DRAM、以及 1 KB 的 flash,同时还可以加快日志记录的速度。
|
||||||
- 将 :ref:`CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL` 设置为 ``Silent``,可以避免为所有可能失败的断言编译专门的断言字符串和源文件名。尽管如此,仍可以通过查看断言失败时的内存地址以在代码中找到失败断言。
|
- 将 :ref:`CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL` 设置为 ``Silent``,可以避免为所有可能失败的断言编译专门的断言字符串和源文件名。尽管如此,仍可以通过查看断言失败时的内存地址以在代码中找到失败断言。
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# Helpers for link-time optimization (LTO) support.
|
||||||
|
#
|
||||||
|
# These helpers are shared between the legacy build system (tools/cmake) and the
|
||||||
|
# build system v2 (tools/cmakev2). They only rely on the component property API
|
||||||
|
# (idf_component_get_property) and the COMPONENT_DIR / LDFRAGMENTS component
|
||||||
|
# properties, which are available in both build systems.
|
||||||
|
|
||||||
|
include_guard(GLOBAL)
|
||||||
|
|
||||||
|
# __lto_collect_fragment_placed_components(<output_var> <component_name>...)
|
||||||
|
#
|
||||||
|
# Returns, in output_var, the subset of the given components whose object code is
|
||||||
|
# placed by an explicit "archive: libNAME.a" directive in some linker fragment
|
||||||
|
# (.lf) file - whether that fragment belongs to the component itself or to a
|
||||||
|
# different component.
|
||||||
|
#
|
||||||
|
# Such components rely on section placement that is matched by object file /
|
||||||
|
# archive name. LTO merges and renames the object files of a component, so the
|
||||||
|
# placement rules silently stop matching and the affected functions (for example
|
||||||
|
# the spi_flash / GDMA HAL routines that must execute from IRAM while the flash
|
||||||
|
# cache is disabled) end up in flash. Compiling these components without LTO keeps
|
||||||
|
# their placement intact.
|
||||||
|
#
|
||||||
|
# Wildcard directives ("archive: *") are intentionally ignored: they match by
|
||||||
|
# section name rather than by object file name and are therefore not affected by
|
||||||
|
# LTO.
|
||||||
|
function(__lto_collect_fragment_placed_components output_var)
|
||||||
|
set(placed "")
|
||||||
|
foreach(component_name ${ARGN})
|
||||||
|
idf_component_get_property(component_dir ${component_name} COMPONENT_DIR)
|
||||||
|
idf_component_get_property(ldfragments ${component_name} LDFRAGMENTS)
|
||||||
|
foreach(ldfragment ${ldfragments})
|
||||||
|
get_filename_component(ldfragment_abs "${ldfragment}" ABSOLUTE BASE_DIR "${component_dir}")
|
||||||
|
if(EXISTS "${ldfragment_abs}")
|
||||||
|
file(READ "${ldfragment_abs}" ldfragment_contents)
|
||||||
|
string(REGEX MATCHALL "archive:[ \t]*lib[A-Za-z0-9_-]+\\.a"
|
||||||
|
archive_matches "${ldfragment_contents}")
|
||||||
|
foreach(archive_match ${archive_matches})
|
||||||
|
string(REGEX REPLACE "archive:[ \t]*lib([A-Za-z0-9_-]+)\\.a" "\\1"
|
||||||
|
archive_name "${archive_match}")
|
||||||
|
list(APPEND placed ${archive_name})
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endforeach()
|
||||||
|
if(placed)
|
||||||
|
list(REMOVE_DUPLICATES placed)
|
||||||
|
endif()
|
||||||
|
set(${output_var} "${placed}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
@@ -43,6 +43,12 @@ else()
|
|||||||
set(CMAKE_C_COMPILER ${_CMAKE_TOOLCHAIN_PREFIX}gcc)
|
set(CMAKE_C_COMPILER ${_CMAKE_TOOLCHAIN_PREFIX}gcc)
|
||||||
set(CMAKE_CXX_COMPILER ${_CMAKE_TOOLCHAIN_PREFIX}g++)
|
set(CMAKE_CXX_COMPILER ${_CMAKE_TOOLCHAIN_PREFIX}g++)
|
||||||
set(CMAKE_ASM_COMPILER ${_CMAKE_TOOLCHAIN_PREFIX}gcc)
|
set(CMAKE_ASM_COMPILER ${_CMAKE_TOOLCHAIN_PREFIX}gcc)
|
||||||
|
# Use the gcc-ar/gcc-ranlib wrappers so that the LTO plugin is loaded when
|
||||||
|
# creating and indexing static archives. This is required for link-time
|
||||||
|
# optimization (CONFIG_COMPILER_LTO_LINKTIME) to work across static libraries;
|
||||||
|
# plain ar/ranlib do not record the LTO symbols in the archive index.
|
||||||
|
set(CMAKE_AR ${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar)
|
||||||
|
set(CMAKE_RANLIB ${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Handle different execution contexts for the toolchain file.
|
# Handle different execution contexts for the toolchain file.
|
||||||
|
|||||||
@@ -1011,6 +1011,43 @@ function(idf_component_include name)
|
|||||||
list(APPEND components_included "${component_name}")
|
list(APPEND components_included "${component_name}")
|
||||||
idf_build_set_property(COMPONENTS_INCLUDED "${components_included}")
|
idf_build_set_property(COMPONENTS_INCLUDED "${components_included}")
|
||||||
|
|
||||||
|
# Compile-time LTO (CONFIG_COMPILER_LTO_COMPILETIME): record which components
|
||||||
|
# must NOT be compiled with LTO. Linker fragments place object code by
|
||||||
|
# archive / object-file name, which LTO renames and merges, so any component
|
||||||
|
# that participates in fragment placement must be excluded. The decision is
|
||||||
|
# recorded in the NO_LTO component property and consumed later, when the
|
||||||
|
# component's static library is created, via a generator expression. Because
|
||||||
|
# the generator expression is evaluated only after every component has been
|
||||||
|
# processed, it does not matter that a component may be placed by a fragment
|
||||||
|
# belonging to a component that is included after it.
|
||||||
|
# A subproject that must not use LTO (for example the bootloader) opts out by
|
||||||
|
# setting the SET_COMPILER_LTO build property to NO, the same way it uses
|
||||||
|
# SET_COMPILER_OPTIMIZATION; an unset property means LTO is allowed. The
|
||||||
|
# result is reused by the apply block further below.
|
||||||
|
idf_build_get_property(set_compiler_lto SET_COMPILER_LTO)
|
||||||
|
if(NOT DEFINED set_compiler_lto OR set_compiler_lto STREQUAL "")
|
||||||
|
set(set_compiler_lto YES)
|
||||||
|
endif()
|
||||||
|
if(CONFIG_COMPILER_LTO_COMPILETIME AND set_compiler_lto)
|
||||||
|
# A component with its own linker fragments relies on object-file-name
|
||||||
|
# placement and must not be compiled with LTO.
|
||||||
|
idf_component_get_property(component_ldfragments "${component_name}" LDFRAGMENTS)
|
||||||
|
if(component_ldfragments)
|
||||||
|
idf_component_set_property("${component_name}" NO_LTO 1)
|
||||||
|
endif()
|
||||||
|
# A component may also place the object code of *other* components by
|
||||||
|
# naming their archive ("archive: libNAME.a") in its own fragments; mark
|
||||||
|
# those too. Names that do not resolve to a build component (for example
|
||||||
|
# toolchain archives such as libc.a) are skipped.
|
||||||
|
__lto_collect_fragment_placed_components(lto_placed_components "${component_name}")
|
||||||
|
foreach(placed_component IN LISTS lto_placed_components)
|
||||||
|
__get_component_interface(COMPONENT "${placed_component}" OUTPUT placed_interface)
|
||||||
|
if(NOT "${placed_interface}" STREQUAL "NOTFOUND")
|
||||||
|
idf_component_set_property("${placed_component}" NO_LTO 1)
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
idf_component_get_property(component_interface "${name}" COMPONENT_INTERFACE)
|
idf_component_get_property(component_interface "${name}" COMPONENT_INTERFACE)
|
||||||
if(DEFINED ARG_INTERFACE)
|
if(DEFINED ARG_INTERFACE)
|
||||||
set(${ARG_INTERFACE} ${component_interface} PARENT_SCOPE)
|
set(${ARG_INTERFACE} ${component_interface} PARENT_SCOPE)
|
||||||
@@ -1064,6 +1101,19 @@ function(idf_component_include name)
|
|||||||
else()
|
else()
|
||||||
target_link_libraries("${component_interface}" INTERFACE "${component_real_target}")
|
target_link_libraries("${component_interface}" INTERFACE "${component_real_target}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Compile-time LTO (CONFIG_COMPILER_LTO_COMPILETIME): compile this static
|
||||||
|
# library with -flto=auto unless it was marked NO_LTO during inclusion
|
||||||
|
# (because it, or another component, places its object code via a linker
|
||||||
|
# fragment, or it opted out explicitly). NO_LTO is read as a generator
|
||||||
|
# expression so that vetoes recorded by components processed after this
|
||||||
|
# one are still taken into account. set_compiler_lto was resolved during
|
||||||
|
# inclusion above (NO when a subproject opts out via SET_COMPILER_LTO).
|
||||||
|
if(CONFIG_COMPILER_LTO_COMPILETIME AND set_compiler_lto)
|
||||||
|
idf_component_get_property(no_lto_genex "${component_name}" NO_LTO GENERATOR_EXPRESSION)
|
||||||
|
target_compile_options("${component_real_target}" PRIVATE
|
||||||
|
"$<$<NOT:$<BOOL:${no_lto_genex}>>:-flto=auto>")
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
idf_die("Unsupported target type '${component_real_target_type}' in component '${component_name}'")
|
idf_die("Unsupported target type '${component_real_target_type}' in component '${component_name}'")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ include(${CMAKE_CURRENT_LIST_DIR}/../cmake/depgraph.cmake)
|
|||||||
# which generates per-component error code tables placed in a link-time array.
|
# which generates per-component error code tables placed in a link-time array.
|
||||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/err_codes.cmake)
|
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/err_codes.cmake)
|
||||||
|
|
||||||
|
# The lto.cmake file from cmakev1 provides __lto_collect_fragment_placed_components(),
|
||||||
|
# used to decide which components must be excluded from link-time optimization.
|
||||||
|
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/lto.cmake)
|
||||||
|
|
||||||
include(component)
|
include(component)
|
||||||
include(build)
|
include(build)
|
||||||
include(kconfig)
|
include(kconfig)
|
||||||
|
|||||||
@@ -385,7 +385,18 @@ function(__init_project_configuration)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
list(APPEND link_options "-fno-lto")
|
# A subproject that must not use LTO (for example the bootloader) opts out by
|
||||||
|
# setting the SET_COMPILER_LTO build property to NO, the same way it uses
|
||||||
|
# SET_COMPILER_OPTIMIZATION; an unset property means LTO is allowed.
|
||||||
|
idf_build_get_property(set_compiler_lto SET_COMPILER_LTO)
|
||||||
|
if(NOT DEFINED set_compiler_lto OR set_compiler_lto STREQUAL "")
|
||||||
|
set(set_compiler_lto YES)
|
||||||
|
endif()
|
||||||
|
if(CONFIG_COMPILER_LTO_LINKTIME AND set_compiler_lto)
|
||||||
|
list(APPEND link_options "-flto=auto")
|
||||||
|
else()
|
||||||
|
list(APPEND compile_options "-fno-lto")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(CONFIG_IDF_TARGET_LINUX AND CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
|
if(CONFIG_IDF_TARGET_LINUX AND CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
|
||||||
# Not all versions of the MacOS linker support the -warn_commons flag.
|
# Not all versions of the MacOS linker support the -warn_commons flag.
|
||||||
@@ -473,16 +484,17 @@ function(__init_project_configuration)
|
|||||||
# paths back to the real paths in the filesystem.
|
# paths back to the real paths in the filesystem.
|
||||||
set(gdbinit_dir ${build_dir}/gdbinit)
|
set(gdbinit_dir ${build_dir}/gdbinit)
|
||||||
set(gdbinit_path "${gdbinit_dir}/prefix_map")
|
set(gdbinit_path "${gdbinit_dir}/prefix_map")
|
||||||
|
set(prefix_map_compile_options)
|
||||||
|
|
||||||
if(CONFIG_COMPILER_HIDE_PATHS_MACROS)
|
if(CONFIG_COMPILER_HIDE_PATHS_MACROS)
|
||||||
list(APPEND compile_options "-fmacro-prefix-map=${CMAKE_SOURCE_DIR}=.")
|
list(APPEND prefix_map_compile_options "-fmacro-prefix-map=${CMAKE_SOURCE_DIR}=.")
|
||||||
list(APPEND compile_options "-fmacro-prefix-map=${idf_path}=/IDF")
|
list(APPEND prefix_map_compile_options "-fmacro-prefix-map=${idf_path}=/IDF")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CONFIG_APP_REPRODUCIBLE_BUILD)
|
if(CONFIG_APP_REPRODUCIBLE_BUILD)
|
||||||
list(APPEND compile_options "-fdebug-prefix-map=${idf_path}=/IDF")
|
list(APPEND prefix_map_compile_options "-fdebug-prefix-map=${idf_path}=/IDF")
|
||||||
list(APPEND compile_options "-fdebug-prefix-map=${project_dir}=/IDF_PROJECT")
|
list(APPEND prefix_map_compile_options "-fdebug-prefix-map=${project_dir}=/IDF_PROJECT")
|
||||||
list(APPEND compile_options "-fdebug-prefix-map=${build_dir}=/IDF_BUILD")
|
list(APPEND prefix_map_compile_options "-fdebug-prefix-map=${build_dir}=/IDF_BUILD")
|
||||||
|
|
||||||
# Generate mapping for component paths
|
# Generate mapping for component paths
|
||||||
set(gdbinit_file_lines)
|
set(gdbinit_file_lines)
|
||||||
@@ -492,7 +504,7 @@ function(__init_project_configuration)
|
|||||||
|
|
||||||
string(TOUPPER ${component_name} component_name_uppercase)
|
string(TOUPPER ${component_name} component_name_uppercase)
|
||||||
set(substituted_path "/COMPONENT_${component_name_uppercase}_DIR")
|
set(substituted_path "/COMPONENT_${component_name_uppercase}_DIR")
|
||||||
list(APPEND compile_options "-fdebug-prefix-map=${component_dir}=${substituted_path}")
|
list(APPEND prefix_map_compile_options "-fdebug-prefix-map=${component_dir}=${substituted_path}")
|
||||||
string(APPEND gdbinit_file_lines "set substitute-path ${substituted_path} ${component_dir}\n")
|
string(APPEND gdbinit_file_lines "set substitute-path ${substituted_path} ${component_dir}\n")
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
@@ -506,15 +518,28 @@ function(__init_project_configuration)
|
|||||||
endif()
|
endif()
|
||||||
string(STRIP "${compiler_sysroot}" compiler_sysroot)
|
string(STRIP "${compiler_sysroot}" compiler_sysroot)
|
||||||
get_filename_component(compiler_sysroot "${compiler_sysroot}/.." REALPATH)
|
get_filename_component(compiler_sysroot "${compiler_sysroot}/.." REALPATH)
|
||||||
list(APPEND compile_options "-fdebug-prefix-map=${compiler_sysroot}=/TOOLCHAIN")
|
list(APPEND prefix_map_compile_options "-fdebug-prefix-map=${compiler_sysroot}=/TOOLCHAIN")
|
||||||
string(APPEND gdbinit_file_lines "set substitute-path /TOOLCHAIN ${compiler_sysroot}\n")
|
string(APPEND gdbinit_file_lines "set substitute-path /TOOLCHAIN ${compiler_sysroot}\n")
|
||||||
else()
|
else()
|
||||||
set(gdbinit_file_lines "# There is no prefix map defined for the project.\n")
|
set(gdbinit_file_lines "# There is no prefix map defined for the project.\n")
|
||||||
endif()
|
endif()
|
||||||
|
list(APPEND compile_options ${prefix_map_compile_options})
|
||||||
# Write the prefix_map file even if it is empty.
|
# Write the prefix_map file even if it is empty.
|
||||||
file(MAKE_DIRECTORY ${gdbinit_dir})
|
file(MAKE_DIRECTORY ${gdbinit_dir})
|
||||||
file(WRITE "${gdbinit_path}" "${gdbinit_file_lines}")
|
file(WRITE "${gdbinit_path}" "${gdbinit_file_lines}")
|
||||||
|
|
||||||
|
if(CONFIG_APP_REPRODUCIBLE_BUILD AND CONFIG_COMPILER_LTO_LINKTIME AND set_compiler_lto)
|
||||||
|
# LTO generates code at link time, where the path remapping applied to
|
||||||
|
# compile_options doesn't take effect, so pass it to the linker as well.
|
||||||
|
# -save-temps keeps LTRANS objects out of $TMPDIR, and a pinned random
|
||||||
|
# seed makes LTO bytecode byte-identical. See the commit message of
|
||||||
|
# "feat(build): add options to enable link-time optimization (LTO)"
|
||||||
|
# for details.
|
||||||
|
list(APPEND link_options ${prefix_map_compile_options})
|
||||||
|
list(APPEND link_options "-save-temps")
|
||||||
|
list(APPEND compile_options "-frandom-seed=1")
|
||||||
|
endif()
|
||||||
|
|
||||||
idf_build_set_property(GDBINIT_FILES_PREFIX_MAP "${gdbinit_path}")
|
idf_build_set_property(GDBINIT_FILES_PREFIX_MAP "${gdbinit_path}")
|
||||||
idf_build_set_property(COMPILE_OPTIONS "${compile_options}" APPEND)
|
idf_build_set_property(COMPILE_OPTIONS "${compile_options}" APPEND)
|
||||||
idf_build_set_property(C_COMPILE_OPTIONS "${c_compile_options}" APPEND)
|
idf_build_set_property(C_COMPILE_OPTIONS "${c_compile_options}" APPEND)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from pytest_embedded_idf.utils import idf_parametrize
|
|||||||
('no_vfs_partial', 'supported_targets'),
|
('no_vfs_partial', 'supported_targets'),
|
||||||
('opt_o0', 'supported_targets'),
|
('opt_o0', 'supported_targets'),
|
||||||
('opt_o2', 'supported_targets'),
|
('opt_o2', 'supported_targets'),
|
||||||
|
('opt_os_lto', 'supported_targets'),
|
||||||
('stack_check_verbose_log', 'supported_targets'),
|
('stack_check_verbose_log', 'supported_targets'),
|
||||||
('sram1_iram', 'esp32'),
|
('sram1_iram', 'esp32'),
|
||||||
('main_task_cpu1', 'esp32'),
|
('main_task_cpu1', 'esp32'),
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||||
|
CONFIG_COMPILER_LTO_LINKTIME=y
|
||||||
|
CONFIG_COMPILER_LTO_COMPILETIME=y
|
||||||
Reference in New Issue
Block a user