Skip to content

fix(esp32): restore emulator float output and add regression coverage#1699

Open
luoliwoshang wants to merge 7 commits intogoplus:mainfrom
luoliwoshang:fix/issue-1685-esp32-float-output
Open

fix(esp32): restore emulator float output and add regression coverage#1699
luoliwoshang wants to merge 7 commits intogoplus:mainfrom
luoliwoshang:fix/issue-1685-esp32-float-output

Conversation

@luoliwoshang
Copy link
Member

@luoliwoshang luoliwoshang commented Mar 10, 2026

Summary

  • keep esp32 float formatting path enabled via --undefined=_printf_float (target ldflags)
  • switch xtensa libm integration from minimal subset to full command-derived groups
    • libm-default-xtensa.a
    • libm-fbuiltin_fno_math_errno-xtensa.a
    • libm-machine_xtensa_d_libm-xtensa.a
  • fix xtensa machine libm compile includes for CI fresh env
    • add newlib/libc/machine/xtensa/include (xtensa/config/core-isa.h)
    • add newlib/libc/xtensa (prefer xtensa sys/fenv.h)
  • add missing source from full capture: newlib/libm/math/ef_acosh.c

How The libm List Was Generated (Reproducible)

The xtensa libm file list and flags are derived from actual newlib build commands, not guessed manually.

  1. Configure an out-of-tree newlib build (xtensa):
SRC="$HOME/Library/Caches/llgo/crosscompile/newlib-esp32-esp-4.3.0_20250211-patch6"
CLANGROOT="$HOME/Library/Caches/llgo/crosscompile/esp-clang-19.1.2_20250905-3"
BD=/tmp/newlib-xtensa-bear-retry
mkdir -p "$BD/xtensa/newlib"
cd "$BD/xtensa/newlib"

CC_CMD="$CLANGROOT/bin/clang -B$BD/xtensa/newlib/ -isystem $BD/xtensa/newlib/targ-include -isystem $SRC/newlib/libc/include -B$BD/xtensa/libgloss/xtensa -L$BD/xtensa/libgloss/libnosys -L$SRC/libgloss/xtensa"

"$SRC/newlib/configure" \
  --srcdir="$SRC/newlib" \
  --cache-file=./config.cache \
  --with-newlib \
  --enable-multilib \
  --with-cross-host="$(uname -m)-apple-darwin$(uname -r)" \
  --disable-nls \
  --program-transform-name='s&^&xtensa-&' \
  --disable-option-checking \
  --with-target-subdir=xtensa \
  --build="$(uname -m)-apple-darwin$(uname -r)" \
  --host=xtensa \
  --target=xtensa \
  AR="$CLANGROOT/bin/llvm-ar" \
  AS="$CLANGROOT/bin/clang" \
  CC="$CC_CMD" \
  LD=xtensa-ld \
  LIBCFLAGS=--target=xtensa \
  NM="$CLANGROOT/bin/llvm-nm" \
  RANLIB="$CLANGROOT/bin/llvm-ranlib"
  1. Capture compile commands with bear wrapper mode:
ESPCLANG="$HOME/Library/Caches/llgo/crosscompile/esp-clang-19.1.2_20250905-3/bin"
CC_WRAPPED="clang --target=xtensa -B$BD/xtensa/newlib/ -isystem $BD/xtensa/newlib/targ-include -isystem $SRC/newlib/libc/include -B$BD/xtensa/libgloss/xtensa -L$BD/xtensa/libgloss/libnosys -L$SRC/libgloss/xtensa"

PATH="$ESPCLANG:$PATH" bear --force-wrapper --output /tmp/libm_bear_full_wrapper.json -- \
  make V=1 -k -j1 libm.a CC="$CC_WRAPPED" CFLAGS='-g -O2 -Wno-error=implicit-function-declaration'
  1. Group by actual command flags from JSON (arguments):
machine_xtensa_d_libm: file under libm/machine/xtensa + contains -D_LIBM
fbuiltin_fno_math_errno: contains both -fbuiltin and -fno-math-errno
default: everything else under libm
  1. Result used by this PR (deduped by group + file):
default: 218
fbuiltin_fno_math_errno: 152
machine_xtensa_d_libm: 10
total: 380

Why ef_acosh.c Was Added

  • Previous hardcoded list had 379 entries.
  • Full bear capture showed one additional default-file compile unit: libm/math/ef_acosh.c.
  • This PR appends it and updates xtensa libm count assertions to 380.

Validation

  • go test ./internal/crosscompile/compile/libc
  • go build -o /tmp/llgo-local ./cmd/llgo && cd _demo/embed && /tmp/llgo-local run -a -target=esp32 -emulator ./esp32/float-1685

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses an issue where floating-point output was not correctly handled by the ESP32 emulator. It re-enables this functionality by adjusting linker flags and including required libm components. Additionally, it introduces a new regression test to ensure the correct behavior of floating-point operations on the ESP32 target, improving the robustness of the embedded system's numerical computations.

