{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreibxcvtvw2wb5gma45sfrerm2cpwrdiuhwik2bvxbzd336htlsniju",
"uri": "at://did:plc:ivbknywyskln22er3nkssdhl/app.bsky.feed.post/3mfdmabjpzl72"
},
"path": "/t/idea-pre-rfc-null-free-pointers/23991?page=5#post_84",
"publishedAt": "2026-02-21T01:11:47.000Z",
"site": "https://internals.rust-lang.org",
"tags": [
"@RalfJung"
],
"textContent": "H4n_uL:\n\n> The revised proposal lists this under migration cost as unmeasured and needing benchmarks.\n\nAs promised in my previous comment, I've measured the codegen impact of `null_pointer_is_valid`. This addresses a gap that @RalfJung rightly pointed out, and that several others in the thread have implicitly assumed without data.\n\nI am not a compiler engineer, so I kept it to five simple C patterns on `aarch64-unknown-none` with Homebrew clang 21.1.8 at `-O2`, comparing baseline against `-fno-delete-null-pointer-checks`.\n\n\n // bench.c\n\n // clang -v | Homebrew clang version 21.1.8\n // clang --target=aarch64-unknown-none -O2 -S bench.c -o baseline.s\n // clang --target=aarch64-unknown-none -O2 -fno-delete-null-pointer-checks -S bench.c -o zeroisvalid.s\n // diff baseline.s zeroisvalid.s\n\n #include <stdint.h>\n #include <stddef.h>\n\n // C1: null check elimination after dereference\n uint32_t check_after_deref(const uint32_t *p) {\n uint32_t v = *p; // dereference\n if (p) return v + 1; // compiler can assume p != null\n return 0; // baseline: this branch eliminated\n }\n\n // C2: two paths merging on null knowledge\n uint32_t branch_after_store(uint32_t *p, uint32_t val) {\n *p = val; // store implies p != null\n if (p) return *p + 1; // redundant check?\n return 42;\n }\n\n // C3: loop with pointer increment\n uint32_t sum_until_null(const uint32_t *const *ptrs) {\n uint32_t sum = 0;\n while (*ptrs) { // null-terminated array of pointers\n sum += **(ptrs++);\n }\n return sum;\n }\n\n // C4: devirtualisation / inlining based on nonnull\n void copy_if_valid(uint32_t *dst, const uint32_t *src, size_t n) {\n if (dst && src) {\n for (size_t i = 0; i < n; i++)\n dst[i] = src[i];\n }\n }\n\n // C5: struct access implying nonnull\n uint32_t read_two_fields(const struct { uint32_t a; uint32_t b; } *s) {\n uint32_t x = s->a; // implies s != null\n if (s) return x + s->b; // check eliminable?\n return 0;\n }\n\n\n\n 8a9,10\n > cbz x0, .LBB0_2\n > // %bb.1:\n 10a13\n > .LBB0_2:\n 21a25,26\n > mov w9, #42 // =0x2a\n > cmp x0, #0\n 23c28\n < add w0, w1, #1\n ---\n > csinc w0, w9, w1, eq\n 111a117,118\n > cbz x0, .LBB4_2\n > // %bb.1:\n 113a121\n > .LBB4_2:\n\n\nThe result: codegen differences appear only where the compiler would otherwise eliminate an implicit null check after a dereference or store: 2–3 additional instructions per site. Explicit null comparisons (C3, C4) produce identical output.\n\nThis doesn't prove the cost is zero for Rust, but given that one of the most performance-sensitive C codebases in existence has operated without this optimisation for over fifteen years, I think the default assumption should be that the cost is manageable unless demonstrated otherwise.",
"title": "Idea / Pre-RFC: Null-free pointers"
}