Commit Graph

423 Commits

Author SHA1 Message Date
Andrew Kelley
fcafc63f3d inline assembly: use types
until now these were stringly typed.

it's kinda obvious when you think about it.
2025-07-16 10:23:02 -07:00
Ali Cheraghi
f43f89a705 spirv: snake-case the spec 2025-07-14 15:16:17 +02:00
Andrew Kelley
30c2921eb8 compiler: update a bunch of format strings 2025-07-07 22:43:52 -07:00
Andrew Kelley
756a2dbf1a compiler: upgrade various std.io API usage 2025-07-07 22:43:52 -07:00
Ivan Stepanov
ee6d19480d spirv: fix signed overflow detection for safe subtraction
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-01 19:26:21 +02:00
Ali Cheraghi
1df79ab895 remove spirv cpu arch 2025-06-23 06:03:03 +02:00
Jacob Young
917640810e Target: pass and use locals by pointer instead of by value
This struct is larger than 256 bytes and code that copies it
consistently shows up in profiles of the compiler.
2025-06-19 11:45:06 -04:00
mlugg
6ffa285fc3 compiler: fix @intFromFloat safety check
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-15 14:15:18 -04:00
mlugg
89ba885970 spirv: make the backend compile again
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-12 13:55:40 +01:00
mlugg
9eb400ef19 compiler: rework backend pipeline to separate codegen and link
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-12 13:55:40 +01:00
mlugg
add2976a9b compiler: implement better shuffle AIR
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-01 08:24:01 +01:00
Jacob Young
d9b6d1ed33 cbe: legalize safety instructions in non-zig1 builds
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-01 08:24:00 +01:00
mlugg
4c4dacf81a Legalize: replace safety_checked_instructions
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-06-01 08:24:00 +01:00
Jacob Young
77e6513030 cbe: implement stdbool.h reserved identifiers
Also remove the legalize pass from zig1.
2025-05-31 18:54:28 -04:00
Jacob Young
6198f7afb7 Sema: remove all_vector_instructions logic
Backends can instead ask legalization on a per-instruction basis.
2025-05-31 18:54:28 -04:00
Jacob Young
c04be630d9 Legalize: introduce a new pass before liveness
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-29 03:57:48 -04:00
Ali Cheraghi
8fa54eb798 spirv: error when execution mode is set more than once 2025-05-21 13:01:21 +03:30
Ali Cheraghi
9209f4b16a spirv: recognize builtin extern vars 2025-05-21 13:01:21 +03:30
Ali Cheraghi
dacd70fbe4 spirv: super basic composite int support 2025-05-21 13:01:20 +03:30
Ali Cheraghi
0901328f12 spirv: write error value in an storage buffer 2025-05-21 12:57:40 +03:30
Ali Cheraghi
fca5f3602d spirv: unroll all vector operations 2025-05-21 12:57:40 +03:30
mlugg
37a9a4e0f1 compiler: refactor Zcu.File and path representation
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-18 17:37:02 +01:00
Ali Cheraghi
296b17f37b spirv: allow offset_and_cast for vectors when possible 2025-04-28 10:42:33 +03:30
dweiller
898ca82458 compiler: add @memmove builtin 2025-04-26 13:34:16 +10:00
Ali Cheraghi
ee06b2ce76 spirv: require int8/int16 capabilities 2025-03-18 07:05:50 +03:30
Ali Cheraghi
d18eaf8586 spirv: aligned load for physical storage variables
Resolves #23212
2025-03-18 07:05:50 +03:30
Ali Cheraghi
54c097f50d spirv: packed struct init + field val access 2025-03-18 07:05:48 +03:30
Ali Cheraghi
50539a2447 spirv/target: arbitrary_precision_integers feature support 2025-03-17 21:56:17 +03:30
Ali Cheraghi
c1977bf0fb Sema: error on illegal code when targeting spirv 2025-03-17 21:56:14 +03:30
Ali Cheraghi
2fc409a32f spirv: don't hardcode test error type alignment 2025-03-17 20:19:08 +03:30
Ali Cheraghi
94af47d28c spirv: do not generate unnecessary forward pointer
Co-authored-by: Robin Voetter <robin@voetter.nl>
2025-02-24 19:12:38 +01:00
Ali Cheraghi
a0eec9ce9e spirv: replace some unreachables with compile errors 2025-02-24 19:12:33 +01:00
Ali Cheraghi
7872082939 spirv: extend supported c constraint values 2025-02-18 18:08:51 +03:30
Ali Cheraghi
d5e1cb3ea2 spirv: ziggify and remove unknown spirv features
`OpCapability` and `OpExtension` now can also be emitted from inline assembly
2025-02-18 18:08:47 +03:30
Ali Cheraghi
85169bbba2 spirv: respect cpu features 2025-02-18 18:07:48 +03:30
Ali Cheraghi
29e46633ce spirv: cache more types & merge constructX functions 2025-02-18 18:07:48 +03:30
Jacob Young
afa74c6b21 Sema: introduce all_vector_instructions backend feature
Sema is arbitrarily scalarizing some operations, which means that when I
try to implement vectorized versions of those operations in a backend,
they are impossible to test due to Sema not producing them. Now, I can
implement them and then temporarily enable the new feature for that
backend in order to test them. Once the backend supports all of them,
the feature can be permanently enabled.

