Skip to content

Commit f293272

Browse files
committed
Arm backend: Add Clang bare-metal toolchain
Add Arm Toolchain for Embedded as an alternative to GCC for Arm bare-metal builds. Wire the Clang toolchain through setup, runner, and Cortex-M workflows. Add Picolibc startup, TLS, UART, scatter-loading, and linker-script integration for Corstone-300 and Corstone-320. Test plan: Built and ran embedded-PTE runners on Corstone-300 U55 and Corstone-320 U85 FVPs with Clang 23.1.0. Verified one NPU inference and delegation with clean completion. Built the Corstone-300 U55 runner with GCC for compatibility. Verified Picolibc startup with cppcheck and compiled it with Arm Clang 23.1.0 and Arm GCC 15.2. Passed targeted lintrunner, CMake formatting, and git diff checks. Authored with assistance from OpenAI Codex. Change-Id: Iad2da4308cd63406bf0f015fbc5e16bfd49990d9 Signed-off-by: Per Held <per.held@arm.com>
1 parent 9aa28d6 commit f293272

18 files changed

Lines changed: 531 additions & 39 deletions

‎CMakeLists.txt‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,8 +313,17 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
313313
endif()
314314

315315
if(EXECUTORCH_OPTIMIZE_SIZE)
316-
# -Os: Optimize for size.
317-
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Os")
316+
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
317+
# TODO: Restore -Os after the LLVM register coalescer crash is fixed:
318+
# https://github.com/llvm/llvm-project/issues/229400
319+
set(_executorch_size_optimization_flag -Oz)
320+
else()
321+
set(_executorch_size_optimization_flag -Os)
322+
endif()
323+
set(CMAKE_CXX_FLAGS_RELEASE
324+
"${CMAKE_CXX_FLAGS_RELEASE} ${_executorch_size_optimization_flag}"
325+
)
326+
unset(_executorch_size_optimization_flag)
318327
else()
319328
# -O2: Moderate opt.
320329
set(CMAKE_CXX_FLAGS_RELEASE "-O2 ${CMAKE_CXX_FLAGS_RELEASE}")

‎backends/arm/cmake/ArmRunnerUtilsInternal.cmake‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,17 @@ function(arm_runner_configure_ethos_u_platform)
335335
add_corstone_subdirectory(
336336
"${ARG_SYSTEM_CONFIG}" "${ARG_SDK_PATH}" "${ARG_MEMORY_MODE}"
337337
)
338+
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND TARGET ethosu_target_link)
339+
get_target_property(
340+
_ethosu_target_link_options ethosu_target_link INTERFACE_LINK_OPTIONS
341+
)
342+
list(REMOVE_ITEM _ethosu_target_link_options --entry Reset_Handler)
343+
list(APPEND _ethosu_target_link_options "LINKER:--entry=Reset_Handler")
344+
set_property(
345+
TARGET ethosu_target_link PROPERTY INTERFACE_LINK_OPTIONS
346+
"${_ethosu_target_link_options}"
347+
)
348+
endif()
338349
configure_timing_adapters("${ARG_SYSTEM_CONFIG}" "${ARG_MEMORY_MODE}")
339350
foreach(_platform_variable TARGET_BOARD ETHOSU_MODEL ETHOSU_ARENA)
340351
if(DEFINED ${_platform_variable})
@@ -747,14 +758,14 @@ function(arm_runner_configure_linker_script)
747758

748759
get_corstone_linker_script(_linker_script "${ARG_SYSTEM_CONFIG}")
749760

750-
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
761+
if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
751762
set(LINK_FILE_EXT ld)
752763
set(LINK_FILE_OPTION "-T")
753764
set(COMPILER_PREPROCESSOR_OPTIONS -E -x c -P)
754765
else()
755766
message(
756767
FATAL_ERROR
757-
"arm_runner_configure_linker_script only supports the GNU compiler."
768+
"arm_runner_configure_linker_script only supports GNU and Clang."
758769
)
759770
endif()
760771

‎backends/arm/cmake/linker_scripts/Corstone-300.ld‎

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ PHDRS
5353
rom_sram PT_LOAD;
5454
rom_rodata PT_LOAD;
5555
rom_model PT_LOAD;
56+
#ifdef __clang__
57+
tls PT_TLS;
58+
#endif
5659
null PT_NULL;
5760
}
5861

@@ -117,6 +120,8 @@ SECTIONS
117120
*(.dtors)
118121

119122
KEEP(*(.eh_frame*))
123+
*(.got)
124+
*(.got.*)
120125
} > ITCM :rom_exec
121126

