authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-16 23:06:59-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-20 12:19:16-07:00
logd17f492017c77d5d52d2fbd65eaa5c1e08b24161
tree4b5f2ecc801b19f546982a0bc159e2db979f90b2
parent2438f61f1c37aefa16852130370df44b3fabf785

stage2: miscellaneous fixes for the branch

* Breaking language change: inline assembly must use string literal syntax. This is in preparation for inline assembly improvements that involve more integration with the Zig language. This means we cannot rely on text substitution. * Liveness: properly handle inline assembly and function calls with more than 3 operands. - More than 35 operands is not yet supported. This is a low priority to implement. - This required implementation in codegen.zig as well. * Liveness: fix bug causing incorrect tomb bits. * Sema: enable switch expressions that are evaluated at compile-time. - Runtime switch instructions still need to be reworked in this branch. There was a TODO left here (by me) with a suggestion to do some bigger changes as part of the AIR memory reworking. Now that time has come and I plan to honor the suggestion in a future commit before merging this branch. * AIR printing: fix missing ')' on alive instructions. We're back to "hello world" working for the x86_64 backend.

8 files changed, 441 insertions(+), 232 deletions(-)

lib/std/Thread.zig+43-21
......@@ -505,8 +505,8 @@ const LinuxThreadImpl = struct {
505505 /// Ported over from musl libc's pthread detached implementation:
506506 /// https://github.com/ifduyue/musl/search?q=__unmapself
507507 fn freeAndExit(self: *ThreadCompletion) noreturn {
508 const unmap_and_exit: []const u8 = switch (target.cpu.arch) {
509 .i386 => (
508 switch (target.cpu.arch) {
509 .i386 => asm volatile (
510510 \\ movl $91, %%eax
511511 \\ movl %[ptr], %%ebx
512512 \\ movl %[len], %%ecx
......@@ -514,8 +514,12 @@ const LinuxThreadImpl = struct {
514514 \\ movl $1, %%eax
515515 \\ movl $0, %%ebx
516516 \\ int $128
517 :
518 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
519 [len] "r" (self.mapped.len)
520 : "memory"
517521 ),
518 .x86_64 => (
522 .x86_64 => asm volatile (
519523 \\ movq $11, %%rax
520524 \\ movq %[ptr], %%rbx
521525 \\ movq %[len], %%rcx
......@@ -523,8 +527,12 @@ const LinuxThreadImpl = struct {
523527 \\ movq $60, %%rax
524528 \\ movq $1, %%rdi
525529 \\ syscall
530 :
531 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
532 [len] "r" (self.mapped.len)
533 : "memory"
526534 ),
527 .arm, .armeb, .thumb, .thumbeb => (
535 .arm, .armeb, .thumb, .thumbeb => asm volatile (
528536 \\ mov r7, #91
529537 \\ mov r0, %[ptr]
530538 \\ mov r1, %[len]
......@@ -532,8 +540,12 @@ const LinuxThreadImpl = struct {
532540 \\ mov r7, #1
533541 \\ mov r0, #0
534542 \\ svc 0
543 :
544 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
545 [len] "r" (self.mapped.len)
546 : "memory"
535547 ),
536 .aarch64, .aarch64_be, .aarch64_32 => (
548 .aarch64, .aarch64_be, .aarch64_32 => asm volatile (
537549 \\ mov x8, #215
538550 \\ mov x0, %[ptr]
539551 \\ mov x1, %[len]
......@@ -541,8 +553,12 @@ const LinuxThreadImpl = struct {
541553 \\ mov x8, #93
542554 \\ mov x0, #0
543555 \\ svc 0
556 :
557 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
558 [len] "r" (self.mapped.len)
559 : "memory"
544560 ),
545 .mips, .mipsel => (
561 .mips, .mipsel => asm volatile (
546562 \\ move $sp, $25
547563 \\ li $2, 4091
548564 \\ move $4, %[ptr]
......@@ -551,8 +567,12 @@ const LinuxThreadImpl = struct {
551567 \\ li $2, 4001
552568 \\ li $4, 0
553569 \\ syscall
570 :
571 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
572 [len] "r" (self.mapped.len)
573 : "memory"
554574 ),
555 .mips64, .mips64el => (
575 .mips64, .mips64el => asm volatile (
556576 \\ li $2, 4091
557577 \\ move $4, %[ptr]
558578 \\ move $5, %[len]
......@@ -560,8 +580,12 @@ const LinuxThreadImpl = struct {
560580 \\ li $2, 4001
561581 \\ li $4, 0
562582 \\ syscall
583 :
584 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
585 [len] "r" (self.mapped.len)
586 : "memory"
563587 ),
564 .powerpc, .powerpcle, .powerpc64, .powerpc64le => (
588 .powerpc, .powerpcle, .powerpc64, .powerpc64le => asm volatile (
565589 \\ li 0, 91
566590 \\ mr %[ptr], 3
567591 \\ mr %[len], 4
......@@ -570,8 +594,12 @@ const LinuxThreadImpl = struct {
570594 \\ li 3, 0
571595 \\ sc
572596 \\ blr
597 :
598 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
599 [len] "r" (self.mapped.len)
600 : "memory"
573601 ),
574 .riscv64 => (
602 .riscv64 => asm volatile (
575603 \\ li a7, 215
576604 \\ mv a0, %[ptr]
577605 \\ mv a1, %[len]
......@@ -579,19 +607,13 @@ const LinuxThreadImpl = struct {
579607 \\ li a7, 93
580608 \\ mv a0, zero
581609 \\ ecall
610 :
611 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
612 [len] "r" (self.mapped.len)
613 : "memory"
582614 ),
583 else => |cpu_arch| {
584 @compileLog("Unsupported linux arch ", cpu_arch);
585 },
586 };
587
588 asm volatile (unmap_and_exit
589 :
590 : [ptr] "r" (@ptrToInt(self.mapped.ptr)),
591 [len] "r" (self.mapped.len)
592 : "memory"
593 );
594
615 else => |cpu_arch| @compileError("Unsupported linux arch: " ++ @tagName(cpu_arch)),
616 }
595617 unreachable;
596618 }
597619 };
lib/std/atomic.zig+21-17
......@@ -46,34 +46,38 @@ test "fence/compilerFence" {
4646
4747/// Signals to the processor that the caller is inside a busy-wait spin-loop.
4848pub inline fn spinLoopHint() void {
49 const hint_instruction = switch (target.cpu.arch) {
50 // No-op instruction that can hint to save (or share with a hardware-thread) pipelining/power resources
49 switch (target.cpu.arch) {
50 // No-op instruction that can hint to save (or share with a hardware-thread)
51 // pipelining/power resources
5152 // https://software.intel.com/content/www/us/en/develop/articles/benefitting-power-and-performance-sleep-loops.html
52 .i386, .x86_64 => "pause",
53 .i386, .x86_64 => asm volatile ("pause" ::: "memory"),
5354
5455 // No-op instruction that serves as a hardware-thread resource yield hint.
5556 // https://stackoverflow.com/a/7588941
56 .powerpc64, .powerpc64le => "or 27, 27, 27",
57 .powerpc64, .powerpc64le => asm volatile ("or 27, 27, 27" ::: "memory"),
5758
58 // `isb` appears more reliable for releasing execution resources than `yield` on common aarch64 CPUs.
59 // `isb` appears more reliable for releasing execution resources than `yield`
60 // on common aarch64 CPUs.
5961 // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8258604
6062 // https://bugs.mysql.com/bug.php?id=100664
61 .aarch64, .aarch64_be, .aarch64_32 => "isb",
63 .aarch64, .aarch64_be, .aarch64_32 => asm volatile ("isb" ::: "memory"),
6264
6365 // `yield` was introduced in v6k but is also available on v6m.
6466 // https://www.keil.com/support/man/docs/armasm/armasm_dom1361289926796.htm
65 .arm, .armeb, .thumb, .thumbeb => blk: {
66 const can_yield = comptime std.Target.arm.featureSetHasAny(target.cpu.features, .{ .has_v6k, .has_v6m });
67 const instruction = if (can_yield) "yield" else "";
68 break :blk instruction;
67 .arm, .armeb, .thumb, .thumbeb => {
68 const can_yield = comptime std.Target.arm.featureSetHasAny(target.cpu.features, .{
69 .has_v6k, .has_v6m,
70 });
71 if (can_yield) {
72 asm volatile ("yield" ::: "memory");
73 } else {
74 asm volatile ("" ::: "memory");
75 }
6976 },
70
71 else => "",
72 };
73
74 // Memory barrier to prevent the compiler from optimizing away the spin-loop
75 // even if no hint_instruction was provided.
76 asm volatile (hint_instruction ::: "memory");
77 // Memory barrier to prevent the compiler from optimizing away the spin-loop
78 // even if no hint_instruction was provided.
79 else => asm volatile ("" ::: "memory"),
80 }
7781}
7882
7983test "spinLoopHint" {
lib/std/atomic/Atomic.zig+70-18
......@@ -178,26 +178,78 @@ pub fn Atomic(comptime T: type) type {
178178 ) u1 {
179179 // x86 supports dedicated bitwise instructions
180180 if (comptime target.cpu.arch.isX86() and @sizeOf(T) >= 2 and @sizeOf(T) <= 8) {
181 const instruction = switch (op) {
182 .Set => "lock bts",
183 .Reset => "lock btr",
184 .Toggle => "lock btc",
185 };
186
187 const suffix = switch (@sizeOf(T)) {
188 2 => "w",
189 4 => "l",
190 8 => "q",
181 const old_bit: u8 = switch (@sizeOf(T)) {
182 2 => switch (op) {
183 .Set => asm volatile ("lock btsw %[bit], %[ptr]"
184 // LLVM doesn't support u1 flag register return values
185 : [result] "={@ccc}" (-> u8)
186 : [ptr] "*p" (&self.value),
187 [bit] "X" (@as(T, bit))
188 : "cc", "memory"
189 ),
190 .Reset => asm volatile ("lock btrw %[bit], %[ptr]"
191 // LLVM doesn't support u1 flag register return values
192 : [result] "={@ccc}" (-> u8)
193 : [ptr] "*p" (&self.value),
194 [bit] "X" (@as(T, bit))
195 : "cc", "memory"
196 ),
197 .Toggle => asm volatile ("lock btcw %[bit], %[ptr]"
198 // LLVM doesn't support u1 flag register return values
199 : [result] "={@ccc}" (-> u8)
200 : [ptr] "*p" (&self.value),
201 [bit] "X" (@as(T, bit))
202 : "cc", "memory"
203 ),
204 },
205 4 => switch (op) {
206 .Set => asm volatile ("lock btsl %[bit], %[ptr]"
207 // LLVM doesn't support u1 flag register return values
208 : [result] "={@ccc}" (-> u8)
209 : [ptr] "*p" (&self.value),
210 [bit] "X" (@as(T, bit))
211 : "cc", "memory"
212 ),
213 .Reset => asm volatile ("lock btrl %[bit], %[ptr]"
214 // LLVM doesn't support u1 flag register return values
215 : [result] "={@ccc}" (-> u8)
216 : [ptr] "*p" (&self.value),
217 [bit] "X" (@as(T, bit))
218 : "cc", "memory"
219 ),
220 .Toggle => asm volatile ("lock btcl %[bit], %[ptr]"
221 // LLVM doesn't support u1 flag register return values
222 : [result] "={@ccc}" (-> u8)
223 : [ptr] "*p" (&self.value),
224 [bit] "X" (@as(T, bit))
225 : "cc", "memory"
226 ),
227 },
228 8 => switch (op) {
229 .Set => asm volatile ("lock btsq %[bit], %[ptr]"
230 // LLVM doesn't support u1 flag register return values
231 : [result] "={@ccc}" (-> u8)
232 : [ptr] "*p" (&self.value),
233 [bit] "X" (@as(T, bit))
234 : "cc", "memory"
235 ),
236 .Reset => asm volatile ("lock btrq %[bit], %[ptr]"
237 // LLVM doesn't support u1 flag register return values
238 : [result] "={@ccc}" (-> u8)
239 : [ptr] "*p" (&self.value),
240 [bit] "X" (@as(T, bit))
241 : "cc", "memory"
242 ),
243 .Toggle => asm volatile ("lock btcq %[bit], %[ptr]"
244 // LLVM doesn't support u1 flag register return values
245 : [result] "={@ccc}" (-> u8)
246 : [ptr] "*p" (&self.value),
247 [bit] "X" (@as(T, bit))
248 : "cc", "memory"
249 ),
250 },
191251 else => @compileError("Invalid atomic type " ++ @typeName(T)),
192252 };
193
194 const old_bit = asm volatile (instruction ++ suffix ++ " %[bit], %[ptr]"
195 : [result] "={@ccc}" (-> u8) // LLVM doesn't support u1 flag register return values
196 : [ptr] "*p" (&self.value),
197 [bit] "X" (@as(T, bit))
198 : "cc", "memory"
199 );
200
201253 return @intCast(u1, old_bit);
202254 }
203255
src/AstGen.zig+1-1
......@@ -6601,7 +6601,7 @@ fn asmExpr(
66016601 const asm_source = switch (node_tags[full.ast.template]) {
66026602 .string_literal => try astgen.strLitAsString(main_tokens[full.ast.template]),
66036603 .multiline_string_literal => try astgen.strLitNodeAsString(full.ast.template),
6604 else => return astgen.failNode(node, "assembly code must use string literal syntax", .{}),
6604 else => return astgen.failNode(full.ast.template, "assembly code must use string literal syntax", .{}),
66056605 };
66066606
66076607 // See https://github.com/ziglang/zig/issues/215 and related issues discussing
src/Liveness.zig+69-3
......@@ -24,6 +24,11 @@ const Log2Int = std.math.Log2Int;
2424tomb_bits: []usize,
2525/// Sparse table of specially handled instructions. The value is an index into the `extra`
2626/// array. The meaning of the data depends on the AIR tag.
27/// * `cond_br` - points to a `CondBr` in `extra` at this index.
28/// * `switch_br` - points to a `SwitchBr` in `extra` at this index.
29/// * `asm`, `call` - the value is a set of bits which are the extra tomb bits of operands.
30/// The main tomb bits are still used and the extra ones are starting with the lsb of the
31/// value here.
2732special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32),
2833/// Auxilliary data. The way this data is interpreted is determined contextually.
2934extra: []const u32,
......@@ -67,6 +72,8 @@ pub fn analyze(gpa: *Allocator, air: Air, zir: Zir) Allocator.Error!Liveness {
6772 defer a.extra.deinit(gpa);
6873 defer a.table.deinit(gpa);
6974
75 std.mem.set(usize, a.tomb_bits, 0);
76
7077 const main_body = air.getMainBody();
7178 try a.table.ensureTotalCapacity(gpa, @intCast(u32, main_body.len));
7279 try analyzeWithContext(&a, null, main_body);
......@@ -103,7 +110,7 @@ pub fn clearOperandDeath(l: Liveness, inst: Air.Inst.Index, operand: OperandInt)
103110 const usize_index = (inst * bpi) / @bitSizeOf(usize);
104111 const mask = @as(usize, 1) <<
105112 @intCast(Log2Int(usize), (inst % (@bitSizeOf(usize) / bpi)) * bpi + operand);
106 l.tomb_bits[usize_index] |= mask;
113 l.tomb_bits[usize_index] &= ~mask;
107114}
108115
109116/// Higher level API.
......@@ -298,7 +305,17 @@ fn analyzeInst(
298305 std.mem.copy(Air.Inst.Ref, buf[1..], args);
299306 return trackOperands(a, new_set, inst, main_tomb, buf);
300307 }
301 @panic("TODO: liveness analysis for function call with greater than 2 args");
308 var extra_tombs: ExtraTombs = .{
309 .analysis = a,
310 .new_set = new_set,
311 .inst = inst,
312 .main_tomb = main_tomb,
313 };
314 try extra_tombs.feed(callee);
315 for (args) |arg| {
316 try extra_tombs.feed(arg);
317 }
318 return extra_tombs.finish();
302319 },
303320 .struct_field_ptr => {
304321 const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data;
......@@ -317,7 +334,19 @@ fn analyzeInst(
317334 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
318335 return trackOperands(a, new_set, inst, main_tomb, buf);
319336 }
320 @panic("TODO: liveness analysis for asm with greater than 3 args");
337 var extra_tombs: ExtraTombs = .{
338 .analysis = a,
339 .new_set = new_set,
340 .inst = inst,
341 .main_tomb = main_tomb,
342 };
343 for (outputs) |output| {
344 try extra_tombs.feed(output);
345 }
346 for (args) |arg| {
347 try extra_tombs.feed(arg);
348 }
349 return extra_tombs.finish();
321350 },
322351 .block => {
323352 const extra = a.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload);
......@@ -531,3 +560,40 @@ fn trackOperands(
531560 }
532561 a.storeTombBits(inst, tomb_bits);
533562}
563
564const ExtraTombs = struct {
565 analysis: *Analysis,
566 new_set: ?*std.AutoHashMapUnmanaged(Air.Inst.Index, void),
567 inst: Air.Inst.Index,
568 main_tomb: bool,
569 bit_index: usize = 0,
570 tomb_bits: Bpi = 0,
571 big_tomb_bits: u32 = 0,
572
573 fn feed(et: *ExtraTombs, op_ref: Air.Inst.Ref) !void {
574 const this_bit_index = et.bit_index;
575 assert(this_bit_index < 32); // TODO mechanism for when there are greater than 32 operands
576 et.bit_index += 1;
577 const gpa = et.analysis.gpa;
578 const op_int = @enumToInt(op_ref);
579 if (op_int < Air.Inst.Ref.typed_value_map.len) return;
580 const op_index: Air.Inst.Index = op_int - @intCast(u32, Air.Inst.Ref.typed_value_map.len);
581 const prev = try et.analysis.table.fetchPut(gpa, op_index, {});
582 if (prev == null) {
583 // Death.
584 if (et.new_set) |ns| try ns.putNoClobber(gpa, op_index, {});
585 if (this_bit_index < bpi - 1) {
586 et.tomb_bits |= @as(Bpi, 1) << @intCast(OperandInt, this_bit_index);
587 } else {
588 const big_bit_index = this_bit_index - (bpi - 1);
589 et.big_tomb_bits |= @as(u32, 1) << @intCast(u5, big_bit_index);
590 }
591 }
592 }
593
594 fn finish(et: *ExtraTombs) !void {
595 et.tomb_bits |= @as(Bpi, @boolToInt(et.main_tomb)) << (bpi - 1);
596 et.analysis.storeTombBits(et.inst, et.tomb_bits);
597 try et.analysis.special.put(et.analysis.gpa, et.inst, et.big_tomb_bits);
598 }
599};
src/Sema.zig+167-166
......@@ -258,24 +258,24 @@ pub fn analyzeBody(
258258 .slice_sentinel => try sema.zirSliceSentinel(block, inst),
259259 .slice_start => try sema.zirSliceStart(block, inst),
260260 .str => try sema.zirStr(block, inst),
261 //.switch_block => try sema.zirSwitchBlock(block, inst, false, .none),
262 //.switch_block_multi => try sema.zirSwitchBlockMulti(block, inst, false, .none),
263 //.switch_block_else => try sema.zirSwitchBlock(block, inst, false, .@"else"),
264 //.switch_block_else_multi => try sema.zirSwitchBlockMulti(block, inst, false, .@"else"),
265 //.switch_block_under => try sema.zirSwitchBlock(block, inst, false, .under),
266 //.switch_block_under_multi => try sema.zirSwitchBlockMulti(block, inst, false, .under),
267 //.switch_block_ref => try sema.zirSwitchBlock(block, inst, true, .none),
268 //.switch_block_ref_multi => try sema.zirSwitchBlockMulti(block, inst, true, .none),
269 //.switch_block_ref_else => try sema.zirSwitchBlock(block, inst, true, .@"else"),
270 //.switch_block_ref_else_multi => try sema.zirSwitchBlockMulti(block, inst, true, .@"else"),
271 //.switch_block_ref_under => try sema.zirSwitchBlock(block, inst, true, .under),
272 //.switch_block_ref_under_multi => try sema.zirSwitchBlockMulti(block, inst, true, .under),
273 //.switch_capture => try sema.zirSwitchCapture(block, inst, false, false),
274 //.switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),
275 //.switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),
276 //.switch_capture_multi_ref => try sema.zirSwitchCapture(block, inst, true, true),
277 //.switch_capture_else => try sema.zirSwitchCaptureElse(block, inst, false),
278 //.switch_capture_else_ref => try sema.zirSwitchCaptureElse(block, inst, true),
261 .switch_block => try sema.zirSwitchBlock(block, inst, false, .none),
262 .switch_block_multi => try sema.zirSwitchBlockMulti(block, inst, false, .none),
263 .switch_block_else => try sema.zirSwitchBlock(block, inst, false, .@"else"),
264 .switch_block_else_multi => try sema.zirSwitchBlockMulti(block, inst, false, .@"else"),
265 .switch_block_under => try sema.zirSwitchBlock(block, inst, false, .under),
266 .switch_block_under_multi => try sema.zirSwitchBlockMulti(block, inst, false, .under),
267 .switch_block_ref => try sema.zirSwitchBlock(block, inst, true, .none),
268 .switch_block_ref_multi => try sema.zirSwitchBlockMulti(block, inst, true, .none),
269 .switch_block_ref_else => try sema.zirSwitchBlock(block, inst, true, .@"else"),
270 .switch_block_ref_else_multi => try sema.zirSwitchBlockMulti(block, inst, true, .@"else"),
271 .switch_block_ref_under => try sema.zirSwitchBlock(block, inst, true, .under),
272 .switch_block_ref_under_multi => try sema.zirSwitchBlockMulti(block, inst, true, .under),
273 .switch_capture => try sema.zirSwitchCapture(block, inst, false, false),
274 .switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),
275 .switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),
276 .switch_capture_multi_ref => try sema.zirSwitchCapture(block, inst, true, true),
277 .switch_capture_else => try sema.zirSwitchCaptureElse(block, inst, false),
278 .switch_capture_else_ref => try sema.zirSwitchCaptureElse(block, inst, true),
279279 .type_info => try sema.zirTypeInfo(block, inst),
280280 .size_of => try sema.zirSizeOf(block, inst),
281281 .bit_size_of => try sema.zirBitSizeOf(block, inst),
......@@ -534,7 +534,6 @@ pub fn analyzeBody(
534534 return break_inst;
535535 }
536536 },
537 else => |t| @panic(@tagName(t)),
538537 };
539538 if (sema.typeOf(air_inst).isNoReturn())
540539 return always_noreturn;
......@@ -4110,8 +4109,8 @@ fn analyzeSwitch(
41104109 const body = sema.code.extra[extra_index..][0..body_len];
41114110 extra_index += body_len;
41124111
4112 const item = sema.resolveInst(item_ref);
41134113 // Validation above ensured these will succeed.
4114 const item = sema.resolveInst(item_ref) catch unreachable;
41154114 const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable;
41164115 if (operand_val.eql(item_val)) {
41174116 return sema.resolveBlockBody(block, src, &child_block, body, merges);
......@@ -4132,9 +4131,9 @@ fn analyzeSwitch(
41324131 const body = sema.code.extra[extra_index + 2 * ranges_len ..][0..body_len];
41334132
41344133 for (items) |item_ref| {
4134 const item = sema.resolveInst(item_ref);
41354135 // Validation above ensured these will succeed.
4136 const item = sema.resolveInst(item_ref) catch unreachable;
4137 const item_val = sema.resolveConstValue(&child_block, item.src, item) catch unreachable;
4136 const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable;
41384137 if (operand_val.eql(item_val)) {
41394138 return sema.resolveBlockBody(block, src, &child_block, body, merges);
41404139 }
......@@ -4171,156 +4170,157 @@ fn analyzeSwitch(
41714170
41724171 // TODO when reworking AIR memory layout make multi cases get generated as cases,
41734172 // not as part of the "else" block.
4174 const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len);
4173 return mod.fail(&block.base, src, "TODO rework runtime switch Sema", .{});
4174 //const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len);
41754175
4176 var case_block = child_block.makeSubBlock();
4177 case_block.runtime_loop = null;
4178 case_block.runtime_cond = operand.src;
4179 case_block.runtime_index += 1;
4180 defer case_block.instructions.deinit(gpa);
4176 //var case_block = child_block.makeSubBlock();
4177 //case_block.runtime_loop = null;
4178 //case_block.runtime_cond = operand.src;
4179 //case_block.runtime_index += 1;
4180 //defer case_block.instructions.deinit(gpa);
41814181
4182 var extra_index: usize = special.end;
4182 //var extra_index: usize = special.end;
41834183
4184 var scalar_i: usize = 0;
4185 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
4186 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
4187 extra_index += 1;
4188 const body_len = sema.code.extra[extra_index];
4189 extra_index += 1;
4190 const body = sema.code.extra[extra_index..][0..body_len];
4191 extra_index += body_len;
4192
4193 case_block.instructions.shrinkRetainingCapacity(0);
4194 // We validate these above; these two calls are guaranteed to succeed.
4195 const item = sema.resolveInst(item_ref) catch unreachable;
4196 const item_val = sema.resolveConstValue(&case_block, .unneeded, item) catch unreachable;
4197
4198 _ = try sema.analyzeBody(&case_block, body);
4199
4200 cases[scalar_i] = .{
4201 .item = item_val,
4202 .body = .{ .instructions = try sema.arena.dupe(Air.Inst.Index, case_block.instructions.items) },
4203 };
4204 }
4205
4206 var first_else_body: Body = undefined;
4207 var prev_condbr: ?*Inst.CondBr = null;
4208
4209 var multi_i: usize = 0;
4210 while (multi_i < multi_cases_len) : (multi_i += 1) {
4211 const items_len = sema.code.extra[extra_index];
4212 extra_index += 1;
4213 const ranges_len = sema.code.extra[extra_index];
4214 extra_index += 1;
4215 const body_len = sema.code.extra[extra_index];
4216 extra_index += 1;
4217 const items = sema.code.refSlice(extra_index, items_len);
4218 extra_index += items_len;
4219
4220 case_block.instructions.shrinkRetainingCapacity(0);
4221
4222 var any_ok: ?Air.Inst.Index = null;
4223
4224 for (items) |item_ref| {
4225 const item = sema.resolveInst(item_ref);
4226 _ = try sema.resolveConstValue(&child_block, item.src, item);
4227
4228 const cmp_ok = try case_block.addBinOp(.cmp_eq, operand, item);
4229 if (any_ok) |some| {
4230 any_ok = try case_block.addBinOp(.bool_or, some, cmp_ok);
4231 } else {
4232 any_ok = cmp_ok;
4233 }
4234 }
4235
4236 var range_i: usize = 0;
4237 while (range_i < ranges_len) : (range_i += 1) {
4238 const first_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
4239 extra_index += 1;
4240 const last_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
4241 extra_index += 1;
4242
4243 const item_first = sema.resolveInst(first_ref);
4244 const item_last = sema.resolveInst(last_ref);
4245
4246 _ = try sema.resolveConstValue(&child_block, item_first.src, item_first);
4247 _ = try sema.resolveConstValue(&child_block, item_last.src, item_last);
4184 //var scalar_i: usize = 0;
4185 //while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
4186 // const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
4187 // extra_index += 1;
4188 // const body_len = sema.code.extra[extra_index];
4189 // extra_index += 1;
4190 // const body = sema.code.extra[extra_index..][0..body_len];
4191 // extra_index += body_len;
42484192
4249 // operand >= first and operand <= last
4250 const range_first_ok = try case_block.addBinOp(
4251 .cmp_gte,
4252 operand,
4253 item_first,
4254 );
4255 const range_last_ok = try case_block.addBinOp(
4256 .cmp_lte,
4257 operand,
4258 item_last,
4259 );
4260 const range_ok = try case_block.addBinOp(
4261 .bool_and,
4262 range_first_ok,
4263 range_last_ok,
4264 );
4265 if (any_ok) |some| {
4266 any_ok = try case_block.addBinOp(.bool_or, some, range_ok);
4267 } else {
4268 any_ok = range_ok;
4269 }
4270 }
4193 // case_block.instructions.shrinkRetainingCapacity(0);
4194 // const item = sema.resolveInst(item_ref);
4195 // // We validate these above; these two calls are guaranteed to succeed.
4196 // const item_val = sema.resolveConstValue(&case_block, .unneeded, item) catch unreachable;
42714197
4272 const new_condbr = try sema.arena.create(Inst.CondBr);
4273 new_condbr.* = .{
4274 .base = .{
4275 .tag = .condbr,
4276 .ty = Type.initTag(.noreturn),
4277 .src = src,
4278 },
4279 .condition = any_ok.?,
4280 .then_body = undefined,
4281 .else_body = undefined,
4282 };
4283 try case_block.instructions.append(gpa, &new_condbr.base);
4198 // _ = try sema.analyzeBody(&case_block, body);
42844199
4285 const cond_body: Body = .{
4286 .instructions = try sema.arena.dupe(Air.Inst.Index, case_block.instructions.items),
4287 };
4200 // cases[scalar_i] = .{
4201 // .item = item_val,
4202 // .body = .{ .instructions = try sema.arena.dupe(Air.Inst.Index, case_block.instructions.items) },
4203 // };
4204 //}
42884205
4289 case_block.instructions.shrinkRetainingCapacity(0);
4290 const body = sema.code.extra[extra_index..][0..body_len];
4291 extra_index += body_len;
4292 _ = try sema.analyzeBody(&case_block, body);
4293 new_condbr.then_body = .{
4294 .instructions = try sema.arena.dupe(Air.Inst.Index, case_block.instructions.items),
4295 };
4296 if (prev_condbr) |condbr| {
4297 condbr.else_body = cond_body;
4298 } else {
4299 first_else_body = cond_body;
4300 }
4301 prev_condbr = new_condbr;
4302 }
4303
4304 const final_else_body: Body = blk: {
4305 if (special.body.len != 0) {
4306 case_block.instructions.shrinkRetainingCapacity(0);
4307 _ = try sema.analyzeBody(&case_block, special.body);
4308 const else_body: Body = .{
4309 .instructions = try sema.arena.dupe(Air.Inst.Index, case_block.instructions.items),
4310 };
4311 if (prev_condbr) |condbr| {
4312 condbr.else_body = else_body;
4313 break :blk first_else_body;
4314 } else {
4315 break :blk else_body;
4316 }
4317 } else {
4318 break :blk .{ .instructions = &.{} };
4319 }
4320 };
4206 //var first_else_body: Body = undefined;
4207 //var prev_condbr: ?*Inst.CondBr = null;
43214208
4322 _ = try child_block.addSwitchBr(src, operand, cases, final_else_body);
4323 return sema.analyzeBlockBody(block, src, &child_block, merges);
4209 //var multi_i: usize = 0;
4210 //while (multi_i < multi_cases_len) : (multi_i += 1) {
4211 // const items_len = sema.code.extra[extra_index];
4212 // extra_index += 1;
4213 // const ranges_len = sema.code.extra[extra_index];
4214 // extra_index += 1;
4215 // const body_len = sema.code.extra[extra_index];
4216 // extra_index += 1;
4217 // const items = sema.code.refSlice(extra_index, items_len);
4218 // extra_index += items_len;
4219
4220 // case_block.instructions.shrinkRetainingCapacity(0);
4221
4222 // var any_ok: ?Air.Inst.Index = null;
4223
4224 // for (items) |item_ref| {
4225 // const item = sema.resolveInst(item_ref);
4226 // _ = try sema.resolveConstValue(&child_block, item.src, item);
4227
4228 // const cmp_ok = try case_block.addBinOp(.cmp_eq, operand, item);
4229 // if (any_ok) |some| {
4230 // any_ok = try case_block.addBinOp(.bool_or, some, cmp_ok);
4231 // } else {
4232 // any_ok = cmp_ok;
4233 // }
4234 // }
4235
4236 // var range_i: usize = 0;
4237 // while (range_i < ranges_len) : (range_i += 1) {
4238 // const first_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
4239 // extra_index += 1;
4240 // const last_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
4241 // extra_index += 1;
4242
4243 // const item_first = sema.resolveInst(first_ref);
4244 // const item_last = sema.resolveInst(last_ref);
4245
4246 // _ = try sema.resolveConstValue(&child_block, item_first.src, item_first);
4247 // _ = try sema.resolveConstValue(&child_block, item_last.src, item_last);
4248
4249 // // operand >= first and operand <= last
4250 // const range_first_ok = try case_block.addBinOp(
4251 // .cmp_gte,
4252 // operand,
4253 // item_first,
4254 // );
4255 // const range_last_ok = try case_block.addBinOp(
4256 // .cmp_lte,
4257 // operand,
4258 // item_last,
4259 // );
4260 // const range_ok = try case_block.addBinOp(
4261 // .bool_and,
4262 // range_first_ok,
4263 // range_last_ok,
4264 // );
4265 // if (any_ok) |some| {
4266 // any_ok = try case_block.addBinOp(.bool_or, some, range_ok);
4267 // } else {
4268 // any_ok = range_ok;
4269 // }
4270 // }
4271
4272 // const new_condbr = try sema.arena.create(Inst.CondBr);
4273 // new_condbr.* = .{
4274 // .base = .{
4275 // .tag = .condbr,
4276 // .ty = Type.initTag(.noreturn),
4277 // .src = src,
4278 // },
4279 // .condition = any_ok.?,
4280 // .then_body = undefined,
4281 // .else_body = undefined,
4282 // };
4283 // try case_block.instructions.append(gpa, &new_condbr.base);
4284
4285 // const cond_body: Body = .{
4286 // .instructions = try sema.arena.dupe(Air.Inst.Index, case_block.instructions.items),
4287 // };
4288
4289 // case_block.instructions.shrinkRetainingCapacity(0);
4290 // const body = sema.code.extra[extra_index..][0..body_len];
4291 // extra_index += body_len;
4292 // _ = try sema.analyzeBody(&case_block, body);
4293 // new_condbr.then_body = .{
4294 // .instructions = try sema.arena.dupe(Air.Inst.Index, case_block.instructions.items),
4295 // };
4296 // if (prev_condbr) |condbr| {
4297 // condbr.else_body = cond_body;
4298 // } else {
4299 // first_else_body = cond_body;
4300 // }
4301 // prev_condbr = new_condbr;
4302 //}
4303
4304 //const final_else_body: Body = blk: {
4305 // if (special.body.len != 0) {
4306 // case_block.instructions.shrinkRetainingCapacity(0);
4307 // _ = try sema.analyzeBody(&case_block, special.body);
4308 // const else_body: Body = .{
4309 // .instructions = try sema.arena.dupe(Air.Inst.Index, case_block.instructions.items),
4310 // };
4311 // if (prev_condbr) |condbr| {
4312 // condbr.else_body = else_body;
4313 // break :blk first_else_body;
4314 // } else {
4315 // break :blk else_body;
4316 // }
4317 // } else {
4318 // break :blk .{ .instructions = &.{} };
4319 // }
4320 //};
4321
4322 //_ = try child_block.addSwitchBr(src, operand, cases, final_else_body);
4323 //return sema.analyzeBlockBody(block, src, &child_block, merges);
43244324}
43254325
43264326fn resolveSwitchItemVal(
......@@ -4332,16 +4332,17 @@ fn resolveSwitchItemVal(
43324332 range_expand: Module.SwitchProngSrc.RangeExpand,
43334333) CompileError!TypedValue {
43344334 const item = sema.resolveInst(item_ref);
4335 const item_ty = sema.typeOf(item);
43354336 // Constructing a LazySrcLoc is costly because we only have the switch AST node.
43364337 // Only if we know for sure we need to report a compile error do we resolve the
43374338 // full source locations.
43384339 if (sema.resolveConstValue(block, .unneeded, item)) |val| {
4339 return TypedValue{ .ty = item.ty, .val = val };
4340 return TypedValue{ .ty = item_ty, .val = val };
43404341 } else |err| switch (err) {
43414342 error.NeededSourceLocation => {
43424343 const src = switch_prong_src.resolve(sema.gpa, block.src_decl, switch_node_offset, range_expand);
43434344 return TypedValue{
4344 .ty = item.ty,
4345 .ty = item_ty,
43454346 .val = try sema.resolveConstValue(block, src, item),
43464347 };
43474348 },
src/codegen.zig+69-5
......@@ -452,6 +452,43 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
452452 },
453453 };
454454
455 const BigTomb = struct {
456 function: *Self,
457 inst: Air.Inst.Index,
458 tomb_bits: Liveness.Bpi,
459 big_tomb_bits: u32,
460 bit_index: usize,
461
462 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
463 const this_bit_index = bt.bit_index;
464 bt.bit_index += 1;
465
466 const op_int = @enumToInt(op_ref);
467 if (op_int < Air.Inst.Ref.typed_value_map.len) return;
468 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
469
470 if (this_bit_index < Liveness.bpi - 1) {
471 const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0;
472 if (!dies) return;
473 } else {
474 const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1));
475 const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0;
476 if (!dies) return;
477 }
478 bt.function.processDeath(op_index);
479 }
480
481 fn finishAir(bt: *BigTomb, result: MCValue) void {
482 const is_used = !bt.function.liveness.isUnused(bt.inst);
483 if (is_used) {
484 log.debug("{} => {}", .{ bt.inst, result });
485 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
486 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
487 }
488 bt.function.finishAirBookkeeping();
489 }
490 };
491
455492 const Self = @This();
456493
457494 fn generate(
......@@ -921,8 +958,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
921958 if (!dies) continue;
922959 const op_int = @enumToInt(op);
923960 if (op_int < Air.Inst.Ref.typed_value_map.len) continue;
924 const operand: Air.Inst.Index = op_int - @intCast(u32, Air.Inst.Ref.typed_value_map.len);
925 self.processDeath(operand);
961 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
962 self.processDeath(op_index);
926963 }
927964 const is_used = @truncate(u1, tomb_bits) == 0;
928965 if (is_used) {
......@@ -2739,7 +2776,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27392776 std.mem.copy(Air.Inst.Ref, buf[1..], args);
27402777 return self.finishAir(inst, result, buf);
27412778 }
2742 @panic("TODO: codegen for function call with greater than 2 args");
2779 var bt = try self.iterateBigTomb(inst, 1 + args.len);
2780 bt.feed(callee);
2781 for (args) |arg| {
2782 bt.feed(arg);
2783 }
2784 return bt.finishAir(result);
27432785 }
27442786
27452787 fn airRef(self: *Self, inst: Air.Inst.Index) !void {
......@@ -3651,7 +3693,25 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
36513693 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
36523694 return self.finishAir(inst, result, buf);
36533695 }
3654 @panic("TODO: codegen for asm with greater than 3 args");
3696 var bt = try self.iterateBigTomb(inst, outputs.len + args.len);
3697 for (outputs) |output| {
3698 bt.feed(output);
3699 }
3700 for (args) |arg| {
3701 bt.feed(arg);
3702 }
3703 return bt.finishAir(result);
3704 }
3705
3706 fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
3707 try self.ensureProcessDeathCapacity(operand_count + 1);
3708 return BigTomb{
3709 .function = self,
3710 .inst = inst,
3711 .tomb_bits = self.liveness.getTombBits(inst),
3712 .big_tomb_bits = self.liveness.special.get(inst) orelse 0,
3713 .bit_index = 0,
3714 };
36553715 }
36563716
36573717 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
......@@ -4492,7 +4552,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
44924552 // First section of indexes correspond to a set number of constant values.
44934553 const ref_int = @enumToInt(inst);
44944554 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
4495 return self.genTypedValue(Air.Inst.Ref.typed_value_map[ref_int]);
4555 const tv = Air.Inst.Ref.typed_value_map[ref_int];
4556 if (!tv.ty.hasCodeGenBits()) {
4557 return MCValue{ .none = {} };
4558 }
4559 return self.genTypedValue(tv);
44964560 }
44974561
44984562 // If the type has no codegen bits, no need to store it.
src/print_air.zig+1-1
......@@ -89,7 +89,7 @@ const Writer = struct {
8989 if (w.liveness.isUnused(inst)) {
9090 try s.writeAll(") unused\n");
9191 } else {
92 try s.writeAll("\n");
92 try s.writeAll(")\n");
9393 }
9494 }
9595 }