Previously, the SPIR-V linker would merge every function's MIR into one module, then parsed
it in `BinaryModule`, and run three passes over it:
- `prune_unused` walked the module from its entry points to drop dead declarations
- `dedup_types` deduplicated types and constants
- `lower_invocation_globals` rewrote function signatures to pass a pointer to global
variables as function parameter
Now codegen emits instructions in the order it resolves them (not the spec specified order)
into one section per function. then the linker sorts them into the correct order while also
interning types, constants, strings, etc in the flush step.
Navs the frontend no longer references are now skipped using `Zcu.resolveReferences` instead
of a pass like `prune_unused`. Extern variables are also only loaded when an MIR references them.
`lower_invocation_globals` is gone too. non-`threadlocal` globals on SPIR-V targets are disallowed now.
Currently only vulkan supports them via the newly added `private` address space. See #36715
The new linker also supports incremental compilation now!
For generic code, it is far more useful to consider this equivalent to
`unreachable`. Meanwhile, it is difficult to accidentally write concrete
code which performs this operation, so very little is actually lost from
not having a compile error.
If we accept that this operation does not trigger a compile error, then
it already has the potential to invoke Illegal Behavior today, because
uninstantiable types have an unspecified size in memory, so the
dereference always potentially exceeds the pointer's provenance. (That
said, this only means it is *possible* for the operation to invoke IB,
so the langspec should nonetheless explicitly specify that dereferencing
a pointer to an uninstantiable type is itself Illegal Behavior.)
Storing uninstantiable types into memory does not require any specific
language rules, because that operation can never be reached anyway due
to it requiring an operand (the RHS of `a = b`) whose type is the store
type, which is (by definition) impossible for uninstantiable types.
Resolves: https://codeberg.org/ziglang/zig/issues/36247
A comptime-known `[]T` should behave as similarly as possible to a comptime-
known `*[n]T`. It is now possible to coerce the former to the latter if
their length matches.
A comptime-known `[]T` should behave as similarly as possible to a comptime-
known `*[n]T`. It is now possible to directly dereference both to an array.
Also adds checks for slices with undefined length in code paths where a
slice is dereferenced as an array which would previously crash.
and remove "release" from the enum tag names.
no functional change, however, despite the addition of
backwards-compatibile declarations in this patch, it is breaking because
expressions that use `==` or `!=` operators will not able to use the
deprecated names.
I predict these names to survive until the 1.0 tag without any more
breakage.
This commit implements the `@backingInt` and `@fromBackingInt` builtins.
These builtins are meant to replace the now-deprecated `@intFromEnum` and
`@enumFromInt` builtins.
`@backingInt` works with all enums and with bitpacks with *explicit* backing
integer types only. It also works with tagged unions, returning the backing
integer of the active tag value.
An `undefined` enum or bitpack yields an `undefined` backing integer.
The builtin is lowered to `bit_cast`.
`@fromBackingInt` infers its result type, which may be any enum or a bitpack
with an *explicit* backing integer type. It takes a parameter of exactly
that backing integer type.
For enums, passing a backing integer that is either `undefined` or would
yield an invalid tag value results in safety-checked Illegal Behavior.
For bitpacks, passing an `undefined` backing integer yields an `undefined`
bitpack.
The builtin is lowered to `bit_cast` or `bit_cast_safe` (more details below).
`@bitCast` now also performs a safety check for invalid tag values if its
destination type is an `enum`.
This commit also introduces a new AIR instruction, `bit_cast_safe`.
Similarly to `int_cast_safe`, it includes a safety check for invalid enum
tag values. When `@enumFromInt` is eventually removed, `int_cast_safe` won't
need to perform this safety check anymore.
Legalize only implements `expand_bit_cast_safe`, and no additional insts
to scalarize `bit_cast_safe` with vector/array operands. That is because
it can just reuse the existing scalarizations for regular `bit_cast`.
The safety check mandated by `bit_cast_safe` is only desired if the result
type is a scalar enum tag value, so the scalarization process is exactly
equivalent. The only difference is that we have to perform a safety check
on the result of the scalarization.
This means that there's no reason for a backend to want scalarization of
regular `bit_cast`, but not of `bit_cast_safe` (or the other way round).
If a backend is having trouble with the `bit_cast_safe` safety check it
should just use `expand_bit_cast_safe` instead.
Also adds a `std.meta.BackingInt()` function to conveniently get the result
type of `@backingInt` and for use in the langref.
Empty enums are uninstantiable, so their backing integer should be too.
This avoids a bunch of weird special cases during semantic analysis.
If you were previously using `enum {}`, your code should continue to work
fine.
If you were previously using `enum(uN) {}` (where `uN` is any integer type),
you'll probably want to use `enum(uN) { _ }` instead.
AstGen relies on this behavior, since it marks InvalidCharacter from parseFloat as unreachable after parseNumberLiteral returns float. Before this commit, there was a mismatch, since parseNumberLiteral was not failing on literals with a period in the exponent.
Fixes#36161
spirv slices are represented via an element pointer + len. On access we
get pointers to individual elements with OpPtrAccessChain
Unfortunately this is not a valid operation in most cases. For any array
defined in a function, we are generated with storage class "function".
The SPIRV spec only allows for variable pointers on data in the Workgroup
(addrspace(.shared)) or StorageBuffer storage classes (definition of
variable pointer in SPIRV spec 2.16)
SPIRV kernels with this constraint unmet cause segfaults on my machine
when trying to load them, and fail to check against spirv-val
AFAICT there is no way to represent a slice of data with storage class
"function". We can use OpAccessChain to extract an offset from an array,
however that would require a slice representation resembling {array,
offset, len}. Since arrays are typed according to their type + len,
there would be no way to re-assign a slice from a [4]f32 array to a
slice from a [2]f32 array. The types would just not match up
So the best we can do is just ban construction of slices in the
scenarios we cannot generate valid code for, and give a useful error
message to the caller
A few examples...
var buf: [3]f32 = undefined;
for (buf[0..3]) |v| {} // errors
fnThatAcceptsSlice(&buf); // errors
for (&buf) |v| {} // succeeds
comptime cbuf: [3]f32 = .{1, 2, 3};
for (cbuf[0..3]) |v| {} // errors
for (comptime cbuf[0..3]) |v| {} // succeeds
This is arguably over-restrictive. We theoretically could handle
buf[0..N] correctly, but currently SPIRV codegen will still produce
invalid OpPtrAccessChains, so I've just completely removed the ability
to produce slices
Co-authored-by: Mick Sayson <mick@sayson.com>
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36118
Reviewed-by: Ali Cheraghi <alichraghi@noreply.codeberg.org>