Skip to content

llvm: improve IR attributes and cast flags for better optimization#21

Merged
Jarred-Sumner merged 2 commits into
upgrade-0.15.2from
root/llvm-codegen-attrs
Apr 26, 2026
Merged

llvm: improve IR attributes and cast flags for better optimization#21
Jarred-Sumner merged 2 commits into
upgrade-0.15.2from
root/llvm-codegen-attrs

Conversation

@Jarred-Sumner

Copy link
Copy Markdown
Collaborator

Summary

Emit several LLVM IR attributes and instruction flags that Clang emits for equivalent C/C++ but Zig currently omits. Each was verified to produce measurably better optimized IR/asm (branch-to-select, vectorization, call-slot elimination, function purity).

  • dereferenceable(N) / dereferenceable_or_null(N) on *T / ?*T params
  • noundef + align + dereferenceable on by-ref aggregate params and sret
  • writable + dead_on_unwind on sret (new Builder attrs)
  • trunc nuw/nsw / zext nneg for unchecked @intCast instead of llvm.assume (keeps memory(none))
  • fast flag on fneg under .optimized float mode
  • optsize alongside cold for @branchHint(.cold) functions
  • index-counted loop for non-byte @memset (SCEV-friendly)
  • 16-byte over-alignment for large anonymous constants

Test plan

  • zig build test-llvm-ir (13 existing + 10 new cases)
  • Full behavior suite, ReleaseFast: 1982 pass / 0 fail
  • Targeted behavior tests (cast, cast_int, pointers, struct, fn, floatop, memset, byval_arg_var) in ReleaseFast + ReleaseSafe
  • Bitcode roundtrip via llvm-dis confirms new attrs/flags encode correctly
  • Full behavior suite, Debug (running)
  • CI cross-target builds

Emit several attributes and instruction flags that Clang emits for
equivalent C/C++ but Zig was leaving on the table, allowing LLVM to
perform optimizations it currently cannot:

- `*T` / `*const T` parameters: add `dereferenceable(N)` (and
  `dereferenceable_or_null(N)` for `?*T`). Enables speculative loads,
  branch-to-select, and loop vectorization on conditional field reads.
- by-ref / by-ref-mut aggregate parameters: add `noundef`, `align(N)`,
  `dereferenceable(N)`. The compiler synthesizes these pointers, so
  validity is guaranteed.
- sret parameter: add `noundef`, `align(N)`, `dereferenceable(N)`,
  `writable`, `dead_on_unwind` (new in Builder). Enables call-slot
  elimination of the temporary alloca+memcpy.
- `@intCast` in unchecked builds: stop emitting `llvm.assume(icmp ...)`,
  which strips `memory(none)` from any function containing a cast.
  Instead emit `trunc nuw/nsw` and `zext nneg` (new in Builder), which
  carry the same range guarantee with no side effects.
- `fneg` under `@setFloatMode(.optimized)`: forward the `fast` flag.
- `@branchHint(.cold)` at function scope: also add `optsize`, matching
  Clang's treatment of cold functions.
- non-byte `@memset` open-coded loop: emit an index-counted loop
  instead of pointer-phi, giving SCEV a clean trip count.
- anonymous unnamed_addr constants >= 16 bytes: over-align to 16 so
  memcpy lowering can use vector moves.

Builder.zig gains the `writable`/`dead_on_unwind` parameter attributes
and `trunc nuw/nsw`/`zext nneg` instruction tags (with bitcode encoding).

Adds llvm_ir test cases for each.
@coderabbitai

coderabbitai Bot commented Apr 26, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a1ab361e-58ce-4748-b015-c1665f2202aa

📥 Commits

Reviewing files that changed from the base of the PR and between 3458234 and 01e81b8.

📒 Files selected for processing (1)
  • test/llvm_ir.zig

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.


Walkthrough

Adds LLVM parameter attributes writable and dead_on_unwind, new cast instruction variants/flags (trunc nsw, trunc nuw, trunc nuw nsw, zext nneg) with bitcode encoding, and updates codegen to emit these attributes/casts, refine pointer-byref attributes, cold-function .optsize, float negation fneg fast, and non-byte memset lowering.

Changes