122127
/*
@@ -143,7 +148,11 @@ SECTIONS
143148
__exidx_end = .;
144149
} > ITCM :rom_exec
145150

151+
#ifdef __clang__
152+
.copy.table :
153+
#else
146154
.copy.table (READONLY) :
155+
#endif
147156
{
148157
. = ALIGN(4);
149158
__copy_table_start__ = .;
@@ -155,17 +164,26 @@ SECTIONS
155164
LONG (__sram_data_start__)
156165
LONG ((__sram_data_end__ - __sram_data_start__) / 4)
157166

158-
LONG (__eddr_data + (__sram_data_end__ - __sram_data_start__))
167+
LONG (LOADADDR(.rodata))
159168
LONG (__rodata_start__)
160169
LONG ((__rodata_end__ - __rodata_start__) / 4)
161170

162171
__copy_table_end__ = .;
163172
} > ITCM :rom_exec
164173

174+
#ifdef __clang__
175+
.zero.table :
176+
#else
165177
.zero.table (READONLY) :
178+
#endif
166179
{
167180
. = ALIGN(4);
168181
__zero_table_start__ = .;
182+
#ifdef __clang__
183+
LONG (ADDR(.tbss))
184+
LONG (SIZEOF(.tbss) / 4)
185+
#endif
186+
169187
LONG (__bss_start__)
170188
LONG ((__bss_end__ - __bss_start__) / 4)
171189
__zero_table_end__ = .;
@@ -188,6 +206,9 @@ SECTIONS
188206

189207
. = ALIGN(4);
190208
/* preinit data */
209+
#ifdef __clang__
210+
__bothinit_array_start = .;
211+
#endif
191212
PROVIDE_HIDDEN (__preinit_array_start = .);
192213
KEEP(*(.preinit_array))
193214
PROVIDE_HIDDEN (__preinit_array_end = .);
@@ -198,6 +219,9 @@ SECTIONS
198219
KEEP(*(SORT(.init_array.*)))
199220
KEEP(*(.init_array))
200221
PROVIDE_HIDDEN (__init_array_end = .);
222+
#ifdef __clang__
223+
__bothinit_array_end = .;
224+
#endif
201225

202226
. = ALIGN(4);
203227
/* finit data */
@@ -209,9 +233,29 @@ SECTIONS
209233
KEEP(*(.jcr*))
210234
. = ALIGN(4);
211235
/* All data end */
236+
#ifndef __clang__
212237
__data_end__ = .;
238+
#endif
213239
} > DTCM :rom_data
214240

241+
#ifdef __clang__
242+
.tdata : AT(LOADADDR(.data) + SIZEOF(.data))
243+
{
244+
__tls_start = .;
245+
*(.tdata .tdata.* .gnu.linkonce.td.*)
246+
. = ALIGN(4);
247+
__data_end__ = .;
248+
} > DTCM :rom_data :tls
249+
250+
.tbss (NOLOAD) :
251+
{
252+
*(.tbss .tbss.* .gnu.linkonce.tb.*)
253+
*(.tcommon)
254+
. = ALIGN(4);
255+
__tls_end = .;
256+
} > DTCM :null :tls
257+
#endif
258+
215259
#if (ETHOSU_MODEL == 0)
216260
.sram.rodata :
217261
{
@@ -246,7 +290,6 @@ SECTIONS
246290
* (expected_output_data_sec)
247291
. = ALIGN(16);
248292
* (sec_command_stream, sec_weight_data, sec_input_data)
249-
* (.got*)
250293
* (ethosu_core_in_queue)
251294
* (ethosu_core_out_queue)
252295
. = ALIGN(4);
@@ -266,6 +309,7 @@ SECTIONS
266309
*(.sram.data)
267310
. = ALIGN(4);
268311
*op_*.cpp.obj (*.text*)
312+
. = ALIGN(4);
269313
__sram_data_end__ = .;
270314
} > BRAM AT >DDR :rom_sram
271315

@@ -320,6 +364,20 @@ SECTIONS
320364
} > DTCM :null
321365
PROVIDE(__stack = __StackTop);
322366

367+
#ifdef __clang__
368+
PROVIDE(__data_start = __data_start__);
369+
PROVIDE(__data_source = LOADADDR(.data));
370+
PROVIDE(__data_size = __data_end__ - __data_start__);
371+
PROVIDE(__bss_start = __bss_start__);
372+
PROVIDE(__bss_size = __bss_end__ - __bss_start__);
373+
PROVIDE(__tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)));
374+
PROVIDE(__tls_base = __tls_start);
375+
PROVIDE(__arm32_tls_tcb_offset = MAX(8, __tls_align));
376+
PROVIDE(__heap_start = __end__);
377+
PROVIDE(__heap_end = __HeapLimit);
378+
PROVIDE(__stack_limit = __StackLimit);
379+
#endif
380+
323381
__RAM_segment_used_end__ = .;
324382