Highlights

  • ESP32 Float Output Restoration: Enabled floating-point output for the ESP32 emulator by adding --undefined=_printf_float to the target linker flags.
  • Newlib libm Integration: Included necessary libm floating-point classification symbols (s_fpclassify.c, sf_fpclassify.c) for the Xtensa toolchain to support float printing.
  • Cross-Compilation Test Updates: Updated the libc cross-compilation tests to correctly account for and verify the new libm-fpclassify compile group.
  • Float Regression Coverage: Introduced a new Go program (float-1685/main.go) as a regression test case to validate ESP32 float output behavior.
  • Automated Regression Test: Integrated the new float regression test into the test-esp-serial-startup.sh script for automated validation.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • _demo/embed/esp32/float-1685/main.go
    • Added a new Go program to test various floating-point operations and println output.
  • _demo/embed/test-esp-serial-startup.sh
    • Added extract_last_nonempty_lines helper function for output parsing.
    • Added run_case_and_compare function for executing and validating test cases against expected output.
    • Integrated a new regression test for ESP32 float output using the float-1685 case.
  • internal/crosscompile/compile/libc/libc_test.go
    • Updated the expected number of compile groups for Xtensa configuration from 3 to 4.
    • Added assertions to verify the presence and correct files (s_fpclassify.c, sf_fpclassify.c) within the new libm-fpclassify compile group.
  • internal/crosscompile/compile/libc/newlibesp.go
    • Defined libmCommonCFlags for libm common source files.
    • Added a new CompileGroup for libm-fpclassify which includes s_fpclassify.c and sf_fpclassify.c, necessary for _printf_float support.
  • targets/esp32.json
    • Configured the ESP32 target to include --undefined=_printf_float in the ldflags array to enable floating-point support in printf.
Activity
  • The pull request was created by luoliwoshang.
  • No human activity (comments, reviews) has been recorded yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

"libm-fpclassify-" + target + ".a",
}

for i, group := range config.Groups {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old guard if i >= len(expectedNames) { return } was removed, but the loop still iterates over config.Groups. If a future change adds a group without updating expectedNames, this will panic with index-out-of-bounds instead of producing a clear test failure.

Consider bounding the loop or adding a length check:

Suggested change
for i, group := range config.Groups {
for i, group := range config.Groups {
if i >= len(expectedNames) {
t.Errorf("unexpected extra group %d: %s", i, group.OutputFileName)
break
}

local expected_lines

echo "Running: llgo run -a -target=${target} -emulator ${case_dir}"
if ! raw_output=$(llgo run -a -target="${target}" -emulator "${case_dir}" 2>&1); then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run_case_and_compare treats a non-zero exit code as a hard failure, but the existing run_emulator_smoke uses set +e and only validates output. ESP32 emulators often exit non-zero on normal completion (e.g. semihosting halt). This inconsistency could make the new regression test flaky in CI.

Consider matching the tolerance approach from run_emulator_smoke, or documenting why stricter handling is intentional here.

run_emulator_smoke "esp32" "ESP32" "Hello World"

echo ""
echo "=== Regression: ESP32 float output (temporary) ==="
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The (temporary) label here has no associated issue reference or removal criteria. Consider adding a link to the tracking issue (e.g. the struczero type-assert hang mentioned in the PR description) so this doesn't become stale.

println(i, f)

// Keep this case on the float-format path without triggering
// esp32 type-assert timeout cases tracked separately.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The comment says "without triggering esp32 type-assert timeout cases" but the code uses a plain composite literal — no type assertion is involved. The intent is clear (avoid the struczero-style v.(bar) path), but the phrasing could confuse readers into thinking this code somehow relates to type assertions. Consider rephrasing to make it explicit, e.g.:

// Uses plain composite literal instead of type assertion to avoid
// the esp32 type-assert timeout tracked separately.

@xgopilot
Copy link
Contributor

xgopilot bot commented Mar 10, 2026

Review Summary

Solid fix — the --undefined=_printf_float linker flag and the new libm-fpclassify compile group are well-structured and correctly wired. The newlibesp.go comment cross-referencing the esp32.json ldflags is helpful. The pre-existing error message mismatch in libc_test.go ("Expected 2 groups" when checking != 3) is properly fixed.

Main concern: run_case_and_compare in the shell script handles non-zero exit codes more strictly than the existing run_emulator_smoke, which could cause flaky CI failures. See inline comments for details.

No security or performance issues found.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enables floating-point output for ESP32 targets by modifying the newlib-esp32 cross-compilation configuration to include libm floating-point classification symbols (s_fpclassify.c, sf_fpclassify.c) in a new compile group. It also adds the --undefined=_printf_float linker flag to the ESP32 target definition. A new Go test case (float-1685) and a shell script utility (run_case_and_compare) are introduced to verify the float output on the ESP32 emulator. A review comment suggests refactoring the extract_last_nonempty_lines function in the test script for improved efficiency and more idiomatic shell usage.

Comment on lines +78 to +92
local n="$1"
awk -v n="$n" '
NF { out[++count] = $0 }
END {
if (n <= 0 || count == 0) {
exit
}
start = count - n + 1
if (start < 1) {
start = 1
}
for (i = start; i <= count; i++) {
print out[i]
}
}'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The extract_last_nonempty_lines function is more complex than necessary and can be inefficient for large inputs as it buffers all non-empty lines in memory. You can achieve the same result more idiomatically and efficiently using a combination of standard shell utilities like grep and tail.

Suggested change
local n="$1"
awk -v n="$n" '
NF { out[++count] = $0 }
END {
if (n <= 0 || count == 0) {
exit
}
start = count - n + 1
if (start < 1) {
start = 1
}
for (i = start; i <= count; i++) {
print out[i]
}
}'
local n="$1"
# Use grep to filter out empty/whitespace-only lines and tail to get the last n lines.
# This is more memory-efficient than the awk implementation as it doesn't
# buffer the entire input.
grep -v '^[[:space:]]*$' | tail -n "$n"

@codecov
Copy link

codecov bot commented Mar 10, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.37%. Comparing base (eda01de) to head (250f41e).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1699      +/-   ##
==========================================
+ Coverage   93.16%   93.37%   +0.20%     
==========================================
  Files          48       48              
  Lines       13314    13732     +418     
==========================================
+ Hits        12404    12822     +418     
  Misses        724      724              
  Partials      186      186              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant