Sunday, October 11, 2026

Why are my builds slow? Using CMake’s Instrumentation FeatureCMake 4.2 dropped the experimental gate on a major new feature: the CMake Instrumentation API. This enables detailed tracking of the entire CMake workflow, including configure time, build execution, testing, and installation, and provides developers and teams with actionable insights into build performance. Support for automated, user-written scripts to handle instrumentation data make it easy to track performance data over time across different machines and environments.📝Kitware Inc
C++26 Contracts: Introduction to Preconditions and PostconditionsIn the article about Standard Library hardening , we looked at checks for invalid Standard Library container access and other broken preconditions. I mentioned that those requirements are expressed in terms of “contracts”. But what about our code? In this post, we’ll explore contracts from C++26. We’ll start with a simple cassert , move the requirement into the function itself, and see how to check results. We’ll also look at declarations and definitions, compiler options, and a few details that can surprise you. Starting with an Assertion Let’s start with a small example: A header file first: // foo.h #include int get_value ( const std :: vector int >& values , std :: size_t index ); And the implementation: // foo.cpp #include "foo.h" // our function declaration... #include int get_value ( const std :: vector int >& values , std :: size_t index ) { assert ( index values . size ()); return values [ index ]; } The function expects a valid index. If the condition is false, assert prints a diagnostic message and terminates the program. However, this only happens when assertions are enabled. Defining NDEBUG disables the check and basically removes the check from the code. What are the issues with this simple approach? The requirement lives inside the implementation. Somebody reading only the declaration sees a vector and an index, without the relationship between them. Adding a Precondition With C++26 contracts, we can move that requirement from an assertion inside the function body to the function itself: int get_value ( const std :: vector int >& values , std :: size_t index ) pre ( index values . size ()) { return values [ index ]; } Notice the new pre(...) syntax after the parameter list. It introduces a precondition assertion : a condition expected to hold when entering the function. What’s more, we can add this precondition to the declaration: int get_value ( const std :: vector int >& values , std :: size_t index ) pre ( index values . size ()); The feature was introduced through P2900, Contracts for C++ . Let’s understand how it works and how it differs from assert . Why Use Contracts Instead of assert ? At first, pre(index might look like another way to write assert(index . Both express a condition that should be true. So what’s the benefit of using contracts? There are a few important differences: Requirements in declarations: We can put pre and post directly in the function declaration. This makes the requirements visible to anyone reading the API, without looking at the implementation. More flexible checking: Traditional assert is enabled or disabled through NDEBUG . Contracts define several evaluation semantics, including ignoring a check, reporting a violation and continuing, or terminating the program. The compiler implementation determines which semantics are available and how they can be selected. Preconditions and postconditions: Contracts distinguish between what the caller must provide and what the function promises to return. With assert , we would have to place checks manually inside the function body, including checks before different return statements. This also makes contracts useful for documenting APIs, even when runtime checking is disabled. A Complete Precondition Example Here’s a complete example (no header file for simplicity for now): #include #include int get_value ( const std :: vector int >& values , std :: size_t index ) pre ( index values . size ()) { return values [ index ]; } int main () { std :: vector int > values { 10 , 20 , 30 }; std :: println ( "{}" , get_value ( values , 1 )); // get_value(values, 10); // violates the precondition } The valid call prints: 20 And here’s the version for you to play with the problematic call uncommented: See at Compiler Explorer On GCC, I’m getting: Program returned: 134 Program stdout 20 Program stderr contract violation in function int get_value(const std::vector &, std::size_t) at /app/example.cpp:6: index Contracts on Declarations and Definitions For clarity, contracts can appear on a declaration, in a definition, or both: int get_value ( const std :: vector int >& values , std :: size_t index ) pre ( index values . size ()); The definition can then omit the contract specifier: int get_value ( const std :: vector int >& values , std :: size_t index ) { return values [ index ]; } The contract assertions are established by the function’s first declaration. A later declaration or definition may omit the contract specifiers, as above, or repeat the same sequence. A later declaration cannot introduce a different set of preconditions or postconditions. In practice, if a function is declared in a header, that first declaration is the natural place for its preconditions and postconditions. If you’re reading the API, it’s probably best to see all preconditions without looking inside the implementation. What’s more, contracts are associated with the function, but they are not part of the function type. Overload resolution, for example, does not distinguish functions based on their contracts. Contract Evaluation Semantics C++26 defines four evaluation semantics: Semantic What happens at runtime? Ignore The predicate is not evaluated. Observe A violation invokes the handler; execution continues if it returns normally. Enforce A violation invokes the handler; execution terminates if it returns normally. Quick-enforce A violation terminates execution without invoking the handler. The implementation determines which semantic applies. C++26 has no portable syntax for forcing an individual ordinary contract assertion to use one specific policy. These rules appear in the contract evaluation specification . With the ignore semantic, no runtime check is performed. With observe, a violation is reported, but execution continues if the handler returns normally. In our example, both policies can allow the program to reach values[index] with an invalid index, resulting in undefined behavior. Thus, adding pre(...) expresses the requirement, while the selected policy determines how checking responds. Compiling with GCC As of October 2026, only GCC 16 supports Contracts. We can use it with special compile flags: g++ -std = c++26 -fcontracts \ -fcontract-evaluation-semantic = enforce example.cpp With that policy, the invalid call invokes the violation handler. If the handler returns normally, execution terminates before the function body performs the invalid access. GCC’s default handler emits diagnostic information. See the GCC options documentation - contracts . GCC also allows us to replace the default contract-violation handler. For example, we can log a custom message when a violation is detected. The handler receives a std::contracts::contract_violation object, which provides information about the failed contract, including the predicate, location, and evaluation semantic. #include #include void handle_contract_violation ( const std :: contracts :: contract_violation & violation ) { std :: println ( stderr , "Contract violated: {}" , violation . comment ()); } See an example @Compiler Explorer Notice that replacing the handler doesn’t change what happens after it returns. With observe , execution continues. With enforce , the program terminates. Not every implementation is required to support replacing the handler, so this feature is implementation-defined. Adding a Postcondition So far, we’ve described what the caller must provide. We can also describe what a function promises when it returns normally. See this example: int clamp_value ( int value , const int low , const int high ) pre ( low high ) post ( result : result >= low && result high ) { if ( value low ) return low ; if ( value > high ) return high ; return value ; } There are two useful pieces here: pre(low checks that the bounds form a valid interval. post(result: ...) checks that the returned value lies inside that interval. The name result is ours to choose. It refers to the function’s result within that postcondition. What’s convenient is that the same postcondition covers every normal return path. We don’t have to repeat an assertion before each return . Here’s an example where I deliberately violated the postcondition: #include int clamp_value ( int value , const int low , const int high ) pre ( low high ) post ( result : result >= low && result high ) { if ( value low ) return low - 10 ; // if ( value > high ) return high ; return value ; } int main () { std :: print ( "{}" , clamp_value ( 10 , 20 , 30 )); } See @Compiler Explorer And on GCC I’m getting: contract violation in function int clamp_value(int, int, int) at /app/example.cpp:5: result >= low && result [assertion_kind: post, semantic: enforce, mode: predicate_false, terminating: yes] terminate called without an active exception Program terminated with signal SIGABRT (6) The precondition is fine, but the postcondition is broken. Const Parameters in Postconditions There’s also a detail in the parameter list: low and high are const . You will get the following error if you try making them non-const: source>:5:28: error: a value parameter used in a postcondition must be const 5 | post(result: result >= low && result When a postcondition uses a parameter passed by value, that parameter must have const type on every declaration. Our comparisons use both bounds this way. The parameter value is only used in the body, so this requirement doesn’t apply to it. Postconditions apply to normal exit; they do not check the result when the function body exits through an exception. See the function contract rules . Assertions Inside a Function For conditions inside an implementation, we have contract_assert : #include std :: size_t count_spaces ( std :: string_view text ) { std :: size_t count = 0 ; for ( char ch : text ) { if ( ch == ' ' ) ++ count ; } contract_assert ( count text . size ()); return count ; } The condition is simple, but it expresses a useful property: the number of spaces cannot exceed the number of characters. In a larger algorithm, such a check could describe a relationship between counters, an intermediate result, or the state after processing a block of data. Together, pre , post , and contract_assert cover function entry, normal exit, and points inside a function. Avoiding Mutation in Predicates One thing to watch during migration is mutation inside predicates: void process ( int count ) pre ( ++ count > 0 ) // error { } See @Compiler Explorer An error from GCC: error: increment of read-only location '(const int)count' 2 | pre(++count > 0) // error Contract predicates use special constification rules. In this example, count is treated as const inside the predicate, so ++count is rejected by the compiler. However, this does not make predicates completely free of side effects. For example, a predicate can still call a function that modifies global state. Since contract evaluation may be ignored, and side effects are not guaranteed even under checking semantics, predicates should not modify program state. Contracts Are Not Input Validation There’s another practical distinction: input validation. Suppose an index comes from a configuration file. If an invalid value requires an error message, that handling belongs in ordinary control flow: if ( index >= values . size ()) { std :: println ( "Invalid index: {}" , index ); return ; } std :: println ( "{}" , get_value ( values , index )); The validation handles an expected failure at the application boundary. The precondition documents the internal helper’s requirements. P2900 explicitly discusses this idea in: Principle 12: Contract Assertions Are Not Flow Control: While a contract assertion provides an algorithm to validate correctness, nothing about a contract assertion guarantees any particular runtime behavior associated with that syntactic construct. And more: Importantly, this aspect of Contracts is why contract assertions must not be used for error handling and input validation: If a function has in-contract requirements to report certain events as errors, that handling must be done with standard C++ control statements that are not optional, never with contract assertions. Considering Predicate Costs Finally, one note about performance implications. Our index comparison is cheap. A condition that verifies whether an entire collection is sorted needs to examine its elements. An expensive check may be useful during testing, but you should measure its effect on representative workloads. Summary C++26 contracts give us three ways to express assumptions and guarantees directly in the language: pre(...) describes conditions expected to hold when a function is entered, post(...) describes conditions expected to hold when a function returns normally, contract_assert(...) checks properties at specific points inside a function. Preconditions and postconditions are associated with the function through its declarations. In particular, the first declaration establishes the contract, which makes declarations in header files a natural place to document an API’s requirements. A contract assertion does not necessarily mean that a runtime check will always be performed. The selected evaluation semantic can ignore, observe, enforce, or quick-enforce the assertion. Code therefore shouldn’t depend on contract predicates for required application logic or side effects. Contracts also aren’t a replacement for normal input validation. Use regular control flow for errors that your program expects and needs to handle. References P2900, Contracts for C++ Contract assertions (since C++26) - cppreference.com - C++ Reference How C++26 Contracts Improve Correctness Without Sacrificing Performance | Citadel Securities How C++26 contracts work | Train IT Test Driving C++26 Contracts with GCC 16.1 | Fabian Koehler📝C++ Stories

If this page is useful, please consider donating a coffee

Saturday, October 10, 2026

Friday, October 9, 2026

Thursday, October 8, 2026

CMake: Making Hard Things EasyAt CppCon 2026, I gave a talk called “CMake: Making Hard Things Easy” about how CMake handles build-system complexity so developers can focus on their software. This article shares practical takeaways from that talk. We started CMake for ITK 26 years ago. Researchers needed to use medical image segmentation and registration algorithms across different platforms without becoming experts in every compiler and linker. The idea was to capture that expertise in the build system so everyone could benefit from it. I wrote about those early days in Happy Birthday CMake!. Today, we take it for granted that we have build tools that can create static or shared libraries and link applications to them on Linux, Windows, or macOS.📝Kitware Inc
Introducing the QML Live Preview MCP Tool for Agentic Development of Embedded DevicesAgents Edit QML, But Don’t See the Result Without Building, Compiling, and Running AI agents can make many UI changes in a single iteration. AI agents edit files, maybe run a build, and then report success. Every change can costs a full rebuild and relaunch, or it goes unchecked by the developer. Qt's QML Live Preview MCP tool makes autonomous AI agent work transparent for the developer at a low token expense: it runs the app and hot reloads changed QML files, without a rebuild and without a restart.📝Qt Blog

Wednesday, October 7, 2026

Tuesday, October 6, 2026

Monday, October 5, 2026

Two-Way Doors: Don’t Code Yourself into a Corner@media only screen and (max-width: 600px) { .body { overflow-x: auto; } .post-content table, .post-content td { width: auto !important; white-space: nowrap; } } This article was adapted from a Google Tech on the Toilet (TotT) episode. You can download a printer-friendly version of this TotT episode and post it in your office. By Nimit Khandelwal and Chris Kennelly On large software projects, even seemingly trivial choices can set assumptions that the codebase naturally evolves around, making them incredibly expensive to fix later. To help achieve decision velocity without accumulating technical debt, leverage the “one-way vs. two-way doors” mental model : One-way doors : Decisions that are rare, highly consequential, and hard to reverse (e.g., picking a database backend). Two-way doors : Decisions that are low-risk and easy to walk back (e.g., choosing a local variable name). Unless designing for flexibility, even trivial choices can quickly harden into painful one-way doors. To maintain high decision velocity safely, use deliberate architectural patterns to engineer reversibility into your code. Instead of coding yourself into a corner, actively design your code with built-in escape hatches. By consciously building flexibility into your systems up front, you can eliminate analysis paralysis and keep your team executing both fast and safely. Here are some patterns you can use to build two-way-doors into your code : Pattern Description How It Creates a Two-Way Door Feature flags Use conditional switches within the code to safely roll out a new feature in production, decoupling deployment from release. Dynamically isolates experimental behavior from the baseline. If a new logic path fails in production, the flag update can be rolled back and the system restored to the previous, good known state. Limiting API exposure Limit access to APIs that are under development; only expand access once the design is highly mature. Restricts access to your module or package, allowing you to iterate and refactor the interface without breaking downstream clients. Reversing a public API is a painful one-way door. Decoupling data formats Keep transient in-memory parsing and active RPC payload layouts separate from your persistent disk structures. Isolates your changes from disk storage. Changing memory layouts is cheap, but writing new data formats to disk is a one-way door since you must support reading that format forever. Dark launches Run new experimental logic in parallel with the status quo, discarding the experimental output but recording telemetry. Allows you to validate performance and correctness at production scale with zero downstream impact, making it incredibly easy to walk back or modify before committing. This content was adapted from a Google Performance Tip of the Week: abseil.io/fast/87 .📝Google Testing Blog
GSOC26: Sharing LLVM-libc's floating-point routines with compiler-rtIntroduction Hello LLVM community! I am Mohamed Emad, CSE student at Zagazig University, Egypt. This summer I worked on the next step of the Hand-in-Hand project, an effort that began in 2024 with #91651 to make LLVM-libc a shared foundation that the rest of LLVM can build on. My part was to share LLVM-libc’s floating-point math routines with compiler-rt. It replaces compiler-rt’s existing builtins, which were written from scratch in C and assembly with a separate implementation per architecture, with LLVM-libc’s code, which is well-tested and well-maintained. Overview When a program targets hardware without a floating-point unit, the compilercannot emit an fadd or fcvt instruction. Instead it emits a call to a helperroutine: __adddf3 to add two doubles, __fixdfsi to convert a double to an int , __letf2 to compare two float128 s. compiler-rt’s builtins libraryships these routines, and it has carried its own soft-float implementations ofthem for years. LLVM-libc implements the same math independently, with correctly-rounded resultsand a large test suite behind it. So the project starts from an awkward fact:LLVM keeps two separate soft-float implementations of the same operations. Twocopies drift apart, each needs its own review and testing, and a bug fixed inone rarely reaches the other. compiler-rt’s versions are also older and lessrigorously verified than libc’s. This project gives compiler-rt one source of truth. LLVM-libc exposes itsroutines as freestanding headers under LIBC_NAMESPACE::shared:: , and eachcompiler-rt builtin becomes a thin wrapper that forwards to the matching libcroutine. A single .c file such as truncdfsf2.c is swapped for a truncdfsf2.cpp that calls shared::truncdfsf2 . The swap happens through aCMake macro, use_libc_builtin() , gated on COMPILER_RT_USE_LIBC_MATH , sodistributors who want the libc-backed path opt in and everyone else keeps theexisting behavior. Figure 1: Two soft-float implementations of the same operation become one shared LLVM-libc routine that compiler-rt forwards to. The result covers the arithmetic builtins (add, subtract, multiply, divide for float , double , and float128 ), float-to-integer and integer-to-floatconversions across the integer widths, float-to-float extend and truncateacross every format including x87 80-bit, half, and bfloat16, and the comparisonroutines. compiler-rt gets libc’s tested math, and LLVM stops maintaining theoperation twice. Figure 2: The build-time swap and the call path: use_libc_builtin() replaces truncdfsf2.c with a wrapper that forwards __truncdfsf2 to shared::truncdfsf2 . Challenges A routine that calls itself. Some of LLVM-libc’s conversions are written asan ordinary cast from one floating-point type to another. That is fine when thehardware can do the conversion. On a target without an FPU, the compiler turnsthat same cast back into a call to the very builtin we are implementing, so theroutine calls itself and never finishes. The way out was to do the conversionthrough an internal, integer-only representation, so nothing the compiler seescan turn back into a floating-point builtin. A companion project, describedbelow, will let us drop even this workaround. Figure 3: An ordinary cast turns back into the builtin and loops forever (left); going through an integer-only representation breaks the loop (right). 16-bit floats the target cannot name. Half precision and bfloat16 are notreal types on every target, and even where they exist, naming one in a functionsignature quietly pulls in yet another conversion builtin. So these routinesnever take a 16-bit value directly. They receive the raw bits and rebuild thenumber from a description of the format, which lets the half and bfloat16conversions work even on targets that have no 16-bit float type at all. The 80-bit x87 format. Intel’s 80-bit extended long double does not followthe same layout rules as the standard IEEE formats the shared code expects, andit needs a 128-bit integer to hold its bit pattern, which does not exist on32-bit x86 where long double is still 80 bits. We handled it the waycompiler-rt already does: convert through a standard double or float at theedges, and compile these routines only where the 80-bit format actually exists. Two libraries in one binary. LLVM-libc’s routines carry their own symbolnames. Linked into compiler-rt next to the target’s real C library, those nameswould collide. We build the shared routines under a private namespace so nothingclashes, and keep the few legacy alias names that some platforms still expect. Seventy builtins, one pattern. Every builtin repeats the same shape in fourplaces: an LLVM-libc header, a compiler-rt wrapper, a build-system entry, and atest. Keeping seventy of them aligned by hand would invite mistakes, so a smallgenerator produces all four from a single description of each builtin. Onechange to the pattern updates every builtin at once. What comes next The functional work is landing. The next phase is measuring and tuning it. Benchmarking. These routines run inside the compiled output of any programbuilt for an FPU-less target, so both their speed and their code size matter. Wewill compare each libc-backed builtin against compiler-rt’s original soft-floatversion across the targets that actually use them: soft-float Arm, x86 withoutSSE, and bare-metal embedded configurations. The comparison covers per-calllatency and the size each routine adds to a static binary. We will test it on raspberry pi pico 2 since it uses Arm Cortex-M33 that doesn’t contain a floating-point unit for double-precision so it will be a good choice for benchmarking and finding the tradeoffs. Optimization. The integer-only conversion path trades some hand-tuned bitmanipulation for a single shared implementation. Where a benchmark shows that costing more than compiler-rt’s original, we will tighten the hot paths, and we will watchcode size closely so the shared routines stay usable in size-constrained builds. Emulated soft-float types. Two efforts in LLVM-libc will replace theworkarounds behind the conversion builtins with something cleaner. A companionGSoC 2026 project by Sukumar Sawant , Emulated Float128 and Float80 in LLVM-libc (tracked in #206895 ), addsstruct-backed versions of the wide formats, and software float16 does the same forhalf. Once a format has an emulated type, converting to it no longer turns backinto the builtin we are implementing, because the compiler has no native type toconvert with and uses the software path instead. The self-call problemdisappears at its source, the half conversions stop needing the raw-bits detour,and the code drops the integer-only workaround for a direct conversion.That is also where most of the optimization headroom lives: an emulated typecarries a proper representation the routines can specialize against, rather thanrebuilding the value from bits on every call. Finishing the port. The comparison stack and the integer, x87, half, andbfloat16 conversion stacks are in review. Once they merge, the remainingbuiltins follow the same generator-driven pattern. Milestones PR Title Status #197950 Introduce libc math routines into compiler-rt builtins Merged #200094 Introduce shared compiler-rt builtins in LLVM-libc Merged #200196 Add precommit CI for the compiler-rt + libc integration Merged #200539 Document builtin compatibility Merged #205669–#205679 Shared add/sub/mul/div builtins for sf / df / tf (LLVM-libc, 11 PRs) Merged #207092 Libc-backed arithmetic builtins (compiler-rt) Merged #207543 Libc-backed float-to-int conversion builtins Merged #209900 Libc-backed int-to-float/double conversion builtins Merged #209984 Libc-backed float-to-float extend/truncate builtins Merged #213481 Filter libc-backed builtins superseded by assembly (CMake) Merged #211881 Libc-backed bfloat16 extend/truncate builtins Open #211882 Libc-backed float16 extend/truncate builtins Open #212649 Add cmp_helper for soft-float comparisons Open #212651 Libc-backed single-float comparison builtins Open #212652 Libc-backed double-float comparison builtins Open #212653 Libc-backed quad-float comparison builtins Open #215725 Libc-backed int-to-quad conversion builtins Open #215726 Add the missing int-float conversion tests Open #215727 Libc-backed quad-to-int conversion builtins Open #215728 Libc-backed float80-to-int conversion builtins Open #215729 Libc-backed float80/bfloat16/float16 conversion builtins Open #183959 Build option to warn about unlisted builtins Open Conclusion This summer I got to take a real piece of LLVM and remove a duplication that had been sitting in it for years. compiler-rt now backs its arithmetic and conversion builtins with LLVM-libc’s tested math, and the comparison and remaining conversion builtins are in review and close behind. Along the way I learned far more about floating-point corner cases than I expected: how a harmless-looking cast can end up calling itself forever, why a 16-bit float can simply not exist on a target, and how the 80-bit x87 format bends nearly every rule around it. There is still work ahead. The open pull requests need to land, and once the emulated soft-float types arrive, the conversions get simpler and the real benchmarking and optimization phase can begin. I am excited to keep pushing this forward with the community. Acknowledgments Huge thanks to my mentors, Tue Ly , Michael Jones , and Muhammad Bassiouni . Their reviews, patience, and design guidance shaped this work. Thank you as well to the LLVM-libc and compiler-rt reviewers for the careful feedback, and to the whole LLVM community for making room for a project like this. It has been a wonderful summer.📝The LLVM Project Blog