AstGen: add result location analysis pass

The main motivation for this change is eliminating the `block_ptr`
result location and corresponding `store_to_block_ptr` ZIR instruction.
This is achieved through a simple pass over the AST before AstGen which
determines, for AST nodes which have a choice on whether to provide a
result location, which choice to make, based on whether the result
pointer is consumed non-trivially.

This eliminates so much logic from AstGen that we almost break even on
line count! AstGen no longer has to worry about instruction rewriting
based on whether or not a result location was consumed: it always knows
what to do ahead of time, which simplifies a *lot* of logic. This also
incidentally fixes a few random AstGen bugs related to result location
handling, leading to the changes in `test/` and `lib/std/`.

This opens the door to future RLS improvements by making them much
easier to implement correctly, and fixes many bugs. Most ZIR is made
more compact after this commit, mainly due to not having redundant
`store_to_block_ptr` instructions lying around, but also due to a few
bugs in the old system which are implicitly fixed here.
This commit is contained in:
mlugg 2023-08-18 15:11:38 +01:00 committed by Andrew Kelley
parent 020105d0dd
commit 321961d860
9 changed files with 1347 additions and 1149 deletions

View file

@ -515,6 +515,7 @@ set(ZIG_STAGE2_SOURCES
"${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig" "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig"
"${CMAKE_SOURCE_DIR}/src/Air.zig" "${CMAKE_SOURCE_DIR}/src/Air.zig"
"${CMAKE_SOURCE_DIR}/src/AstGen.zig" "${CMAKE_SOURCE_DIR}/src/AstGen.zig"
"${CMAKE_SOURCE_DIR}/src/AstRlAnnotate.zig"
"${CMAKE_SOURCE_DIR}/src/Compilation.zig" "${CMAKE_SOURCE_DIR}/src/Compilation.zig"
"${CMAKE_SOURCE_DIR}/src/Liveness.zig" "${CMAKE_SOURCE_DIR}/src/Liveness.zig"
"${CMAKE_SOURCE_DIR}/src/Module.zig" "${CMAKE_SOURCE_DIR}/src/Module.zig"

View file

@ -370,10 +370,9 @@ fn render_cmake(
} }
}, },
else => { else => {},
break :blk value;
},
} }
break :blk value;
}; };
if (booldefine) { if (booldefine) {

File diff suppressed because it is too large Load diff

1086
src/AstRlAnnotate.zig Normal file

File diff suppressed because it is too large Load diff

View file

@ -1346,11 +1346,6 @@ fn analyzeBodyInner(
i += 1; i += 1;
continue; continue;
}, },
.store_to_block_ptr => {
try sema.zirStoreToBlockPtr(block, inst);
i += 1;
continue;
},
.store_to_inferred_ptr => { .store_to_inferred_ptr => {
try sema.zirStoreToInferredPtr(block, inst); try sema.zirStoreToInferredPtr(block, inst);
i += 1; i += 1;
@ -5269,35 +5264,6 @@ fn addDeclaredHereNote(sema: *Sema, parent: *Module.ErrorMsg, decl_ty: Type) !vo
try mod.errNoteNonLazy(src_loc, parent, "{s} declared here", .{category}); try mod.errNoteNonLazy(src_loc, parent, "{s} declared here", .{category});
} }
fn zirStoreToBlockPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
const tracy = trace(@src());
defer tracy.end();
const bin_inst = sema.code.instructions.items(.data)[inst].bin;
const ptr = sema.inst_map.get(Zir.refToIndex(bin_inst.lhs).?) orelse {
// This is an elided instruction, but AstGen was unable to omit it.
return;
};
const operand = try sema.resolveInst(bin_inst.rhs);
const src: LazySrcLoc = sema.src;
blk: {
const ptr_inst = Air.refToIndex(ptr) orelse break :blk;
switch (sema.air_instructions.items(.tag)[ptr_inst]) {
.inferred_alloc_comptime => {
const iac = &sema.air_instructions.items(.data)[ptr_inst].inferred_alloc_comptime;
return sema.storeToInferredAllocComptime(block, src, operand, iac);
},
.inferred_alloc => {
const ia = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?;
return sema.storeToInferredAlloc(block, ptr, operand, ia);
},
else => break :blk,
}
}
return sema.storePtr(block, src, ptr, operand);
}
fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
const tracy = trace(@src()); const tracy = trace(@src());
defer tracy.end(); defer tracy.end();

