* Introduce helper functions on Module.WipZirCode and zir.Code
* Move some logic around
* re-introduce ref_start_index
* prefer usize for local variables + `@intCast` at the end.
Empirically this is easier to optimize.
* Avoid using mem.{bytesAsSlice,sliceAsBytes} because it incurs an
unnecessary multiplication/division which may cause problems for the
optimizer.
* Use a regular enum, not packed, for `Ref`. Memory layout is
guaranteed for enums which specify their tag type. Packed enums have
ABI alignment of 1 byte which is too small.
This provides us greatly increased type safety and prevents the common
mistake of using a zir.Inst.Ref where a zir.Inst.Index was expected or
vice-versa. It also increases the ergonomics of using the typed values
which can be directly referenced with a Ref over the previous zir.Const
approach.
The main pain point is casting between a []Ref and []u32, which could be
alleviated in the future with a new std.mem function.
* comment out the failing stage2 test cases
(so that we can uncomment the ones that are newly passing with
further commits)
* Sema: implement negate, negatewrap
* astgen: implement field access, multiline string literals, and
character literals
* Module: when resolving an AST node into a byte offset, use the
main_tokens array, not the firstToken function
* add `Module.setBlockBody` and related functions
* redo astgen for `and` and `or` to use fewer ZIR instructions and
require less processing for comptime known values
* Sema: rework `analyzeBody` function. See the new doc comments in this
commit. Divides ZIR instructions up into 3 categories:
- always noreturn
- never noreturn
- sometimes noreturn
The current plan is to avoid using async and related features in the
stage2 compiler so that we can bootstrap before implementing them.
Having this untested and incomplete code in the codebase increases
friction while working on stage2, in particular when preforming
larger refactors such as the current zir memory layout rework.
Therefore remove all async related code, leaving only error messages
in astgen.
These were previously implemented as a sub/sub_wrap instruction with a
lhs of 0. Making this separate instructions however allows us to save
some memory as there is no need to store a lhs.
* free Module.Fn ZIR code when destroying the owner Decl
* unreachable_safe and unreachable_unsafe are collapsed into one ZIR
instruction with a safety flag.
* astgen: emit an unreachable instruction for unreachable literals
* don't forget to call deinit on ZIR code
* astgen: implement some builtin functions
The LLVM backend is now structured into 3 different structs, namely
Object, DeclGen and FuncGen. Object represents an object that is
generated by the LLVM backend. DeclGen is responsible for generating
a decl and FuncGen is responsible for generating llvm instructions
from tzir in a function.
We are now passing this test:
```zig
export fn _start() noreturn {}
```
```
test.zig:1:30: error: expected noreturn, found void
```
I ran into an issue where we get an integer overflow trying to compute
node index offsets from the containing Decl. The problem is that the
parser adds the Decl node after adding the child nodes. For some things,
it is easy to reserve the node index and then set it later, however, for
this case, it is not a trivial code change, because depending on tokens
after parsing the decl determines whether we want to add a new node or
not.
Possible strategies here:
1. Rework the parser code to make sure that Decl nodes are before
children nodes in the AST node array.
2. Use signed integers for Decl node offsets.
3. Just flip the order of subtraction and addition. Expect Decl Node
index to be greater than children Node indexes.
I opted for (3) because it seems like the simplest thing to do. We'll
want to unify the logic for computing the offsets though because if the
logic gets repeated, it will probably get repeated wrong.