mirror of
https://github.com/ziglang/zig.git
synced 2025-12-16 03:03:09 +00:00
AstGen: scope result location related functions
This commit is contained in:
parent
402f87a213
commit
d123a5ec67
119
src/AstGen.zig
119
src/AstGen.zig
@ -138,7 +138,7 @@ pub const ResultLoc = union(enum) {
|
||||
/// There is a pointer for the expression to store its result into, however, its type
|
||||
/// is inferred based on peer type resolution for a `zir.Inst.Block`.
|
||||
/// The result instruction from the expression must be ignored.
|
||||
block_ptr: *Module.Scope.GenZir,
|
||||
block_ptr: *Scope.GenZir,
|
||||
|
||||
pub const Strategy = struct {
|
||||
elide_store_to_block_ptr_instructions: bool,
|
||||
@ -154,6 +154,41 @@ pub const ResultLoc = union(enum) {
|
||||
break_operand,
|
||||
};
|
||||
};
|
||||
|
||||
fn strategy(rl: ResultLoc, block_scope: *Scope.GenZir) Strategy {
|
||||
var elide_store_to_block_ptr_instructions = false;
|
||||
switch (rl) {
|
||||
// In this branch there will not be any store_to_block_ptr instructions.
|
||||
.discard, .none, .ty, .ref => return .{
|
||||
.tag = .break_operand,
|
||||
.elide_store_to_block_ptr_instructions = false,
|
||||
},
|
||||
// The pointer got passed through to the sub-expressions, so we will use
|
||||
// break_void here.
|
||||
// In this branch there will not be any store_to_block_ptr instructions.
|
||||
.ptr => return .{
|
||||
.tag = .break_void,
|
||||
.elide_store_to_block_ptr_instructions = false,
|
||||
},
|
||||
.inferred_ptr, .bitcasted_ptr, .block_ptr => {
|
||||
if (block_scope.rvalue_rl_count == block_scope.break_count) {
|
||||
// Neither prong of the if consumed the result location, so we can
|
||||
// use break instructions to create an rvalue.
|
||||
return .{
|
||||
.tag = .break_operand,
|
||||
.elide_store_to_block_ptr_instructions = true,
|
||||
};
|
||||
} else {
|
||||
// Allow the store_to_block_ptr instructions to remain so that
|
||||
// semantic analysis can turn them into bitcasts.
|
||||
return .{
|
||||
.tag = .break_void,
|
||||
.elide_store_to_block_ptr_instructions = false,
|
||||
};
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn typeExpr(mod: *Module, scope: *Scope, type_node: ast.Node.Index) InnerError!zir.Inst.Ref {
|
||||
@ -989,7 +1024,7 @@ fn labeledBlockExpr(
|
||||
.block_inst = block_inst,
|
||||
}),
|
||||
};
|
||||
setBlockResultLoc(&block_scope, rl);
|
||||
block_scope.setBreakResultLoc(rl);
|
||||
defer block_scope.instructions.deinit(mod.gpa);
|
||||
defer block_scope.labeled_breaks.deinit(mod.gpa);
|
||||
defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa);
|
||||
@ -1003,7 +1038,7 @@ fn labeledBlockExpr(
|
||||
const zir_tags = gz.astgen.instructions.items(.tag);
|
||||
const zir_datas = gz.astgen.instructions.items(.data);
|
||||
|
||||
const strat = rlStrategy(rl, &block_scope);
|
||||
const strat = rl.strategy(&block_scope);
|
||||
switch (strat.tag) {
|
||||
.break_void => {
|
||||
// The code took advantage of the result location as a pointer.
|
||||
@ -1740,7 +1775,7 @@ fn orelseCatchExpr(
|
||||
.force_comptime = parent_gz.force_comptime,
|
||||
.instructions = .{},
|
||||
};
|
||||
setBlockResultLoc(&block_scope, rl);
|
||||
block_scope.setBreakResultLoc(rl);
|
||||
defer block_scope.instructions.deinit(mod.gpa);
|
||||
|
||||
// This could be a pointer or value depending on the `operand_rl` parameter.
|
||||
@ -1856,7 +1891,7 @@ fn finishThenElseBlock(
|
||||
) InnerError!zir.Inst.Ref {
|
||||
// We now have enough information to decide whether the result instruction should
|
||||
// be communicated via result location pointer or break instructions.
|
||||
const strat = rlStrategy(rl, block_scope);
|
||||
const strat = rl.strategy(block_scope);
|
||||
const astgen = block_scope.astgen;
|
||||
switch (strat.tag) {
|
||||
.break_void => {
|
||||
@ -2035,7 +2070,7 @@ fn ifExpr(
|
||||
.force_comptime = parent_gz.force_comptime,
|
||||
.instructions = .{},
|
||||
};
|
||||
setBlockResultLoc(&block_scope, rl);
|
||||
block_scope.setBreakResultLoc(rl);
|
||||
defer block_scope.instructions.deinit(mod.gpa);
|
||||
|
||||
const cond = c: {
|
||||
@ -2190,7 +2225,7 @@ fn whileExpr(
|
||||
.force_comptime = parent_gz.force_comptime,
|
||||
.instructions = .{},
|
||||
};
|
||||
setBlockResultLoc(&loop_scope, rl);
|
||||
loop_scope.setBreakResultLoc(rl);
|
||||
defer loop_scope.instructions.deinit(mod.gpa);
|
||||
|
||||
var continue_scope: Scope.GenZir = .{
|
||||
@ -2338,7 +2373,7 @@ fn forExpr(
|
||||
.force_comptime = parent_gz.force_comptime,
|
||||
.instructions = .{},
|
||||
};
|
||||
setBlockResultLoc(&loop_scope, rl);
|
||||
loop_scope.setBreakResultLoc(rl);
|
||||
defer loop_scope.instructions.deinit(mod.gpa);
|
||||
|
||||
var cond_scope: Scope.GenZir = .{
|
||||
@ -2520,7 +2555,7 @@ fn switchExpr(
|
||||
.force_comptime = parent_gz.force_comptime,
|
||||
.instructions = .{},
|
||||
};
|
||||
setBlockResultLoc(&block_scope, rl);
|
||||
block_scope.setBreakResultLoc(rl);
|
||||
defer block_scope.instructions.deinit(mod.gpa);
|
||||
|
||||
var items = std.ArrayList(zir.Inst.Ref).init(mod.gpa);
|
||||
@ -3911,69 +3946,3 @@ fn rvalue(
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZir) ResultLoc.Strategy {
|
||||
var elide_store_to_block_ptr_instructions = false;
|
||||
switch (rl) {
|
||||
// In this branch there will not be any store_to_block_ptr instructions.
|
||||
.discard, .none, .ty, .ref => return .{
|
||||
.tag = .break_operand,
|
||||
.elide_store_to_block_ptr_instructions = false,
|
||||
},
|
||||
// The pointer got passed through to the sub-expressions, so we will use
|
||||
// break_void here.
|
||||
// In this branch there will not be any store_to_block_ptr instructions.
|
||||
.ptr => return .{
|
||||
.tag = .break_void,
|
||||
.elide_store_to_block_ptr_instructions = false,
|
||||
},
|
||||
.inferred_ptr, .bitcasted_ptr, .block_ptr => {
|
||||
if (block_scope.rvalue_rl_count == block_scope.break_count) {
|
||||
// Neither prong of the if consumed the result location, so we can
|
||||
// use break instructions to create an rvalue.
|
||||
return .{
|
||||
.tag = .break_operand,
|
||||
.elide_store_to_block_ptr_instructions = true,
|
||||
};
|
||||
} else {
|
||||
// Allow the store_to_block_ptr instructions to remain so that
|
||||
// semantic analysis can turn them into bitcasts.
|
||||
return .{
|
||||
.tag = .break_void,
|
||||
.elide_store_to_block_ptr_instructions = false,
|
||||
};
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn setBlockResultLoc(block_scope: *Scope.GenZir, parent_rl: ResultLoc) void {
|
||||
// Depending on whether the result location is a pointer or value, different
|
||||
// ZIR needs to be generated. In the former case we rely on storing to the
|
||||
// pointer to communicate the result, and use breakvoid; in the latter case
|
||||
// the block break instructions will have the result values.
|
||||
// One more complication: when the result location is a pointer, we detect
|
||||
// the scenario where the result location is not consumed. In this case
|
||||
// we emit ZIR for the block break instructions to have the result values,
|
||||
// and then rvalue() on that to pass the value to the result location.
|
||||
switch (parent_rl) {
|
||||
.discard, .none, .ty, .ptr, .ref => {
|
||||
block_scope.break_result_loc = parent_rl;
|
||||
},
|
||||
|
||||
.inferred_ptr => |ptr| {
|
||||
block_scope.rl_ptr = ptr;
|
||||
block_scope.break_result_loc = .{ .block_ptr = block_scope };
|
||||
},
|
||||
|
||||
.bitcasted_ptr => |ptr| {
|
||||
block_scope.rl_ptr = ptr;
|
||||
block_scope.break_result_loc = .{ .block_ptr = block_scope };
|
||||
},
|
||||
|
||||
.block_ptr => |parent_block_scope| {
|
||||
block_scope.rl_ptr = parent_block_scope.rl_ptr;
|
||||
block_scope.break_result_loc = .{ .block_ptr = block_scope };
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -920,7 +920,7 @@ pub const Scope = struct {
|
||||
label: ?Label = null,
|
||||
break_block: zir.Inst.Index = 0,
|
||||
continue_block: zir.Inst.Index = 0,
|
||||
/// Only valid when setBlockResultLoc is called.
|
||||
/// Only valid when setBreakResultLoc is called.
|
||||
break_result_loc: AstGen.ResultLoc = undefined,
|
||||
/// When a block has a pointer result location, here it is.
|
||||
rl_ptr: zir.Inst.Ref = .none,
|
||||
@ -973,6 +973,37 @@ pub const Scope = struct {
|
||||
return &gz.astgen.decl.container.file_scope.tree;
|
||||
}
|
||||
|
||||
pub fn setBreakResultLoc(gz: *GenZir, parent_rl: AstGen.ResultLoc) void {
|
||||
// Depending on whether the result location is a pointer or value, different
|
||||
// ZIR needs to be generated. In the former case we rely on storing to the
|
||||
// pointer to communicate the result, and use breakvoid; in the latter case
|
||||
// the block break instructions will have the result values.
|
||||
// One more complication: when the result location is a pointer, we detect
|
||||
// the scenario where the result location is not consumed. In this case
|
||||
// we emit ZIR for the block break instructions to have the result values,
|
||||
// and then rvalue() on that to pass the value to the result location.
|
||||
switch (parent_rl) {
|
||||
.discard, .none, .ty, .ptr, .ref => {
|
||||
gz.break_result_loc = parent_rl;
|
||||
},
|
||||
|
||||
.inferred_ptr => |ptr| {
|
||||
gz.rl_ptr = ptr;
|
||||
gz.break_result_loc = .{ .block_ptr = gz };
|
||||
},
|
||||
|
||||
.bitcasted_ptr => |ptr| {
|
||||
gz.rl_ptr = ptr;
|
||||
gz.break_result_loc = .{ .block_ptr = gz };
|
||||
},
|
||||
|
||||
.block_ptr => |parent_block_scope| {
|
||||
gz.rl_ptr = parent_block_scope.rl_ptr;
|
||||
gz.break_result_loc = .{ .block_ptr = gz };
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setBoolBrBody(gz: GenZir, inst: zir.Inst.Index) !void {
|
||||
const gpa = gz.astgen.mod.gpa;
|
||||
try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user