-airLoad and airStore now properly report an error if they are used with an array, instead of having the C compiler emit a vague error
-airStoreUndefined now works with array types
-structFieldPtr now works with array types, allowing generics' tests to pass
-add additional test cases that were found to be passing
-add basic int128 test cases which previously did not pass but weren't covered
-most test cases in cast.zig now pass
-i128/u128 or smaller int constants can now be rendered
-unsigned int constants are now always suffixed with 'u' to prevent random compile errors
-pointers with a val tag of 'zero' now just emit a 0 constant which coerces to the pointer type and fixes some warnings with ordered comparisons
-pointers with a val tag of 'one' are now casted back to the pointer type
-support pointers with a u64 val
-fix bug where rendering an array's type will emit more indirection than is needed
-render uint128_t/int128_t manually when needed
-implement ptr_add/sub AIR handlers manually so they manually cast to int types which avoids UB if the result or ptr operand is NULL
-implement airPtrElemVal/Ptr
-airAlloc for arrays will not allocate a ref as the local for the array is already a reference/pointer to the array itself
-fix airPtrToInt by casting to the int type
This commit makes airStore() handle undefined values directly instead of
delegating to renderValue(): the call to renderValue() happens too late,
when "dest = " has already been written to the stream, at which point
there's no sane way to initialize e.g. struct values by assignment.
Instead, we make airStore() use memset(dest, 0xaa, sizeof(dest)), which
should transparently handle all types.
Also moves the newly-passing tests to the top of test/behavior.zig.
1. Changed Zig pointers to functions to be typedef'd so then we can
treat them the same as other types.
2. Distinguished between const slices (zig_L prefix) and mut slices
(zig_M prefix).
3. Changed lowering of Zig "const pointers" (e.g. *const u8) to to C
"pointers to const" (e.g. const char *) rather than C "const
pointers" (e.g. char * const)
4. Ensured that all typedefs are "linked" even if the decl doesn't
require any forward declarations
5. Added test that exercises function pointer type rendering
6. Changed .slice_ptr instruction to allocate pointer local rather than
a uintptr_t local
This effectively allows us to compile
```zig
pub fn main() void {}
```
which then calls into `std.start`.
Changes required to make this happen:
* handle signed int to immediate in x86_64 and aarch64 codegen
* ensure that on arm64 macOS, `.x19` is a caller-preserved register -
I'm not sure about that one at all and would like to brainstorm it
with anyone interested and especially Joachim.
* finally, fix a bug in the linker - mark new got entry as dirty upon
atom growth.
New AIR instruction: `optional_payload_ptr_set`
It's like `optional_payload_ptr` except it sets the non-null bit.
When storing to the payload via a result location that is an optional,
`optional_payload_ptr_set` is now emitted. There is a new algorithm in
`zirCoerceResultPtr` which stores a dummy value through the result
pointer into a temporary block, and then pops off the AIR instructions
from the temporary block in order to determine how to transform the
result location pointer in case any in-between coercions need to happen.
Fixes a couple of behavior tests regarding optionals.
1. Function signatures that return a no member struct return void
2. Undefined var decls don't get a value generated for them
3. Don't generate bitcast code if the result isn't used, since
bitcast is a pure function. Right now struct handling code
generates some weird unused bitcast AIR, and this optimization
side steps that issue.
LLVM and compiler-rt must agree on how the parameters are passed, it
turns out that in LLVM13 something changed and broke the test case for
AArch64 systems.
It has nothing to do with fma at all.
Closes#9900
* CBE: mark call.zig tests as passing
* CBE: mark enum.zig tests as passing
* CBE: mark defer.zig tests as passing
* CBE: mark hasdecl.zig tests as passing
* CBE: mark hasfield.zig tests as passing
* CBE: mark ptrcast.zig tests as passing
* CBE: mark bitcast.zig tests as passing
* CBE: mark pub_enum.zig tests as passing
* CBE: mark underscore.zig tests as passing
* CBE: mark usingnamespace.zig tests as passing
* CBE: mark bugs/655.zig tests as passing
* CBE: mark bugs/679.zig tests as passing
* CBE: mark bugs/704.zig tests as passing
* CBE: mark bugs/1486.zig tests as passing
* CBE: mark bugs/2346.zig tests as passing
* CBE: mark bugs/2889.zig tests as passing
* CBE: mark bugs/4560.zig tests as passing
* CBE: mark bugs/4769_a.zig tests as passing
* CBE: mark bugs/4769_b.zig tests as passing
* CBE: mark bugs/6850.zig tests as passing
The ensureUnusedCapacity did not reserve a big enough number. I changed
it to no longer guess the capacity because I saw that the number of
possible items was not determinable ahead of time and this can therefore
avoid allocating more memory than necessary.
The main problem that motivated these changes is that global constants
which are referenced by pointer would not be emitted into the binary.
This happened because `semaDecl` did not add `codegen_decl` tasks for
global constants, instead relying on the constant values being copied as
necessary. However when the global constants are referenced by pointer,
they need to be sent to the linker to be emitted.
After making global const arrays, structs, and unions get emitted, this
uncovered a latent issue: the anonymous decls that they referenced would
get garbage collected (via `deleteUnusedDecl`) even though they would
later be referenced by the global const.
In order to solve this problem, I introduced `anon_work_queue` which is
the same as `work_queue` except a lower priority. The `codegen_decl`
task for anon decls goes into the `anon_work_queue` ensuring that the
owner decl gets a chance to mark its anon decls as alive before they are
possibly deleted.
This caused a few regressions, which I made the judgement call to add
workarounds for. Two steps forward, one step back, is still progress.
The regressions were:
* Two behavior tests having to do with unions. These tests were
intentionally exercising the LLVM constant value lowering, however,
due to the bug with garbage collection that was fixed in this commit,
the LLVM code was not getting exercised, and union types/values were
not implemented correctly, due to me forgetting that LLVM does not
allow bitcasting aggregate values.
- This is worked around by allowing those 2 test cases to regress,
moving them to the "passing for stage1 only" section.
* The test-stage2 test cases (in test/cases/*) for non-LLVM backends
previously did not have any calls to lower struct values, but now
they do. The code that was there was just `@panic("TODO")`. I
replaced that code with a stub that generates the wrong value. This
is an intentional miscompilation that will obviously need to get
fixed before any struct behavior tests pass. None of the current
tests we have exercise loading any values from these global const
structs, so there is not a problem until we try to improve these
backends.
Previously, it would emit a ret_ptr AIR instruction but that is not
correct because such an instruction would reference the result pointer
of the caller function rather than the callee function.
Instead, we emit an alloc instruction in this case. `ret_load` already
handles inlining correctly.
* C pointer types always have allowzero set to true but they omit the
word allowzero when printed.
* Implement coercion from C pointers to other pointers.
* Implement in-memory coercion for slices and pointer-like optionals.
* Make slicing a C pointer drop the allowzero bit.
* Value representation for pointer-like optionals is now allowed to use
pointer tag values in addition to the `opt_payload` tag.
Switch prong values are fetched by index in semantic analysis by prong
offset, but these were computed as capture offset. This means that a switch
where the first prong does not capture and the second does, the switch_capture
zir instruction would be assigned switch_prong 0 instead of 1.
* AstGen: always use `typeof` and never `typeof_elem` on the
`switch_cond`/`switch_cond_ref` instruction because both variants
return a value and not a pointer.
- Delete the `typeof_elem` ZIR instruction since it is no longer
needed.
* Sema: validateUnionInit now recognizes a comptime mutable value and
no longer emits a compile error saying "cannot evaluate constant
expression"
- Still to-do is detecting comptime union values in a function that
is not being executed at compile-time.
- This is still to-do for structs too.
* Sema: when emitting a call AIR instruction, call resolveTypeLayout on
all the parameter types as well as the return type.
* `Type.structFieldOffset` now works for unions in addition to structs.
Over the last year of using std.log in practice, it has become clear to
me that having the current 8 distinct log levels does more harm than
good. It is too subjective which level a given message should have which
makes filtering based on log level weaker as not all messages will have
been assigned the log level one might expect.
Instead, more granular filtering should be achieved by leveraging the
logging scope feature. Filtering based on a combination of scope and log
level should be sufficiently powerful for all use-cases.
Note that the self hosted compiler has already limited itself to 4
distinct log levels for many months and implemented granular filtering
based on both log scope and level. This has worked very well in practice
while working on the self hosted compiler.