This also deletes the Air instructions `int_from_bool` and
`int_from_ptr`, which are just bitcasts with a fixed result type, since
changing `un_op` to `ty_op` takes up the same amount of memory.
2025-01-31 23:00:34 -05:00
mlugg
0ec6b2dd88 compiler: simplify generic functions, fix issues with inline calls
The original motivation here was to fix regressions caused by #22414.
However, while working on this, I ended up discussing a language
simplification with Andrew, which changes things a little from how they
worked before #22414.

The main user-facing change here is that any reference to a prior
function parameter, even if potentially comptime-known at the usage
site or even not analyzed, now makes a function generic. This applies
even if the parameter being referenced is not a `comptime` parameter,
since it could still be populated when performing an inline call. This
is a breaking language change.

The detection of this is done in AstGen; when evaluating a parameter
type or return type, we track whether it referenced any prior parameter,
and if so, we mark this type as being "generic" in ZIR. This will cause
Sema to not evaluate it until the time of instantiation or inline call.

A lovely consequence of this from an implementation perspective is that
it eliminates the need for most of the "generic poison" system. In
particular, `error.GenericPoison` is now completely unnecessary, because
we identify generic expressions earlier in the pipeline; this simplifies
the compiler and avoids redundant work. This also entirely eliminates
the concept of the "generic poison value". The only remnant of this
system is the "generic poison type" (`Type.generic_poison` and
`InternPool.Index.generic_poison_type`). This type is used in two
places:

* During semantic analysis, to represent an unknown result type.
* When storing generic function types, to represent a generic parameter/return type.

It's possible that these use cases should instead use `.none`, but I
leave that investigation to a future adventurer.

One last thing. Prior to #22414, inline calls were a little inefficient,
because they re-evaluated even non-generic parameter types whenever they
were called. Changing this behavior is what ultimately led to #22538.
Well, because the new logic will mark a type expression as generic if
there is any change its resolved type could differ in an inline call,
this redundant work is unnecessary! So, this is another way in which the
new design reduces redundant work and complexity.

Resolves: #22494
Resolves: #22532
Resolves: #22538
2025-01-21 02:41:42 +00:00
mlugg
d00e05f186 all: update to std.builtin.Type.Pointer.Size field renames
This was done by regex substitution with `sed`. I then manually went
over the entire diff and fixed any incorrect changes.

This diff also changes a lot of `callconv(.C)` to `callconv(.c)`, since
my regex happened to also trigger here. I opted to leave these changes
in, since they *are* a correct migration, even if they're not the one I
was trying to do!
2025-01-16 12:46:29 +00:00
mlugg
3afda4322c compiler: analyze type and value of global declaration separately
This commit separates semantic analysis of the annotated type vs value
of a global declaration, therefore allowing recursive and mutually
recursive values to be declared.

Every `Nav` which undergoes analysis now has *two* corresponding
`AnalUnit`s: `.{ .nav_val = n }` and `.{ .nav_ty = n }`. The `nav_val`
unit is responsible for *fully resolving* the `Nav`: determining its
value, linksection, addrspace, etc. The `nav_ty` unit, on the other
hand, resolves only the information necessary to construct a *pointer*
to the `Nav`: its type, addrspace, etc. (It does also analyze its
linksection, but that could be moved to `nav_val` I think; it doesn't
make any difference).