Cohort / File(s) Summary
LLVM Builder & IR
lib/std/zig/llvm/Builder.zig, lib/std/zig/llvm/ir.zig
Added Attribute.Kind tags writable, dead_on_unwind; introduced new cast instruction tags and mapping to cast opcodes; added FunctionBlock.CastFlags bitcode abbrev with packed flags field and registered it in abbrevs.
Code Generation
src/codegen/llvm.zig
Emit/propagate new parameter attributes (including writable/dead_on_unwind for sret), extend by-ref/pointer attrs with dereferenceable/dereferenceable_or_null and .noundef, add .optsize for cold functions, select trunc nuw/nsw and zext nneg when appropriate, emit fneg fast under fast float mode, and change non-byte memset to induction-loop form.
Tests
test/llvm_ir.zig
Added IR pattern tests for pointer dereferenceable attributes, sret with writable/dead_on_unwind/noundef, by-ref propagation, cast flags (nuw, nsw, combined; nneg), fneg fast behavior, and cold-function cold + optsize emission.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly summarizes the main changes: improving LLVM IR attributes and cast flags for better optimization, which aligns with the primary content across all modified files.
Description check ✅ Passed The description is well-related to the changeset, providing a detailed summary of LLVM IR attributes, instruction flags, and codegen patterns being added to match Clang's output and improve optimization.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/std/zig/llvm/ir.zig`:
- Around line 1537-1541: The packed struct named flags currently uses generic
field names bit0/bit1 which are easy to misuse; rename those fields to semantic
names (e.g., nuw and nsw) and add either a brief comment noting the special-case
mapping for zext (bit0 represents nneg) or add small helper accessors on the
flags type (e.g., isNuw(), isNsw(), isNneg()) so callers use semantic APIs
instead of raw bitN; update all uses of flags.bit0/bit1 to the new names or
helpers to keep opcode→bit mapping clear and maintainable.

In `@test/llvm_ir.zig`:
- Around line 160-175: Add a new test case that exercises the combined trunc
flags by calling cases.addMatches with a unique name like "intCast trunc nuw
nsw", using an export fn entry(x: i64) i32 { return `@intCast`(x); } (or an
appropriate signed/unsigned signature that triggers both nuw and nsw) and
include the expected LLVM match string "trunc nuw nsw i64" in the match array;
place it alongside the existing "intCast trunc nuw" and "intCast trunc nsw"
blocks so the .@"trunc nuw nsw" branch in src/codegen/llvm.zig is covered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9494e6cb-b9e2-44c5-8b5f-2905f24a9620

📥 Commits

Reviewing files that changed from the base of the PR and between 24475de and 3458234.

📒 Files selected for processing (4)
  • lib/std/zig/llvm/Builder.zig
  • lib/std/zig/llvm/ir.zig
  • src/codegen/llvm.zig
  • test/llvm_ir.zig

Comment thread lib/std/zig/llvm/ir.zig
Comment on lines +1537 to +1541
/// trunc: bit0 = nuw, bit1 = nsw. zext: bit0 = nneg.
flags: packed struct(u2) {
bit0: bool,
bit1: bool,
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider naming cast flag bits semantically.

bit0 / bit1 are correct but easy to misuse later. Using semantic names (or a small helper type) would make opcode→bit mapping safer to maintain.

Optional readability tweak
-        /// trunc: bit0 = nuw, bit1 = nsw. zext: bit0 = nneg.
-        flags: packed struct(u2) {
-            bit0: bool,
-            bit1: bool,
-        },
+        /// trunc: low = nuw, high = nsw. zext: low = nneg.
+        flags: packed struct(u2) {
+            low: bool,
+            high: bool,
+        },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// trunc: bit0 = nuw, bit1 = nsw. zext: bit0 = nneg.
flags: packed struct(u2) {
bit0: bool,
bit1: bool,
},
/// trunc: low = nuw, high = nsw. zext: low = nneg.
flags: packed struct(u2) {
low: bool,
high: bool,
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/std/zig/llvm/ir.zig` around lines 1537 - 1541, The packed struct named
flags currently uses generic field names bit0/bit1 which are easy to misuse;
rename those fields to semantic names (e.g., nuw and nsw) and add either a brief
comment noting the special-case mapping for zext (bit0 represents nneg) or add
small helper accessors on the flags type (e.g., isNuw(), isNsw(), isNneg()) so
callers use semantic APIs instead of raw bitN; update all uses of
flags.bit0/bit1 to the new names or helpers to keep opcode→bit mapping clear and
maintainable.

Comment thread test/llvm_ir.zig
@Jarred-Sumner Jarred-Sumner merged commit c6df1f4 into upgrade-0.15.2 Apr 26, 2026
12 checks passed
Jarred-Sumner added a commit to oven-sh/bun that referenced this pull request May 4, 2026
Picks up oven-sh/zig#21 which adds:
- dereferenceable(N) on *T pointer params and by-ref aggregate params
- writable/dead_on_unwind/noundef/align on sret
- trunc nuw/nsw and zext nneg for unchecked @intcast (replaces
  llvm.assume so functions stay memory(none))
- fneg fast under @setFloatMode(.optimized)
- optsize for @branchHint(.cold) functions
- index-counted @Memset loop for non-byte elements
- 16-byte over-align for large anonymous constants
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