authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-14 21:57:40-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-20 12:19:16-07:00
logc020a302960c499ffe811dd0601a2d386c191b91
treecdcc37a5a2efc0ade7be07c8c014d1f82f8d547b
parent27be4f31402557972ae28d552f4ec4617357d454

Sema: remove br_block_flat AIR instruction

Thanks to the new AIR memory layout, we can do this by turning a br operand into a block, rather than having this special purpose instruction.

5 files changed, 105 insertions(+), 115 deletions(-)

BRANCH_TODO-42
......@@ -16,48 +16,6 @@
1616 return inst.val;
1717 }
1818
19 pub fn breakBlock(base: *Inst) ?*Block {
20 return switch (base.tag) {
21 .br => base.castTag(.br).?.block,
22 .br_void => base.castTag(.br_void).?.block,
23 .br_block_flat => base.castTag(.br_block_flat).?.block,
24 else => null,
25 };
26 }
27
28 pub const convertable_br_size = std.math.max(@sizeOf(BrBlockFlat), @sizeOf(Br));
29 pub const convertable_br_align = std.math.max(@alignOf(BrBlockFlat), @alignOf(Br));
30 comptime {
31 assert(@offsetOf(BrBlockFlat, "base") == @offsetOf(Br, "base"));
32 }
33
34 pub const BrBlockFlat = struct {
35 pub const base_tag = Tag.br_block_flat;
36
37 base: Inst,
38 block: *Block,
39 body: Body,
40
41 pub fn operandCount(self: *const BrBlockFlat) usize {
42 _ = self;
43 return 0;
44 }
45 pub fn getOperand(self: *const BrBlockFlat, index: usize) ?*Inst {
46 _ = self;
47 _ = index;
48 return null;
49 }
50 };
51
52 /// Same as `br` except the operand is a list of instructions to be treated as
53 /// a flat block; that is there is only 1 break instruction from the block, and
54 /// it is implied to be after the last instruction, and the last instruction is
55 /// the break operand.
56 /// This instruction exists for late-stage semantic analysis patch ups, to
57 /// replace one br operand with multiple instructions, without moving anything else around.
58 br_block_flat,
59
60
6119
6220
6321/// For debugging purposes, prints a function representation to stderr.
src/Air.zig-4
......@@ -308,10 +308,6 @@ pub const Inst = struct {
308308 operand: Ref,
309309 payload: u32,
310310 },
311 constant: struct {
312 ty: Type,
313 val: Value,
314 },
315311 dbg_stmt: struct {
316312 line: u32,
317313 column: u32,
src/Liveness.zig-2
......@@ -299,8 +299,6 @@ fn analyzeInst(
299299 const extra = a.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload);
300300 const body = a.air.extra[extra.end..][0..extra.data.body_len];
301301 try analyzeWithContext(a, new_set, body);
302 // We let this continue so that it can possibly mark the block as
303 // unreferenced below.
304302 return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none });
305303 },
306304 .loop => {
src/Module.zig+1-1
......@@ -1185,7 +1185,7 @@ pub const Scope = struct {
11851185 block_inst: Air.Inst.Index,
11861186 /// Separate array list from break_inst_list so that it can be passed directly
11871187 /// to resolvePeerTypes.
1188 results: ArrayListUnmanaged(Air.Inst.Index),
1188 results: ArrayListUnmanaged(Air.Inst.Ref),
11891189 /// Keeps track of the break instructions so that the operand can be replaced
11901190 /// if we need to add type coercion at the end of block analysis.
11911191 /// Same indexes, capacity, length as `results`.
src/Sema.zig+104-66
......@@ -163,36 +163,36 @@ pub fn analyzeBody(
163163 const air_inst: Air.Inst.Ref = switch (tags[inst]) {
164164 // zig fmt: off
165165 .arg => try sema.zirArg(block, inst),
166 //.alloc => try sema.zirAlloc(block, inst),
167 //.alloc_inferred => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_const)),
168 //.alloc_inferred_mut => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_mut)),
169 //.alloc_inferred_comptime => try sema.zirAllocInferredComptime(block, inst),
170 //.alloc_mut => try sema.zirAllocMut(block, inst),
171 //.alloc_comptime => try sema.zirAllocComptime(block, inst),
172 //.anyframe_type => try sema.zirAnyframeType(block, inst),
173 //.array_cat => try sema.zirArrayCat(block, inst),
174 //.array_mul => try sema.zirArrayMul(block, inst),
175 //.array_type => try sema.zirArrayType(block, inst),
176 //.array_type_sentinel => try sema.zirArrayTypeSentinel(block, inst),
177 //.vector_type => try sema.zirVectorType(block, inst),
178 //.as => try sema.zirAs(block, inst),
179 //.as_node => try sema.zirAsNode(block, inst),
180 //.bit_and => try sema.zirBitwise(block, inst, .bit_and),
181 //.bit_not => try sema.zirBitNot(block, inst),
182 //.bit_or => try sema.zirBitwise(block, inst, .bit_or),
183 //.bitcast => try sema.zirBitcast(block, inst),
184 //.bitcast_result_ptr => try sema.zirBitcastResultPtr(block, inst),
185 //.block => try sema.zirBlock(block, inst),
186 //.suspend_block => try sema.zirSuspendBlock(block, inst),
187 //.bool_not => try sema.zirBoolNot(block, inst),
188 //.bool_br_and => try sema.zirBoolBr(block, inst, false),
189 //.bool_br_or => try sema.zirBoolBr(block, inst, true),
190 //.c_import => try sema.zirCImport(block, inst),
191 //.call => try sema.zirCall(block, inst, .auto, false),
192 //.call_chkused => try sema.zirCall(block, inst, .auto, true),
193 //.call_compile_time => try sema.zirCall(block, inst, .compile_time, false),
194 //.call_nosuspend => try sema.zirCall(block, inst, .no_async, false),
195 //.call_async => try sema.zirCall(block, inst, .async_kw, false),
166 .alloc => try sema.zirAlloc(block, inst),
167 .alloc_inferred => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_const)),
168 .alloc_inferred_mut => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_mut)),
169 .alloc_inferred_comptime => try sema.zirAllocInferredComptime(block, inst),
170 .alloc_mut => try sema.zirAllocMut(block, inst),
171 .alloc_comptime => try sema.zirAllocComptime(block, inst),
172 .anyframe_type => try sema.zirAnyframeType(block, inst),
173 .array_cat => try sema.zirArrayCat(block, inst),
174 .array_mul => try sema.zirArrayMul(block, inst),
175 .array_type => try sema.zirArrayType(block, inst),
176 .array_type_sentinel => try sema.zirArrayTypeSentinel(block, inst),
177 .vector_type => try sema.zirVectorType(block, inst),
178 .as => try sema.zirAs(block, inst),
179 .as_node => try sema.zirAsNode(block, inst),
180 .bit_and => try sema.zirBitwise(block, inst, .bit_and),
181 .bit_not => try sema.zirBitNot(block, inst),
182 .bit_or => try sema.zirBitwise(block, inst, .bit_or),
183 .bitcast => try sema.zirBitcast(block, inst),
184 .bitcast_result_ptr => try sema.zirBitcastResultPtr(block, inst),
185 .block => try sema.zirBlock(block, inst),
186 .suspend_block => try sema.zirSuspendBlock(block, inst),
187 .bool_not => try sema.zirBoolNot(block, inst),
188 .bool_br_and => try sema.zirBoolBr(block, inst, false),
189 .bool_br_or => try sema.zirBoolBr(block, inst, true),
190 .c_import => try sema.zirCImport(block, inst),
191 .call => try sema.zirCall(block, inst, .auto, false),
192 .call_chkused => try sema.zirCall(block, inst, .auto, true),
193 .call_compile_time => try sema.zirCall(block, inst, .compile_time, false),
194 .call_nosuspend => try sema.zirCall(block, inst, .no_async, false),
195 .call_async => try sema.zirCall(block, inst, .async_kw, false),
196196 .cmp_eq => try sema.zirCmp(block, inst, .eq),
197197 .cmp_gt => try sema.zirCmp(block, inst, .gt),
198198 .cmp_gte => try sema.zirCmp(block, inst, .gte),
......@@ -1957,24 +1957,23 @@ fn analyzeBlockBody(
19571957
19581958 // Blocks must terminate with noreturn instruction.
19591959 assert(child_block.instructions.items.len != 0);
1960 assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn());
1960 assert(sema.getTypeOf(indexToRef(child_block.instructions.items[child_block.instructions.items.len - 1])).isNoReturn());
19611961
19621962 if (merges.results.items.len == 0) {
19631963 // No need for a block instruction. We can put the new instructions
19641964 // directly into the parent block.
1965 const copied_instructions = try sema.arena.dupe(Air.Inst.Index, child_block.instructions.items);
1966 try parent_block.instructions.appendSlice(gpa, copied_instructions);
1967 return copied_instructions[copied_instructions.len - 1];
1965 try parent_block.instructions.appendSlice(gpa, child_block.instructions.items);
1966 return indexToRef(child_block.instructions.items[child_block.instructions.items.len - 1]);
19681967 }
19691968 if (merges.results.items.len == 1) {
19701969 const last_inst_index = child_block.instructions.items.len - 1;
19711970 const last_inst = child_block.instructions.items[last_inst_index];
1972 if (last_inst.breakBlock()) |br_block| {
1971 if (sema.getBreakBlock(last_inst)) |br_block| {
19731972 if (br_block == merges.block_inst) {
19741973 // No need for a block instruction. We can put the new instructions directly
19751974 // into the parent block. Here we omit the break instruction.
1976 const copied_instructions = try sema.arena.dupe(Air.Inst.Index, child_block.instructions.items[0..last_inst_index]);
1977 try parent_block.instructions.appendSlice(gpa, copied_instructions);
1975 const without_break = child_block.instructions.items[0..last_inst_index];
1976 try parent_block.instructions.appendSlice(gpa, without_break);
19781977 return merges.results.items[0];
19791978 }
19801979 }
......@@ -1998,36 +1997,50 @@ fn analyzeBlockBody(
19981997 // Now that the block has its type resolved, we need to go back into all the break
19991998 // instructions, and insert type coercion on the operands.
20001999 for (merges.br_list.items) |br| {
2001 if (sema.getTypeOf(br.operand).eql(resolved_ty)) {
2000 const br_operand = sema.air_instructions.items(.data)[br].br.operand;
2001 const br_operand_src = src;
2002 const br_operand_ty = sema.getTypeOf(br_operand);
2003 if (br_operand_ty.eql(resolved_ty)) {
20022004 // No type coercion needed.
20032005 continue;
20042006 }
20052007 var coerce_block = parent_block.makeSubBlock();
20062008 defer coerce_block.instructions.deinit(gpa);
2007 const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br.operand, br.operand.src);
2009 const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br_operand, br_operand_src);
20082010 // If no instructions were produced, such as in the case of a coercion of a
20092011 // constant value to a new type, we can simply point the br operand to it.
20102012 if (coerce_block.instructions.items.len == 0) {
2011 br.operand = coerced_operand;
2013 sema.air_instructions.items(.data)[br].br.operand = coerced_operand;
20122014 continue;
20132015 }
2014 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand);
2015 // Here we depend on the br instruction having been over-allocated (if necessary)
2016 // inside zirBreak so that it can be converted into a br_block_flat instruction.
2017 const br_src = br.base.src;
2018 const br_ty = br.base.ty;
2019 const br_block_flat = @ptrCast(*Inst.BrBlockFlat, br);
2020 br_block_flat.* = .{
2021 .base = .{
2022 .src = br_src,
2023 .ty = br_ty,
2024 .tag = .br_block_flat,
2025 },
2026 .block = merges.block_inst,
2027 .body = .{
2028 .instructions = try sema.arena.dupe(Air.Inst.Index, coerce_block.instructions.items),
2029 },
2030 };
2016 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] ==
2017 refToIndex(coerced_operand).?);
2018
2019 // Convert the br operand to a block.
2020 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
2021 coerce_block.instructions.items.len);
2022 try sema.air_instructions.ensureUnusedCapacity(gpa, 2);
2023 const sub_block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
2024 const sub_br_inst = sub_block_inst + 1;
2025 sema.air_instructions.items(.data)[br].br.operand = indexToRef(sub_block_inst);
2026 sema.air_instructions.appendAssumeCapacity(.{
2027 .tag = .block,
2028 .data = .{ .ty_pl = .{
2029 .ty = try sema.addType(br_operand_ty),
2030 .payload = sema.addExtraAssumeCapacity(Air.Block{
2031 .body_len = @intCast(u32, coerce_block.instructions.items.len),
2032 }),
2033 } },
2034 });
2035 sema.air_extra.appendSliceAssumeCapacity(coerce_block.instructions.items);
2036 sema.air_extra.appendAssumeCapacity(sub_br_inst);
2037 sema.air_instructions.appendAssumeCapacity(.{
2038 .tag = .br,
2039 .data = .{ .br = .{
2040 .block_inst = sub_block_inst,
2041 .operand = coerced_operand,
2042 } },
2043 });
20312044 }
20322045 return indexToRef(merges.block_inst);
20332046}
......@@ -2257,10 +2270,11 @@ fn analyzeCall(
22572270 ensure_result_used: bool,
22582271 args: []const Air.Inst.Ref,
22592272) CompileError!Air.Inst.Ref {
2260 if (func.ty.zigTypeTag() != .Fn)
2261 return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func.ty});
2273 const func_ty = sema.getTypeOf(func);
2274 if (func_ty.zigTypeTag() != .Fn)
2275 return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func_ty});
22622276
2263 const cc = func.ty.fnCallingConvention();
2277 const cc = func_ty.fnCallingConvention();
22642278 if (cc == .Naked) {
22652279 // TODO add error note: declared here
22662280 return sema.mod.fail(
......@@ -2270,8 +2284,8 @@ fn analyzeCall(
22702284 .{},
22712285 );
22722286 }
2273 const fn_params_len = func.ty.fnParamLen();
2274 if (func.ty.fnIsVarArgs()) {
2287 const fn_params_len = func_ty.fnParamLen();
2288 if (func_ty.fnIsVarArgs()) {
22752289 assert(cc == .C);
22762290 if (args.len < fn_params_len) {
22772291 // TODO add error note: declared here
......@@ -2310,11 +2324,9 @@ fn analyzeCall(
23102324
23112325 const gpa = sema.gpa;
23122326
2313 const ret_type = func.ty.fnReturnType();
2314
23152327 const is_comptime_call = block.is_comptime or modifier == .compile_time;
23162328 const is_inline_call = is_comptime_call or modifier == .always_inline or
2317 func.ty.fnCallingConvention() == .Inline;
2329 func_ty.fnCallingConvention() == .Inline;
23182330 const result: Air.Inst.Ref = if (is_inline_call) res: {
23192331 const func_val = try sema.resolveConstValue(block, func_src, func);
23202332 const module_fn = switch (func_val.tag()) {
......@@ -2400,7 +2412,19 @@ fn analyzeCall(
24002412 break :res result;
24012413 } else res: {
24022414 try sema.requireRuntimeBlock(block, call_src);
2403 break :res try block.addCall(call_src, ret_type, func, args);
2415 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
2416 args.len);
2417 const func_inst = try block.addInst(.{
2418 .tag = .call,
2419 .data = .{ .pl_op = .{
2420 .operand = func,
2421 .payload = sema.addExtraAssumeCapacity(Air.Call{
2422 .args_len = @intCast(u32, args.len),
2423 }),
2424 } },
2425 });
2426 sema.appendRefsAssumeCapacity(args);
2427 break :res func_inst;
24042428 };
24052429
24062430 if (ensure_result_used) {
......@@ -8140,3 +8164,17 @@ pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 {
81408164 }
81418165 return result;
81428166}
8167
8168fn appendRefsAssumeCapacity(sema: *Sema, refs: []const Air.Inst.Ref) void {
8169 const coerced = @bitCast([]const u32, refs);
8170 sema.air_extra.appendSliceAssumeCapacity(coerced);
8171}
8172
8173fn getBreakBlock(sema: *Sema, inst_index: Air.Inst.Index) ?Air.Inst.Index {
8174 const air_datas = sema.air_instructions.items(.data);
8175 const air_tags = sema.air_instructions.items(.tag);
8176 switch (air_tags[inst_index]) {
8177 .br => return air_datas[inst_index].br.block_inst,
8178 else => return null,
8179 }
8180}