The same declaration can be added to the dependency set multiple
times, and in this case we still need to emit it once. By making
this list a hash map instead, we can do that quite easily.
This commit also introduces some additional debug logging regarding
decls.
Implements AIR lowering for is_null and is_non_null tags.
Additionally this cleans up and centralizes the logic to convert from 'direct'
representation to 'indirect' representation and vice-versa. The related functions,
as well as the functions that use it, are all moved near eachother so that the
conversion logic remains in a central place. Extracting/inserting fields
and loading/storing pointers should go through these functions.
The result-id and result-type-id of the OpPhi used to merge the
break values was not properly emitted, as some of the operands
were not written out. This caused an invalid spir-v module.
Implements code generation for the try air tag. This commit also adds
a utility `errorUnionLayout` function that helps keeping the layout
of a spir-v error union consistent.
resolve() is now able to emit instructions. If usage of this function is
interleaved with calls to emitRaw() and writeOperand(), then an
instruction may get inserted between operands, causing an invalid module.
The solution here is to just perform a temporary allocation.
It seems that some implementations may have problems with these right now,
like Intel and Rusticl. In theory, these attributes should be superficial
on the pointer type, as alignment guarantees are also added via the
alignment option of the OpLoad and OpStore instructions. Therefore, get rid
of them for now.
Entry points need to be attributed with a complete list of
global variables that they use. To that end, the global dependencies
mechanism is extended to also allow functions - when flushing the
module, the list of dependencies is examined to generate this
list of global variable result-ids.
SPIR-V cannot represent function pointers without extensions
that no vendor implements. For the time being, generate a test
kernel for each error, so that we can at least run SOME tests.
In the future we may be able to emulate function pointers in some
way, but that is not today.
SPIR-V globals must be emitted in order, so that any
declaration precedes usage. Zig, however, generates globals in
random order. To this end we keep for each global a list of
dependencies and perform a topological sort when flushing the
module.
Lowering constants is currently not really compatible with unions. In
this commit, constant lowering is drastically overhauled: instead of
playing nice and generating SPIR-V constant representations for everything
directly, we're just going to treat globals as an untyped bag of bytes (
or rather, SPIR-V 32-bit words), which we cast to the desired type at
usage. This is similar to how Rust generates constants in its LLVm backend.
Similar to function locals, taking the address of a global that does
not have an explicit address space assigned to it should result
in a generic pointer, not a global pointer. Also similar to function
locals, they cannot be generated into the generic storage class, and
so are generated into the global storage class and then cast to a
generic pointer, using OpSpecConstantOp. Note that using
OpSpecConstantOp results is only allowed by a hand full of other
OpSpecConstant instructions - which is why we generate constant
structs using OpSpecConstantComposite: These may use OpVariable
and OpSpecConstantOp results, while OpConstantComposite may not.
This cloneAir/cloneLiveness idea used to ignore Zig's internals
has proven buggy. Instead, just generate the code directly from
updateFunc and updateDecl as the other backends do, but pretend
that Zig is not an incremental compiler. The SPIR-V backend will
for the time being not support this.
Bools have a different immediate representation and memory
representation - which means that they must be converted every time
a bool is loaded from or stored into memory.
Taking the address of a local variable should result in a generic
pointer - too much code breaks if we do not do this. We cannot
lower locals into the generic storage class directly though, so
instead, lower the variables into the Function storage class
implicitly, and convert the pointer to a generic pointer.
Also Correct OpInboundsAccessChain generation (we only need
the one index).
There are two main ways in which a value can be stored: "Direct", as it
will be operated on as an immediate value, and "indirect", as it is stored
in memory. Some types need a different representation here: Bools, for
example, are opaque in SPIR-V, and so these need to have a different
representation in memory. The bool operations are not easily interchangable
with integer operations, though, so they need to be OpTypeBool as
immediate value.
Previously they were strong aliases, but as these types are used quite
intermittendly it resulted in a lot of toRef() calls. Removing them
improves readability a bit.
- Adds the Int8. Int16, Int64 and GenericPointer capabilities.
TODO: This should integrate with the feature system.
- Default some struct fields of SPIR-V types so that we dont
need to type them all the time.
- Store struct field name's in SPIR-V types, and generate the
OpMemberName decoration if they are non-null.
- Also add the field names to the actual SPIR-V types.
- Generate OpName for functions.
Implements the div-family and intcast AIR instructions, and starts
implementing a mechanism for masking the value of 'strange' integers
before they are used in an operation that does not hold under modulo.
Implements lowering for the add_with_overflow AIR instructions. Also implements
a helper function, simpleStructType, to quickly generate a SPIR-V structure type
without having to do the whole allocation dance.
When a result of a pure instruction is not used, it also does not need to
be generated. The other backends already implement these checks, they were
ignored in SPIR-V up until now. New instructions added in the future should
have these be implemented from the start.
Implements type lowering for arrays and structs, and implements instruction
lowering for bitcast and call. Bitcast currently naively maps to the OpBitcast
instruction - this is only valid for some primitive types, and should be
improved to work with composites.