Analyzing a `nav_ty` for a declaration with no type annotation will just
mark a dependency on the `nav_val`, analyze it, and finish. Conversely,
analyzing a `nav_val` for a declaration *with* a type annotation will
first mark a dependency on the `nav_ty` and analyze it, using this as
the result type when evaluating the value body.

The `nav_val` and `nav_ty` units always have references to one another:
so, if a `Nav`'s type is referenced, its value implicitly is too, and
vice versa. However, these dependencies are trivial, so, to save memory,
are only known implicitly by logic in `resolveReferences`.

In general, analyzing ZIR `decl_val` will only analyze `nav_ty` of the
corresponding `Nav`. There are two exceptions to this. If the
declaration is an `extern` declaration, then we immediately ensure the
`Nav` value is resolved (which doesn't actually require any more
analysis, since such a declaration has no value body anyway).
Additionally, if the resolved type has type tag `.@"fn"`, we again
immediately resolve the `Nav` value. The latter restriction is in place
for two reasons:

* Functions are special, in that their externs are allowed to trivially
  alias; i.e. with a declaration `extern fn foo(...)`, you can write
  `const bar = foo;`. This is not allowed for non-function externs, and
  it means that function types are the only place where it is possible
  for a declaration `Nav` to have a `.@"extern"` value without actually
  being declared `extern`. We need to identify this situation
  immediately so that the `decl_ref` can create a pointer to the *real*
  extern `Nav`, not this alias.
* In certain situations, such as taking a pointer to a `Nav`, Sema needs
  to queue analysis of a runtime function if the value is a function. To
  do this, the function value needs to be known, so we need to resolve
  the value immediately upon `&foo` where `foo` is a function.

This restriction is simple to codify into the eventual language
specification, and doesn't limit the utility of this feature in
practice.

A consequence of this commit is that codegen and linking logic needs to
be more careful when looking at `Nav`s. In general:

* When `updateNav` or `updateFunc` is called, it is safe to assume that
  the `Nav` being updated (the owner `Nav` for `updateFunc`) is fully
  resolved.
* Any `Nav` whose value is/will be an `@"extern"` or a function is fully
  resolved; see `Nav.getExtern` for a helper for a common case here.
* Any other `Nav` may only have its type resolved.

This didn't seem to be too tricky to satisfy in any of the existing
codegen/linker backends.

Resolves: #131
2024-12-24 02:18:41 +00:00
Robin Voetter
efb7539cb6 spirv: dont emit forward pointer for annotation instructions 2024-11-09 01:53:13 +01:00
Robin Voetter
89bd987f1c spirv: emit ArrayStride for many-item pointers 2024-11-08 20:43:57 +01:00
Robin Voetter
b16252b17e spirv: make all vulkan structs Block for now 2024-11-08 20:43:57 +01:00
Robin Voetter
d35dfc5a3f add storage_buffer address space 2024-11-08 20:43:57 +01:00
Robin Voetter
688d7055e3 spirv: assembler hacky constant placeholders 2024-11-08 20:43:55 +01:00
Robin Voetter
b5301558ae spirv: make default generic address space for vulkan Function
We are not using Private variables. This needs to be cleaned up a bit
more, this will happen with the general address space improvements.
2024-11-08 20:38:23 +01:00
Robin Voetter
7682ced08e spirv: track global OpVariables properly in assembler
Also cleans up the assembler a bit in general.
2024-11-08 20:38:22 +01:00
Robin Voetter
4fbc100959 spirv: properly resolve type inputs in assembly
For now the frontend still allows type inputs in assembly. We
might as well resolve them properly in the SPIR-V backend.
2024-11-08 20:38:21 +01:00
Robin Voetter
ba5f57616f Merge pull request #21861 from alichraghi/master
spirv: push constants and small fixes
2024-11-01 03:44:37 +01:00
Ali Cheraghi
c07b3c8279 spirv: decorate arrays stride 2024-11-01 02:04:27 +03:30