325383
/* Check if data + heap + stack exceeds DTCM limit */

‎backends/arm/cmake/linker_scripts/Corstone-320.ld‎

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,12 @@ PHDRS
5050
{
5151
rom_boot PT_LOAD;
5252
rom_exec PT_LOAD;
53+
rom_sram PT_LOAD;
5354
rom_dram PT_LOAD;
5455
rom_model PT_LOAD;
56+
#ifdef __clang__
57+
tls PT_TLS;
58+
#endif
5559
null PT_NULL;
5660
}
5761

@@ -100,7 +104,11 @@ SECTIONS
100104
* Alignment requirement from up to 496 interrupts, rounded to the closest
101105
* power of two equals 512 (words), thus 2048 bytes.
102106
*/
107+
#ifdef __clang__
108+
.data.vtable_rw (NOLOAD):
109+
#else
103110
.data.vtable_rw (COPY):
111+
#endif
104112
{
105113
. = ALIGN(0x800);
106114
KEEP(*(.vtable_rw))
@@ -143,12 +151,34 @@ SECTIONS
143151
*(vtable)
144152
*(.data)
145153
*(.data.*)
154+
*(.got)
155+
*(.got.*)
146156
*(.rodata*)
147157

148158
. = ALIGN(4);
159+
#ifndef __clang__
149160
__data_end__ = .;
161+
#endif
150162
} > BRAM :rom_exec
151163

164+
#ifdef __clang__
165+
.tdata :
166+
{
167+
__tls_start = .;
168+
*(.tdata .tdata.* .gnu.linkonce.td.*)
169+
. = ALIGN(4);
170+
__data_end__ = .;
171+
} > BRAM :rom_exec :tls
172+
173+
.tbss (NOLOAD) :
174+
{
175+
*(.tbss .tbss.* .gnu.linkonce.tb.*)
176+
*(.tcommon)
177+
. = ALIGN(4);
178+
__tls_end = .;
179+
} > BRAM :null :tls
180+
#endif
181+
152182
.ARM.extab :
153183
{
154184
*(.ARM.extab* .gnu.linkonce.armextab.*)
@@ -178,6 +208,11 @@ SECTIONS
178208
. = ALIGN(4);
179209
__zero_table_start__ = .;
180210

211+
#ifdef __clang__
212+
LONG (ADDR(.tbss))
213+
LONG (SIZEOF(.tbss) / 4)
214+
#endif
215+
181216
LONG (ADDR(.bss))
182217
LONG (SIZEOF(.bss) / 4)
183218

@@ -194,6 +229,9 @@ SECTIONS
194229

195230
. = ALIGN(4);
196231
/* preinit data */
232+
#ifdef __clang__
233+
__bothinit_array_start = .;
234+
#endif
197235
PROVIDE_HIDDEN (__preinit_array_start = .);
198236
KEEP(*(.preinit_array))
199237
PROVIDE_HIDDEN (__preinit_array_end = .);
@@ -204,6 +242,9 @@ SECTIONS
204242
KEEP(*(SORT(.init_array.*)))
205243
KEEP(*(.init_array))
206244
PROVIDE_HIDDEN (__init_array_end = .);
245+
#ifdef __clang__
246+
__bothinit_array_end = .;
247+
#endif
207248

208249
. = ALIGN(4);
209250
/* finit data */
@@ -213,7 +254,8 @@ SECTIONS
213254
PROVIDE_HIDDEN (__fini_array_end = .);
214255

215256
KEEP(*(.jcr*))
216-
} > SRAM :rom_dram
257+
. = ALIGN(4);
258+
} > SRAM :rom_sram
217259

218260
#if (ETHOSU_MODEL == 0)
219261
.sram.rodata :
@@ -239,6 +281,7 @@ SECTIONS
239281
*(.fast_memory)
240282
. = ALIGN(16);
241283
*(.bss.ethosu_scratch);
284+
. = ALIGN(4);
242285
} > SRAM :null
243286

244287
.ddr :
@@ -307,6 +350,20 @@ SECTIONS
307350
} > DTCM :null
308351
PROVIDE(__stack = __StackTop);
309352

353+
#ifdef __clang__
354+
PROVIDE(__data_start = __data_start__);
355+
PROVIDE(__data_source = LOADADDR(.data));
356+
PROVIDE(__data_size = __data_end__ - __data_start__);
357+
PROVIDE(__bss_start = __bss_start__);
358+
PROVIDE(__bss_size = __bss_end__ - __bss_start__);
359+
PROVIDE(__tls_align = MAX(ALIGNOF(.tdata), ALIGNOF(.tbss)));
360+
PROVIDE(__tls_base = __tls_start);
361+
PROVIDE(__arm32_tls_tcb_offset = MAX(8, __tls_align));
362+
PROVIDE(__heap_start = __end__);
363+
PROVIDE(__heap_end = __HeapLimit);
364+
PROVIDE(__stack_limit = __StackLimit);
365+
#endif
366+
310367
/* Check if stack exceeds DTCM limit */
311368
ASSERT(LENGTH(DTCM) >= __STACK_SIZE, "region DTCM overflowed with stack")
312369
}

