Repository navigation
Conversation
Q5's answer: a current project builds all three engines with -fno-exceptions -fno-rtti on the M33, M55 and Hexagon. A3 made the engines and C ABIs build that way; no test had run there, and nothing had been built with -fno-rtti. - TAP_SR_NO_EXCEPTIONS_RTTI (root, OFF): every target compiles C++ with -fno-exceptions -fno-rtti (an option: CMAKE_CXX_FLAGS would replace the cross toolchains' _INIT flags). GCC and Clang only. - tests/support/tap_sr_test/expect_rejected.h: TAP_SR_EXPECT_REJECTED, _REJECTED_WITH and _ACCEPTED. With exceptions they are EXPECT_THROW, a throw whose what() is validate()'s text, and EXPECT_NO_THROW; without, a death test on std::terminate() where gtest has them, and the statement run. The six test files that used EXPECT_THROW or a try of their own go through them; their names are unchanged. - CI: the hosted no-exceptions job (name kept) now runs every battery, the examples and the C ABIs; the nine QEMU legs run again with the option, each with its own exclusions and budget. - ROADMAP: Q5 confirmed, A12, P14, the verdict rule and C3's freeze; PLAN.md D17; README (error model, options); CLAUDE.md's rule. Host: GCC 13 and Clang 18, Release, -Werror, C ABIs and examples on: 343/343 with exceptions and 343/343 without, both compilers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016GKzq1VSdcMYCRMhddiMaf
The no-exceptions measurement found the ratchet's counts were not the engines': built with -fno-exceptions -fno-rtti and the engines inlined into the workloads' main as before, rational's M55 float interpolators read +39 ... +53 % and bridge's M55 Q31 up row +15 %, every checksum unchanged. The disassembly: DspTap's dot kernel compiles to the same instructions either way; inlined into a 2 KB main whose control flow had lost its EH landing pads, GCC left the inner MAC loop's two pointers on the stack, 6 instructions a tap becoming 10. The engine had not changed; its caller had, and the baselines had been measuring the caller's register allocation. So the hot path becomes the engine's own: - bridge: process, pull and flush out of line (TAP_SR_BRIDGE_NOINLINE). Measured on the M33, M55 and Hexagon in both configurations: within +-0.7 % of every baseline, the configurations identical (PLAN.md M7f). - rational: the entries out of line (TAP_SR_RATIONAL_NOINLINE); each block runs against a cursor (the stream state and the hot constants as locals, open()/close(): an out-of-line this is reachable from compact()'s memmove, so a member read in the loop was a reload per frame); the history one planar allocation; the channel count dispatched to a compile-time constant for one and two channels (process_block<Ch>, as bridge's process() does for its taps: the lever PLAN.md 6 had named). Measured in both configurations on every target, against the previous baselines: identical to the instruction with and without exceptions on every row; M55 17 of 18 rows improved (Q15 -9.5 ... -17.8 %, the chain -12.9 %, up2_float +0.5 %, down2_float +1.8 % the one row up), M33 to -16 % (worst +2.0 %), Hexagon to -9 % (worst +0.2 %), host stereo -5 %. Three variants measured and declined, in PLAN.md section 6. - The ratchet jobs (QEMU and host) build the exceptions-off configuration too and count it against the same baselines: the identity is gated. - rational's baselines re-recorded on every target; its ceilings on the three: engine .text +25 ... +50 % (three channel instantiations), the M33 call stacks +64 ... +104 B (the out-of-line frame), the reason in PLAN.md 6. Footprint: the history's P ch sizeof(std::vector) term is gone (test_footprint.cpp, the README's table regenerated from the test). - Batteries: rational 138/138 and bridge 110/110 under clang in both configurations; bit-identity by the scipy vectors, the matrix and the ratchet's checksums. clang-tidy clean both ways. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016GKzq1VSdcMYCRMhddiMaf
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
Two commits, one item: the roadmap's A12, from Q5's answer that a current project builds all three engines with
-fno-exceptions -fno-rttion the M33, M55 and Hexagon.Commit 1 — every battery runs in that configuration.
TAP_SR_NO_EXCEPTIONS_RTTI(root, default OFF) builds every target with-fno-exceptions -fno-rtti. It is an option rather thanCMAKE_CXX_FLAGS, which would replace the cross toolchains' flags.tests/support/tap_sr_test/expect_rejected.h:TAP_SR_EXPECT_REJECTED,TAP_SR_EXPECT_REJECTED_WITHandTAP_SR_EXPECT_ACCEPTED. With exceptions they areEXPECT_THROW, a throw whosewhat()isvalidate()'s text, andEXPECT_NO_THROW; without, a death test onstd::terminate()where GoogleTest has death tests, and the statement run. The six test files that usedEXPECT_THROWor atryof their own go through them, test names unchanged.no-exceptionsjob now runs every battery, the examples and the C ABIs; the nine QEMU legs run again in that configuration.EXPECT_THROWor owntryin a test).Commit 2 — the hot path becomes the engine's own. The measurement found the ratchet's counts were not the engines'. Built the new way with the engines inlined into the workloads'
mainas before, rational's M55 float interpolators read +39% and +53% and bridge's M55 Q31 up row +15%, with every checksum unchanged.mainwhose control flow had lost its EH landing pads, GCC's register allocator left the inner MAC loop's two pointers on the stack. The loop went from 6 instructions a tap to 10; the four extra were a load and a store of each pointer. The baselines had been measuring the caller's register allocation.process,pullandflushout of line (TAP_SR_BRIDGE_NOINLINE). Within ±0.7% of every baseline on all three targets, both configurations identical (PLAN.md M7f).TAP_SR_RATIONAL_NOINLINE); each block runs against a cursor (the stream state and the hot constants as locals,open()/close()); the history is one planar allocation; the channel count is dispatched to a compile-time constant for one and two channels (process_block<Ch>, as bridge'sprocess()does for its taps — the lever rational's PLAN.md section 6 had already named). The cursor exists because an out-of-linethisis reachable fromcompact()'smemmove, so any member read in the loop was a reload per frame.P ch sizeof(std::vector)term; the README table is regenerated from the test.Why
A3 made the engines build without exceptions; no test had run that way, nothing had been built with
-fno-rtti, and once measured, the configuration the consumer ships ran up to 53% slower for a reason outside the engines. The 1.0 freeze now covers the configuration (P14), with every battery and the ratchet gating it.Verification
Batteries, both configurations, clang 18,
-Werror, C ABIs on: every engine 343/343 before the lever; after it, rational 138/138 and bridge 110/110 with and without exceptions. GCC 13 passes the same with exceptions on. Bit-identity: the scipy vectors, the 182-row matrix, the DspTap cross-check and every ratchet checksum unchanged.QEMU legs, exceptions off: all nine passed locally (M33: rational 9, bridge 8, async 8; M55 the same; Hexagon: rational 121, bridge 105, async 64) before the lever, then rational and bridge again after it.
Ratchet, final shape, both configurations, against the previous baselines (M33 and M55: arm-none-eabi-gcc 13.2.1, QEMU 8.2.2; Hexagon: clang 19.1.5; host: g++-13 under callgrind, as CI runs them):
Ceilings (rational, re-recorded): engine
.text+25% (M33/M55) to +50% (Hexagon Q15) for the three channel instantiations; image +2…+5%; M33 per-call stack 184–208 B against 88–128 B before, the out-of-line frame. bridge's and async's did not move.Declined on measurement (M55, both configurations):
emit()out of line per output (identical, floats +4.8%); the cursor with the entries inlined (floats −1.5…−2.8% but the chain +13.4% without exceptions and the configurations apart again);-fno-version-loops-for-strideson the outlined entries (−1.5 points only).Style: clang-tidy clean on every changed TU in both configurations; clang-format;
check_retired_words; the icount and ceilings README tables pass their freshness checks.Not run here: Windows (unchanged, exceptions on) and the arm64 TSan workflow; CI is their gate.
Notes for the reviewer
basic_stage's history layout is private.emitis nowstaticin rational's stage (the book'srational_emitinclude updated).bridgeandrationalare no longer inlinable. A consumer's own inlining of them was never a promise, and it is what made their cost depend on the caller.Generated by Claude Code