Skip to content

Commit abb5207

Browse files
benvanikclaude
andcommitted
Replace libc printf with eyalroz/printf and add streaming status formatting.
Integrates eyalroz/printf as a vendored dependency to replace all libc snprintf/vsnprintf calls across the runtime. This gives us a consistent, portable printf implementation across all platforms (including embedded and bare-metal targets) with no platform-specific quirks. Beyond the drop-in replacement, this extends the status formatting API with streaming support: - Add iree_status_format_to: callback-based streaming formatter that avoids contiguous buffers and pre-measurement passes. The callback returns bool for flow control, enabling early termination on buffer full or allocation failure. - Add iree_string_builder_append_status: single-pass status-to-builder formatting via iree_status_format_to, resolving the status/string_builder dependency cycle that previously required a local copy in libhsa.c. - Rewrite iree_status_fprint to stream directly to FILE* with zero heap allocation (previously required a contiguous buffer). - Rewrite iree_status_to_string as single-pass using string builder instead of two-pass measure-then-format. - Fix va_copy usage in status allocation: use C99 va_copy for the two-pass measure-then-format pattern instead of calling va_start twice on the same parameter (implementation-defined behavior). - Convert Status::ToString (C++) and ApiStatusToString (Python bindings) from two-pass iree_status_format to single-pass iree_status_format_to. - Replace HIP dynamic_symbols.c allocate-format-free pattern with direct iree_string_builder_append_status (eliminates intermediate heap allocation). RISC-V toolchain fix: strip debug info from libgcc.a during CMake configure. eyalroz/printf's long double support in %Lf/%Le compiles va_arg(args, long double) which on RISC-V (128-bit quad precision) generates references to __extenddftf2/__trunctfdf2 soft-float builtins. These are resolved from libgcc.a, but the GCC 12.2.0-compiled objects in that archive contain DWARF debug info with relocation types 60/61 (in .debug_rnglists and .debug_loclists sections) that LLD cannot parse. The pre-built RISC-V toolchain lacks compiler-rt builtins as an alternative, so we strip the debug metadata from libgcc.a at configure time using llvm-objcopy --strip-debug. This preserves all code and data sections and is idempotent. The strip runs inside the RISCV_TOOLCHAIN_ROOT guard so users with their own toolchains are unaffected. Verified locally with the same toolchain used in CI (toolchain_iree_manylinux_2_28_20231012). Co-Authored-By: Claude <[email protected]>
1 parent 3a4c991 commit abb5207

72 files changed

Lines changed: 940 additions & 413 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,6 @@
3737
[submodule "third_party/hsa-runtime-headers"]
3838
path = third_party/hsa-runtime-headers
3939
url = https://github.com/iree-org/hsa-runtime-headers.git
40+
[submodule "third_party/printf"]
41+
path = third_party/printf
42+
url = https://github.com/eyalroz/printf.git

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,9 @@ if(IREE_ENABLE_CPUINFO)
10421042
endif()
10431043
endif()
10441044

1045+
# Platform-independent printf implementation (eyalroz/printf).
1046+
add_subdirectory(build_tools/third_party/printf EXCLUDE_FROM_ALL)
1047+
10451048
# This defines the iree-flatcc-cli target, so we don't use EXCLUDE_FROM_ALL.
10461049
add_subdirectory(build_tools/third_party/flatcc)
10471050

MODULE.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ use_repo(
8080
"iree_cuda",
8181
"llvm-raw",
8282
"nccl",
83+
"printf_lib",
8384
"rccl",
8485
"spirv_cross",
8586
"stablehlo",

build_tools/bazel/extensions.bzl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ def _iree_extension_impl(module_ctx):
9696
path = "third_party/rccl",
9797
)
9898

99+
# eyalroz/printf (platform-independent printf implementation)
100+
new_local_repository(
101+
name = "printf_lib",
102+
build_file = "@iree_core//:build_tools/third_party/printf/BUILD.overlay",
103+
path = "third_party/printf",
104+
)
105+
99106
# WebGPU headers
100107
new_local_repository(
101108
name = "webgpu_headers",

build_tools/bazel_to_cmake/bazel_to_cmake_targets.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ def __init__(self, repo_map: Dict[str, str]):
119119
"@vulkan_headers": ["Vulkan::Headers"],
120120
# Misc single targets
121121
"@com_google_benchmark//:benchmark": ["benchmark"],
122+
"@printf_lib//:printf": ["printf::printf"],
122123
"@com_github_dvidelabs_flatcc//:flatcc": ["flatcc"],
123124
"@com_github_dvidelabs_flatcc//:parsing": ["flatcc::parsing"],
124125
"@com_github_dvidelabs_flatcc//:runtime": ["flatcc::runtime"],

build_tools/cmake/linux_riscv64.cmake

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,22 @@ if(NOT "${RISCV_TOOLCHAIN_ROOT}" STREQUAL "")
2626
set(CMAKE_RANLIB "${RISCV_TOOLCHAIN_ROOT}/bin/${RISCV_TOOLCHAIN_PREFIX}llvm-ranlib")
2727
set(CMAKE_STRIP "${RISCV_TOOLCHAIN_ROOT}/bin/${RISCV_TOOLCHAIN_PREFIX}llvm-strip")
2828
set(CMAKE_SYSROOT "${RISCV_TOOLCHAIN_ROOT}/sysroot")
29+
# Strip debug info from libgcc.a to work around LLD incompatibility. The
30+
# GCC 12.2.0-compiled libgcc.a contains DWARF debug info with relocations
31+
# that LLD cannot parse (unknown relocation types 60/61 in .debug_rnglists
32+
# and .debug_loclists sections). Code and data sections are unaffected;
33+
# only debug metadata is removed. This is idempotent.
34+
file(GLOB_RECURSE _LIBGCC_ARCHIVES "${RISCV_TOOLCHAIN_ROOT}/lib/gcc/*/libgcc.a")
35+
foreach(_LIBGCC IN LISTS _LIBGCC_ARCHIVES)
36+
execute_process(
37+
COMMAND "${RISCV_TOOLCHAIN_ROOT}/bin/${RISCV_TOOLCHAIN_PREFIX}llvm-objcopy"
38+
--strip-debug "${_LIBGCC}"
39+
RESULT_VARIABLE _strip_result
40+
)
41+
if(NOT _strip_result EQUAL 0)
42+
message(WARNING "Failed to strip debug from ${_LIBGCC}")
43+
endif()
44+
endforeach()
2945
endif()
3046

3147
# Specify ISA spec for march=rv64gc. This is to resolve the mismatch between

build_tools/scripts/git/runtime_submodules.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ third_party/googletest
44
third_party/hip-build-deps
55
third_party/hsa-runtime-headers
66
third_party/musl
7+
third_party/printf
78
third_party/spirv_cross
89
third_party/tracy
910
third_party/vulkan_headers
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright 2026 The IREE Authors
2+
#
3+
# Licensed under the Apache License v2.0 with LLVM Exceptions.
4+
# See https://llvm.org/LICENSE.txt for license information.
5+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
7+
package(default_visibility = ["//visibility:public"])
8+
9+
cc_library(
10+
name = "printf",
11+
srcs = ["src/printf/printf.c"],
12+
hdrs = ["src/printf/printf.h"],
13+
copts = select({
14+
"@bazel_tools//src/conditions:windows": [],
15+
"//conditions:default": [
16+
"-Wno-sign-conversion",
17+
],
18+
}),
19+
local_defines = [
20+
"PRINTF_SUPPORT_WRITEBACK_SPECIFIER=0",
21+
],
22+
strip_include_prefix = "src",
23+
)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Copyright 2026 The IREE Authors
2+
#
3+
# Licensed under the Apache License v2.0 with LLVM Exceptions.
4+
# See https://llvm.org/LICENSE.txt for license information.
5+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
7+
set(PRINTF_ROOT "${IREE_ROOT_DIR}/third_party/printf/")
8+
9+
# We don't install any headers because their only use is via the
10+
# iree/base/printf.h wrapper.
11+
set(IREE_HDRS_ROOT_PATH OFF)
12+
# Considered part of the runtime.
13+
set(IREE_INSTALL_LIBRARY_TARGETS_DEFAULT_COMPONENT IREEBundledLibraries)
14+
set(IREE_INSTALL_LIBRARY_TARGETS_DEFAULT_EXPORT_SET Runtime)
15+
16+
external_cc_library(
17+
PACKAGE
18+
printf
19+
NAME
20+
printf
21+
ROOT
22+
${PRINTF_ROOT}
23+
INCLUDES
24+
"${PRINTF_ROOT}/src"
25+
SRCS
26+
"src/printf/printf.c"
27+
HDRS
28+
"src/printf/printf.h"
29+
COPTS
30+
"-DPRINTF_SUPPORT_WRITEBACK_SPECIFIER=0"
31+
PUBLIC
32+
)

runtime/bindings/python/status_utils.cc

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,16 @@ PyObject* ApiStatusToPyExcClass(iree_status_t status) {
2929
} // namespace
3030

3131
std::string ApiStatusToString(iree_status_t status) {
32-
iree_host_size_t buffer_length = 0;
33-
if (IREE_UNLIKELY(!iree_status_format(status, /*buffer_capacity=*/0,
34-
/*buffer=*/NULL, &buffer_length))) {
35-
return "";
36-
}
3732
std::string result;
38-
result.resize(buffer_length);
39-
// NOTE: buffer capacity needs to be +1 for the NUL terminator in snprintf.
40-
return iree_status_format(status, result.size() + 1,
41-
const_cast<char*>(result.data()), &buffer_length)
42-
? result
43-
: "";
33+
iree_status_format_to(
34+
status,
35+
[](iree_string_view_t chunk, void* user_data) -> bool {
36+
auto* str = static_cast<std::string*>(user_data);
37+
str->append(chunk.data, chunk.size);
38+
return true;
39+
},
40+
&result);
41+
return result;
4442
}
4543

4644
nanobind::python_error ApiStatusToPyExc(iree_status_t status,

0 commit comments

Comments
 (0)