‎backends/arm/scripts/build_executor_runner.sh‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ help() {
6868
echo " --output=<FOLDER> Output folder Default: <MODEL>/<MODEL>_<TARGET INFO>.pte"
6969
echo " --et_build_root=<FOLDER> Build output root folder to use, defaults to ${et_build_root}"
7070
echo " --ethosu_tools_dir=<FOLDER> Path to your Ethos-U tools dir if you not using default: ${ethosu_tools_dir}"
71-
echo " --toolchain=<TOOLCHAIN> Toolchain can be specified (arm-none-eabi-gcc, arm-zephyr-eabi-gcc). Default: ${toolchain}"
71+
echo " --toolchain=<TOOLCHAIN> Toolchain can be specified (arm-none-eabi-gcc, arm-none-eabi-clang, arm-zephyr-eabi-gcc). Default: ${toolchain}"
7272
echo " --select_ops_list=<OPS> Comma separated list of portable (non-delegated) kernels to include Default: ${select_ops_list}"
7373
echo " NOTE: This is used when select_ops_model is not possible to use, e.g. for semihosting or bundleio."
7474
echo " See https://docs.pytorch.org/executorch/stable/kernel-library-selective-build.html for more information."
@@ -98,16 +98,21 @@ done
9898

9999
if [[ ${toolchain} == "arm-none-eabi-gcc" ]]; then
100100
toolchain_cmake=${et_root_dir}/examples/arm/ethos-u-setup/${toolchain}.cmake
101+
size_tool=arm-none-eabi-size
102+
elif [[ ${toolchain} == "arm-none-eabi-clang" ]]; then
103+
toolchain_cmake=${et_root_dir}/examples/arm/ethos-u-setup/${toolchain}.cmake
104+
size_tool=llvm-size
101105
elif [[ ${toolchain} == "arm-zephyr-eabi-gcc" ]]; then
102106
toolchain_cmake=${et_root_dir}/examples/zephyr/x86_64-linux-arm-zephyr-eabi-gcc.cmake
107+
size_tool=arm-zephyr-eabi-size
103108
elif [[ ${toolchain} == "aarch64-linux-musl-gcc" ]]; then
104109
echo "Error: aarch64-linux-musl-gcc is not supported for this bare-metal runner."
105110
echo " Use executorch/backends/arm/scripts/build_executorch.sh with aarch64-linux-musl-gcc"
106111
echo " or run the direct drive CMake flow to build executor_runner for Linux."
107112
exit 1;
108113
else
109114
echo "Error: Invalid toolchain selection, provided: ${toolchain}"
110-
echo " Valid options are {arm-none-eabi-gcc, arm-zephyr-eabi-gcc}"
115+
echo " Valid options are {arm-none-eabi-gcc, arm-none-eabi-clang, arm-zephyr-eabi-gcc}"
111116
exit 1;
112117
fi
113118
toolchain_cmake=$(realpath ${toolchain_cmake})
@@ -252,6 +257,6 @@ cmake --build ${output_folder} -j"${parallel_jobs}" -- arm_executor_runner
252257

253258
echo "[${BASH_SOURCE[0]}] Generated ${toolchain} elf file:"
254259
find ${output_folder} -name "arm_executor_runner"
255-
echo "executable_text: $(find ${output_folder} -name arm_executor_runner -exec ${toolchain/-gcc/-size} {} \; | grep -v filename | awk '{print $1}') bytes"
256-
echo "executable_data: $(find ${output_folder} -name arm_executor_runner -exec ${toolchain/-gcc/-size} {} \; | grep -v filename | awk '{print $2}') bytes"
257-
echo "executable_bss: $(find ${output_folder} -name arm_executor_runner -exec ${toolchain/-gcc/-size} {} \; | grep -v filename | awk '{print $3}') bytes"
260+
echo "executable_text: $(find ${output_folder} -name arm_executor_runner -exec ${size_tool} {} \; | grep -v filename | awk '{print $1}') bytes"
261+
echo "executable_data: $(find ${output_folder} -name arm_executor_runner -exec ${size_tool} {} \; | grep -v filename | awk '{print $2}') bytes"
262+
echo "executable_bss: $(find ${output_folder} -name arm_executor_runner -exec ${size_tool} {} \; | grep -v filename | awk '{print $3}') bytes"

0 commit comments

Comments
 (0)