mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
Merge branch 'feat/llvm-opt-integration' into 'master'
feat(compiler): add ESP-IDF LLVM optimization enablement framework See merge request espressif/esp-idf!51154
This commit is contained in:
15
Kconfig
15
Kconfig
@@ -459,6 +459,21 @@ mainmenu "Espressif IoT Development Framework Configuration"
|
||||
the ZCMP extension for source files that contain functions which may
|
||||
execute while mstatus.mie = 0.
|
||||
|
||||
menu "LLVM optimizations"
|
||||
depends on IDF_TOOLCHAIN_CLANG && IDF_TARGET_ESP32P4
|
||||
|
||||
config COMPILER_LLVM_MEMCPY_OPTIMIZATION
|
||||
bool "Optimize memcpy with PIE"
|
||||
default n
|
||||
help
|
||||
Optimize memcpy using the RISC-V PIE extension on ESP32-P4.
|
||||
Applied only to sources or components opted in with
|
||||
ENABLE_LLVM_OPT or idf_component_enable_llvm_opt, or by
|
||||
applying IDF_LLVM_OPT_MEMCPY or IDF_LLVM_OPT_ALL with
|
||||
target_compile_options.
|
||||
|
||||
endmenu
|
||||
|
||||
choice COMPILER_OPTIMIZATION_ASSERTION_LEVEL
|
||||
prompt "Assertion level"
|
||||
default COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE
|
||||
|
||||
@@ -471,6 +471,74 @@ This can be useful if there is upstream code that emits warnings.
|
||||
|
||||
When using these commands, place them after the call to ``idf_component_register`` in the component CMakeLists file.
|
||||
|
||||
.. only:: esp32p4
|
||||
|
||||
.. _cmake-llvm-optimizations:
|
||||
|
||||
LLVM Optimizations
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
ESP-IDF can apply extra LLVM/Clang optimizations to selected sources. Enable the options in menuconfig. Mark a component or a file list in CMake. ESP-IDF sets the corresponding compiler flags, so projects do not write those flags by hand.
|
||||
|
||||
In :ref:`project-configuration-menu`, enable :menuitem:`CONFIG_COMPILER_LLVM_MEMCPY_OPTIMIZATION` under Compiler options > LLVM optimizations. Flags apply only to components or source files you select in CMake.
|
||||
|
||||
Apply to the whole component:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
idf_component_register(SRCS "foo.c" "bar.c"
|
||||
INCLUDE_DIRS "."
|
||||
ENABLE_LLVM_OPT)
|
||||
|
||||
Apply to one or more source files (call after ``idf_component_register``):
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
idf_component_enable_llvm_opt(SRCS "foo.c" "bar.c")
|
||||
|
||||
Source paths must match the paths passed to ``idf_component_register``. As with ``set_source_files_properties``, source-level selection is not supported with sources discovered through ``SRC_DIRS``.
|
||||
|
||||
Third-party components such as LVGL do not call these helpers. After ``idf_component_register``, get the dependency's library target and apply the exported flags with standard CMake. ``IDF_LLVM_OPT_ALL`` holds the flags for every LLVM optimization enabled in menuconfig; ``IDF_LLVM_OPT_MEMCPY`` holds only the flags for :menuitem:`CONFIG_COMPILER_LLVM_MEMCPY_OPTIMIZATION`. Both are empty when those options are off or the compiler is not Clang:
|
||||
|
||||
Library target of the managed ``lvgl`` component (registry name :code:`lvgl__lvgl`), not its source list:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
idf_component_get_property(lvgl_lib "lvgl__lvgl" COMPONENT_LIB)
|
||||
if(IDF_LLVM_OPT_ALL)
|
||||
target_compile_options(${lvgl_lib} PRIVATE ${IDF_LLVM_OPT_ALL})
|
||||
endif()
|
||||
|
||||
To apply only the memcpy optimization:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
idf_component_get_property(lvgl_lib "lvgl__lvgl" COMPONENT_LIB)
|
||||
if(IDF_LLVM_OPT_MEMCPY)
|
||||
target_compile_options(${lvgl_lib} PRIVATE ${IDF_LLVM_OPT_MEMCPY})
|
||||
endif()
|
||||
|
||||
This does not patch the third-party CMakeLists. Do not apply it to every source in a large component; see the limitations below.
|
||||
|
||||
Advanced users can append extra Clang or LLVM flags. These are applied together with any LLVM optimizations enabled in menuconfig. Omit ``SRCS`` to apply them to the whole component:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
idf_component_enable_llvm_opt(
|
||||
SRCS "foo.c"
|
||||
OPTIONS "-mllvm=-my-custom-llvm-option")
|
||||
|
||||
The current menuconfig option speeds up ``memcpy`` using the RISC-V PIE extension. Install and select the Espressif Clang toolchain with ``IDF_TOOLCHAIN=clang``; IDF does not substitute flags when the active compiler is GCC. Further menuconfig options can be added later without changing the CMake enable API. See :example:`system/llvm_opt` and :example:`system/llvm_memcpy_opt`.
|
||||
|
||||
Limitations:
|
||||
|
||||
- Requires the Espressif Clang toolchain. ``ENABLE_LLVM_OPT``, ``idf_component_enable_llvm_opt``, ``IDF_LLVM_OPT_ALL``, and ``IDF_LLVM_OPT_MEMCPY`` have no effect when the selected compiler is not Clang.
|
||||
- Currently validated on ESP32-P4 only.
|
||||
- Apply only to hot paths. Prefer a measured file list. Results depend on alignment, size, chip revision, and flash/cache layout.
|
||||
- Do **not** enable this for the whole project or for every source in a large component. The generated code uses the PIE coprocessor. The first PIE instruction in a task that does not currently own PIE traps into the kernel, which lazy-saves the previous owner's registers and restores this task's; that switch repeats for every such task and can make the application slower overall. Extra code size (I-cache) can also hurt, but is secondary. See :doc:`../api-reference/system/freertos_idf`.
|
||||
- Do not use this memcpy option from an ISR: PIE coprocessor use in interrupt context is not allowed and aborts.
|
||||
- Do not treat the CMake markers as a global ``-O`` replacement.
|
||||
|
||||
|
||||
.. _component-configuration:
|
||||
|
||||
@@ -1565,6 +1633,10 @@ The arguments for ``idf_component_register`` include:
|
||||
- KCONFIG_PROJBUILD - override the default Kconfig.projbuild file
|
||||
- WHOLE_ARCHIVE - if specified, the component library is surrounded by ``-Wl,--whole-archive``, ``-Wl,--no-whole-archive`` when linked. This has the same effect as setting ``WHOLE_ARCHIVE`` component property.
|
||||
|
||||
.. only:: esp32p4
|
||||
|
||||
On ESP32-P4, ``idf_component_register`` also accepts ``ENABLE_LLVM_OPT`` to apply LLVM optimizations enabled in menuconfig to all source files in the component. Ignored when the selected compiler is not Clang. See :ref:`cmake-llvm-optimizations`.
|
||||
|
||||
The following are used for :ref:`embedding data into the component <cmake_embed_data>`, and is considered as source files when determining if a component is config-only. This means that even if the component does not specify source files, a static library is still created internally for the component if it specifies either:
|
||||
|
||||
- EMBED_FILES - binary files to be embedded in the component
|
||||
|
||||
@@ -471,6 +471,74 @@ ESP-IDF 在搜索所有待构建的组件时,会按照以下优先级搜索组
|
||||
|
||||
请注意,上述两条命令只能在组件 CMakeLists 文件的 ``idf_component_register`` 命令之后调用。
|
||||
|
||||
.. only:: esp32p4
|
||||
|
||||
.. _cmake-llvm-optimizations:
|
||||
|
||||
LLVM 优化
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
ESP-IDF 可以把额外的 LLVM/Clang 优化应用到选定的源文件上:在 menuconfig 中启用相应选项,再在 CMake 里标记组件或文件列表。menuconfig 选项对应的编译器标志由 ESP-IDF 设置,项目无需手写这些标志。
|
||||
|
||||
在 :ref:`project-configuration-menu` 的 Compiler options > LLVM optimizations 下启用 :menuitem:`CONFIG_COMPILER_LLVM_MEMCPY_OPTIMIZATION`。相关标志仅应用于你在 CMake 中选定的组件或源文件。
|
||||
|
||||
应用于整个组件:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
idf_component_register(SRCS "foo.c" "bar.c"
|
||||
INCLUDE_DIRS "."
|
||||
ENABLE_LLVM_OPT)
|
||||
|
||||
应用于一个或多个源文件(在 ``idf_component_register`` 之后调用):
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
idf_component_enable_llvm_opt(SRCS "foo.c" "bar.c")
|
||||
|
||||
源文件路径必须与传递给 ``idf_component_register`` 的路径一致。与 ``set_source_files_properties`` 一样,通过 ``SRC_DIRS`` 查找到的源文件不支持按文件选择优化。
|
||||
|
||||
LVGL 等第三方组件不会调用上述 helper。在 ``idf_component_register`` 之后,用标准 CMake 命令取出依赖的库 target 并套用导出的标志。``IDF_LLVM_OPT_ALL`` 包含 menuconfig 中已启用的全部 LLVM 优化对应的标志;``IDF_LLVM_OPT_MEMCPY`` 只包含 :menuitem:`CONFIG_COMPILER_LLVM_MEMCPY_OPTIMIZATION` 对应的标志。当对应选项关闭或编译器不是 Clang 时,二者均为空:
|
||||
|
||||
managed ``lvgl`` 组件的库 target(组件仓库名为 :code:`lvgl__lvgl`,不是它的源文件列表):
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
idf_component_get_property(lvgl_lib "lvgl__lvgl" COMPONENT_LIB)
|
||||
if(IDF_LLVM_OPT_ALL)
|
||||
target_compile_options(${lvgl_lib} PRIVATE ${IDF_LLVM_OPT_ALL})
|
||||
endif()
|
||||
|
||||
若只应用 memcpy 优化:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
idf_component_get_property(lvgl_lib "lvgl__lvgl" COMPONENT_LIB)
|
||||
if(IDF_LLVM_OPT_MEMCPY)
|
||||
target_compile_options(${lvgl_lib} PRIVATE ${IDF_LLVM_OPT_MEMCPY})
|
||||
endif()
|
||||
|
||||
这样无需修改第三方的 CMakeLists。不要对大型组件中的全部源文件启用;见下方限制。
|
||||
|
||||
高级用户可以追加额外的 Clang 或 LLVM 标志。这些标志会与 menuconfig 中启用的 LLVM 优化一起应用。省略 ``SRCS`` 时,自定义标志将应用于整个组件:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
idf_component_enable_llvm_opt(
|
||||
SRCS "foo.c"
|
||||
OPTIONS "-mllvm=-my-custom-llvm-option")
|
||||
|
||||
当前 menuconfig 选项使用 RISC-V PIE 扩展加速 ``memcpy``。请安装并选用乐鑫 Clang 工具链(``IDF_TOOLCHAIN=clang``);当活动编译器为 GCC 时,IDF 不会注入这些标志。后续可在不改动 CMake 启用 API 的情况下,通过 menuconfig 添加新选项。示例见 :example:`system/llvm_opt` 与 :example:`system/llvm_memcpy_opt`。
|
||||
|
||||
使用限制:
|
||||
|
||||
- 需要乐鑫 Clang 工具链。如果所选编译器不是 Clang,``ENABLE_LLVM_OPT``、``idf_component_enable_llvm_opt``、``IDF_LLVM_OPT_ALL`` 和 ``IDF_LLVM_OPT_MEMCPY`` 不会产生任何效果。
|
||||
- 当前仅在 ESP32-P4 上完成验证。
|
||||
- 仅在优化热点路径时启用。优先对已测量的文件列表启用。结果依赖对齐、长度、芯片修订以及 Flash/Cache 布局。
|
||||
- **不要** 对整个工程或大型组件中的全部源文件启用。生成代码会使用 PIE 协处理器:某个尚未拥有 PIE 的任务第一次执行 PIE 指令时会陷入内核,内核惰性保存上一任拥有者的寄存器并恢复当前任务的寄存器;每个这样的任务都会重复该切换,整体应用可能反而变慢。额外代码体积(I-cache)也可能有影响,但是次要因素。详见 :doc:`../api-reference/system/freertos_idf`。
|
||||
- 不要在 ISR 中使用该 memcpy 选项:中断上下文中使用 PIE 协处理器不被允许,并会导致运行中止。
|
||||
- 不要把 CMake 标记当作全局 ``-O`` 替代。
|
||||
|
||||
|
||||
.. _component-configuration:
|
||||
|
||||
@@ -1565,6 +1633,10 @@ ESP-IDF 组件命令
|
||||
- KCONFIG_PROJBUILD - 覆盖默认的 Kconfig.projbuild 文件。
|
||||
- WHOLE_ARCHIVE - 如果指定了此参数,链接时会在组件库的前后分别添加 ``-Wl,--whole-archive`` 和 ``-Wl,--no-whole-archive``。这与设置 ``WHOLE_ARCHIVE`` 组件属性的效果一致。
|
||||
|
||||
.. only:: esp32p4
|
||||
|
||||
在 ESP32-P4 上,``idf_component_register`` 还接受 ``ENABLE_LLVM_OPT``,用于将 menuconfig 中启用的 LLVM 优化应用于组件中的所有源文件。如果所选编译器不是 Clang,该参数会被忽略。详见 :ref:`cmake-llvm-optimizations`。
|
||||
|
||||
以下内容用于 :ref:`将数据嵌入到组件中<cmake_embed_data>`,并在确定组件是否仅用于配置时被视为源文件。这意味着,即使组件没有指定源文件,如果组件指定了以下其中之一,仍然会在内部为组件创建一个静态库。
|
||||
|
||||
- EMBED_FILES - 嵌入组件的二进制文件
|
||||
|
||||
3
examples/system/llvm_memcpy_opt/.build-test-rules.yml
Normal file
3
examples/system/llvm_memcpy_opt/.build-test-rules.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
examples/system/llvm_memcpy_opt:
|
||||
enable:
|
||||
- if: IDF_TARGET == "esp32p4" and CONFIG_NAME == "default"
|
||||
7
examples/system/llvm_memcpy_opt/CMakeLists.txt
Normal file
7
examples/system/llvm_memcpy_opt/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
project(llvm_memcpy_opt)
|
||||
61
examples/system/llvm_memcpy_opt/README.md
Normal file
61
examples/system/llvm_memcpy_opt/README.md
Normal file
@@ -0,0 +1,61 @@
|
||||
| Supported Targets | ESP32-P4 |
|
||||
| ----------------- | -------- |
|
||||
|
||||
# LLVM memcpy optimization
|
||||
|
||||
Benchmark for the ESP-IDF LLVM optimization framework: `menuconfig` enables
|
||||
the memcpy optimization, and CMake marks which sources receive it.
|
||||
|
||||
This example compares two source files in the same component implementing the
|
||||
same 16-byte aligned, fixed 1024-byte `memcpy`:
|
||||
|
||||
- `memcpy_baseline.c`: built without LLVM optimization.
|
||||
- `memcpy_optimized.c`: selected with
|
||||
`idf_component_enable_llvm_opt(SRCS "memcpy_optimized.c")`.
|
||||
|
||||
The project uses performance optimization. When **Compiler options > LLVM
|
||||
optimizations > Optimize memcpy with PIE** is enabled, ESP-IDF injects the
|
||||
framework flags only into `memcpy_optimized.c`.
|
||||
|
||||
Use the Espressif Clang toolchain (`IDF_TOOLCHAIN=clang`) for the memcpy
|
||||
speedup. With the default GCC toolchain the example still **builds and runs**
|
||||
(so CI can board-test correctness on ESP32-P4): the LLVM opt-in markers are
|
||||
ignored and CMake prints a warning with the Clang commands below. Cycle counts
|
||||
under GCC are not a meaningful “optimized vs baseline” comparison.
|
||||
|
||||
Both ESP32-P4 ECO4 (PIE 2.1) and ECO5+ (PIE 2.2) are supported. The framework
|
||||
picks the PIE feature from the chip revision selected in menuconfig (IDF
|
||||
default is ECO5+ / rev >= 3.0). For an ECO4 board, set the revision under
|
||||
**Component config > ESP System Settings** (or equivalent) to a rev < 3.0
|
||||
option so `CONFIG_ESP32P4_SELECTS_REV_LESS_V3` is enabled. No example-specific
|
||||
revision pin is required.
|
||||
|
||||
## Build and run
|
||||
|
||||
```bash
|
||||
cd "$IDF_PATH/examples/system/llvm_memcpy_opt"
|
||||
IDF_TOOLCHAIN=clang idf.py set-target esp32p4
|
||||
IDF_TOOLCHAIN=clang idf.py build flash monitor
|
||||
```
|
||||
|
||||
`sdkconfig.defaults` enables the menuconfig option for this example. It can
|
||||
also be changed with:
|
||||
|
||||
```bash
|
||||
IDF_TOOLCHAIN=clang idf.py menuconfig
|
||||
```
|
||||
|
||||
The example verifies both copies against the source, then prints minimum cycle
|
||||
counts from 512 runs (benchmark helper placed in IRAM). Example ECO4 / PIE 2.1
|
||||
output:
|
||||
|
||||
```text
|
||||
LLVM memcpy optimization: enabled
|
||||
Copy: 1024 bytes, source/destination alignment: 16 bytes
|
||||
Baseline cycles: 671
|
||||
Optimized cycles: 136
|
||||
Result: PASS
|
||||
```
|
||||
|
||||
Cycle counts depend on chip revision, clock configuration, toolchain version,
|
||||
and memory placement. Use values printed by the target as benchmark results.
|
||||
18
examples/system/llvm_memcpy_opt/main/CMakeLists.txt
Normal file
18
examples/system/llvm_memcpy_opt/main/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
idf_component_register(SRCS "llvm_memcpy_main.c"
|
||||
"memcpy_baseline.c"
|
||||
"memcpy_optimized.c"
|
||||
INCLUDE_DIRS "")
|
||||
|
||||
# Framework markers are ignored on non-Clang (by design). Keep the example
|
||||
# buildable under the default GCC example CI jobs so P4 board tests can run;
|
||||
# print a clear warning so local users still know to use Clang for speedup.
|
||||
if(NOT CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
message(WARNING
|
||||
"examples/system/llvm_memcpy_opt: compiler is not Clang; "
|
||||
"ENABLE_LLVM_OPT / idf_component_enable_llvm_opt are ignored. "
|
||||
"For the memcpy optimization use:\n"
|
||||
" IDF_TOOLCHAIN=clang idf.py set-target esp32p4\n"
|
||||
" IDF_TOOLCHAIN=clang idf.py build flash monitor")
|
||||
endif()
|
||||
|
||||
idf_component_enable_llvm_opt(SRCS "memcpy_optimized.c")
|
||||
82
examples/system/llvm_memcpy_opt/main/llvm_memcpy_main.c
Normal file
82
examples/system/llvm_memcpy_opt/main/llvm_memcpy_main.c
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_attr.h"
|
||||
#include "esp_cpu.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define COPY_SIZE 1024
|
||||
#define BENCHMARK_RUNS 512
|
||||
|
||||
#if CONFIG_COMPILER_LLVM_MEMCPY_OPTIMIZATION
|
||||
#define LLVM_MEMCPY_STATUS "enabled"
|
||||
#else
|
||||
#define LLVM_MEMCPY_STATUS "disabled"
|
||||
#endif
|
||||
|
||||
void memcpy_baseline_1024(void *dst, const void *src);
|
||||
void memcpy_optimized_1024(void *dst, const void *src);
|
||||
|
||||
typedef void (*copy_fn_t)(void *, const void *);
|
||||
|
||||
static _Alignas(16) uint8_t s_source[COPY_SIZE];
|
||||
static _Alignas(16) uint8_t s_baseline_destination[COPY_SIZE];
|
||||
static _Alignas(16) uint8_t s_optimized_destination[COPY_SIZE];
|
||||
static portMUX_TYPE s_benchmark_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
static IRAM_ATTR uint32_t benchmark(copy_fn_t copy, uint8_t *destination)
|
||||
{
|
||||
uint32_t best = UINT32_MAX;
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
copy(destination, s_source);
|
||||
}
|
||||
|
||||
for (int i = 0; i < BENCHMARK_RUNS; ++i) {
|
||||
portENTER_CRITICAL(&s_benchmark_lock);
|
||||
__asm__ volatile ("" ::: "memory");
|
||||
uint32_t start = esp_cpu_get_cycle_count();
|
||||
copy(destination, s_source);
|
||||
uint32_t end = esp_cpu_get_cycle_count();
|
||||
__asm__ volatile ("" ::: "memory");
|
||||
portEXIT_CRITICAL(&s_benchmark_lock);
|
||||
|
||||
uint32_t cycles = end - start;
|
||||
if (cycles < best) {
|
||||
best = cycles;
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
for (int i = 0; i < COPY_SIZE; ++i) {
|
||||
s_source[i] = (uint8_t)(i * 3 + 1);
|
||||
}
|
||||
|
||||
memcpy_baseline_1024(s_baseline_destination, s_source);
|
||||
memcpy_optimized_1024(s_optimized_destination, s_source);
|
||||
bool result_matches = memcmp(s_baseline_destination, s_optimized_destination, COPY_SIZE) == 0
|
||||
&& memcmp(s_source, s_optimized_destination, COPY_SIZE) == 0;
|
||||
|
||||
uint32_t baseline_cycles = benchmark(memcpy_baseline_1024, s_baseline_destination);
|
||||
uint32_t optimized_cycles = benchmark(memcpy_optimized_1024, s_optimized_destination);
|
||||
|
||||
printf("LLVM memcpy optimization: %s\n", LLVM_MEMCPY_STATUS);
|
||||
printf("Copy: %d bytes, source/destination alignment: 16 bytes\n", COPY_SIZE);
|
||||
printf("Baseline cycles: %" PRIu32 "\n", baseline_cycles);
|
||||
printf("Optimized cycles: %" PRIu32 "\n", optimized_cycles);
|
||||
printf("Result: %s\n", result_matches ? "PASS" : "FAIL");
|
||||
}
|
||||
15
examples/system/llvm_memcpy_opt/main/memcpy_baseline.c
Normal file
15
examples/system/llvm_memcpy_opt/main/memcpy_baseline.c
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
__attribute__((noinline))
|
||||
void memcpy_baseline_1024(void *dst, const void *src)
|
||||
{
|
||||
dst = __builtin_assume_aligned(dst, 16);
|
||||
src = __builtin_assume_aligned(src, 16);
|
||||
memcpy(dst, src, 1024);
|
||||
}
|
||||
15
examples/system/llvm_memcpy_opt/main/memcpy_optimized.c
Normal file
15
examples/system/llvm_memcpy_opt/main/memcpy_optimized.c
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
__attribute__((noinline))
|
||||
void memcpy_optimized_1024(void *dst, const void *src)
|
||||
{
|
||||
dst = __builtin_assume_aligned(dst, 16);
|
||||
src = __builtin_assume_aligned(src, 16);
|
||||
memcpy(dst, src, 1024);
|
||||
}
|
||||
15
examples/system/llvm_memcpy_opt/pytest_llvm_memcpy_opt.py
Normal file
15
examples/system/llvm_memcpy_opt/pytest_llvm_memcpy_opt.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
from pytest_embedded_idf.utils import idf_parametrize
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@idf_parametrize('target', ['esp32p4'], indirect=['target'])
|
||||
def test_llvm_memcpy_opt(dut: Dut) -> None:
|
||||
"""Board smoke: correctness PASS. Speedup needs Clang + menuconfig option."""
|
||||
dut.expect(r'LLVM memcpy optimization: (enabled|disabled)', timeout=60)
|
||||
dut.expect(r'Baseline cycles:\s+\d+', timeout=30)
|
||||
dut.expect(r'Optimized cycles:\s+\d+', timeout=30)
|
||||
dut.expect_exact('Result: PASS', timeout=30)
|
||||
2
examples/system/llvm_memcpy_opt/sdkconfig.defaults
Normal file
2
examples/system/llvm_memcpy_opt/sdkconfig.defaults
Normal file
@@ -0,0 +1,2 @@
|
||||
CONFIG_COMPILER_OPTIMIZATION_PERF=y
|
||||
CONFIG_COMPILER_LLVM_MEMCPY_OPTIMIZATION=y
|
||||
3
examples/system/llvm_opt/.build-test-rules.yml
Normal file
3
examples/system/llvm_opt/.build-test-rules.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
examples/system/llvm_opt:
|
||||
enable:
|
||||
- if: IDF_TARGET == "esp32p4" and CONFIG_NAME == "default"
|
||||
7
examples/system/llvm_opt/CMakeLists.txt
Normal file
7
examples/system/llvm_opt/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
project(llvm_opt)
|
||||
43
examples/system/llvm_opt/README.md
Normal file
43
examples/system/llvm_opt/README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
| Supported Targets | ESP32-P4 |
|
||||
| ----------------- | -------- |
|
||||
|
||||
# LLVM optimization (component scope)
|
||||
|
||||
Minimal example for the ESP-IDF LLVM optimization framework. It shows how to
|
||||
apply the LLVM optimizations selected in menuconfig to **an entire component**
|
||||
with the `ENABLE_LLVM_OPT` argument of `idf_component_register`:
|
||||
|
||||
```cmake
|
||||
idf_component_register(SRCS "llvm_opt_main.c"
|
||||
INCLUDE_DIRS ""
|
||||
ENABLE_LLVM_OPT)
|
||||
```
|
||||
|
||||
Enable the option in menuconfig: **Compiler options > LLVM optimizations >
|
||||
Optimize memcpy with PIE**. On ESP32-P4 with the Clang toolchain, ESP-IDF then
|
||||
injects the corresponding compiler flags into every source file of this
|
||||
component.
|
||||
|
||||
With the default GCC toolchain the example still builds (for CI board smoke
|
||||
tests); `ENABLE_LLVM_OPT` is ignored and CMake prints a warning. Use Clang for
|
||||
real LLVM opt flags:
|
||||
|
||||
|
||||
## Build and run
|
||||
|
||||
Activate the Clang toolchain before configuring the project:
|
||||
|
||||
```bash
|
||||
cd "$IDF_PATH/examples/system/llvm_opt"
|
||||
IDF_TOOLCHAIN=clang idf.py set-target esp32p4
|
||||
IDF_TOOLCHAIN=clang idf.py build flash monitor
|
||||
```
|
||||
|
||||
`CONFIG_COMPILER_LLVM_MEMCPY_OPTIMIZATION` is enabled by `sdkconfig.defaults`
|
||||
and can be changed with `idf.py menuconfig`.
|
||||
|
||||
## Related example
|
||||
|
||||
For the file-level API (`idf_component_enable_llvm_opt`) and a `memcpy`
|
||||
benchmark comparing optimized and baseline sources, see the
|
||||
`llvm_memcpy_opt` example.
|
||||
11
examples/system/llvm_opt/main/CMakeLists.txt
Normal file
11
examples/system/llvm_opt/main/CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
idf_component_register(SRCS "llvm_opt_main.c"
|
||||
INCLUDE_DIRS ""
|
||||
ENABLE_LLVM_OPT)
|
||||
|
||||
if(NOT CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
message(WARNING
|
||||
"examples/system/llvm_opt: compiler is not Clang; "
|
||||
"ENABLE_LLVM_OPT is ignored. For real LLVM opt flags use:\n"
|
||||
" IDF_TOOLCHAIN=clang idf.py set-target esp32p4\n"
|
||||
" IDF_TOOLCHAIN=clang idf.py build flash monitor")
|
||||
endif()
|
||||
19
examples/system/llvm_opt/main/llvm_opt_main.c
Normal file
19
examples/system/llvm_opt/main/llvm_opt_main.c
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
#if CONFIG_COMPILER_LLVM_MEMCPY_OPTIMIZATION
|
||||
printf("LLVM optimization for ESP32-P4 memcpy: enabled in menuconfig\n");
|
||||
#else
|
||||
printf("LLVM optimization for ESP32-P4 memcpy: not enabled (select it in menuconfig)\n");
|
||||
#endif
|
||||
printf("This component is built with ENABLE_LLVM_OPT.\n");
|
||||
}
|
||||
12
examples/system/llvm_opt/pytest_llvm_opt.py
Normal file
12
examples/system/llvm_opt/pytest_llvm_opt.py
Normal file
@@ -0,0 +1,12 @@
|
||||
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
from pytest_embedded_idf.utils import idf_parametrize
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@idf_parametrize('target', ['esp32p4'], indirect=['target'])
|
||||
def test_llvm_opt(dut: Dut) -> None:
|
||||
dut.expect(r'LLVM optimization for ESP32-P4 memcpy:', timeout=30)
|
||||
dut.expect_exact('This component is built with ENABLE_LLVM_OPT.', timeout=10)
|
||||
2
examples/system/llvm_opt/sdkconfig.defaults
Normal file
2
examples/system/llvm_opt/sdkconfig.defaults
Normal file
@@ -0,0 +1,2 @@
|
||||
CONFIG_COMPILER_OPTIMIZATION_PERF=y
|
||||
CONFIG_COMPILER_LLVM_MEMCPY_OPTIMIZATION=y
|
||||
@@ -1,3 +1,5 @@
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/llvm_optimizations.cmake)
|
||||
|
||||
#
|
||||
# Internal function for retrieving component properties from a component target.
|
||||
#
|
||||
@@ -440,8 +442,9 @@ endfunction()
|
||||
# @param[in, optional] KCONFIG (single value) override the default Kconfig
|
||||
# @param[in, optional] KCONFIG_PROJBUILD (single value) override the default Kconfig
|
||||
# @param[in, optional] WHOLE_ARCHIVE (option) link the component as --whole-archive
|
||||
# @param[in, optional] ENABLE_LLVM_OPT (option) enable the LLVM optimizations selected in menuconfig
|
||||
function(idf_component_register)
|
||||
set(options WHOLE_ARCHIVE)
|
||||
set(options WHOLE_ARCHIVE ENABLE_LLVM_OPT)
|
||||
set(single_value KCONFIG KCONFIG_PROJBUILD)
|
||||
set(multi_value SRCS SRC_DIRS EXCLUDE_SRCS
|
||||
INCLUDE_DIRS PRIV_INCLUDE_DIRS LDFRAGMENTS REQUIRES
|
||||
@@ -487,9 +490,16 @@ function(idf_component_register)
|
||||
|
||||
idf_build_get_property(config_dir CONFIG_DIR)
|
||||
|
||||
# Publish IDF_LLVM_OPT_* even when this component does not opt in, so the
|
||||
# application can apply them to a third-party target with standard CMake.
|
||||
__idf_llvm_opt_publish_flags()
|
||||
|
||||
# The contents of 'sources' is from the __component_add_sources call
|
||||
if(sources OR __EMBED_FILES OR __EMBED_TXTFILES)
|
||||
add_library(${component_lib} STATIC ${sources})
|
||||
if(__ENABLE_LLVM_OPT)
|
||||
__idf_apply_llvm_opt_to_target(${component_lib})
|
||||
endif()
|
||||
__component_set_property(${component_target} COMPONENT_TYPE LIBRARY)
|
||||
__component_add_include_dirs(${component_lib} "${__INCLUDE_DIRS}" PUBLIC)
|
||||
__component_add_include_dirs(${component_lib} "${__PRIV_INCLUDE_DIRS}" PRIVATE)
|
||||
|
||||
140
tools/cmake/llvm_optimizations.cmake
Normal file
140
tools/cmake/llvm_optimizations.cmake
Normal file
@@ -0,0 +1,140 @@
|
||||
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
include_guard(GLOBAL)
|
||||
|
||||
# ESP-IDF LLVM optimization framework: map menuconfig-selected optimizations to
|
||||
# compiler flags. Component CMakeLists only mark opt-in scopes (file / list /
|
||||
# component). Flags for each menuconfig option are also published as
|
||||
# IDF_LLVM_OPT_* for standard CMake (third-party).
|
||||
|
||||
function(__idf_llvm_opt_collect_flags memcpy_out all_out)
|
||||
set(memcpy "")
|
||||
set(all "")
|
||||
|
||||
if(CONFIG_IDF_TOOLCHAIN_CLANG AND CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
if(CONFIG_COMPILER_LLVM_MEMCPY_OPTIMIZATION)
|
||||
list(APPEND memcpy "-mllvm=-riscv-esp32-p4-mem-intrin")
|
||||
# PIE 2.1 vs 2.2 comes from -mcpu (components/soc/project_include.cmake).
|
||||
# +espv-lowering is still required for .m intrinsic isel (vld/vst.128.ip.m).
|
||||
# Do not pass +xespv2p1: that name is not a Clang target-feature and is ignored.
|
||||
list(APPEND memcpy "-Xclang" "-target-feature" "-Xclang" "+espv-lowering")
|
||||
endif()
|
||||
list(APPEND all ${memcpy})
|
||||
endif()
|
||||
|
||||
set(${memcpy_out} "${memcpy}" PARENT_SCOPE)
|
||||
set(${all_out} "${all}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# SHELL-wrapped lists for target_compile_options. Empty when Kconfig is off or not Clang.
|
||||
function(__idf_llvm_opt_publish_flags)
|
||||
__idf_llvm_opt_collect_flags(memcpy_flags all_flags)
|
||||
__idf_llvm_opt_shell_wrap_xclang("${memcpy_flags}" memcpy_wrapped)
|
||||
__idf_llvm_opt_shell_wrap_xclang("${all_flags}" all_wrapped)
|
||||
set(IDF_LLVM_OPT_MEMCPY "${memcpy_wrapped}" CACHE INTERNAL
|
||||
"Flags for CONFIG_COMPILER_LLVM_MEMCPY_OPTIMIZATION; empty if off or not Clang" FORCE)
|
||||
set(IDF_LLVM_OPT_ALL "${all_wrapped}" CACHE INTERNAL
|
||||
"Flags for all LLVM optimizations enabled in menuconfig; empty if none or not Clang" FORCE)
|
||||
endfunction()
|
||||
|
||||
function(__idf_llvm_opt_get_options output)
|
||||
set(options ${ARGN})
|
||||
__idf_llvm_opt_publish_flags()
|
||||
|
||||
if(NOT CONFIG_IDF_TOOLCHAIN_CLANG OR NOT CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
set(${output} "" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
__idf_llvm_opt_collect_flags(_memcpy all_flags)
|
||||
list(APPEND options ${all_flags})
|
||||
set(${output} "${options}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# target_compile_options de-duplicates identical tokens, which splits
|
||||
# "-Xclang" "<arg>" pairs. SHELL: keeps each consecutive -Xclang group intact.
|
||||
# Source COMPILE_OPTIONS are not de-duplicated and do not honour SHELL:.
|
||||
function(__idf_llvm_opt_shell_wrap_xclang input output)
|
||||
set(result "")
|
||||
set(i 0)
|
||||
list(LENGTH input n)
|
||||
while(i LESS n)
|
||||
list(GET input ${i} item)
|
||||
if(item STREQUAL "-Xclang")
|
||||
set(group "")
|
||||
while(i LESS n)
|
||||
list(GET input ${i} tok)
|
||||
if(NOT tok STREQUAL "-Xclang")
|
||||
break()
|
||||
endif()
|
||||
math(EXPR nxt "${i} + 1")
|
||||
if(nxt GREATER_EQUAL n)
|
||||
message(FATAL_ERROR "LLVM opt flags: -Xclang missing argument")
|
||||
endif()
|
||||
list(GET input ${nxt} arg)
|
||||
if(group STREQUAL "")
|
||||
set(group "-Xclang ${arg}")
|
||||
else()
|
||||
string(APPEND group " -Xclang ${arg}")
|
||||
endif()
|
||||
math(EXPR i "${nxt} + 1")
|
||||
endwhile()
|
||||
list(APPEND result "SHELL:${group}")
|
||||
else()
|
||||
list(APPEND result "${item}")
|
||||
math(EXPR i "${i} + 1")
|
||||
endif()
|
||||
endwhile()
|
||||
set(${output} "${result}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(__idf_apply_llvm_opt_to_target target)
|
||||
__idf_llvm_opt_get_options(options ${ARGN})
|
||||
if(options)
|
||||
__idf_llvm_opt_shell_wrap_xclang("${options}" options)
|
||||
target_compile_options(${target} PRIVATE ${options})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(__idf_apply_llvm_opt_to_sources)
|
||||
set(multi_value SRCS OPTIONS)
|
||||
cmake_parse_arguments(ARG "" "" "${multi_value}" ${ARGN})
|
||||
|
||||
if(ARG_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unknown idf_component_enable_llvm_opt arguments: ${ARG_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
|
||||
__idf_llvm_opt_get_options(options ${ARG_OPTIONS})
|
||||
if(options)
|
||||
set_property(SOURCE ${ARG_SRCS} APPEND PROPERTY COMPILE_OPTIONS ${options})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Apply menuconfig-selected LLVM optimizations to one source, a source list, or
|
||||
# the whole current component. OPTIONS is an advanced escape hatch for custom
|
||||
# Clang/LLVM optimization options and is ignored with non-Clang toolchains.
|
||||
function(idf_component_enable_llvm_opt)
|
||||
set(multi_value SRCS OPTIONS)
|
||||
cmake_parse_arguments(ARG "" "" "${multi_value}" ${ARGN})
|
||||
|
||||
if(ARG_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unknown idf_component_enable_llvm_opt arguments: ${ARG_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
|
||||
if(ARG_SRCS)
|
||||
__idf_apply_llvm_opt_to_sources(SRCS ${ARG_SRCS} OPTIONS ${ARG_OPTIONS})
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(COMPONENT_LIB AND TARGET ${COMPONENT_LIB})
|
||||
set(component_target ${COMPONENT_LIB})
|
||||
elseif(COMPONENT_TARGET AND TARGET ${COMPONENT_TARGET})
|
||||
set(component_target ${COMPONENT_TARGET})
|
||||
else()
|
||||
message(FATAL_ERROR "idf_component_enable_llvm_opt must be called after idf_component_register")
|
||||
endif()
|
||||
|
||||
__idf_apply_llvm_opt_to_target(${component_target} ${ARG_OPTIONS})
|
||||
endfunction()
|
||||
93
tools/cmake/tests/llvm_optimizations/CMakeLists.txt
Normal file
93
tools/cmake/tests/llvm_optimizations/CMakeLists.txt
Normal file
@@ -0,0 +1,93 @@
|
||||
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
# Never compiles: only inspects COMPILE_OPTIONS. Host CI (Windows minimal
|
||||
# cmake jobs) has no C compiler on PATH, so skip compiler detection.
|
||||
set(CMAKE_C_COMPILER "${CMAKE_COMMAND}" CACHE FILEPATH "" FORCE)
|
||||
set(CMAKE_C_COMPILER_WORKS TRUE CACHE BOOL "" FORCE)
|
||||
set(CMAKE_C_COMPILER_FORCED TRUE)
|
||||
project(llvm_optimizations_test C)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../llvm_optimizations.cmake)
|
||||
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/component.c "void component(void) {}\n")
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/selected.c "void selected(void) {}\n")
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/plain.c "void plain(void) {}\n")
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/gcc.c "void gcc_source(void) {}\n")
|
||||
|
||||
set(CMAKE_C_COMPILER_ID Clang)
|
||||
set(CONFIG_IDF_TOOLCHAIN_CLANG ON)
|
||||
set(CONFIG_COMPILER_LLVM_MEMCPY_OPTIMIZATION ON)
|
||||
|
||||
add_library(component STATIC ${CMAKE_CURRENT_BINARY_DIR}/component.c)
|
||||
set(COMPONENT_LIB component)
|
||||
idf_component_enable_llvm_opt()
|
||||
get_target_property(component_options component COMPILE_OPTIONS)
|
||||
list(JOIN component_options " " component_options)
|
||||
if(NOT component_options MATCHES "riscv-esp32-p4-mem-intrin")
|
||||
message(FATAL_ERROR "Component scope is missing the selected LLVM optimization")
|
||||
endif()
|
||||
if(NOT component_options MATCHES "SHELL:")
|
||||
message(FATAL_ERROR "Component scope must SHELL-wrap -Xclang pairs")
|
||||
endif()
|
||||
if(component_options MATCHES "xespv2p1")
|
||||
message(FATAL_ERROR "PIE revision must come from -mcpu, not +xespv2p1")
|
||||
endif()
|
||||
if(NOT component_options MATCHES "espv-lowering")
|
||||
message(FATAL_ERROR "Component scope is missing +espv-lowering for .m intrinsic isel")
|
||||
endif()
|
||||
|
||||
if(NOT IDF_LLVM_OPT_ALL OR NOT IDF_LLVM_OPT_MEMCPY)
|
||||
message(FATAL_ERROR "Exported IDF_LLVM_OPT_ALL / IDF_LLVM_OPT_MEMCPY must be set when memcpy is enabled")
|
||||
endif()
|
||||
if(NOT "${IDF_LLVM_OPT_ALL}" STREQUAL "${IDF_LLVM_OPT_MEMCPY}")
|
||||
message(FATAL_ERROR "With only the memcpy optimization enabled, IDF_LLVM_OPT_ALL must equal IDF_LLVM_OPT_MEMCPY")
|
||||
endif()
|
||||
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/third_party.c "void third_party(void) {}\n")
|
||||
add_library(fake_lvgl STATIC ${CMAKE_CURRENT_BINARY_DIR}/third_party.c)
|
||||
target_compile_options(fake_lvgl PRIVATE ${IDF_LLVM_OPT_ALL})
|
||||
get_target_property(third_party_options fake_lvgl COMPILE_OPTIONS)
|
||||
list(JOIN third_party_options " " third_party_options)
|
||||
if(NOT third_party_options MATCHES "riscv-esp32-p4-mem-intrin")
|
||||
message(FATAL_ERROR "Standard CMake apply of IDF_LLVM_OPT_ALL missed the memcpy flags")
|
||||
endif()
|
||||
if(NOT third_party_options MATCHES "SHELL:")
|
||||
message(FATAL_ERROR "IDF_LLVM_OPT_ALL must be SHELL-wrapped for target_compile_options")
|
||||
endif()
|
||||
|
||||
add_library(selected STATIC
|
||||
${CMAKE_CURRENT_BINARY_DIR}/selected.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/plain.c)
|
||||
set(COMPONENT_LIB selected)
|
||||
idf_component_enable_llvm_opt(
|
||||
SRCS ${CMAKE_CURRENT_BINARY_DIR}/selected.c
|
||||
OPTIONS "-custom-llvm-option")
|
||||
get_source_file_property(selected_options ${CMAKE_CURRENT_BINARY_DIR}/selected.c COMPILE_OPTIONS)
|
||||
get_source_file_property(plain_options ${CMAKE_CURRENT_BINARY_DIR}/plain.c COMPILE_OPTIONS)
|
||||
list(JOIN selected_options " " selected_options)
|
||||
if(NOT selected_options MATCHES "riscv-esp32-p4-mem-intrin"
|
||||
OR NOT selected_options MATCHES "custom-llvm-option")
|
||||
message(FATAL_ERROR "Source scope is missing selected or custom options")
|
||||
endif()
|
||||
if(selected_options MATCHES "SHELL:")
|
||||
message(FATAL_ERROR "Source COMPILE_OPTIONS must not use SHELL:")
|
||||
endif()
|
||||
if(plain_options)
|
||||
message(FATAL_ERROR "LLVM optimization leaked to an unselected source")
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_COMPILER_ID GNU)
|
||||
unset(CONFIG_IDF_TOOLCHAIN_CLANG)
|
||||
add_library(gcc_component STATIC ${CMAKE_CURRENT_BINARY_DIR}/gcc.c)
|
||||
set(COMPONENT_LIB gcc_component)
|
||||
idf_component_enable_llvm_opt(OPTIONS "-custom-llvm-option")
|
||||
get_target_property(gcc_options gcc_component COMPILE_OPTIONS)
|
||||
if(gcc_options)
|
||||
message(FATAL_ERROR "LLVM optimization options must be ignored for non-Clang compilers")
|
||||
endif()
|
||||
if(IDF_LLVM_OPT_ALL OR IDF_LLVM_OPT_MEMCPY)
|
||||
message(FATAL_ERROR "Exported LLVM opt flags must be empty for non-Clang compilers")
|
||||
endif()
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
include_guard(GLOBAL)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/llvm_optimizations.cmake)
|
||||
|
||||
#[[
|
||||
check_expected_tool_version()
|
||||
|
||||
@@ -607,6 +609,7 @@ endfunction()
|
||||
[REQUIRED_IDF_TARGETS <target>...]
|
||||
[EMBED_FILES <file>...]
|
||||
[EMBED_TXTFILES <file>...]
|
||||
[ENABLE_LLVM_OPT]
|
||||
[WHOLE_ARCHIVE])
|
||||
|
||||
*SRCS[in,opt]*
|
||||
@@ -659,12 +662,16 @@ endfunction()
|
||||
|
||||
Link the component as --whole-archive.
|
||||
|
||||
*ENABLE_LLVM_OPT[in,opt]*
|
||||
|
||||
Enable the LLVM optimizations selected in menuconfig for this component.
|
||||
|
||||
Register a new component with the build system using the provided options.
|
||||
This function also automatically links all commonly required and managed
|
||||
components to the component's target.
|
||||
#]]
|
||||
function(idf_component_register)
|
||||
set(options WHOLE_ARCHIVE)
|
||||
set(options WHOLE_ARCHIVE ENABLE_LLVM_OPT)
|
||||
set(one_value KCONFIG KCONFIG_PROJBUILD)
|
||||
set(multi_value SRCS SRC_DIRS EXCLUDE_SRCS
|
||||
INCLUDE_DIRS PRIV_INCLUDE_DIRS LDFRAGMENTS REQUIRES
|
||||
@@ -737,8 +744,13 @@ function(idf_component_register)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
__idf_llvm_opt_publish_flags()
|
||||
|
||||
if(sources OR ARG_EMBED_FILES OR ARG_EMBED_TXTFILES)
|
||||
add_library("${COMPONENT_TARGET}" STATIC ${sources})
|
||||
if(ARG_ENABLE_LLVM_OPT)
|
||||
__idf_apply_llvm_opt_to_target("${COMPONENT_TARGET}")
|
||||
endif()
|
||||
|
||||
foreach(include_dir IN LISTS include_dirs)
|
||||
target_include_directories("${COMPONENT_TARGET}" PUBLIC "${include_dir}")
|
||||
|
||||
@@ -440,3 +440,14 @@ def test_cmake_preset_sdkconfig_defaults_integration(test_app_copy: Path) -> Non
|
||||
sdkconfig_content = sdkconfig_path.read_text()
|
||||
assert 'CONFIG_LWIP_IPV6=y' in sdkconfig_content
|
||||
assert 'CONFIG_ESP_TASK_WDT_TIMEOUT_S=15' in sdkconfig_content
|
||||
|
||||
|
||||
def test_cmake_llvm_optimizations_framework(idf_copy: Path) -> None:
|
||||
logging.info('Test CMake configuration of the LLVM optimization framework')
|
||||
test_project = idf_copy / 'tools' / 'cmake' / 'tests' / 'llvm_optimizations'
|
||||
# The standalone test project fakes the compiler ID and CONFIG_* variables to
|
||||
# verify component/source scope flag selection, custom options, non-leakage
|
||||
# to unselected sources, exported IDF_LLVM_OPT_* for stock CMake, and the
|
||||
# non-Clang no-op. run_cmake raises on failure.
|
||||
# Windows runners default to NMake; nmake is absent. Ninja is on PATH.
|
||||
run_cmake(str(test_project), '-G', 'Ninja')
|
||||
|
||||
Reference in New Issue
Block a user