aboutsummaryrefslogtreecommitdiff
path: root/src/codegen
AgeCommit message (Collapse)Author
2025-07-02compiler: delete aarch64 backendAndrew Kelley
this backend was abandoned before it was completed, and it is not worth salvaging.
2025-07-02Merge pull request #24302 from alexrp/riscvAlex Rønne Petersen
Native RISC-V bootstrap and test fixes
2025-07-01spirv: fix signed overflow detection for safe subtractionIvan Stepanov
The overflow check for safe signed subtraction was using the formula (rhs < 0) == (lhs > result). This logic is flawed and incorrectly reports an overflow when the right-hand side is zero. For the expression 42 - 0, this check evaluated to (0 < 0) == (42 > 42), which is false == false, resulting in true. This caused the generated SPIR-V to incorrectly branch to an OpUnreachable instruction, preventing the result from being stored. Fixes #24281.
2025-07-01llvm: Disable the machine outliner pass on RISC-VAlex Rønne Petersen
2025-07-01llvm: Fix alignment of by-ref ptr_elem_val and slice_elem_valAlex Rønne Petersen
2025-06-23remove `spirv` cpu archAli Cheraghi
2025-06-19x86_64: increase passing test coverage on windowsJacob Young
Now that codegen has no references to linker state this is much easier. Closes #24153
2025-06-19Target: pass and use locals by pointer instead of by valueJacob Young
This struct is larger than 256 bytes and code that copies it consistently shows up in profiles of the compiler.
2025-06-15compiler: fix `@intFromFloat` safety checkmlugg
This safety check was completely broken; it triggered unchecked illegal behavior *in order to implement the safety check*. You definitely can't do that! Instead, we must explicitly check the boundaries. This is a tiny bit fiddly, because we need to make sure we do floating-point rounding in the correct direction, and also handle the fact that the operation truncates so the boundary works differently for min vs max. Instead of implementing this safety check in Sema, there are now dedicated AIR instructions for safety-checked intfromfloat (two instructions; which one is used depends on the float mode). Currently, no backend directly implements them; instead, a `Legalize.Feature` is added which expands the safety check, and this feature is enabled for all backends we currently test, including the LLVM backend. The `u0` case is still handled in Sema, because Sema needs to check for that anyway due to the comptime-known result. The old safety check here was also completely broken and has therefore been rewritten. In that case, we just check for 'abs(input) < 1.0'. I've added a bunch of test coverage for the boundary cases of `@intFromFloat`, both for successes (in `test/behavior/cast.zig`) and failures (in `test/cases/safety/`). Resolves: #24161
2025-06-15Legalize: make the feature set comptime-known in zig1Jacob Young
This allows legalizations to be added that aren't used by zig1 without affecting the size of zig1.
2025-06-12x86_64: remove air references from mirJacob Young
2025-06-12spirv: make the backend compile againmlugg
Unfortunately, the self-hosted SPIR-V backend is quite tightly coupled with the self-hosted SPIR-V linker through its `Object` concept (which is much like `llvm.Object`). Reworking this would be too much work for this branch. So, for now, I have introduced a special case (similar to the LLVM backend's special case) to the codegen logic when using this backend. We will want to delete this special case at some point, but it need not block this work.
2025-06-12compiler: rework backend pipeline to separate codegen and linkmlugg
The idea here is that instead of the linker calling into codegen, instead codegen should run before we touch the linker, and after MIR is produced, it is sent to the linker. Aside from simplifying the call graph (by preventing N linkers from each calling into M codegen backends!), this has the huge benefit that it is possible to parallellize codegen separately from linking. The threading model can look like this: * 1 semantic analysis thread, which generates AIR * N codegen threads, which process AIR into MIR * 1 linker thread, which emits MIR to the binary The codegen threads are also responsible for `Air.Legalize` and `Air.Liveness`; it's more efficient to do this work here instead of blocking the main thread for this trivially parallel task. I have repurposed the `Zcu.Feature.separate_thread` backend feature to indicate support for this 1:N:1 threading pattern. This commit makes the C backend support this feature, since it was relatively easy to divorce from `link.C`: it just required eliminating some shared buffers. Other backends don't currently support this feature. In fact, they don't even compile -- the next few commits will fix them back up.
2025-06-12link: divorce LLD from the self-hosted linkersmlugg
Similar to the previous commit, this commit untangles LLD integration from the self-hosted linkers. Despite the big network of functions which were involved, it turns out what was going on here is quite simple. The LLD linking logic is actually very self-contained; it requires a few flags from the `link.File.OpenOptions`, but that's really about it. We don't need any of the mutable state on `Elf`/`Coff`/`Wasm`, for instance. There was some legacy code trying to handle support for using self-hosted codegen with LLD, but that's not a supported use case, so I've just stripped it out. For now, I've just pasted the logic for linking the 3 targets we currently support using LLD for into this new linker implementation, `link.Lld`; however, it's almost certainly possible to combine some of the logic and simplify this file a bit. But to be honest, it's not actually that bad right now. This commit ends up eliminating the distinction between `flush` and `flushZcu` (formerly `flushModule`) in linkers, where the latter previously meant something along the lines of "flush, but if you're going to be linking with LLD, just flush the ZCU object file, don't actually link"?. The distinction here doesn't seem like it was properly defined, and most linkers seem to treat them as essentially identical anyway. Regardless, all calls to `flushZcu` are gone now, so it's deleted -- one `flush` to rule them all! The end result of this commit and the preceding one is that LLVM and LLD fit into the pipeline much more sanely: * If we're using LLVM for the ZCU, that state is on `zcu.llvm_object` * If we're using LLD to link, then the `link.File` is a `link.Lld` * Calls to "ZCU link functions" (e.g. `updateNav`) lower to calls to the LLVM object if it's available, or otherwise to the `link.File` if it's available (neither is available under `-fno-emit-bin`) * After everything is done, linking is finalized by calling `flush` on the `link.File`; for `link.Lld` this invokes LLD, for other linkers it flushes self-hosted linker state There's one messy thing remaining, and that's how self-hosted function codegen in a ZCU works; right now, we process AIR with a call sequence something like this: * `link.doTask` * `Zcu.PerThread.linkerUpdateFunc` * `link.File.updateFunc` * `link.Elf.updateFunc` * `link.Elf.ZigObject.updateFunc` * `codegen.generateFunction` * `arch.x86_64.CodeGen.generate` So, we start in the linker, take a scenic detour through `Zcu`, go back to the linker, into its implementation, and then... right back out, into code which is generic over the linker implementation, and then dispatch on the *backend* instead! Of course, within `arch.x86_64.CodeGen`, there are some more places which switch on the `link` implementation being used. This is all pretty silly... so it shall be my next target.
2025-06-12compiler: slightly untangle LLVM from the linkersmlugg
The main goal of this commit is to make it easier to decouple codegen from the linkers by being able to do LLVM codegen without going through the `link.File`; however, this ended up being a nice refactor anyway. Previously, every linker stored an optional `llvm.Object`, which was populated when using LLVM for the ZCU *and* linking an output binary; and `Zcu` also stored an optional `llvm.Object`, which was used only when we needed LLVM for the ZCU (e.g. for `-femit-llvm-bc`) but were not emitting a binary. This situation was incredibly silly. It meant there were N+1 places the LLVM object might be instead of just 1, and it meant that every linker had to start a bunch of methods by checking for an LLVM object, and just dispatching to the corresponding method on *it* instead if it was not `null`. Instead, we now always store the LLVM object on the `Zcu` -- which makes sense, because it corresponds to the object emitted by, well, the Zig Compilation Unit! The linkers now mostly don't make reference to LLVM. `Compilation` makes sure to emit the LLVM object if necessary before calling `flush`, so it is ready for the linker. Also, all of the `link.File` methods which act on the ZCU -- like `updateNav` -- now check for the LLVM object in `link.zig` instead of in every single individual linker implementation. Notably, the change to LLVM emit improves this rather ludicrous call chain in the `-fllvm -flld` case: * Compilation.flush * link.File.flush * link.Elf.flush * link.Elf.linkWithLLD * link.Elf.flushModule * link.emitLlvmObject * Compilation.emitLlvmObject * llvm.Object.emit Replacing it with this one: * Compilation.flush * llvm.Object.emit ...although we do currently still end up in `link.Elf.linkWithLLD` to do the actual linking. The logic for invoking LLD should probably also be unified at least somewhat; I haven't done that in this commit.
2025-06-06x86_64: add support for pie executablesJacob Young
2025-06-05std.Target: Introduce Cpu convenience functions for feature tests.Alex Rønne Petersen
Before: * std.Target.arm.featureSetHas(target.cpu.features, .has_v7) * std.Target.x86.featureSetHasAny(target.cpu.features, .{ .sse, .avx, .cmov }) * std.Target.wasm.featureSetHasAll(target.cpu.features, .{ .atomics, .bulk_memory }) After: * target.cpu.has(.arm, .has_v7) * target.cpu.hasAny(.x86, &.{ .sse, .avx, .cmov }) * target.cpu.hasAll(.wasm, &.{ .atomics, .bulk_memory })
2025-06-04valgrind: Add riscv64-linux support.Alex Rønne Petersen
This appeared in Valgrind 3.25.0.
2025-06-01compiler: combine `@intCast` safety checksmlugg
`castTruncatedData` was a poorly worded error (all shrinking casts "truncate bits", it's just that we assume those bits to be zext/sext of the other bits!), and `negativeToUnsigned` was a pointless distinction which forced the compiler to emit worse code (since two separate safety checks were required for casting e.g. 'i32' to 'u16') and wasn't even implemented correctly. This commit combines those safety panics into one function, `integerOutOfBounds`. The name maybe isn't perfect, but that's not hugely important; what matters is the new default message, which is clearer than the old ones: "integer does not fit in destination type".
2025-06-01compiler: implement better shuffle AIRmlugg
Runtime `@shuffle` has two cases which backends generally want to handle differently for efficiency: * One runtime vector operand; some result elements may be comptime-known * Two runtime vector operands; some result elements may be undefined The latter case happens if both vectors given to `@shuffle` are runtime-known and they are both used (i.e. the mask refers to them). Otherwise, if the result is not entirely comptime-known, we are in the former case. `Sema` now diffentiates these two cases in the AIR so that backends can easily handle them however they want to. Note that this *doesn't* really involve Sema doing any more work than it would otherwise need to, so there's not really a negative here! Most existing backends have their lowerings for `@shuffle` migrated in this commit. The LLVM backend uses new lowerings suggested by Jacob as ones which it will handle effectively. The x86_64 backend has not yet been migrated; for now there's a panic in there. Jacob will implement that before this is merged anywhere.
2025-06-01cbe: legalize safety instructions in non-zig1 buildsJacob Young
This is valid if the bootstrap dev env doesn't need to support runtime safety. Another solution can always be implemented if needs change.
2025-06-01Legalize: replace `safety_checked_instructions`mlugg
This adds 4 `Legalize.Feature`s: * `expand_intcast_safe` * `expand_add_safe` * `expand_sub_safe` * `expand_mul_safe` These do pretty much what they say on the tin. This logic was previously in Sema, used when `Zcu.Feature.safety_checked_instructions` was not supported by the backend. That `Zcu.Feature` has been removed in favour of this legalization.
2025-05-31cbe: implement `stdbool.h` reserved identifiersJacob Young
Also remove the legalize pass from zig1.
2025-05-31Sema: remove `all_vector_instructions` logicJacob Young
Backends can instead ask legalization on a per-instruction basis.
2025-05-31Legalize: implement scalarization of binary operationsJacob Young
2025-05-29Legalize: introduce a new pass before livenessJacob Young
Each target can opt into different sets of legalize features. By performing these transformations before liveness, instructions that become unreferenced will have up-to-date liveness information.
2025-05-28x86_64: implement integer `@reduce(.Max)`Jacob Young
2025-05-28x86_64: implement integer `@reduce(.Min)`Jacob Young
2025-05-28x86_64: implement optimized float `@reduce(.Mul)`Jacob Young
2025-05-28x86_64: rewrite bitwise `@reduce`Jacob Young
2025-05-27compiler: tlv pointers are not comptime-knownmlugg
Pointers to thread-local variables do not have their addresses known until runtime, so it is nonsensical for them to be comptime-known. There was logic in the compiler which was essentially attempting to treat them as not being comptime-known despite the pointer being an interned value. This was a bit of a mess, the check was frequent enough to actually show up in compiler profiles, and it was very awkward for backends to deal with, because they had to grapple with the fact that a "constant" they were lowering might actually require runtime operations. So, instead, do not consider these pointers to be comptime-known in *any* way. Never intern such a pointer; instead, when the address of a threadlocal is taken, emit an AIR instruction which computes the pointer at runtime. This avoids lots of special handling for TLVs across basically all codegen backends; of all somewhat-functional backends, the only one which wasn't improved by this change was the LLVM backend, because LLVM pretends this complexity around threadlocals doesn't exist. This change simplifies Sema and codegen, avoids a potential source of bugs, and potentially improves Sema performance very slightly by avoiding a non-trivial check on a hot path.
2025-05-21spirv: error when execution mode is set more than onceAli Cheraghi
2025-05-21spirv: recognize builtin extern varsAli Cheraghi
2025-05-21spirv: super basic composite int supportAli Cheraghi
2025-05-21spirv: write error value in an storage bufferAli Cheraghi
2025-05-21spirv: unroll all vector operationsAli Cheraghi
2025-05-20Merge pull request #23836 from mlugg/incr-fixesMatthew Lugg
Incremental fixes, refactor `Zcu.File`
2025-05-18compiler: refactor `Zcu.File` and path representationmlugg
This commit makes some big changes to how we track state for Zig source files. In particular, it changes: * How `File` tracks its path on-disk * How AstGen discovers files * How file-level errors are tracked * How `builtin.zig` files and modules are created The original motivation here was to address incremental compilation bugs with the handling of files, such as #22696. To fix this, a few changes are necessary. Just like declarations may become unreferenced on an incremental update, meaning we suppress analysis errors associated with them, it is also possible for all imports of a file to be removed on an incremental update, in which case file-level errors for that file should be suppressed. As such, after AstGen, the compiler must traverse files (starting from analysis roots) and discover the set of "live files" for this update. Additionally, the compiler's previous handling of retryable file errors was not very good; the source location the error was reported as was based only on the first discovered import of that file. This source location also disappeared on future incremental updates. So, as a part of the file traversal above, we also need to figure out the source locations of imports which errors should be reported against. Another observation I made is that the "file exists in multiple modules" error was not implemented in a particularly good way (I get to say that because I wrote it!). It was subject to races, where the order in which different imports of a file were discovered affects both how errors are printed, and which module the file is arbitrarily assigned, with the latter in turn affecting which other files are considered for import. The thing I realised here is that while the AstGen worker pool is running, we cannot know for sure which module(s) a file is in; we could always discover an import later which changes the answer. So, here's how the AstGen workers have changed. We initially ensure that `zcu.import_table` contains the root files for all modules in this Zcu, even if we don't know any imports for them yet. Then, the AstGen workers do not need to be aware of modules. Instead, they simply ignore module imports, and only spin off more workers when they see a by-path import. During AstGen, we can't use module-root-relative paths, since we don't know which modules files are in; but we don't want to unnecessarily use absolute files either, because those are non-portable and can make `error.NameTooLong` more likely. As such, I have introduced a new abstraction, `Compilation.Path`. This type is a way of representing a filesystem path which has a *canonical form*. The path is represented relative to one of a few special directories: the lib directory, the global cache directory, or the local cache directory. As a fallback, we use absolute (or cwd-relative on WASI) paths. This is kind of similar to `std.Build.Cache.Path` with a pre-defined list of possible `std.Build.Cache.Directory`, but has stricter canonicalization rules based on path resolution to make sure deduplicating files works properly. A `Compilation.Path` can be trivially converted to a `std.Build.Cache.Path` from a `Compilation`, but is smaller, has a canonical form, and has a digest which will be consistent across different compiler processes with the same lib and cache directories (important when we serialize incremental compilation state in the future). `Zcu.File` and `Zcu.EmbedFile` both contain a `Compilation.Path`, which is used to access the file on-disk; module-relative sub paths are used quite rarely (`EmbedFile` doesn't even have one now for simplicity). After the AstGen workers all complete, we know that any file which might be imported is definitely in `import_table` and up-to-date. So, we perform a single-threaded graph traversal; similar to what `resolveReferences` plays for `AnalUnit`s, but for files instead. We figure out which files are alive, and which module each file is in. If a file turns out to be in multiple modules, we set a field on `Zcu` to indicate this error. If a file is in a different module to a prior update, we set a flag instructing `updateZirRefs` to invalidate all dependencies on the file. This traversal also discovers "import errors"; these are errors associated with a specific `@import`. With Zig's current design, there is only one possible error here: "import outside of module root". This must be identified during this traversal instead of during AstGen, because it depends on which module the file is in. I tried also representing "module not found" errors in this same way, but it turns out to be much more useful to report those in Sema, because of use cases like optional dependencies where a module import is behind a comptime-known build option. For simplicity, `failed_files` now just maps to `?[]u8`, since the source location is always the whole file. In fact, this allows removing `LazySrcLoc.Offset.entire_file` completely, slightly simplifying some error reporting logic. File-level errors are now directly built in the `std.zig.ErrorBundle.Wip`. If the payload is not `null`, it is the message for a retryable error (i.e. an error loading the source file), and will be reported with a "file imported here" note pointing to the import site discovered during the single-threaded file traversal. The last piece of fallout here is how `Builtin` works. Rather than constructing "builtin" modules when creating `Package.Module`s, they are now constructed on-the-fly by `Zcu`. The map `Zcu.builtin_modules` maps from digests to `*Package.Module`s. These digests are abstract hashes of the `Builtin` value; i.e. all of the options which are placed into "builtin.zig". During the file traversal, we populate `builtin_modules` as needed, so that when we see this imports in Sema, we just grab the relevant entry from this map. This eliminates a bunch of awkward state tracking during construction of the module graph. It's also now clearer exactly what options the builtin module has, since previously it inherited some options arbitrarily from the first-created module with that "builtin" module! The user-visible effects of this commit are: * retryable file errors are now consistently reported against the whole file, with a note pointing to a live import of that file * some theoretical bugs where imports are wrongly considered distinct (when the import path moves out of the cwd and then back in) are fixed * some consistency issues with how file-level errors are reported are fixed; these errors will now always be printed in the same order regardless of how the AstGen pass assigns file indices * incremental updates do not print retryable file errors differently between updates or depending on file structure/contents * incremental updates support files changing modules * incremental updates support files becoming unreferenced Resolves: #22696
2025-05-17x86_64: rewrite `@splat`Jacob Young
2025-05-17x86_64: rewrite scalar `<<|`Jacob Young
Closes #23035
2025-05-17x86_64: rewrite vector `+|`Jacob Young
2025-05-12llvm: Fix a bunch of volatile semantics violations.Alex Rønne Petersen
Also fix some cases where we were being overzealous in applying volatile.
2025-05-12llvm: Don't set nonnull attribute on pointers in non-generic address spaces.Alex Rønne Petersen
LLVM considers null pointers to be valid for such address spaces.
2025-05-12llvm: Don't set nonnull attribute on allowzero slices.Alex Rønne Petersen
2025-05-12llvm: Set null_pointer_is_valid attribute when accessing allowzero pointers.Alex Rønne Petersen
This informs optimization passes that they shouldn't assume that a load from a null pointer invokes undefined behavior. Closes #15816.
2025-05-03Merge pull request #23263 from mlugg/comptime-field-ptrMatthew Lugg
Sema: fix pointers to comptime fields of comptime-known aggregate pointers
2025-05-03std.Target: Add Cpu.Arch.or1k and basic target info.Alex Rønne Petersen
2025-05-02inline assembly: implement gcc's "%=" syntaxsamy007
2025-05-01wasm-c-abi: llvm fix struct handling + reorganizePavel Verigo
I changed to `wasm/abi.zig`, this design is certainly better than the previous one. Still there is some conflict of interest between llvm and self-hosted backend, better design will appear when abi tests will be tested with self-hosted. Resolves: #23304 Resolves: #23305
2025-04-30Merge pull request #23654 from alichraghi/continueRobin Voetter
Compilation: don't build compiler_rt or ubsan_rt for amdgcn and ptx