View file

@ -602,20 +602,9 @@ pub const Inst = struct {
/// Same as `store` except provides a source location. /// Same as `store` except provides a source location.
/// Uses the `pl_node` union field. Payload is `Bin`. /// Uses the `pl_node` union field. Payload is `Bin`.
store_node, store_node,
/// This instruction is not really supposed to be emitted from AstGen; nevertheless it
/// is sometimes emitted due to deficiencies in AstGen. When Sema sees this instruction,
/// it must clean up after AstGen's mess by looking at various context clues and
/// then treating it as one of the following:
/// * no-op
/// * store_to_inferred_ptr
/// * store
/// Uses the `bin` union field with LHS as the pointer to store to.
store_to_block_ptr,
/// Same as `store` but the type of the value being stored will be used to infer /// Same as `store` but the type of the value being stored will be used to infer
/// the pointer type. /// the pointer type.
/// Uses the `bin` union field - Astgen.zig depends on the ability to change /// Uses the `bin` union field.
/// the tag of an instruction from `store_to_block_ptr` to `store_to_inferred_ptr`
/// without changing the data.
store_to_inferred_ptr, store_to_inferred_ptr,
/// String Literal. Makes an anonymous Decl and then takes a pointer to it. /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
/// Uses the `str` union field. /// Uses the `str` union field.
@ -1109,7 +1098,6 @@ pub const Inst = struct {
.shr, .shr,
.store, .store,
.store_node, .store_node,
.store_to_block_ptr,
.store_to_inferred_ptr, .store_to_inferred_ptr,
.str, .str,
.sub, .sub,
@ -1295,7 +1283,6 @@ pub const Inst = struct {
.atomic_store, .atomic_store,
.store, .store,
.store_node, .store_node,
.store_to_block_ptr,
.store_to_inferred_ptr, .store_to_inferred_ptr,
.resolve_inferred_alloc, .resolve_inferred_alloc,
.validate_array_init_ty, .validate_array_init_ty,
@ -1667,7 +1654,6 @@ pub const Inst = struct {
.slice_length = .pl_node, .slice_length = .pl_node,
.store = .bin, .store = .bin,
.store_node = .pl_node, .store_node = .pl_node,
.store_to_block_ptr = .bin,
.store_to_inferred_ptr = .bin, .store_to_inferred_ptr = .bin,
.str = .str, .str = .str,
.negate = .un_node, .negate = .un_node,

View file

@ -145,7 +145,6 @@ const Writer = struct {
switch (tag) { switch (tag) {
.as, .as,
.store, .store,
.store_to_block_ptr,
.store_to_inferred_ptr, .store_to_inferred_ptr,
=> try self.writeBin(stream, inst), => try self.writeBin(stream, inst),

View file

@ -29,4 +29,4 @@ export fn f4() void {
// :2:22: error: expected type 'usize', found 'void' // :2:22: error: expected type 'usize', found 'void'
// :7:9: error: expected type 'usize', found 'void' // :7:9: error: expected type 'usize', found 'void'
// :14:9: error: expected type 'usize', found 'void' // :14:9: error: expected type 'usize', found 'void'
// :19:27: error: expected type 'usize', found 'void' // :20:9: error: expected type 'usize', found 'void'

View file

@ -34,8 +34,7 @@ export fn entry() void {
// backend=stage2 // backend=stage2
// target=native // target=native
// //
// :2:20: error: incompatible types: 'i32' and 'void' // :2:20: error: expected type 'i32', found 'void'
// :2:30: note: type 'i32' here
// :8:15: error: incompatible types: 'i32' and 'void' // :8:15: error: incompatible types: 'i32' and 'void'
// :8:25: note: type 'i32' here // :8:25: note: type 'i32' here
// :16:16: error: expected type 'tmp.h.T', found 'void' // :16:16: error: expected type 'tmp.h.T', found 'void'