[libc-commits] [libc] [libc] Add bare-metal Hexagon port (PR #217528)

via libc-commits libc-commits at lists.llvm.org
Fri Aug 28 07:59:11 PDT 2026


https://github.com/quic-k updated https://github.com/llvm/llvm-project/pull/217528

>From 89ea5c9cf52065c67e0d14eb431f3c785eb82bae Mon Sep 17 00:00:00 2001
From: Kushal Pal <kushpal at qti.qualcomm.com>
Date: Mon, 17 Aug 2026 19:46:48 +0530
Subject: [PATCH] [libc] Add bare-metal Hexagon port

Add support for llvm-libc on Hexagon bare-metal targets.

Recognize the hexagon architecture in CMake.
Add the Hexagon bare-metal config: entrypoints.txt and headers.txt, reusing
the common bare-metal config.json.
Add Hexagon startup code (crt1) with semihosting trap handling.
Add a Hexagon FEnvImpl and select it in FEnvImpl.h; define fenv_t for Hexagon.
Add Hexagon setjmp/longjmp, saving the callee-saved registers r16-r27 as
register pairs along with sp, fp and lr, and define __jmp_buf accordingly.

Signed-off-by: Kushal Pal <kushpal at qti.qualcomm.com>
---
 .../cmake/modules/LLVMLibCArchitectures.cmake |   4 +
 libc/config/baremetal/hexagon/entrypoints.txt | 998 ++++++++++++++++++
 libc/config/baremetal/hexagon/headers.txt     |  32 +
 libc/include/llvm-libc-types/__jmp_buf.h      |   6 +
 libc/include/llvm-libc-types/fenv_t.h         |   2 +
 libc/src/__support/FPUtil/FEnvImpl.h          |   2 +
 libc/src/__support/FPUtil/hexagon/FEnvImpl.h  | 210 ++++
 .../macros/properties/architectures.h         |   4 +
 libc/src/__support/memory_size.h              |   7 +-
 libc/src/setjmp/hexagon/CMakeLists.txt        |  19 +
 libc/src/setjmp/hexagon/longjmp.cpp           |  35 +
 libc/src/setjmp/hexagon/setjmp.cpp            |  36 +
 libc/startup/baremetal/hexagon/CMakeLists.txt |  12 +
 libc/startup/baremetal/hexagon/start.cpp      |  95 ++
 libc/test/UnitTest/CMakeLists.txt             |  24 +-
 .../UnitTest/HexagonBaremetalTestUtils.cpp    | 119 +++
 libc/test/UnitTest/LibcTest.cpp               |   8 +-
 libc/test/UnitTest/LibcTest.h                 |   2 +
 libc/test/UnitTest/llvm-libc-baremetal.ld     |   3 +
 libc/test/src/math/smoke/exp10m1f_test.cpp    |  11 +-
 20 files changed, 1614 insertions(+), 15 deletions(-)
 create mode 100644 libc/config/baremetal/hexagon/entrypoints.txt
 create mode 100644 libc/config/baremetal/hexagon/headers.txt
 create mode 100644 libc/src/__support/FPUtil/hexagon/FEnvImpl.h
 create mode 100644 libc/src/setjmp/hexagon/CMakeLists.txt
 create mode 100644 libc/src/setjmp/hexagon/longjmp.cpp
 create mode 100644 libc/src/setjmp/hexagon/setjmp.cpp
 create mode 100644 libc/startup/baremetal/hexagon/CMakeLists.txt
 create mode 100644 libc/startup/baremetal/hexagon/start.cpp
 create mode 100644 libc/test/UnitTest/HexagonBaremetalTestUtils.cpp

diff --git a/libc/cmake/modules/LLVMLibCArchitectures.cmake b/libc/cmake/modules/LLVMLibCArchitectures.cmake
index 3149c16f9e2eb..327ddc5dfa5ce 100644
--- a/libc/cmake/modules/LLVMLibCArchitectures.cmake
+++ b/libc/cmake/modules/LLVMLibCArchitectures.cmake
@@ -53,6 +53,8 @@ function(get_arch_and_system_from_triple triple arch_var sys_var)
     set(target_arch "nvptx")
   elseif(target_arch MATCHES "^spirv")
     set(target_arch "spirv")
+  elseif(target_arch MATCHES "^hexagon")
+    set(target_arch "hexagon")
   else()
     return()
   endif()
@@ -190,6 +192,8 @@ elseif(LIBC_TARGET_ARCHITECTURE STREQUAL "spirv")
   set(LIBC_TARGET_ARCHITECTURE_IS_SPIRV TRUE)
 elseif(LIBC_TARGET_ARCHITECTURE STREQUAL "power")
   set(LIBC_TARGET_ARCHITECTURE_IS_POWERPC TRUE)
+elseif(LIBC_TARGET_ARCHITECTURE STREQUAL "hexagon")
+  set(LIBC_TARGET_ARCHITECTURE_IS_HEXAGON TRUE)
 else()
   message(FATAL_ERROR
     "Unsupported libc target architecture ${LIBC_TARGET_ARCHITECTURE}")
diff --git a/libc/config/baremetal/hexagon/entrypoints.txt b/libc/config/baremetal/hexagon/entrypoints.txt
new file mode 100644
index 0000000000000..c6244a2f4d728
--- /dev/null
+++ b/libc/config/baremetal/hexagon/entrypoints.txt
@@ -0,0 +1,998 @@
+set(TARGET_LIBC_ENTRYPOINTS
+    # assert.h entrypoints
+    libc.src.assert.__assert_fail
+
+    # compiler entrypoints (no corresponding header)
+    libc.src.compiler.__stack_chk_fail
+
+    # ctype.h entrypoints
+    libc.src.ctype.isalnum
+    libc.src.ctype.isalpha
+    libc.src.ctype.isascii
+    libc.src.ctype.isblank
+    libc.src.ctype.iscntrl
+    libc.src.ctype.isdigit
+    libc.src.ctype.isgraph
+    libc.src.ctype.islower
+    libc.src.ctype.isprint
+    libc.src.ctype.ispunct
+    libc.src.ctype.isspace
+    libc.src.ctype.isupper
+    libc.src.ctype.isxdigit
+    libc.src.ctype.toascii
+    libc.src.ctype.tolower
+    libc.src.ctype.toupper
+
+    # ctype.h entrypoints
+    libc.src.ctype.isalnum_l
+    libc.src.ctype.isalpha_l
+    libc.src.ctype.isblank_l
+    libc.src.ctype.iscntrl_l
+    libc.src.ctype.isdigit_l
+    libc.src.ctype.isgraph_l
+    libc.src.ctype.islower_l
+    libc.src.ctype.isprint_l
+    libc.src.ctype.ispunct_l
+    libc.src.ctype.isspace_l
+    libc.src.ctype.isupper_l
+    libc.src.ctype.isxdigit_l
+    libc.src.ctype.tolower_l
+    libc.src.ctype.toupper_l
+
+    # errno.h entrypoints
+    libc.src.errno.errno
+
+    # locale.h entrypoints
+    libc.src.locale.localeconv
+    libc.src.locale.duplocale
+    libc.src.locale.freelocale
+    libc.src.locale.localeconv
+    libc.src.locale.newlocale
+    libc.src.locale.setlocale
+    libc.src.locale.uselocale
+
+    # setjmp.h entrypoints
+    libc.src.setjmp.longjmp
+    libc.src.setjmp.setjmp
+
+    # string.h entrypoints
+    libc.src.string.memccpy
+    libc.src.string.memchr
+    libc.src.string.memcmp
+    libc.src.string.memcpy
+    libc.src.string.memmem
+    libc.src.string.memmove
+    libc.src.string.mempcpy
+    libc.src.string.memrchr
+    libc.src.string.memset
+    libc.src.string.memset_explicit
+    libc.src.string.stpcpy
+    libc.src.string.stpncpy
+    libc.src.string.strcasestr
+    libc.src.string.strcat
+    libc.src.string.strchr
+    libc.src.string.strchrnul
+    libc.src.string.strcmp
+    libc.src.string.strcoll
+    libc.src.string.strcpy
+    libc.src.string.strcspn
+    libc.src.string.strdup
+    libc.src.string.strerror
+    libc.src.string.strerror_r
+    libc.src.string.strlcat
+    libc.src.string.strlcpy
+    libc.src.string.strlen
+    libc.src.string.strncat
+    libc.src.string.strncmp
+    libc.src.string.strncpy
+    libc.src.string.strndup
+    libc.src.string.strnlen
+    libc.src.string.strnlen_s
+    libc.src.string.strpbrk
+    libc.src.string.strrchr
+    libc.src.string.strsep
+    libc.src.string.strspn
+    libc.src.string.strstr
+    libc.src.string.strtok
+    libc.src.string.strtok_r
+    libc.src.string.strxfrm
+
+    # string.h entrypoints
+    libc.src.string.strcoll_l
+    libc.src.string.strxfrm_l
+
+    # strings.h entrypoints
+    libc.src.strings.bcmp
+    libc.src.strings.bcopy
+    libc.src.strings.bzero
+    libc.src.strings.ffs
+    libc.src.strings.ffsl
+    libc.src.strings.ffsll
+    libc.src.strings.index
+    libc.src.strings.rindex
+    libc.src.strings.strcasecmp
+    libc.src.strings.strncasecmp
+
+    # strings.h entrypoints
+    libc.src.strings.strcasecmp_l
+    libc.src.strings.strncasecmp_l
+
+    # inttypes.h entrypoints
+    libc.src.inttypes.imaxabs
+    libc.src.inttypes.imaxdiv
+    libc.src.inttypes.strtoimax
+    libc.src.inttypes.strtoumax
+
+    # stdio.h entrypoints
+    libc.src.stdio.asprintf
+    libc.src.stdio.feof
+    libc.src.stdio.ferror
+    libc.src.stdio.fgetc
+    libc.src.stdio.fgets
+    libc.src.stdio.fprintf
+    libc.src.stdio.fputc
+    libc.src.stdio.fputs
+    libc.src.stdio.fread
+    libc.src.stdio.fscanf
+    libc.src.stdio.fwrite
+    libc.src.stdio.getchar
+    libc.src.stdio.printf
+    libc.src.stdio.putc
+    libc.src.stdio.putchar
+    libc.src.stdio.puts
+    libc.src.stdio.remove
+    libc.src.stdio.scanf
+    libc.src.stdio.snprintf
+    libc.src.stdio.sprintf
+    libc.src.stdio.sscanf
+    libc.src.stdio.stderr
+    libc.src.stdio.stdin
+    libc.src.stdio.stdout
+    libc.src.stdio.vasprintf
+    libc.src.stdio.vfprintf
+    libc.src.stdio.vfscanf
+    libc.src.stdio.vprintf
+    libc.src.stdio.vscanf
+    libc.src.stdio.vsnprintf
+    libc.src.stdio.vsprintf
+    libc.src.stdio.vsscanf
+
+    # stdbit.h entrypoints
+    libc.src.stdbit.stdc_bit_ceil_uc
+    libc.src.stdbit.stdc_bit_ceil_ui
+    libc.src.stdbit.stdc_bit_ceil_ul
+    libc.src.stdbit.stdc_bit_ceil_ull
+    libc.src.stdbit.stdc_bit_ceil_us
+    libc.src.stdbit.stdc_bit_floor_uc
+    libc.src.stdbit.stdc_bit_floor_ui
+    libc.src.stdbit.stdc_bit_floor_ul
+    libc.src.stdbit.stdc_bit_floor_ull
+    libc.src.stdbit.stdc_bit_floor_us
+    libc.src.stdbit.stdc_bit_width_uc
+    libc.src.stdbit.stdc_bit_width_ui
+    libc.src.stdbit.stdc_bit_width_ul
+    libc.src.stdbit.stdc_bit_width_ull
+    libc.src.stdbit.stdc_bit_width_us
+    libc.src.stdbit.stdc_count_ones_uc
+    libc.src.stdbit.stdc_count_ones_ui
+    libc.src.stdbit.stdc_count_ones_ul
+    libc.src.stdbit.stdc_count_ones_ull
+    libc.src.stdbit.stdc_count_ones_us
+    libc.src.stdbit.stdc_count_zeros_uc
+    libc.src.stdbit.stdc_count_zeros_ui
+    libc.src.stdbit.stdc_count_zeros_ul
+    libc.src.stdbit.stdc_count_zeros_ull
+    libc.src.stdbit.stdc_count_zeros_us
+    libc.src.stdbit.stdc_first_leading_one_uc
+    libc.src.stdbit.stdc_first_leading_one_ui
+    libc.src.stdbit.stdc_first_leading_one_ul
+    libc.src.stdbit.stdc_first_leading_one_ull
+    libc.src.stdbit.stdc_first_leading_one_us
+    libc.src.stdbit.stdc_first_leading_zero_uc
+    libc.src.stdbit.stdc_first_leading_zero_ui
+    libc.src.stdbit.stdc_first_leading_zero_ul
+    libc.src.stdbit.stdc_first_leading_zero_ull
+    libc.src.stdbit.stdc_first_leading_zero_us
+    libc.src.stdbit.stdc_first_trailing_one_uc
+    libc.src.stdbit.stdc_first_trailing_one_ui
+    libc.src.stdbit.stdc_first_trailing_one_ul
+    libc.src.stdbit.stdc_first_trailing_one_ull
+    libc.src.stdbit.stdc_first_trailing_one_us
+    libc.src.stdbit.stdc_first_trailing_zero_uc
+    libc.src.stdbit.stdc_first_trailing_zero_ui
+    libc.src.stdbit.stdc_first_trailing_zero_ul
+    libc.src.stdbit.stdc_first_trailing_zero_ull
+    libc.src.stdbit.stdc_first_trailing_zero_us
+    libc.src.stdbit.stdc_has_single_bit_uc
+    libc.src.stdbit.stdc_has_single_bit_ui
+    libc.src.stdbit.stdc_has_single_bit_ul
+    libc.src.stdbit.stdc_has_single_bit_ull
+    libc.src.stdbit.stdc_has_single_bit_us
+    libc.src.stdbit.stdc_leading_ones_uc
+    libc.src.stdbit.stdc_leading_ones_ui
+    libc.src.stdbit.stdc_leading_ones_ul
+    libc.src.stdbit.stdc_leading_ones_ull
+    libc.src.stdbit.stdc_leading_ones_us
+    libc.src.stdbit.stdc_leading_zeros_uc
+    libc.src.stdbit.stdc_leading_zeros_ui
+    libc.src.stdbit.stdc_leading_zeros_ul
+    libc.src.stdbit.stdc_leading_zeros_ull
+    libc.src.stdbit.stdc_leading_zeros_us
+    libc.src.stdbit.stdc_trailing_ones_uc
+    libc.src.stdbit.stdc_trailing_ones_ui
+    libc.src.stdbit.stdc_trailing_ones_ul
+    libc.src.stdbit.stdc_trailing_ones_ull
+    libc.src.stdbit.stdc_trailing_ones_us
+    libc.src.stdbit.stdc_trailing_zeros_uc
+    libc.src.stdbit.stdc_trailing_zeros_ui
+    libc.src.stdbit.stdc_trailing_zeros_ul
+    libc.src.stdbit.stdc_trailing_zeros_ull
+    libc.src.stdbit.stdc_trailing_zeros_us
+
+    # stdlib.h entrypoints
+    libc.src.stdlib._Exit
+    libc.src.stdlib.a64l
+    libc.src.stdlib.abort
+    libc.src.stdlib.abs
+    libc.src.stdlib.aligned_alloc
+    libc.src.stdlib.atexit
+    libc.src.stdlib.atof
+    libc.src.stdlib.atoi
+    libc.src.stdlib.atol
+    libc.src.stdlib.atoll
+    libc.src.stdlib.bsearch
+    libc.src.stdlib.calloc
+    libc.src.stdlib.div
+    libc.src.stdlib.exit
+    libc.src.stdlib.free
+    libc.src.stdlib.l64a
+    libc.src.stdlib.labs
+    libc.src.stdlib.ldiv
+    libc.src.stdlib.llabs
+    libc.src.stdlib.lldiv
+    libc.src.stdlib.malloc
+    libc.src.stdlib.malloc_usable_size
+    libc.src.stdlib.memalignment
+    libc.src.stdlib.qsort
+    libc.src.stdlib.qsort_r
+    libc.src.stdlib.rand
+    libc.src.stdlib.realloc
+    libc.src.stdlib.srand
+    # libc.src.stdlib.strfromd
+    # libc.src.stdlib.strfromf
+    # libc.src.stdlib.strfroml
+    libc.src.stdlib.strtod
+    libc.src.stdlib.strtof
+    libc.src.stdlib.strtol
+    libc.src.stdlib.strtold
+    libc.src.stdlib.strtoll
+    libc.src.stdlib.strtoul
+    libc.src.stdlib.strtoull
+
+    # stdlib.h entrypoints
+    libc.src.stdlib.strtod_l
+    libc.src.stdlib.strtof_l
+    libc.src.stdlib.strtol_l
+    libc.src.stdlib.strtold_l
+    libc.src.stdlib.strtoll_l
+    libc.src.stdlib.strtoul_l
+    libc.src.stdlib.strtoull_l
+
+    # time.h entrypoints
+    libc.src.time.asctime
+    libc.src.time.asctime_r
+    libc.src.time.ctime
+    libc.src.time.ctime_r
+    libc.src.time.difftime
+    libc.src.time.gmtime
+    libc.src.time.gmtime_r
+    libc.src.time.localtime
+    libc.src.time.localtime_r
+    libc.src.time.mktime
+    libc.src.time.strftime
+    libc.src.time.strftime_l
+    libc.src.time.timespec_get
+
+    # wchar.h entrypoints
+    libc.src.wchar.btowc
+    libc.src.wchar.wcslen
+    libc.src.wchar.wctob
+
+    # wctype.h entrypoints
+    libc.src.wctype.iswalpha
+    libc.src.wctype.iswgraph
+    libc.src.wctype.iswcntrl
+    libc.src.wctype.iswdigit
+    libc.src.wctype.iswupper
+    libc.src.wctype.iswalnum
+    libc.src.wctype.iswlower
+    libc.src.wctype.iswspace
+    libc.src.wctype.iswblank
+    libc.src.wctype.iswxdigit
+    libc.src.wctype.iswpunct
+    libc.src.wctype.iswprint
+    libc.src.wctype.iswctype
+    libc.src.wctype.wctype
+
+    # internal entrypoints
+    libc.startup.baremetal.init
+    libc.startup.baremetal.fini
+)
+
+set(TARGET_LIBM_ENTRYPOINTS
+    # complex.h entrypoints
+    libc.src.complex.cabs
+    libc.src.complex.cabsf
+    libc.src.complex.carg
+    libc.src.complex.cargf
+    libc.src.complex.creal
+    libc.src.complex.crealf
+    libc.src.complex.creall
+    libc.src.complex.cimag
+    libc.src.complex.cimagf
+    libc.src.complex.cimagl
+    libc.src.complex.conj
+    libc.src.complex.conjf
+    libc.src.complex.conjl
+    libc.src.complex.cproj
+    libc.src.complex.cprojf
+    libc.src.complex.cprojl
+
+    # fenv.h entrypoints
+    libc.src.fenv.feclearexcept
+    libc.src.fenv.fedisableexcept
+    libc.src.fenv.feenableexcept
+    libc.src.fenv.fegetenv
+    libc.src.fenv.fegetexcept
+    libc.src.fenv.fegetexceptflag
+    libc.src.fenv.fegetround
+    libc.src.fenv.feholdexcept
+    libc.src.fenv.feraiseexcept
+    libc.src.fenv.fesetenv
+    libc.src.fenv.fesetexcept
+    libc.src.fenv.fesetexceptflag
+    libc.src.fenv.fesetround
+    libc.src.fenv.fetestexcept
+    libc.src.fenv.fetestexceptflag
+    libc.src.fenv.feupdateenv
+
+    # math.h entrypoints
+    libc.src.math.acosf
+    libc.src.math.acoshf
+    libc.src.math.acospif
+    libc.src.math.asinf
+    libc.src.math.asinhf
+    libc.src.math.asinpi
+    libc.src.math.asinpif
+    libc.src.math.atan2
+    libc.src.math.atan2f
+    # libc.src.math.atan2l
+    libc.src.math.atan
+    libc.src.math.atanf
+    libc.src.math.atanhf
+    libc.src.math.canonicalize
+    libc.src.math.canonicalizef
+    libc.src.math.canonicalizel
+    libc.src.math.cbrt
+    libc.src.math.cbrtf
+    libc.src.math.cbrtbf16
+    libc.src.math.ceil
+    libc.src.math.ceilf
+    libc.src.math.ceill
+    libc.src.math.copysign
+    libc.src.math.copysignf
+    libc.src.math.copysignl
+    libc.src.math.cos
+    libc.src.math.cosf
+    libc.src.math.coshf
+    libc.src.math.cospif
+    libc.src.math.dfmal
+    libc.src.math.dmull
+    libc.src.math.dsqrtl
+    libc.src.math.daddl
+    libc.src.math.ddivl
+    libc.src.math.dsubl
+    libc.src.math.erff
+    libc.src.math.exp
+    libc.src.math.exp10
+    libc.src.math.exp10f
+    libc.src.math.exp10m1f
+    libc.src.math.exp2
+    libc.src.math.exp2f
+    libc.src.math.exp2m1f
+    libc.src.math.expf
+    libc.src.math.expm1
+    libc.src.math.expm1f
+    libc.src.math.fabs
+    libc.src.math.fabsf
+    libc.src.math.fabsl
+    libc.src.math.fadd
+    libc.src.math.faddl
+    libc.src.math.fadd
+    libc.src.math.fdim
+    libc.src.math.fdimf
+    libc.src.math.fdiml
+    libc.src.math.fdiv
+    libc.src.math.fdivl
+    libc.src.math.ffma
+    libc.src.math.ffmal
+    libc.src.math.floor
+    libc.src.math.floorf
+    libc.src.math.floorl
+    libc.src.math.fma
+    libc.src.math.fmabf16
+    libc.src.math.fmaf
+    libc.src.math.fmax
+    libc.src.math.fmaxf
+    libc.src.math.fmaximum
+    libc.src.math.fmaximum_mag
+    libc.src.math.fmaximum_mag_num
+    libc.src.math.fmaximum_mag_numf
+    libc.src.math.fmaximum_mag_numl
+    libc.src.math.fmaximum_magf
+    libc.src.math.fmaximum_magl
+    libc.src.math.fmaximum_num
+    libc.src.math.fmaximum_numf
+    libc.src.math.fmaximum_numl
+    libc.src.math.fmaximumf
+    libc.src.math.fmaximuml
+    libc.src.math.fmaxl
+    libc.src.math.fmin
+    libc.src.math.fminf
+    libc.src.math.fminimum
+    libc.src.math.fminimum_mag
+    libc.src.math.fminimum_mag_num
+    libc.src.math.fminimum_mag_numf
+    libc.src.math.fminimum_mag_numl
+    libc.src.math.fminimum_magf
+    libc.src.math.fminimum_magl
+    libc.src.math.fminimum_num
+    libc.src.math.fminimum_numf
+    libc.src.math.fminimum_numl
+    libc.src.math.fminimumf
+    libc.src.math.fminimuml
+    libc.src.math.fminl
+    libc.src.math.fmod
+    libc.src.math.fmodf
+    # libc.src.math.fmodl
+    libc.src.math.fmul
+    libc.src.math.fmull
+    libc.src.math.frexp
+    libc.src.math.frexpf
+    libc.src.math.frexpl
+    libc.src.math.fromfp
+    libc.src.math.fromfpf
+    libc.src.math.fromfpl
+    libc.src.math.fromfpx
+    libc.src.math.fromfpxf
+    libc.src.math.fromfpxl
+    libc.src.math.fsqrt
+    libc.src.math.fsqrtl
+    libc.src.math.fsub
+    libc.src.math.fsubl
+    libc.src.math.getpayload
+    libc.src.math.getpayloadf
+    libc.src.math.getpayloadl
+    libc.src.math.hypot
+    libc.src.math.hypotf
+    libc.src.math.ilogb
+    libc.src.math.ilogbf
+    libc.src.math.ilogbl
+    libc.src.math.iscanonical
+    libc.src.math.iscanonicalf
+    libc.src.math.iscanonicall
+    libc.src.math.isnan
+    libc.src.math.isnanf
+    libc.src.math.isnanl
+    libc.src.math.issignaling
+    libc.src.math.issignalingf
+    libc.src.math.issignalingl
+    libc.src.math.ldexp
+    libc.src.math.ldexpf
+    libc.src.math.ldexpl
+    libc.src.math.llogb
+    libc.src.math.llogbf
+    libc.src.math.llogbl
+    libc.src.math.llrint
+    libc.src.math.llrintf
+    libc.src.math.llrintl
+    libc.src.math.llround
+    libc.src.math.llroundf
+    libc.src.math.llroundl
+    libc.src.math.log
+    libc.src.math.log10
+    libc.src.math.log10f
+    libc.src.math.log1p
+    libc.src.math.log1pf
+    libc.src.math.log2
+    libc.src.math.log2f
+    libc.src.math.logb
+    libc.src.math.logbf
+    libc.src.math.logbl
+    libc.src.math.logf
+    libc.src.math.lrint
+    libc.src.math.lrintf
+    libc.src.math.lrintl
+    libc.src.math.lround
+    libc.src.math.lroundf
+    libc.src.math.lroundl
+    libc.src.math.modf
+    libc.src.math.modff
+    libc.src.math.modfl
+    libc.src.math.nan
+    libc.src.math.nanf
+    libc.src.math.nanl
+    libc.src.math.nearbyint
+    libc.src.math.nearbyintf
+    libc.src.math.nearbyintl
+    libc.src.math.nextafter
+    libc.src.math.nextafterf
+    libc.src.math.nextafterl
+    libc.src.math.nextdown
+    libc.src.math.nextdownf
+    libc.src.math.nextdownl
+    libc.src.math.nexttoward
+    libc.src.math.nexttowardf
+    libc.src.math.nexttowardl
+    libc.src.math.nextup
+    libc.src.math.nextupf
+    libc.src.math.nextupl
+    libc.src.math.pow
+    libc.src.math.powf
+    libc.src.math.remainder
+    libc.src.math.remainderf
+    libc.src.math.remainderl
+    libc.src.math.remquo
+    libc.src.math.remquof
+    libc.src.math.remquol
+    libc.src.math.rint
+    libc.src.math.rintf
+    libc.src.math.rintl
+    libc.src.math.round
+    libc.src.math.roundeven
+    libc.src.math.roundevenf
+    libc.src.math.roundevenl
+    libc.src.math.roundf
+    libc.src.math.roundl
+    libc.src.math.scalbln
+    libc.src.math.scalblnf
+    libc.src.math.scalblnl
+    libc.src.math.scalbn
+    libc.src.math.scalbnf
+    libc.src.math.scalbnl
+    libc.src.math.setpayload
+    libc.src.math.setpayloadf
+    libc.src.math.setpayloadl
+    libc.src.math.setpayloadsig
+    libc.src.math.setpayloadsigf
+    libc.src.math.setpayloadsigl
+    libc.src.math.sin
+    libc.src.math.sincos
+    libc.src.math.sincosf
+    libc.src.math.sinf
+    libc.src.math.sinhf
+    libc.src.math.sinpif
+    libc.src.math.sqrt
+    libc.src.math.sqrtf
+    libc.src.math.sqrtl
+    libc.src.math.tan
+    libc.src.math.tanf
+    libc.src.math.tanhf
+    libc.src.math.totalorder
+    libc.src.math.totalorderf
+    libc.src.math.totalorderl
+    libc.src.math.totalordermag
+    libc.src.math.totalordermagf
+    libc.src.math.totalordermagl
+    libc.src.math.trunc
+    libc.src.math.truncf
+    libc.src.math.truncl
+    libc.src.math.ufromfp
+    libc.src.math.ufromfpf
+    libc.src.math.ufromfpl
+    libc.src.math.ufromfpx
+    libc.src.math.ufromfpxf
+    libc.src.math.ufromfpxl
+)
+
+if(LIBC_TYPES_HAS_CFLOAT16)
+  list(APPEND TARGET_LIBM_ENTRYPOINTS
+    # complex.h C23 _Complex _Float16 entrypoints
+    libc.src.complex.crealf16
+    libc.src.complex.cimagf16
+    libc.src.complex.conjf16
+    libc.src.complex.cprojf16
+  )
+endif()
+
+if(LIBC_TYPES_HAS_FLOAT16)
+  list(APPEND TARGET_LIBM_ENTRYPOINTS
+    # math.h C23 _Float16 entrypoints
+    libc.src.math.acosf16
+    libc.src.math.acoshf16
+    libc.src.math.asinf16
+    libc.src.math.asinhf16
+    libc.src.math.atan2f16
+    libc.src.math.canonicalizef16
+    libc.src.math.cbrtf16
+    libc.src.math.ceilf16
+    libc.src.math.copysignf16
+    libc.src.math.cosf16
+    libc.src.math.coshf16
+    libc.src.math.cospif16
+    libc.src.math.exp10f16
+    libc.src.math.exp10m1f16
+    libc.src.math.exp2f16
+    libc.src.math.exp2m1f16
+    libc.src.math.expf16
+    libc.src.math.expm1f16
+    libc.src.math.f16add
+    libc.src.math.f16addf
+    libc.src.math.f16addl
+    libc.src.math.f16div
+    libc.src.math.f16divf
+    libc.src.math.f16divl
+    libc.src.math.f16fma
+    libc.src.math.f16fmaf
+    libc.src.math.f16fmal
+    libc.src.math.f16mul
+    libc.src.math.f16mulf
+    libc.src.math.f16mull
+    libc.src.math.f16sqrt
+    libc.src.math.f16sqrtf
+    libc.src.math.f16sqrtl
+    libc.src.math.f16sub
+    libc.src.math.f16subf
+    libc.src.math.f16subl
+    libc.src.math.fabsf16
+    libc.src.math.fdimf16
+    libc.src.math.floorf16
+    libc.src.math.fmaf16
+    libc.src.math.fmaxf16
+    libc.src.math.fmaximum_mag_numf16
+    libc.src.math.fmaximum_magf16
+    libc.src.math.fmaximum_numf16
+    libc.src.math.fmaximumf16
+    libc.src.math.fminf16
+    libc.src.math.fminimum_mag_numf16
+    libc.src.math.fminimum_magf16
+    libc.src.math.fminimum_numf16
+    libc.src.math.fminimumf16
+    libc.src.math.fmodf16
+    libc.src.math.frexpf16
+    libc.src.math.fromfpf16
+    libc.src.math.fromfpxf16
+    libc.src.math.getpayloadf16
+    libc.src.math.hypotf16
+    libc.src.math.ilogbf16
+    libc.src.math.iscanonicalf16
+    libc.src.math.isnanf16
+    libc.src.math.issignalingf16
+    libc.src.math.ldexpf16
+    libc.src.math.lgammaf16
+    libc.src.math.llogbf16
+    libc.src.math.llrintf16
+    libc.src.math.llroundf16
+    libc.src.math.log10f16
+    libc.src.math.log10p1f16
+    libc.src.math.log2f16
+    libc.src.math.log2p1f16
+    libc.src.math.logbf16
+    libc.src.math.logf16
+    libc.src.math.lrintf16
+    libc.src.math.lroundf16
+    libc.src.math.modff16
+    libc.src.math.nanf16
+    libc.src.math.nearbyintf16
+    libc.src.math.nextafterf16
+    libc.src.math.nextdownf16
+    libc.src.math.nexttowardf16
+    libc.src.math.nextupf16
+    libc.src.math.remainderf16
+    libc.src.math.remquof16
+    libc.src.math.rintf16
+    libc.src.math.roundevenf16
+    libc.src.math.roundf16
+    libc.src.math.scalblnf16
+    libc.src.math.scalbnf16
+    libc.src.math.setpayloadf16
+    libc.src.math.setpayloadsigf16
+    libc.src.math.sinf16
+    libc.src.math.sinhf16
+    libc.src.math.sinpif16
+    libc.src.math.sqrtf16
+    libc.src.math.tanf16
+    libc.src.math.tanhf16
+    libc.src.math.tanpif16
+    libc.src.math.totalorderf16
+    libc.src.math.totalordermagf16
+    libc.src.math.truncf16
+    libc.src.math.ufromfpf16
+    libc.src.math.ufromfpxf16
+  )
+
+  if(LIBC_TYPES_HAS_FLOAT128)
+    list(APPEND TARGET_LIBM_ENTRYPOINTS
+      # math.h C23 mixed _Float16 and _Float128 entrypoints
+      libc.src.math.f16addf128
+      libc.src.math.f16divf128
+      libc.src.math.f16fmaf128
+      libc.src.math.f16mulf128
+      libc.src.math.f16sqrtf128
+      libc.src.math.f16subf128
+    )
+  endif()
+endif()
+
+if(LIBC_TYPES_HAS_CFLOAT128)
+  list(APPEND TARGET_LIBM_ENTRYPOINTS
+    # complex.h C23 _Complex _Float128 entrypoints
+    libc.src.complex.crealf128
+    libc.src.complex.cimagf128
+    libc.src.complex.conjf128
+    libc.src.complex.cprojf128
+  )
+endif()
+
+if(LIBC_TYPES_HAS_FLOAT128)
+  list(APPEND TARGET_LIBM_ENTRYPOINTS
+    # math.h C23 _Float128 entrypoints
+    libc.src.math.atan2f128
+    libc.src.math.canonicalizef128
+    libc.src.math.ceilf128
+    libc.src.math.copysignf128
+    libc.src.math.daddf128
+    libc.src.math.ddivf128
+    libc.src.math.dfmaf128
+    libc.src.math.dmulf128
+    libc.src.math.dsqrtf128
+    libc.src.math.dsubf128
+    libc.src.math.fabsf128
+    libc.src.math.faddf128
+    libc.src.math.fdimf128
+    libc.src.math.fdivf128
+    libc.src.math.ffmaf128
+    libc.src.math.floorf128
+    libc.src.math.fmaxf128
+    libc.src.math.fmaximum_mag_numf128
+    libc.src.math.fmaximum_magf128
+    libc.src.math.fmaximum_numf128
+    libc.src.math.fmaximumf128
+    libc.src.math.fminf128
+    libc.src.math.fminimum_mag_numf128
+    libc.src.math.fminimum_magf128
+    libc.src.math.fminimum_numf128
+    libc.src.math.fminimumf128
+    # libc.src.math.fmodf128
+    libc.src.math.fmulf128
+    libc.src.math.frexpf128
+    libc.src.math.fromfpf128
+    libc.src.math.fromfpxf128
+    libc.src.math.fsqrtf128
+    libc.src.math.fsubf128
+    libc.src.math.getpayloadf128
+    libc.src.math.ilogbf128
+    libc.src.math.iscanonicalf128
+    libc.src.math.isnanf128
+    libc.src.math.issignalingf128
+    libc.src.math.ldexpf128
+    libc.src.math.llogbf128
+    libc.src.math.llrintf128
+    libc.src.math.llroundf128
+    libc.src.math.logbf128
+    libc.src.math.lrintf128
+    libc.src.math.lroundf128
+    libc.src.math.modff128
+    libc.src.math.nanf128
+    libc.src.math.nearbyintf128
+    libc.src.math.nextafterf128
+    libc.src.math.nextdownf128
+    libc.src.math.nextupf128
+    libc.src.math.remainderf128
+    libc.src.math.remquof128
+    libc.src.math.rintf128
+    libc.src.math.roundevenf128
+    libc.src.math.roundf128
+    libc.src.math.scalblnf128
+    libc.src.math.scalbnf128
+    libc.src.math.setpayloadf128
+    libc.src.math.setpayloadsigf128
+    libc.src.math.sqrtf128
+    libc.src.math.totalorderf128
+    libc.src.math.totalordermagf128
+    libc.src.math.truncf128
+    libc.src.math.ufromfpf128
+    libc.src.math.ufromfpxf128
+  )
+endif()
+
+list(APPEND TARGET_LIBM_ENTRYPOINTS
+  # bfloat16 entrypoints
+  libc.src.math.atanbf16
+  libc.src.math.acosbf16
+  libc.src.math.asinbf16
+  libc.src.math.bf16add
+  libc.src.math.bf16addf
+  libc.src.math.bf16addl
+  libc.src.math.bf16div
+  libc.src.math.bf16divf
+  libc.src.math.bf16divl
+  libc.src.math.bf16fma
+  libc.src.math.bf16fmaf
+  libc.src.math.bf16fmal
+  libc.src.math.bf16mul
+  libc.src.math.bf16mulf
+  libc.src.math.bf16mull
+  libc.src.math.bf16sub
+  libc.src.math.bf16subf
+  libc.src.math.bf16subl
+  libc.src.math.canonicalizebf16
+  libc.src.math.ceilbf16
+  libc.src.math.copysignbf16
+  libc.src.math.expbf16
+  libc.src.math.fabsbf16
+  libc.src.math.fdimbf16
+  libc.src.math.floorbf16
+  libc.src.math.fmaxbf16
+  libc.src.math.fmaximumbf16
+  libc.src.math.fmaximum_magbf16
+  libc.src.math.fmaximum_mag_numbf16
+  libc.src.math.fmaximum_numbf16
+  libc.src.math.fminbf16
+  libc.src.math.fminimumbf16
+  libc.src.math.fminimum_magbf16
+  libc.src.math.fminimum_mag_numbf16
+  libc.src.math.fminimum_numbf16
+  libc.src.math.fmodbf16
+  libc.src.math.frexpbf16
+  libc.src.math.fromfpbf16
+  libc.src.math.fromfpxbf16
+  libc.src.math.getpayloadbf16
+  libc.src.math.hypotbf16
+  libc.src.math.ilogbbf16
+  libc.src.math.iscanonicalbf16
+  libc.src.math.issignalingbf16
+  libc.src.math.ldexpbf16
+  libc.src.math.llogbbf16
+  libc.src.math.llrintbf16
+  libc.src.math.llroundbf16
+  libc.src.math.log_bf16
+  libc.src.math.logbbf16
+  libc.src.math.lrintbf16
+  libc.src.math.lroundbf16
+  libc.src.math.modfbf16
+  libc.src.math.nanbf16
+  libc.src.math.nearbyintbf16
+  libc.src.math.nextafterbf16
+  libc.src.math.nextdownbf16
+  libc.src.math.nexttowardbf16
+  libc.src.math.nextupbf16
+  libc.src.math.remainderbf16
+  libc.src.math.remquobf16
+  libc.src.math.rintbf16
+  libc.src.math.roundbf16
+  libc.src.math.roundevenbf16
+  libc.src.math.scalblnbf16
+  libc.src.math.scalbnbf16
+  libc.src.math.setpayloadbf16
+  libc.src.math.setpayloadsigbf16
+  libc.src.math.sqrtbf16
+  libc.src.math.tanbf16
+  libc.src.math.truncbf16
+  libc.src.math.totalorderbf16
+  libc.src.math.totalordermagbf16
+  libc.src.math.ufromfpbf16
+  libc.src.math.ufromfpxbf16
+)
+
+if(LIBC_TYPES_HAS_FLOAT128)
+  list(APPEND TARGET_LIBM_ENTRYPOINTS
+    # math.h C++23 mixed bfloat16 and _Float128 entrypoints
+    libc.src.math.bf16addf128
+    libc.src.math.bf16divf128
+    libc.src.math.bf16fmaf128
+    libc.src.math.bf16mulf128
+    libc.src.math.bf16subf128
+  )
+endif()
+
+if(LIBC_COMPILER_HAS_FIXED_POINT)
+  list(APPEND TARGET_LIBM_ENTRYPOINTS
+    # stdfix.h _Fract and _Accum entrypoints
+    libc.src.stdfix.abshk
+    libc.src.stdfix.abshr
+    libc.src.stdfix.absk
+    libc.src.stdfix.abslk
+    libc.src.stdfix.abslr
+    libc.src.stdfix.absr
+    libc.src.stdfix.exphk
+    libc.src.stdfix.expk
+    libc.src.stdfix.roundhk
+    libc.src.stdfix.roundhr
+    libc.src.stdfix.roundk
+    libc.src.stdfix.roundlk
+    libc.src.stdfix.roundlr
+    libc.src.stdfix.roundr
+    libc.src.stdfix.rounduhk
+    libc.src.stdfix.rounduhr
+    libc.src.stdfix.rounduk
+    libc.src.stdfix.roundulk
+    libc.src.stdfix.roundulr
+    libc.src.stdfix.roundur
+    libc.src.stdfix.sqrtuhk
+    libc.src.stdfix.sqrtuhr
+    libc.src.stdfix.sqrtuk
+    libc.src.stdfix.sqrtur
+    # libc.src.stdfix.sqrtulk
+    libc.src.stdfix.sqrtulr
+    libc.src.stdfix.uhksqrtus
+    libc.src.stdfix.uksqrtui
+    libc.src.stdfix.hrbits
+    libc.src.stdfix.uhrbits
+    libc.src.stdfix.rbits
+    libc.src.stdfix.urbits
+    libc.src.stdfix.lrbits
+    libc.src.stdfix.ulrbits
+    libc.src.stdfix.hkbits
+    libc.src.stdfix.uhkbits
+    libc.src.stdfix.kbits
+    libc.src.stdfix.ukbits
+    libc.src.stdfix.lkbits
+    libc.src.stdfix.ulkbits
+    libc.src.stdfix.bitshr
+    libc.src.stdfix.bitsr
+    libc.src.stdfix.bitslr
+    libc.src.stdfix.bitshk
+    libc.src.stdfix.bitsk
+    libc.src.stdfix.bitslk
+    libc.src.stdfix.bitsuhr
+    libc.src.stdfix.bitsur
+    libc.src.stdfix.bitsulr
+    libc.src.stdfix.bitsuhk
+    libc.src.stdfix.bitsuk
+    libc.src.stdfix.bitsulk
+    libc.src.stdfix.countlshr
+    libc.src.stdfix.countlsr
+    libc.src.stdfix.countlslr
+    libc.src.stdfix.countlshk
+    libc.src.stdfix.countlsk
+    libc.src.stdfix.countlslk
+    libc.src.stdfix.countlsuhr
+    libc.src.stdfix.countlsur
+    libc.src.stdfix.countlsulr
+    libc.src.stdfix.countlsuhk
+    libc.src.stdfix.countlsuk
+    libc.src.stdfix.countlsulk
+    libc.src.stdfix.idivr
+    libc.src.stdfix.idivlr
+    libc.src.stdfix.idivk
+    libc.src.stdfix.idivlk
+    libc.src.stdfix.idivur
+    libc.src.stdfix.idivulr
+    libc.src.stdfix.idivuk
+    libc.src.stdfix.idivulk
+    libc.src.stdfix.divir
+    libc.src.stdfix.divilr
+    libc.src.stdfix.divik
+    libc.src.stdfix.divilk
+    libc.src.stdfix.diviur
+    libc.src.stdfix.diviulr
+    libc.src.stdfix.diviuk
+    libc.src.stdfix.diviulk
+  )
+endif()
+
+if(LLVM_LIBC_ENABLE_EXPERIMENTAL_ENTRYPOINTS)
+  list(APPEND TARGET_LIBC_ENTRYPOINTS
+    # regex.h entrypoints
+    libc.src.regex.regcomp
+    libc.src.regex.regexec
+    libc.src.regex.regerror
+    libc.src.regex.regfree
+  )
+endif()
+
+set(TARGET_LLVMLIBC_ENTRYPOINTS
+  ${TARGET_LIBC_ENTRYPOINTS}
+  ${TARGET_LIBM_ENTRYPOINTS}
+)
diff --git a/libc/config/baremetal/hexagon/headers.txt b/libc/config/baremetal/hexagon/headers.txt
new file mode 100644
index 0000000000000..a69660a97fdae
--- /dev/null
+++ b/libc/config/baremetal/hexagon/headers.txt
@@ -0,0 +1,32 @@
+set(TARGET_PUBLIC_HEADERS
+    libc.include.assert
+    libc.include.complex
+    libc.include.ctype
+    libc.include.elf
+    libc.include.errno
+    libc.include.features
+    libc.include.fenv
+    libc.include.float
+    libc.include.inttypes
+    libc.include.limits
+    libc.include.locale
+    libc.include.math
+    libc.include.setjmp
+    libc.include.stdbit
+    libc.include.stdckdint
+    libc.include.stdfix
+    libc.include.stdint
+    libc.include.stdio
+    libc.include.stdlib
+    libc.include.string
+    libc.include.strings
+    libc.include.sys_queue
+    libc.include.time
+    libc.include.uchar
+    libc.include.wchar
+    libc.include.wctype
+)
+
+if(LLVM_LIBC_ENABLE_EXPERIMENTAL_ENTRYPOINTS)
+  list(APPEND TARGET_PUBLIC_HEADERS libc.include.regex)
+endif()
diff --git a/libc/include/llvm-libc-types/__jmp_buf.h b/libc/include/llvm-libc-types/__jmp_buf.h
index a80afa2e6b815..ed5f1d7cab35f 100644
--- a/libc/include/llvm-libc-types/__jmp_buf.h
+++ b/libc/include/llvm-libc-types/__jmp_buf.h
@@ -60,6 +60,12 @@ typedef struct {
 #if __ARM_FP
   long fopaque[8]; // d8-d15
 #endif
+#elif defined(__hexagon__)
+  // Callee-saved registers r16-r27, followed by sp (r29), fp (r30) and lr
+  // (r31). r16-r27 are saved and restored as register pairs using memd, which
+  // requires the buffer to be 8-byte aligned, hence long long rather than
+  // long. That also rounds the size up, leaving one word of tail padding.
+  long long opaque[8];
 #else
 #error "__jmp_buf not available for your target architecture."
 #endif
diff --git a/libc/include/llvm-libc-types/fenv_t.h b/libc/include/llvm-libc-types/fenv_t.h
index 2cfeff7b8a9f8..fab3e4475883a 100644
--- a/libc/include/llvm-libc-types/fenv_t.h
+++ b/libc/include/llvm-libc-types/fenv_t.h
@@ -25,6 +25,8 @@ typedef struct {
 } fenv_t;
 #elif defined(__riscv)
 typedef unsigned int fenv_t;
+#elif defined(__hexagon__)
+typedef unsigned int fenv_t;
 #elif defined(__AMDGPU__) || defined(__NVPTX__) || defined(__SPIRV__)
 typedef struct {
   unsigned int __fpc;
diff --git a/libc/src/__support/FPUtil/FEnvImpl.h b/libc/src/__support/FPUtil/FEnvImpl.h
index 46572260cbc9e..7a3af0f22eb07 100644
--- a/libc/src/__support/FPUtil/FEnvImpl.h
+++ b/libc/src/__support/FPUtil/FEnvImpl.h
@@ -118,6 +118,8 @@ LIBC_INLINE int set_env(const fenv_t *env) {
 #include "arm/FEnvImpl.h"
 #elif defined(LIBC_TARGET_ARCH_IS_ANY_RISCV) && defined(__riscv_flen)
 #include "riscv/FEnvImpl.h"
+#elif defined(LIBC_TARGET_ARCH_IS_HEXAGON)
+#include "hexagon/FEnvImpl.h"
 #else
 
 namespace LIBC_NAMESPACE_DECL {
diff --git a/libc/src/__support/FPUtil/hexagon/FEnvImpl.h b/libc/src/__support/FPUtil/hexagon/FEnvImpl.h
new file mode 100644
index 0000000000000..1dc72d1bcc8d9
--- /dev/null
+++ b/libc/src/__support/FPUtil/hexagon/FEnvImpl.h
@@ -0,0 +1,210 @@
+//===-- hexagon floating point env manipulation functions -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_HEXAGON_FENVIMPL_H
+#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_HEXAGON_FENVIMPL_H
+
+#include "hdr/fenv_macros.h"
+#include "hdr/stdint_proxy.h"
+#include "hdr/types/fenv_t.h"
+#include "src/__support/macros/attributes.h" // For LIBC_INLINE_ASM
+#include "src/__support/macros/config.h"     // For LIBC_INLINE
+
+namespace LIBC_NAMESPACE_DECL {
+namespace fputil {
+
+// Hexagon keeps all floating-point state in the User Status Register (USR):
+//
+//   bits  5:1   sticky exception status flags
+//                 bit 1  FPINVF  invalid
+//                 bit 2  FPDBZF  divide-by-zero
+//                 bit 3  FPOVFF  overflow
+//                 bit 4  FPUNFF  underflow
+//                 bit 5  FPINPF  inexact
+//   bits 23:22   FPRND   rounding mode
+//                 00 round to nearest (ties to even)
+//                 01 toward zero
+//                 10 downward (toward -inf)
+//                 11 upward (toward +inf)
+//   bits 29:25   exception trap-enable bits (status flag << 24)
+//                 bit 25 FPINVE, bit 26 FPDBZE, bit 27 FPOVFE,
+//                 bit 28 FPUNFE, bit 29 FPINEE
+//
+// Note the hardware status/enable bit positions do NOT match the FE_* macro
+// values used by llvm-libc, so we translate between the two below.
+struct FEnv {
+  // Hardware status-flag bit positions within USR.
+  static constexpr uint32_t INVALID = 0x02;   // bit 1
+  static constexpr uint32_t DIVBYZERO = 0x04; // bit 2
+  static constexpr uint32_t OVERFLOW = 0x08;  // bit 3
+  static constexpr uint32_t UNDERFLOW = 0x10; // bit 4
+  static constexpr uint32_t INEXACT = 0x20;   // bit 5
+  static constexpr uint32_t STATUS_MASK =
+      INVALID | DIVBYZERO | OVERFLOW | UNDERFLOW | INEXACT;
+
+  // The trap-enable bits sit STATUS bits shifted up by this amount.
+  static constexpr uint32_t ENABLE_SHIFT = 24;
+  static constexpr uint32_t ENABLE_MASK = STATUS_MASK << ENABLE_SHIFT;
+
+  // Rounding mode field.
+  static constexpr uint32_t RND_SHIFT = 22;
+  static constexpr uint32_t RND_MASK = 0x3;
+  static constexpr uint32_t RND_TONEAREST = 0x0;
+  static constexpr uint32_t RND_TOWARDZERO = 0x1;
+  static constexpr uint32_t RND_DOWNWARD = 0x2;
+  static constexpr uint32_t RND_UPWARD = 0x3;
+
+  // Combined mask of all FP-relevant USR bits.  fegetenv/fesetenv only touch
+  // these so that non-FP USR fields (loop counters, prefetch state, etc.) are
+  // never disturbed.
+  static constexpr uint32_t FP_MASK =
+      STATUS_MASK | (RND_MASK << RND_SHIFT) | ENABLE_MASK;
+
+  LIBC_INLINE static uint32_t get_usr() {
+    uint32_t usr;
+    LIBC_INLINE_ASM("%0 = usr\n\t" : "=r"(usr));
+    return usr;
+  }
+
+  LIBC_INLINE static void set_usr(uint32_t usr) {
+    LIBC_INLINE_ASM("usr = %0\n\t" : : "r"(usr));
+  }
+
+  // Translate a set of FE_* exception macros into hardware status-flag bits.
+  LIBC_INLINE static uint32_t exception_macro_to_bits(int except) {
+    return ((except & FE_INVALID) ? INVALID : 0) |
+           ((except & FE_DIVBYZERO) ? DIVBYZERO : 0) |
+           ((except & FE_OVERFLOW) ? OVERFLOW : 0) |
+           ((except & FE_UNDERFLOW) ? UNDERFLOW : 0) |
+           ((except & FE_INEXACT) ? INEXACT : 0);
+  }
+
+  // Translate hardware status-flag bits back into FE_* exception macros.
+  LIBC_INLINE static int exception_bits_to_macro(uint32_t status) {
+    return ((status & INVALID) ? FE_INVALID : 0) |
+           ((status & DIVBYZERO) ? FE_DIVBYZERO : 0) |
+           ((status & OVERFLOW) ? FE_OVERFLOW : 0) |
+           ((status & UNDERFLOW) ? FE_UNDERFLOW : 0) |
+           ((status & INEXACT) ? FE_INEXACT : 0);
+  }
+};
+
+LIBC_INLINE int enable_except(int excepts) {
+  uint32_t usr = FEnv::get_usr();
+  uint32_t old_enabled = (usr >> FEnv::ENABLE_SHIFT) & FEnv::STATUS_MASK;
+  uint32_t new_enabled = old_enabled | FEnv::exception_macro_to_bits(excepts);
+  FEnv::set_usr((usr & ~FEnv::ENABLE_MASK) |
+                (new_enabled << FEnv::ENABLE_SHIFT));
+  return FEnv::exception_bits_to_macro(old_enabled);
+}
+
+LIBC_INLINE int disable_except(int excepts) {
+  uint32_t usr = FEnv::get_usr();
+  uint32_t old_enabled = (usr >> FEnv::ENABLE_SHIFT) & FEnv::STATUS_MASK;
+  uint32_t new_enabled = old_enabled & ~FEnv::exception_macro_to_bits(excepts);
+  FEnv::set_usr((usr & ~FEnv::ENABLE_MASK) |
+                (new_enabled << FEnv::ENABLE_SHIFT));
+  return FEnv::exception_bits_to_macro(old_enabled);
+}
+
+LIBC_INLINE int get_except() {
+  uint32_t enabled =
+      (FEnv::get_usr() >> FEnv::ENABLE_SHIFT) & FEnv::STATUS_MASK;
+  return FEnv::exception_bits_to_macro(enabled);
+}
+
+LIBC_INLINE int clear_except(int excepts) {
+  uint32_t usr = FEnv::get_usr();
+  usr &= ~FEnv::exception_macro_to_bits(excepts);
+  FEnv::set_usr(usr);
+  return 0;
+}
+
+LIBC_INLINE int test_except(int excepts) {
+  uint32_t to_test = FEnv::exception_macro_to_bits(excepts);
+  uint32_t status = FEnv::get_usr() & FEnv::STATUS_MASK;
+  return FEnv::exception_bits_to_macro(status & to_test);
+}
+
+LIBC_INLINE int set_except(int excepts) {
+  uint32_t usr = FEnv::get_usr();
+  FEnv::set_usr(usr | FEnv::exception_macro_to_bits(excepts));
+  return 0;
+}
+
+LIBC_INLINE int raise_except(int excepts) {
+  // Setting the sticky status flag triggers a trap if the corresponding
+  // enable bit is set; otherwise it simply records the exception.
+  uint32_t usr = FEnv::get_usr();
+  FEnv::set_usr(usr | FEnv::exception_macro_to_bits(excepts));
+  return 0;
+}
+
+LIBC_INLINE int get_round() {
+  uint32_t rm = (FEnv::get_usr() >> FEnv::RND_SHIFT) & FEnv::RND_MASK;
+  switch (rm) {
+  case FEnv::RND_TONEAREST:
+    return FE_TONEAREST;
+  case FEnv::RND_TOWARDZERO:
+    return FE_TOWARDZERO;
+  case FEnv::RND_DOWNWARD:
+    return FE_DOWNWARD;
+  case FEnv::RND_UPWARD:
+    return FE_UPWARD;
+  default:
+    return -1; // Error value.
+  }
+}
+
+LIBC_INLINE int set_round(int mode) {
+  uint32_t rm;
+  switch (mode) {
+  case FE_TONEAREST:
+    rm = FEnv::RND_TONEAREST;
+    break;
+  case FE_TOWARDZERO:
+    rm = FEnv::RND_TOWARDZERO;
+    break;
+  case FE_DOWNWARD:
+    rm = FEnv::RND_DOWNWARD;
+    break;
+  case FE_UPWARD:
+    rm = FEnv::RND_UPWARD;
+    break;
+  default:
+    return -1; // To indicate failure.
+  }
+  uint32_t usr = FEnv::get_usr();
+  FEnv::set_usr((usr & ~(FEnv::RND_MASK << FEnv::RND_SHIFT)) |
+                (rm << FEnv::RND_SHIFT));
+  return 0;
+}
+
+LIBC_INLINE int get_env(fenv_t *envp) {
+  uint32_t *state = reinterpret_cast<uint32_t *>(envp);
+  *state = FEnv::get_usr() & FEnv::FP_MASK;
+  return 0;
+}
+
+LIBC_INLINE int set_env(const fenv_t *envp) {
+  uint32_t usr = FEnv::get_usr();
+  if (envp == FE_DFL_ENV) {
+    // Default environment: round to nearest, no exceptions raised or enabled.
+    FEnv::set_usr(usr & ~FEnv::FP_MASK);
+    return 0;
+  }
+  uint32_t state = *reinterpret_cast<const uint32_t *>(envp);
+  // Preserve non-FP USR bits; only load the FP-relevant fields.
+  FEnv::set_usr((usr & ~FEnv::FP_MASK) | (state & FEnv::FP_MASK));
+  return 0;
+}
+
+} // namespace fputil
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_HEXAGON_FENVIMPL_H
diff --git a/libc/src/__support/macros/properties/architectures.h b/libc/src/__support/macros/properties/architectures.h
index f73fa7a7643ab..c1c425067ddd9 100644
--- a/libc/src/__support/macros/properties/architectures.h
+++ b/libc/src/__support/macros/properties/architectures.h
@@ -70,4 +70,8 @@
 #define LIBC_TARGET_ARCH_IS_ANY_RISCV
 #endif
 
+#if defined(__hexagon__)
+#define LIBC_TARGET_ARCH_IS_HEXAGON
+#endif
+
 #endif // LLVM_LIBC_SRC___SUPPORT_MACROS_PROPERTIES_ARCHITECTURES_H
diff --git a/libc/src/__support/memory_size.h b/libc/src/__support/memory_size.h
index 49c3598960885..f85956e0b11b3 100644
--- a/libc/src/__support/memory_size.h
+++ b/libc/src/__support/memory_size.h
@@ -47,8 +47,11 @@ class SafeMemSize {
     type result;
     if (LIBC_UNLIKELY((value | other.value) < 0)) {
       result = -1;
-    } else {
-      result = value + other.value;
+    } else if (LIBC_UNLIKELY(add_overflow(value, other.value, result))) {
+      // Signed addition would overflow. Computing `value + other.value`
+      // directly is undefined behavior, so detect the overflow with the
+      // builtin and mark the result invalid instead.
+      result = -1;
     }
     return SafeMemSize{result};
   }
diff --git a/libc/src/setjmp/hexagon/CMakeLists.txt b/libc/src/setjmp/hexagon/CMakeLists.txt
new file mode 100644
index 0000000000000..55c80b0bede0d
--- /dev/null
+++ b/libc/src/setjmp/hexagon/CMakeLists.txt
@@ -0,0 +1,19 @@
+add_entrypoint_object(
+  setjmp
+  SRCS
+    setjmp.cpp
+  HDRS
+    ../setjmp_impl.h
+  DEPENDS
+    libc.hdr.types.jmp_buf
+)
+
+add_entrypoint_object(
+  longjmp
+  SRCS
+    longjmp.cpp
+  HDRS
+    ../longjmp.h
+  DEPENDS
+    libc.hdr.types.jmp_buf
+)
diff --git a/libc/src/setjmp/hexagon/longjmp.cpp b/libc/src/setjmp/hexagon/longjmp.cpp
new file mode 100644
index 0000000000000..aad260bba4606
--- /dev/null
+++ b/libc/src/setjmp/hexagon/longjmp.cpp
@@ -0,0 +1,35 @@
+//===-- Implementation of longjmp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/setjmp/longjmp.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// Restore the registers saved by setjmp, see the comment there for the layout
+// of the buffer.
+[[gnu::naked]] LLVM_LIBC_FUNCTION(void, longjmp, (jmp_buf buf, int val)) {
+  asm(R"(
+      r17:16 = memd(r0+#0)
+      r19:18 = memd(r0+#8)
+      r21:20 = memd(r0+#16)
+      r23:22 = memd(r0+#24)
+      r25:24 = memd(r0+#32)
+      r27:26 = memd(r0+#40)
+      r29 = memw(r0+#48)
+      r30 = memw(r0+#52)
+      r31 = memw(r0+#56)
+
+      # return val ?: 1;
+      { p0 = cmp.eq(r1,#0); if (p0.new) r1 = #1 }
+      r0 = r1
+      jumpr r31)");
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/setjmp/hexagon/setjmp.cpp b/libc/src/setjmp/hexagon/setjmp.cpp
new file mode 100644
index 0000000000000..edf4c33aa6621
--- /dev/null
+++ b/libc/src/setjmp/hexagon/setjmp.cpp
@@ -0,0 +1,36 @@
+//===-- Implementation of setjmp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/setjmp/setjmp_impl.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// The Hexagon ABI callee-saved registers are r16-r27. Those are stored as
+// register pairs, which requires the buffer to be 8-byte aligned. The stack
+// pointer (r29), frame pointer (r30) and link register (r31) are stored
+// individually; r28 is caller-saved so it does not need to be preserved.
+[[gnu::naked]] LLVM_LIBC_FUNCTION(int, setjmp, (jmp_buf buf)) {
+  asm(R"(
+      memd(r0+#0) = r17:16
+      memd(r0+#8) = r19:18
+      memd(r0+#16) = r21:20
+      memd(r0+#24) = r23:22
+      memd(r0+#32) = r25:24
+      memd(r0+#40) = r27:26
+      memw(r0+#48) = r29
+      memw(r0+#52) = r30
+      memw(r0+#56) = r31
+
+      # Return zero.
+      r0 = #0
+      jumpr r31)");
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/startup/baremetal/hexagon/CMakeLists.txt b/libc/startup/baremetal/hexagon/CMakeLists.txt
new file mode 100644
index 0000000000000..372731be765df
--- /dev/null
+++ b/libc/startup/baremetal/hexagon/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_startup_object(
+  crt1
+  SRC
+  start.cpp
+  DEPENDS
+    libc.src.stdlib.atexit
+    libc.src.stdlib.exit
+    libc.src.string.memcpy
+    libc.src.string.memset
+    libc.startup.baremetal.init
+    libc.startup.baremetal.fini
+)
diff --git a/libc/startup/baremetal/hexagon/start.cpp b/libc/startup/baremetal/hexagon/start.cpp
new file mode 100644
index 0000000000000..946cea86b42fb
--- /dev/null
+++ b/libc/startup/baremetal/hexagon/start.cpp
@@ -0,0 +1,95 @@
+//===-- Implementation of crt for hexagon ---------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "hdr/stdint_proxy.h"
+#include "src/__support/macros/config.h"
+#include "src/stdlib/atexit.h"
+#include "src/stdlib/exit.h"
+#include "src/string/memcpy.h"
+#include "src/string/memset.h"
+#include "startup/baremetal/fini.h"
+#include "startup/baremetal/init.h"
+
+extern "C" {
+int main(int argc, char **argv);
+void _start();
+
+// Semihosting library initialisation if applicable. Required for printf, etc.
+[[gnu::weak]] void _platform_init() {}
+
+// These symbols are provided by the linker. The exact names are not defined by
+// a standard.
+extern uintptr_t __stack;
+extern uintptr_t __data_source[];
+extern uintptr_t __data_start[];
+extern uintptr_t __data_size[];
+extern uintptr_t __bss_start[];
+extern uintptr_t __bss_size[];
+} // extern "C"
+
+namespace LIBC_NAMESPACE_DECL {
+
+[[noreturn]] void do_start() {
+  // TODO: This startup code is the MVP for running under the Hexagon
+  // simulator with semihosting. It does not configure the MMU or caches.
+
+  // Clear the Supervisor Status Register so that a subsequent trap0
+  // semihosting call is serviced by the monitor.
+  asm volatile("ssr = %0" : : "r"(0));
+
+  // Perform the equivalent of scatterloading
+  LIBC_NAMESPACE::memcpy(__data_start, __data_source,
+                         reinterpret_cast<uintptr_t>(__data_size));
+  LIBC_NAMESPACE::memset(__bss_start, '\0',
+                         reinterpret_cast<uintptr_t>(__bss_size));
+  __libc_init_array();
+
+  _platform_init();
+  LIBC_NAMESPACE::atexit(&__libc_fini_array);
+  LIBC_NAMESPACE::exit(main(0, 0));
+}
+} // namespace LIBC_NAMESPACE_DECL
+
+extern "C" {
+[[gnu::section(".text.init.enter"), gnu::naked]]
+void _start() {
+  // Setup event vector base (needed for semihosting trap handling).
+  asm volatile("r0 = ##__llvm_libc_hexagon_event_vectors\n\t"
+               "evb = r0");
+  // Setup stack pointer, 16-byte aligned.
+  asm volatile("r0 = ##__stack\n\t"
+               "sp = and(r0, #-16)");
+  // Setup GP register for the small-data area.
+  asm volatile("r0 = ##_SDA_BASE_\n\t"
+               "gp = r0");
+  asm volatile("jump %0" : : "X"(LIBC_NAMESPACE::do_start));
+}
+} // extern "C"
+
+// The simulator services semihosting when trap0 is executed. Put the trap0
+// handler at event-vector slot 8; other slots are unexpected and spin.
+asm(".text\n\t"
+    ".p2align 4\n"
+    "__llvm_libc_hexagon_event_spin:\n\t"
+    "jump __llvm_libc_hexagon_event_spin\n"
+    "__llvm_libc_hexagon_event_trap0:\n\t"
+    "rte\n\t"
+    ".p2align 12, 0\n\t"
+    ".global __llvm_libc_hexagon_event_vectors\n"
+    "__llvm_libc_hexagon_event_vectors:\n\t"
+    "jump __llvm_libc_hexagon_event_spin\n\t"  // 0  reset
+    "jump __llvm_libc_hexagon_event_spin\n\t"  // 1  nmi
+    "jump __llvm_libc_hexagon_event_spin\n\t"  // 2  error
+    "jump __llvm_libc_hexagon_event_spin\n\t"  // 3  reserved
+    "jump __llvm_libc_hexagon_event_spin\n\t"  // 4  tlb miss x
+    "jump __llvm_libc_hexagon_event_spin\n\t"  // 5  reserved
+    "jump __llvm_libc_hexagon_event_spin\n\t"  // 6  tlb miss rw
+    "jump __llvm_libc_hexagon_event_spin\n\t"  // 7  reserved
+    "jump __llvm_libc_hexagon_event_trap0\n\t" // 8  trap0
+    "jump __llvm_libc_hexagon_event_spin\n\t"  // 9  trap1
+);
diff --git a/libc/test/UnitTest/CMakeLists.txt b/libc/test/UnitTest/CMakeLists.txt
index e6d978e16b46f..d62f747175e61 100644
--- a/libc/test/UnitTest/CMakeLists.txt
+++ b/libc/test/UnitTest/CMakeLists.txt
@@ -29,7 +29,7 @@ function(_create_unittest_framework_library fq_target_name)
       ${TEST_LIB_HDRS}
   )
   target_include_directories(${fq_target_name} PRIVATE ${LIBC_SOURCE_DIR})
-  if(TARGET libc.src.time.clock)
+  if(libc.src.time.clock IN_LIST TARGET_LLVMLIBC_ENTRYPOINTS)
     target_compile_definitions(${fq_target_name} PRIVATE TARGET_SUPPORTS_CLOCK)
   endif()
 
@@ -125,11 +125,6 @@ add_unittest_framework_library(
     .LibcTest
 )
 
-set(libc_hermetic_test_support_srcs HermeticTestUtils.cpp)
-if(LIBC_TARGET_OS_IS_BAREMETAL AND LIBC_TARGET_ARCHITECTURE_IS_ARM)
-  list(APPEND libc_hermetic_test_support_srcs ArmBaremetalTestUtils.cpp)
-endif()
-
 if(LIBC_TEST_SUBPROCESS_TESTS)
   set(libc_death_test_deps
     libc.hdr.stdint_proxy
@@ -164,12 +159,27 @@ else()
   )
 endif()
 
+set(libc_hermetic_test_support_srcs HermeticTestUtils.cpp)
+set(libc_hermetic_test_support_deps libc.hdr.stdint_proxy)
+if(LIBC_TARGET_OS_IS_BAREMETAL)
+  if(LIBC_TARGET_ARCHITECTURE_IS_ARM)
+    list(APPEND libc_hermetic_test_support_srcs ArmBaremetalTestUtils.cpp)
+  elseif(LIBC_TARGET_ARCHITECTURE_IS_HEXAGON)
+    list(APPEND libc_hermetic_test_support_srcs HexagonBaremetalTestUtils.cpp)
+  endif()
+  list(APPEND libc_hermetic_test_support_deps
+    libc.hdr.types.size_t
+    libc.hdr.types.ssize_t
+    libc.hdr.types.struct_timespec
+  )
+endif()
+
 add_unittest_framework_library(
   HermeticTestUtils
   SRCS
     ${libc_hermetic_test_support_srcs}
   DEPENDS
-    libc.hdr.stdint_proxy
+    ${libc_hermetic_test_support_deps}
 )
 
 add_header_library(
diff --git a/libc/test/UnitTest/HexagonBaremetalTestUtils.cpp b/libc/test/UnitTest/HexagonBaremetalTestUtils.cpp
new file mode 100644
index 0000000000000..a2bafe6ca9740
--- /dev/null
+++ b/libc/test/UnitTest/HexagonBaremetalTestUtils.cpp
@@ -0,0 +1,119 @@
+//===--- Hexagon bare-metal hermetic test utilities -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// llvm-libc's bare-metal port defers all OS interaction to a small set of
+// "vendor-provided" hook symbols (see
+// libc/src/__support/OSUtil/baremetal/io.h and the bare-metal time sources).
+// This file implements those hooks for the Hexagon simulator using Angel-style
+// semihosting, i.e. a `trap0(#0)` with:
+//   R0 = system call code (also the return value)
+//   R1 = pointer to an argument block (also errno on failure)
+//   R2 = exit status (for SYS_EXIT only)
+// The read/write calls return the number of bytes NOT transferred.
+//
+// It is compiled into HermeticTestUtils so hermetic tests can run under
+// the simulator. It is purely test-support code and is not intended for use as
+// general-purpose semihosting support.
+//
+//===----------------------------------------------------------------------===//
+
+#include "hdr/stdint_proxy.h"
+#include "hdr/types/size_t.h"
+#include "hdr/types/ssize_t.h"
+#include "hdr/types/struct_timespec.h"
+
+namespace {
+
+enum HexagonSyscall {
+  SYS_WRITE = 5,
+  SYS_READ = 6,
+  SYS_TIME = 0x11,
+  SYS_EXIT = 24,
+};
+
+// Software interrupt used to enter the simulator monitor.
+#define HEXAGON_SWI "trap0(#0)"
+
+// Issue a semihosting call: R0 = code, R1 = pointer to argument block.
+// Returns the value the simulator places in R0.
+int hexagon_semihost(int code, size_t *args) {
+  register uintptr_t r0 __asm__("r0") = static_cast<uintptr_t>(code);
+  register uintptr_t r1 __asm__("r1") = reinterpret_cast<uintptr_t>(args);
+  __asm__ __volatile__(HEXAGON_SWI : "=r"(r0), "=r"(r1) : "r"(r0), "r"(r1));
+  return static_cast<int>(r0);
+}
+
+} // namespace
+
+extern "C" {
+
+// The cookie type/objects only need to exist; their contents encode the file
+// descriptor used by the simulator-backed implementation.
+struct __llvm_libc_stdio_cookie {
+  int fd;
+};
+
+__llvm_libc_stdio_cookie __llvm_libc_stdin_cookie = {0};
+__llvm_libc_stdio_cookie __llvm_libc_stdout_cookie = {1};
+__llvm_libc_stdio_cookie __llvm_libc_stderr_cookie = {2};
+
+ssize_t __llvm_libc_stdio_write(void *cookie, const char *buf, size_t size) {
+  int fd = cookie ? static_cast<__llvm_libc_stdio_cookie *>(cookie)->fd : 1;
+  size_t args[] = {
+      static_cast<size_t>(fd),
+      reinterpret_cast<size_t>(buf),
+      size,
+  };
+  int not_written = hexagon_semihost(SYS_WRITE, args);
+  if (not_written < 0)
+    return -1;
+  // The simulator returns the number of bytes NOT written.
+  return static_cast<ssize_t>(size) - not_written;
+}
+
+ssize_t __llvm_libc_stdio_read(void *cookie, char *buf, size_t size) {
+  int fd = cookie ? static_cast<__llvm_libc_stdio_cookie *>(cookie)->fd : 0;
+  size_t args[] = {
+      static_cast<size_t>(fd),
+      reinterpret_cast<size_t>(buf),
+      size,
+  };
+  int not_read = hexagon_semihost(SYS_READ, args);
+  if (not_read < 0)
+    return -1;
+  // The simulator returns the number of bytes NOT read.
+  return static_cast<ssize_t>(size) - not_read;
+}
+
+[[noreturn]] void __llvm_libc_exit(int status) {
+  // The simulator reads the exit status from R2 and the syscall code from R0.
+  register uintptr_t r2 __asm__("r2") = static_cast<uintptr_t>(status);
+  register uintptr_t r0 __asm__("r0") = SYS_EXIT;
+  __asm__ __volatile__(HEXAGON_SWI : : "r"(r0), "r"(r2) : "memory");
+  __builtin_unreachable();
+}
+
+// The bare-metal config builds with LIBC_ERRNO_MODE_EXTERNAL, so the vendor
+// must provide storage for errno via this hook.
+int *__llvm_libc_errno() {
+  static int errno_storage;
+  return &errno_storage;
+}
+
+// Vendor hook used by the bare-metal `timespec_get()` implementation. The
+// Hexagon simulator's SYS_TIME call returns wall-clock time in whole seconds
+// (R0 = code on entry, R0 = seconds on return).
+bool __llvm_libc_timespec_get_utc(struct timespec *ts) {
+  register uintptr_t r0 __asm__("r0") = SYS_TIME;
+  __asm__ __volatile__(HEXAGON_SWI : "=r"(r0) : "r"(r0));
+  ts->tv_sec = static_cast<time_t>(r0);
+  ts->tv_nsec = 0;
+  return true;
+}
+
+} // extern "C"
diff --git a/libc/test/UnitTest/LibcTest.cpp b/libc/test/UnitTest/LibcTest.cpp
index 385f968ce2863..4fad25e368c51 100644
--- a/libc/test/UnitTest/LibcTest.cpp
+++ b/libc/test/UnitTest/LibcTest.cpp
@@ -195,11 +195,15 @@ int Test::runTests(const TestOptions &Options) {
     tlog << green << "[ RUN      ] " << reset << TestName << '\n';
     RunContext Ctx;
     internal::current_context = &Ctx;
-    [[maybe_unused]] const uint64_t start_time = static_cast<uint64_t>(clock());
+#ifdef LIBC_TEST_USE_CLOCK
+    const uint64_t start_time = static_cast<uint64_t>(clock());
+#endif
     T->SetUp();
     T->Run();
     T->TearDown();
-    [[maybe_unused]] const uint64_t end_time = static_cast<uint64_t>(clock());
+#ifdef LIBC_TEST_USE_CLOCK
+    const uint64_t end_time = static_cast<uint64_t>(clock());
+#endif
     internal::current_context = nullptr;
     switch (Ctx.status()) {
     case RunContext::RunResult::Fail:
diff --git a/libc/test/UnitTest/LibcTest.h b/libc/test/UnitTest/LibcTest.h
index 2758d3ef83bab..f41ff5fdac769 100644
--- a/libc/test/UnitTest/LibcTest.h
+++ b/libc/test/UnitTest/LibcTest.h
@@ -526,6 +526,8 @@ CString libc_make_test_file_path_func(const char *file_name);
 // EXPECT_DEATH can appear in a test of any function, e.g. checking for a crash
 // if passing nullptr to the function. So it must be defined, even if it can't
 // do anything.
+#define EXPECT_EXITS(FUNC, EXIT)
+#define ASSERT_EXITS(FUNC, EXIT)
 #define EXPECT_DEATH(FUNC, SIG)
 #define ASSERT_DEATH(FUNC, SIG)
 
diff --git a/libc/test/UnitTest/llvm-libc-baremetal.ld b/libc/test/UnitTest/llvm-libc-baremetal.ld
index e88a68cf62aa4..2f84d934edfe1 100644
--- a/libc/test/UnitTest/llvm-libc-baremetal.ld
+++ b/libc/test/UnitTest/llvm-libc-baremetal.ld
@@ -80,6 +80,9 @@ SECTIONS {
 
   .data : ALIGN(8) {
     PROVIDE(__data_start = .);
+    /* Base of the small-data area. Targets with GP-relative small-data
+       addressing (e.g. Hexagon) load GP with this at startup. */
+    PROVIDE(_SDA_BASE_ = .);
     *(.data .data.* .gnu.linkonce.d.*)
     *(.sdata .sdata.* .gnu.linkonce.s.*)
     . = ALIGN(8);
diff --git a/libc/test/src/math/smoke/exp10m1f_test.cpp b/libc/test/src/math/smoke/exp10m1f_test.cpp
index 87320fa2fe2f0..6f51abbd19143 100644
--- a/libc/test/src/math/smoke/exp10m1f_test.cpp
+++ b/libc/test/src/math/smoke/exp10m1f_test.cpp
@@ -17,10 +17,13 @@ TEST_F(LlvmLibcExp10m1fTest, SpecialNumbers) {
   EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::exp10m1f(sNaN), FE_INVALID);
   EXPECT_MATH_ERRNO(0);
 
-  EXPECT_EQ(FPBits(aNaN).uintval(),
-            FPBits(LIBC_NAMESPACE::exp10m1f(aNaN)).uintval());
-  EXPECT_EQ(FPBits(neg_aNaN).uintval(),
-            FPBits(LIBC_NAMESPACE::exp10m1f(neg_aNaN)).uintval());
+  // Use EXPECT_FP_EQ (which treats any two NaNs as equal) rather than comparing
+  // raw bit patterns: exp10m1f(NaN) is computed as NaN + inf, and some targets
+  // (e.g. Hexagon) produce a non-canonical NaN encoding (0xFFFFFFFF) instead of
+  // the canonical quiet NaN. Both are valid NaNs, so only require NaN-ness
+  // here.
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::exp10m1f(aNaN));
+  EXPECT_FP_EQ(neg_aNaN, LIBC_NAMESPACE::exp10m1f(neg_aNaN));
   EXPECT_FP_EQ_ALL_ROUNDING(inf, LIBC_NAMESPACE::exp10m1f(inf));
   EXPECT_FP_EQ_ALL_ROUNDING(-1.0f, LIBC_NAMESPACE::exp10m1f(neg_inf));
   EXPECT_FP_EQ_ALL_ROUNDING(zero, LIBC_NAMESPACE::exp10m1f(zero));



More information about the libc-commits mailing list