authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-24 23:09:12-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-25 22:44:18-07:00
loge97157f71c79257ab575598d142f3caa785a2a76
tree0484774fa99b759668a134a06d1bc76216e0b811
parentb68fa9970b5cf5bb5954da476cc8679512ce489b

stage2: codegen for conditional branching

* Move branch-local register and stack allocation metadata to the function-local struct. Conditional branches clone this data in order to restore it after generating machine code for a branch. Branch-local data is now only the instruction table mapping *ir.Inst to MCValue. * Implement conditional branching - Process operand deaths - Handle register and stack allocation metadata * Avoid storing unreferenced or void typed instructions into the branch-local instruction table. * Fix integer types reporting the wrong value for hasCodeGenBits. * Remove the codegen optimization for eliding length-0 jumps. I need to reexamine how this works because it was causing invalid jumps to be emitted.

3 files changed, 235 insertions(+), 115 deletions(-)

src-self-hosted/codegen.zig+227-110
...@@ -273,8 +273,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -273,8 +273,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
273 /// across each runtime branch upon joining.273 /// across each runtime branch upon joining.
274 branch_stack: *std.ArrayList(Branch),274 branch_stack: *std.ArrayList(Branch),
275275
276 /// The key must be canonical register.
277 registers: std.AutoHashMapUnmanaged(Register, *ir.Inst) = .{},
278 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
279 /// Maps offset to what is stored there.
280 stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
281
282 /// Offset from the stack base, representing the end of the stack frame.
283 max_end_stack: u32 = 0,
284 /// Represents the current end stack offset. If there is no existing slot
285 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
286 next_stack_offset: u32 = 0,
287
276 const MCValue = union(enum) {288 const MCValue = union(enum) {
277 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.289 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
290 /// TODO Look into deleting this tag and using `dead` instead, since every use
291 /// of MCValue.none should be instead looking at the type and noticing it is 0 bits.
278 none,292 none,
279 /// Control flow will not allow this value to be observed.293 /// Control flow will not allow this value to be observed.
280 unreach,294 unreach,
...@@ -346,71 +360,55 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -346,71 +360,55 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
346360
347 const Branch = struct {361 const Branch = struct {
348 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},362 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},
349 /// The key must be canonical register.
350 registers: std.AutoHashMapUnmanaged(Register, RegisterAllocation) = .{},
351 free_registers: FreeRegInt = math.maxInt(FreeRegInt),
352
353 /// Maps offset to what is stored there.
354 stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
355 /// Offset from the stack base, representing the end of the stack frame.
356 max_end_stack: u32 = 0,
357 /// Represents the current end stack offset. If there is no existing slot
358 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
359 next_stack_offset: u32 = 0,
360
361 fn markRegUsed(self: *Branch, reg: Register) void {
362 if (FreeRegInt == u0) return;
363 const index = reg.allocIndex() orelse return;
364 const ShiftInt = math.Log2Int(FreeRegInt);
365 const shift = @intCast(ShiftInt, index);
366 self.free_registers &= ~(@as(FreeRegInt, 1) << shift);
367 }
368
369 fn markRegFree(self: *Branch, reg: Register) void {
370 if (FreeRegInt == u0) return;
371 const index = reg.allocIndex() orelse return;
372 const ShiftInt = math.Log2Int(FreeRegInt);
373 const shift = @intCast(ShiftInt, index);
374 self.free_registers |= @as(FreeRegInt, 1) << shift;
375 }
376
377 /// Before calling, must ensureCapacity + 1 on branch.registers.
378 /// Returns `null` if all registers are allocated.
379 fn allocReg(self: *Branch, inst: *ir.Inst) ?Register {
380 const free_index = @ctz(FreeRegInt, self.free_registers);
381 if (free_index >= callee_preserved_regs.len) {
382 return null;
383 }
384 self.free_registers &= ~(@as(FreeRegInt, 1) << free_index);
385 const reg = callee_preserved_regs[free_index];
386 self.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst });
387 log.debug("alloc {} => {*}", .{reg, inst});
388 return reg;
389 }
390
391 /// Does not track the register.
392 fn findUnusedReg(self: *Branch) ?Register {
393 const free_index = @ctz(FreeRegInt, self.free_registers);
394 if (free_index >= callee_preserved_regs.len) {
395 return null;
396 }
397 return callee_preserved_regs[free_index];
398 }
399363
400 fn deinit(self: *Branch, gpa: *Allocator) void {364 fn deinit(self: *Branch, gpa: *Allocator) void {
401 self.inst_table.deinit(gpa);365 self.inst_table.deinit(gpa);
402 self.registers.deinit(gpa);
403 self.stack.deinit(gpa);
404 self.* = undefined;366 self.* = undefined;
405 }367 }
406 };368 };
407369
408 const RegisterAllocation = struct {370 fn markRegUsed(self: *Self, reg: Register) void {
409 inst: *ir.Inst,371 if (FreeRegInt == u0) return;
410 };372 const index = reg.allocIndex() orelse return;
373 const ShiftInt = math.Log2Int(FreeRegInt);
374 const shift = @intCast(ShiftInt, index);
375 self.free_registers &= ~(@as(FreeRegInt, 1) << shift);
376 }
377
378 fn markRegFree(self: *Self, reg: Register) void {
379 if (FreeRegInt == u0) return;
380 const index = reg.allocIndex() orelse return;
381 const ShiftInt = math.Log2Int(FreeRegInt);
382 const shift = @intCast(ShiftInt, index);
383 self.free_registers |= @as(FreeRegInt, 1) << shift;
384 }
385
386 /// Before calling, must ensureCapacity + 1 on self.registers.
387 /// Returns `null` if all registers are allocated.
388 fn allocReg(self: *Self, inst: *ir.Inst) ?Register {
389 const free_index = @ctz(FreeRegInt, self.free_registers);
390 if (free_index >= callee_preserved_regs.len) {
391 return null;
392 }
393 self.free_registers &= ~(@as(FreeRegInt, 1) << free_index);
394 const reg = callee_preserved_regs[free_index];
395 self.registers.putAssumeCapacityNoClobber(reg, inst);
396 log.debug("alloc {} => {*}", .{reg, inst});
397 return reg;
398 }
399
400 /// Does not track the register.
401 fn findUnusedReg(self: *Self) ?Register {
402 const free_index = @ctz(FreeRegInt, self.free_registers);
403 if (free_index >= callee_preserved_regs.len) {
404 return null;
405 }
406 return callee_preserved_regs[free_index];
407 }
411408
412 const StackAllocation = struct {409 const StackAllocation = struct {
413 inst: *ir.Inst,410 inst: *ir.Inst,
411 /// TODO do we need size? should be determined by inst.ty.abiSize()
414 size: u32,412 size: u32,
415 };413 };
416414
...@@ -435,8 +433,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -435,8 +433,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
435 branch_stack.items[0].deinit(bin_file.allocator);433 branch_stack.items[0].deinit(bin_file.allocator);
436 branch_stack.deinit();434 branch_stack.deinit();
437 }435 }
438 const branch = try branch_stack.addOne();436 try branch_stack.append(.{});
439 branch.* = .{};
440437
441 const src_data: struct {lbrace_src: usize, rbrace_src: usize, source: []const u8} = blk: {438 const src_data: struct {lbrace_src: usize, rbrace_src: usize, source: []const u8} = blk: {
442 if (module_fn.owner_decl.scope.cast(Module.Scope.File)) |scope_file| {439 if (module_fn.owner_decl.scope.cast(Module.Scope.File)) |scope_file| {
...@@ -476,6 +473,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -476,6 +473,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
476 .rbrace_src = src_data.rbrace_src,473 .rbrace_src = src_data.rbrace_src,
477 .source = src_data.source,474 .source = src_data.source,
478 };475 };
476 defer function.registers.deinit(bin_file.allocator);
477 defer function.stack.deinit(bin_file.allocator);
479 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);478 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
480479
481 var call_info = function.resolveCallingConventionValues(src, fn_type) catch |err| switch (err) {480 var call_info = function.resolveCallingConventionValues(src, fn_type) catch |err| switch (err) {
...@@ -487,7 +486,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -487,7 +486,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
487 function.args = call_info.args;486 function.args = call_info.args;
488 function.ret_mcv = call_info.return_value;487 function.ret_mcv = call_info.return_value;
489 function.stack_align = call_info.stack_align;488 function.stack_align = call_info.stack_align;
490 branch.max_end_stack = call_info.stack_byte_count;489 function.max_end_stack = call_info.stack_byte_count;
491490
492 function.gen() catch |err| switch (err) {491 function.gen() catch |err| switch (err) {
493 error.CodegenFail => return Result{ .fail = function.err_msg.? },492 error.CodegenFail => return Result{ .fail = function.err_msg.? },
...@@ -523,7 +522,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -523,7 +522,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
523 try self.dbgSetPrologueEnd();522 try self.dbgSetPrologueEnd();
524 try self.genBody(self.mod_fn.analysis.success);523 try self.genBody(self.mod_fn.analysis.success);
525524
526 const stack_end = self.branch_stack.items[0].max_end_stack;525 const stack_end = self.max_end_stack;
527 if (stack_end > math.maxInt(i32))526 if (stack_end > math.maxInt(i32))
528 return self.fail(self.src, "too much stack used in call parameters", .{});527 return self.fail(self.src, "too much stack used in call parameters", .{});
529 const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);528 const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
...@@ -580,13 +579,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -580,13 +579,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
580 }579 }
581580
582 fn genBody(self: *Self, body: ir.Body) InnerError!void {581 fn genBody(self: *Self, body: ir.Body) InnerError!void {
583 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
584 const inst_table = &branch.inst_table;
585 for (body.instructions) |inst| {582 for (body.instructions) |inst| {
583 try self.ensureProcessDeathCapacity(@popCount(@TypeOf(inst.deaths), inst.deaths));
584
586 const mcv = try self.genFuncInst(inst);585 const mcv = try self.genFuncInst(inst);
587 log.debug("{*} => {}", .{inst, mcv});586 if (!inst.isUnused()) {
588 // TODO don't put void or dead things in here587 log.debug("{*} => {}", .{inst, mcv});
589 try inst_table.putNoClobber(self.gpa, inst, mcv);588 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
589 try branch.inst_table.putNoClobber(self.gpa, inst, mcv);
590 }
590591
591 var i: ir.Inst.DeathsBitIndex = 0;592 var i: ir.Inst.DeathsBitIndex = 0;
592 while (inst.getOperand(i)) |operand| : (i += 1) {593 while (inst.getOperand(i)) |operand| : (i += 1) {
...@@ -628,21 +629,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -628,21 +629,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
628 self.dbg_line.appendAssumeCapacity(DW.LNS_copy);629 self.dbg_line.appendAssumeCapacity(DW.LNS_copy);
629 }630 }
630631
632 /// Asserts there is already capacity to insert into top branch inst_table.
631 fn processDeath(self: *Self, inst: *ir.Inst) void {633 fn processDeath(self: *Self, inst: *ir.Inst) void {
634 if (inst.tag == .constant) return; // Constants are immortal.
635 const prev_value = self.getResolvedInstValue(inst);
632 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];636 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
633 const entry = branch.inst_table.getEntry(inst) orelse return;637 branch.inst_table.putAssumeCapacity(inst, .dead);
634 const prev_value = entry.value;
635 entry.value = .dead;
636 switch (prev_value) {638 switch (prev_value) {
637 .register => |reg| {639 .register => |reg| {
638 const canon_reg = toCanonicalReg(reg);640 const canon_reg = toCanonicalReg(reg);
639 _ = branch.registers.remove(canon_reg);641 _ = self.registers.remove(canon_reg);
640 branch.markRegFree(canon_reg);642 self.markRegFree(canon_reg);
641 },643 },
642 else => {}, // TODO process stack allocation death644 else => {}, // TODO process stack allocation death
643 }645 }
644 }646 }
645647
648 fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
649 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
650 try table.ensureCapacity(self.gpa, table.items().len + additional_count);
651 }
652
646 /// Adds a Type to the .debug_info at the current position. The bytes will be populated later,653 /// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
647 /// after codegen for this symbol is done.654 /// after codegen for this symbol is done.
648 fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {655 fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
...@@ -705,13 +712,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -705,13 +712,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
705 fn allocMem(self: *Self, inst: *ir.Inst, abi_size: u32, abi_align: u32) !u32 {712 fn allocMem(self: *Self, inst: *ir.Inst, abi_size: u32, abi_align: u32) !u32 {
706 if (abi_align > self.stack_align)713 if (abi_align > self.stack_align)
707 self.stack_align = abi_align;714 self.stack_align = abi_align;
708 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
709 // TODO find a free slot instead of always appending715 // TODO find a free slot instead of always appending
710 const offset = mem.alignForwardGeneric(u32, branch.next_stack_offset, abi_align);716 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align);
711 branch.next_stack_offset = offset + abi_size;717 self.next_stack_offset = offset + abi_size;
712 if (branch.next_stack_offset > branch.max_end_stack)718 if (self.next_stack_offset > self.max_end_stack)
713 branch.max_end_stack = branch.next_stack_offset;719 self.max_end_stack = self.next_stack_offset;
714 try branch.stack.putNoClobber(self.gpa, offset, .{720 try self.stack.putNoClobber(self.gpa, offset, .{
715 .inst = inst,721 .inst = inst,
716 .size = abi_size,722 .size = abi_size,
717 });723 });
...@@ -737,15 +743,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -737,15 +743,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
737 const abi_align = elem_ty.abiAlignment(self.target.*);743 const abi_align = elem_ty.abiAlignment(self.target.*);
738 if (abi_align > self.stack_align)744 if (abi_align > self.stack_align)
739 self.stack_align = abi_align;745 self.stack_align = abi_align;
740 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
741746
742 if (reg_ok) {747 if (reg_ok) {
743 // Make sure the type can fit in a register before we try to allocate one.748 // Make sure the type can fit in a register before we try to allocate one.
744 const ptr_bits = arch.ptrBitWidth();749 const ptr_bits = arch.ptrBitWidth();
745 const ptr_bytes: u64 = @divExact(ptr_bits, 8);750 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
746 if (abi_size <= ptr_bytes) {751 if (abi_size <= ptr_bytes) {
747 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);752 try self.registers.ensureCapacity(self.gpa, self.registers.items().len + 1);
748 if (branch.allocReg(inst)) |reg| {753 if (self.allocReg(inst)) |reg| {
749 return MCValue{ .register = registerAlias(reg, abi_size) };754 return MCValue{ .register = registerAlias(reg, abi_size) };
750 }755 }
751 }756 }
...@@ -758,20 +763,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -758,20 +763,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
758 /// allocated. A second call to `copyToTmpRegister` may return the same register.763 /// allocated. A second call to `copyToTmpRegister` may return the same register.
759 /// This can have a side effect of spilling instructions to the stack to free up a register.764 /// This can have a side effect of spilling instructions to the stack to free up a register.
760 fn copyToTmpRegister(self: *Self, src: usize, mcv: MCValue) !Register {765 fn copyToTmpRegister(self: *Self, src: usize, mcv: MCValue) !Register {
761 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];766 const reg = self.findUnusedReg() orelse b: {
762
763 const reg = branch.findUnusedReg() orelse b: {
764 // We'll take over the first register. Move the instruction that was previously767 // We'll take over the first register. Move the instruction that was previously
765 // there to a stack allocation.768 // there to a stack allocation.
766 const reg = callee_preserved_regs[0];769 const reg = callee_preserved_regs[0];
767 const regs_entry = branch.registers.remove(reg).?;770 const regs_entry = self.registers.remove(reg).?;
768 const spilled_inst = regs_entry.value.inst;771 const spilled_inst = regs_entry.value;
769772
770 const stack_mcv = try self.allocRegOrMem(spilled_inst, false);773 const stack_mcv = try self.allocRegOrMem(spilled_inst, false);
771 const inst_entry = branch.inst_table.getEntry(spilled_inst).?;774 const reg_mcv = self.getResolvedInstValue(spilled_inst);
772 const reg_mcv = inst_entry.value;
773 assert(reg == toCanonicalReg(reg_mcv.register));775 assert(reg == toCanonicalReg(reg_mcv.register));
774 inst_entry.value = stack_mcv;776 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
777 try branch.inst_table.put(self.gpa, spilled_inst, stack_mcv);
775 try self.genSetStack(src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);778 try self.genSetStack(src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);
776779
777 break :b reg;780 break :b reg;
...@@ -784,22 +787,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -784,22 +787,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
784 /// `reg_owner` is the instruction that gets associated with the register in the register table.787 /// `reg_owner` is the instruction that gets associated with the register in the register table.
785 /// This can have a side effect of spilling instructions to the stack to free up a register.788 /// This can have a side effect of spilling instructions to the stack to free up a register.
786 fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {789 fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {
787 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];790 try self.registers.ensureCapacity(self.gpa, self.registers.items().len + 1);
788 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
789791
790 const reg = branch.allocReg(reg_owner) orelse b: {792 const reg = self.allocReg(reg_owner) orelse b: {
791 // We'll take over the first register. Move the instruction that was previously793 // We'll take over the first register. Move the instruction that was previously
792 // there to a stack allocation.794 // there to a stack allocation.
793 const reg = callee_preserved_regs[0];795 const reg = callee_preserved_regs[0];
794 const regs_entry = branch.registers.getEntry(reg).?;796 const regs_entry = self.registers.getEntry(reg).?;
795 const spilled_inst = regs_entry.value.inst;797 const spilled_inst = regs_entry.value;
796 regs_entry.value = .{ .inst = reg_owner };798 regs_entry.value = reg_owner;
797799
798 const stack_mcv = try self.allocRegOrMem(spilled_inst, false);800 const stack_mcv = try self.allocRegOrMem(spilled_inst, false);
799 const inst_entry = branch.inst_table.getEntry(spilled_inst).?;801 const reg_mcv = self.getResolvedInstValue(spilled_inst);
800 const reg_mcv = inst_entry.value;
801 assert(reg == toCanonicalReg(reg_mcv.register));802 assert(reg == toCanonicalReg(reg_mcv.register));
802 inst_entry.value = stack_mcv;803 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
804 try branch.inst_table.put(self.gpa, spilled_inst, stack_mcv);
803 try self.genSetStack(reg_owner.src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);805 try self.genSetStack(reg_owner.src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);
804806
805 break :b reg;807 break :b reg;
...@@ -934,9 +936,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -934,9 +936,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
934 .register => |reg| {936 .register => |reg| {
935 // If it's in the registers table, need to associate the register with the937 // If it's in the registers table, need to associate the register with the
936 // new instruction.938 // new instruction.
937 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];939 if (self.registers.getEntry(toCanonicalReg(reg))) |entry| {
938 if (branch.registers.getEntry(toCanonicalReg(reg))) |entry| {940 entry.value = inst;
939 entry.value = .{ .inst = inst };
940 }941 }
941 log.debug("reusing {} => {*}", .{reg, inst});942 log.debug("reusing {} => {*}", .{reg, inst});
942 },943 },
...@@ -1231,8 +1232,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1231,8 +1232,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1231 if (inst.base.isUnused())1232 if (inst.base.isUnused())
1232 return MCValue.dead;1233 return MCValue.dead;
12331234
1234 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];1235 try self.registers.ensureCapacity(self.gpa, self.registers.items().len + 1);
1235 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
12361236
1237 const result = self.args[self.arg_index];1237 const result = self.args[self.arg_index];
1238 self.arg_index += 1;1238 self.arg_index += 1;
...@@ -1240,8 +1240,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1240,8 +1240,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1240 const name_with_null = inst.name[0..mem.lenZ(inst.name) + 1];1240 const name_with_null = inst.name[0..mem.lenZ(inst.name) + 1];
1241 switch (result) {1241 switch (result) {
1242 .register => |reg| {1242 .register => |reg| {
1243 branch.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), .{ .inst = &inst.base });1243 self.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), &inst.base);
1244 branch.markRegUsed(reg);1244 self.markRegUsed(reg);
12451245
1246 try self.dbg_info.ensureCapacity(self.dbg_info.items.len + 8 + name_with_null.len);1246 try self.dbg_info.ensureCapacity(self.dbg_info.items.len + 8 + name_with_null.len);
1247 self.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);1247 self.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
...@@ -1536,13 +1536,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1536,13 +1536,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
15361536
1537 fn genDbgStmt(self: *Self, inst: *ir.Inst.NoOp) !MCValue {1537 fn genDbgStmt(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
1538 try self.dbgAdvancePCAndLine(inst.base.src);1538 try self.dbgAdvancePCAndLine(inst.base.src);
1539 return MCValue.none;1539 assert(inst.base.isUnused());
1540 return MCValue.dead;
1540 }1541 }
15411542
1542 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {1543 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {
1543 const cond = try self.resolveInst(inst.condition);1544 const cond = try self.resolveInst(inst.condition);
15441545
1545 // TODO deal with liveness / deaths condbr's then_entry_deaths and else_entry_deaths
1546 const reloc: Reloc = switch (arch) {1546 const reloc: Reloc = switch (arch) {
1547 .i386, .x86_64 => reloc: {1547 .i386, .x86_64 => reloc: {
1548 try self.code.ensureCapacity(self.code.items.len + 6);1548 try self.code.ensureCapacity(self.code.items.len + 6);
...@@ -1595,9 +1595,117 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1595,9 +1595,117 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1595 },1595 },
1596 else => return self.fail(inst.base.src, "TODO implement condbr {}", .{ self.target.cpu.arch }),1596 else => return self.fail(inst.base.src, "TODO implement condbr {}", .{ self.target.cpu.arch }),
1597 };1597 };
1598
1599 // Capture the state of register and stack allocation state so that we can revert to it.
1600 const parent_next_stack_offset = self.next_stack_offset;
1601 const parent_free_registers = self.free_registers;
1602 var parent_stack = try self.stack.clone(self.gpa);
1603 defer parent_stack.deinit(self.gpa);
1604 var parent_registers = try self.registers.clone(self.gpa);
1605 defer parent_registers.deinit(self.gpa);
1606
1607 try self.branch_stack.append(.{});
1608
1609 const then_deaths = inst.thenDeaths();
1610 try self.ensureProcessDeathCapacity(then_deaths.len);
1611 for (then_deaths) |operand| {
1612 self.processDeath(operand);
1613 }
1598 try self.genBody(inst.then_body);1614 try self.genBody(inst.then_body);
1615
1616 // Revert to the previous register and stack allocation state.
1617
1618 var saved_then_branch = self.branch_stack.pop();
1619 defer saved_then_branch.deinit(self.gpa);
1620
1621 self.registers.deinit(self.gpa);
1622 self.registers = parent_registers;
1623 parent_registers = .{};
1624
1625 self.stack.deinit(self.gpa);
1626 self.stack = parent_stack;
1627 parent_stack = .{};
1628
1629 self.next_stack_offset = parent_next_stack_offset;
1630 self.free_registers = parent_free_registers;
1631
1599 try self.performReloc(inst.base.src, reloc);1632 try self.performReloc(inst.base.src, reloc);
1633 const else_branch = self.branch_stack.addOneAssumeCapacity();
1634 else_branch.* = .{};
1635
1636 const else_deaths = inst.elseDeaths();
1637 try self.ensureProcessDeathCapacity(else_deaths.len);
1638 for (else_deaths) |operand| {
1639 self.processDeath(operand);
1640 }
1600 try self.genBody(inst.else_body);1641 try self.genBody(inst.else_body);
1642
1643 // At this point, each branch will possibly have conflicting values for where
1644 // each instruction is stored. They agree, however, on which instructions are alive/dead.
1645 // We use the first ("then") branch as canonical, and here emit
1646 // instructions into the second ("else") branch to make it conform.
1647 // We continue respect the data structure semantic guarantees of the else_branch so
1648 // that we can use all the code emitting abstractions. This is why at the bottom we
1649 // assert that parent_branch.free_registers equals the saved_then_branch.free_registers
1650 // rather than assigning it.
1651 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2];
1652 try parent_branch.inst_table.ensureCapacity(self.gpa, parent_branch.inst_table.items().len +
1653 else_branch.inst_table.items().len);
1654 for (else_branch.inst_table.items()) |else_entry| {
1655 const canon_mcv = if (saved_then_branch.inst_table.remove(else_entry.key)) |then_entry| blk: {
1656 // The instruction's MCValue is overridden in both branches.
1657 parent_branch.inst_table.putAssumeCapacity(else_entry.key, then_entry.value);
1658 if (else_entry.value == .dead) {
1659 assert(then_entry.value == .dead);
1660 continue;
1661 }
1662 break :blk then_entry.value;
1663 } else blk: {
1664 if (else_entry.value == .dead)
1665 continue;
1666 // The instruction is only overridden in the else branch.
1667 var i: usize = self.branch_stack.items.len - 2;
1668 while (true) {
1669 i -= 1;
1670 if (self.branch_stack.items[i].inst_table.get(else_entry.key)) |mcv| {
1671 assert(mcv != .dead);
1672 break :blk mcv;
1673 }
1674 }
1675 };
1676 log.debug("consolidating else_entry {*} {}=>{}", .{else_entry.key, else_entry.value, canon_mcv});
1677 // TODO make sure the destination stack offset / register does not already have something
1678 // going on there.
1679 try self.setRegOrMem(inst.base.src, else_entry.key.ty, canon_mcv, else_entry.value);
1680 // TODO track the new register / stack allocation
1681 }
1682 try parent_branch.inst_table.ensureCapacity(self.gpa, parent_branch.inst_table.items().len +
1683 saved_then_branch.inst_table.items().len);
1684 for (saved_then_branch.inst_table.items()) |then_entry| {
1685 // We already deleted the items from this table that matched the else_branch.
1686 // So these are all instructions that are only overridden in the then branch.
1687 parent_branch.inst_table.putAssumeCapacity(then_entry.key, then_entry.value);
1688 if (then_entry.value == .dead)
1689 continue;
1690 const parent_mcv = blk: {
1691 var i: usize = self.branch_stack.items.len - 2;
1692 while (true) {
1693 i -= 1;
1694 if (self.branch_stack.items[i].inst_table.get(then_entry.key)) |mcv| {
1695 assert(mcv != .dead);
1696 break :blk mcv;
1697 }
1698 }
1699 };
1700 log.debug("consolidating then_entry {*} {}=>{}", .{then_entry.key, parent_mcv, then_entry.value});
1701 // TODO make sure the destination stack offset / register does not already have something
1702 // going on there.
1703 try self.setRegOrMem(inst.base.src, then_entry.key.ty, parent_mcv, then_entry.value);
1704 // TODO track the new register / stack allocation
1705 }
1706
1707 self.branch_stack.pop().deinit(self.gpa);
1708
1601 return MCValue.unreach;1709 return MCValue.unreach;
1602 }1710 }
16031711
...@@ -1671,11 +1779,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1671,11 +1779,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1671 switch (reloc) {1779 switch (reloc) {
1672 .rel32 => |pos| {1780 .rel32 => |pos| {
1673 const amt = self.code.items.len - (pos + 4);1781 const amt = self.code.items.len - (pos + 4);
1674 // If it wouldn't jump at all, elide it.1782 // Here it would be tempting to implement testing for amt == 0 and then elide the
1675 if (amt == 0) {1783 // jump. However, that will cause a problem because other jumps may assume that they
1676 self.code.items.len -= 5;1784 // can jump to this code. Or maybe I didn't understand something when I was debugging.
1677 return;1785 // It could be worth another look. Anyway, that's why that isn't done here. Probably the
1678 }1786 // best place to elide jumps will be in semantic analysis, by inlining blocks that only
1787 // only have 1 break instruction.
1679 const s32_amt = math.cast(i32, amt) catch1788 const s32_amt = math.cast(i32, amt) catch
1680 return self.fail(src, "unable to perform relocation: jump too far", .{});1789 return self.fail(src, "unable to perform relocation: jump too far", .{});
1681 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);1790 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
...@@ -2280,8 +2389,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2280,8 +2389,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2280 }2389 }
22812390
2282 fn resolveInst(self: *Self, inst: *ir.Inst) !MCValue {2391 fn resolveInst(self: *Self, inst: *ir.Inst) !MCValue {
2392 // If the type has no codegen bits, no need to store it.
2393 if (!inst.ty.hasCodeGenBits())
2394 return MCValue.none;
2395
2283 // Constants have static lifetimes, so they are always memoized in the outer most table.2396 // Constants have static lifetimes, so they are always memoized in the outer most table.
2284 if (inst.cast(ir.Inst.Constant)) |const_inst| {2397 if (inst.castTag(.constant)) |const_inst| {
2285 const branch = &self.branch_stack.items[0];2398 const branch = &self.branch_stack.items[0];
2286 const gop = try branch.inst_table.getOrPut(self.gpa, inst);2399 const gop = try branch.inst_table.getOrPut(self.gpa, inst);
2287 if (!gop.found_existing) {2400 if (!gop.found_existing) {
...@@ -2290,6 +2403,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2290,6 +2403,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2290 return gop.entry.value;2403 return gop.entry.value;
2291 }2404 }
22922405
2406 return self.getResolvedInstValue(inst);
2407 }
2408
2409 fn getResolvedInstValue(self: *Self, inst: *ir.Inst) MCValue {
2293 // Treat each stack item as a "layer" on top of the previous one.2410 // Treat each stack item as a "layer" on top of the previous one.
2294 var i: usize = self.branch_stack.items.len;2411 var i: usize = self.branch_stack.items.len;
2295 while (true) {2412 while (true) {
src-self-hosted/link/Elf.zig+6-3
...@@ -1640,9 +1640,12 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {...@@ -1640,9 +1640,12 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
1640 else => false,1640 else => false,
1641 };1641 };
1642 if (is_fn) {1642 if (is_fn) {
1643 //if (mem.eql(u8, mem.spanZ(decl.name), "add")) {1643 {
1644 // typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*);1644 //if (mem.eql(u8, mem.spanZ(decl.name), "add")) {
1645 //}1645 //}
1646 std.debug.print("\n{}\n", .{decl.name});
1647 typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*);
1648 }
16461649
1647 // For functions we need to add a prologue to the debug line program.1650 // For functions we need to add a prologue to the debug line program.
1648 try dbg_line_buffer.ensureCapacity(26);1651 try dbg_line_buffer.ensureCapacity(26);
src-self-hosted/type.zig+2-2
...@@ -771,8 +771,8 @@ pub const Type = extern union {...@@ -771,8 +771,8 @@ pub const Type = extern union {
771 .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,771 .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,
772 .array_u8 => self.arrayLen() != 0,772 .array_u8 => self.arrayLen() != 0,
773 .array_sentinel, .single_const_pointer, .single_mut_pointer, .many_const_pointer, .many_mut_pointer, .c_const_pointer, .c_mut_pointer, .const_slice, .mut_slice, .pointer => self.elemType().hasCodeGenBits(),773 .array_sentinel, .single_const_pointer, .single_mut_pointer, .many_const_pointer, .many_mut_pointer, .c_const_pointer, .c_mut_pointer, .const_slice, .mut_slice, .pointer => self.elemType().hasCodeGenBits(),
774 .int_signed => self.cast(Payload.IntSigned).?.bits == 0,774 .int_signed => self.cast(Payload.IntSigned).?.bits != 0,
775 .int_unsigned => self.cast(Payload.IntUnsigned).?.bits == 0,775 .int_unsigned => self.cast(Payload.IntUnsigned).?.bits != 0,
776776
777 .error_union => {777 .error_union => {
778 const payload = self.cast(Payload.ErrorUnion).?;778 const payload = self.cast(Payload.ErrorUnion).?;