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 {
273273 /// across each runtime branch upon joining.
274274 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
276288 const MCValue = union(enum) {
277289 /// 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.
278292 none,
279293 /// Control flow will not allow this value to be observed.
280294 unreach,
......@@ -346,71 +360,55 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
346360
347361 const Branch = struct {
348362 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
400364 fn deinit(self: *Branch, gpa: *Allocator) void {
401365 self.inst_table.deinit(gpa);
402 self.registers.deinit(gpa);
403 self.stack.deinit(gpa);
404366 self.* = undefined;
405367 }
406368 };
407369
408 const RegisterAllocation = struct {
409 inst: *ir.Inst,
410 };
370 fn markRegUsed(self: *Self, reg: Register) void {
371 if (FreeRegInt == u0) return;
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
412409 const StackAllocation = struct {
413410 inst: *ir.Inst,
411 /// TODO do we need size? should be determined by inst.ty.abiSize()
414412 size: u32,
415413 };
416414
......@@ -435,8 +433,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
435433 branch_stack.items[0].deinit(bin_file.allocator);
436434 branch_stack.deinit();
437435 }
438 const branch = try branch_stack.addOne();
439 branch.* = .{};
436 try branch_stack.append(.{});
440437
441438 const src_data: struct {lbrace_src: usize, rbrace_src: usize, source: []const u8} = blk: {
442439 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 {
476473 .rbrace_src = src_data.rbrace_src,
477474 .source = src_data.source,
478475 };
476 defer function.registers.deinit(bin_file.allocator);
477 defer function.stack.deinit(bin_file.allocator);
479478 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
480479
481480 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 {
487486 function.args = call_info.args;
488487 function.ret_mcv = call_info.return_value;
489488 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
492491 function.gen() catch |err| switch (err) {
493492 error.CodegenFail => return Result{ .fail = function.err_msg.? },
......@@ -523,7 +522,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
523522 try self.dbgSetPrologueEnd();
524523 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;
527526 if (stack_end > math.maxInt(i32))
528527 return self.fail(self.src, "too much stack used in call parameters", .{});
529528 const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
......@@ -580,13 +579,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
580579 }
581580
582581 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;
585582 for (body.instructions) |inst| {
583 try self.ensureProcessDeathCapacity(@popCount(@TypeOf(inst.deaths), inst.deaths));
584
586585 const mcv = try self.genFuncInst(inst);
587 log.debug("{*} => {}", .{inst, mcv});
588 // TODO don't put void or dead things in here
589 try inst_table.putNoClobber(self.gpa, inst, mcv);
586 if (!inst.isUnused()) {
587 log.debug("{*} => {}", .{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
591592 var i: ir.Inst.DeathsBitIndex = 0;
592593 while (inst.getOperand(i)) |operand| : (i += 1) {
......@@ -628,21 +629,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
628629 self.dbg_line.appendAssumeCapacity(DW.LNS_copy);
629630 }
630631
632 /// Asserts there is already capacity to insert into top branch inst_table.
631633 fn processDeath(self: *Self, inst: *ir.Inst) void {
634 if (inst.tag == .constant) return; // Constants are immortal.
635 const prev_value = self.getResolvedInstValue(inst);
632636 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
633 const entry = branch.inst_table.getEntry(inst) orelse return;
634 const prev_value = entry.value;
635 entry.value = .dead;
637 branch.inst_table.putAssumeCapacity(inst, .dead);
636638 switch (prev_value) {
637639 .register => |reg| {
638640 const canon_reg = toCanonicalReg(reg);
639 _ = branch.registers.remove(canon_reg);
640 branch.markRegFree(canon_reg);
641 _ = self.registers.remove(canon_reg);
642 self.markRegFree(canon_reg);
641643 },
642644 else => {}, // TODO process stack allocation death
643645 }
644646 }
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
646653 /// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
647654 /// after codegen for this symbol is done.
648655 fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
......@@ -705,13 +712,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
705712 fn allocMem(self: *Self, inst: *ir.Inst, abi_size: u32, abi_align: u32) !u32 {
706713 if (abi_align > self.stack_align)
707714 self.stack_align = abi_align;
708 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
709715 // TODO find a free slot instead of always appending
710 const offset = mem.alignForwardGeneric(u32, branch.next_stack_offset, abi_align);
711 branch.next_stack_offset = offset + abi_size;
712 if (branch.next_stack_offset > branch.max_end_stack)
713 branch.max_end_stack = branch.next_stack_offset;
714 try branch.stack.putNoClobber(self.gpa, offset, .{
716 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align);
717 self.next_stack_offset = offset + abi_size;
718 if (self.next_stack_offset > self.max_end_stack)
719 self.max_end_stack = self.next_stack_offset;
720 try self.stack.putNoClobber(self.gpa, offset, .{
715721 .inst = inst,
716722 .size = abi_size,
717723 });
......@@ -737,15 +743,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
737743 const abi_align = elem_ty.abiAlignment(self.target.*);
738744 if (abi_align > self.stack_align)
739745 self.stack_align = abi_align;
740 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
741746
742747 if (reg_ok) {
743748 // Make sure the type can fit in a register before we try to allocate one.
744749 const ptr_bits = arch.ptrBitWidth();
745750 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
746751 if (abi_size <= ptr_bytes) {
747 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
748 if (branch.allocReg(inst)) |reg| {
752 try self.registers.ensureCapacity(self.gpa, self.registers.items().len + 1);
753 if (self.allocReg(inst)) |reg| {
749754 return MCValue{ .register = registerAlias(reg, abi_size) };
750755 }
751756 }
......@@ -758,20 +763,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
758763 /// allocated. A second call to `copyToTmpRegister` may return the same register.
759764 /// This can have a side effect of spilling instructions to the stack to free up a register.
760765 fn copyToTmpRegister(self: *Self, src: usize, mcv: MCValue) !Register {
761 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
762
763 const reg = branch.findUnusedReg() orelse b: {
766 const reg = self.findUnusedReg() orelse b: {
764767 // We'll take over the first register. Move the instruction that was previously
765768 // there to a stack allocation.
766769 const reg = callee_preserved_regs[0];
767 const regs_entry = branch.registers.remove(reg).?;
768 const spilled_inst = regs_entry.value.inst;
770 const regs_entry = self.registers.remove(reg).?;
771 const spilled_inst = regs_entry.value;
769772
770773 const stack_mcv = try self.allocRegOrMem(spilled_inst, false);
771 const inst_entry = branch.inst_table.getEntry(spilled_inst).?;
772 const reg_mcv = inst_entry.value;
774 const reg_mcv = self.getResolvedInstValue(spilled_inst);
773775 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);
775778 try self.genSetStack(src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);
776779
777780 break :b reg;
......@@ -784,22 +787,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
784787 /// `reg_owner` is the instruction that gets associated with the register in the register table.
785788 /// This can have a side effect of spilling instructions to the stack to free up a register.
786789 fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {
787 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
788 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
790 try self.registers.ensureCapacity(self.gpa, self.registers.items().len + 1);
789791
790 const reg = branch.allocReg(reg_owner) orelse b: {
792 const reg = self.allocReg(reg_owner) orelse b: {
791793 // We'll take over the first register. Move the instruction that was previously
792794 // there to a stack allocation.
793795 const reg = callee_preserved_regs[0];
794 const regs_entry = branch.registers.getEntry(reg).?;
795 const spilled_inst = regs_entry.value.inst;
796 regs_entry.value = .{ .inst = reg_owner };
796 const regs_entry = self.registers.getEntry(reg).?;
797 const spilled_inst = regs_entry.value;
798 regs_entry.value = reg_owner;
797799
798800 const stack_mcv = try self.allocRegOrMem(spilled_inst, false);
799 const inst_entry = branch.inst_table.getEntry(spilled_inst).?;
800 const reg_mcv = inst_entry.value;
801 const reg_mcv = self.getResolvedInstValue(spilled_inst);
801802 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);
803805 try self.genSetStack(reg_owner.src, spilled_inst.ty, stack_mcv.stack_offset, reg_mcv);
804806
805807 break :b reg;
......@@ -934,9 +936,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
934936 .register => |reg| {
935937 // If it's in the registers table, need to associate the register with the
936938 // new instruction.
937 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
938 if (branch.registers.getEntry(toCanonicalReg(reg))) |entry| {
939 entry.value = .{ .inst = inst };
939 if (self.registers.getEntry(toCanonicalReg(reg))) |entry| {
940 entry.value = inst;
940941 }
941942 log.debug("reusing {} => {*}", .{reg, inst});
942943 },
......@@ -1231,8 +1232,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12311232 if (inst.base.isUnused())
12321233 return MCValue.dead;
12331234
1234 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1235 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
1235 try self.registers.ensureCapacity(self.gpa, self.registers.items().len + 1);
12361236
12371237 const result = self.args[self.arg_index];
12381238 self.arg_index += 1;
......@@ -1240,8 +1240,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12401240 const name_with_null = inst.name[0..mem.lenZ(inst.name) + 1];
12411241 switch (result) {
12421242 .register => |reg| {
1243 branch.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), .{ .inst = &inst.base });
1244 branch.markRegUsed(reg);
1243 self.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), &inst.base);
1244 self.markRegUsed(reg);
12451245
12461246 try self.dbg_info.ensureCapacity(self.dbg_info.items.len + 8 + name_with_null.len);
12471247 self.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
......@@ -1536,13 +1536,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
15361536
15371537 fn genDbgStmt(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
15381538 try self.dbgAdvancePCAndLine(inst.base.src);
1539 return MCValue.none;
1539 assert(inst.base.isUnused());
1540 return MCValue.dead;
15401541 }
15411542
15421543 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {
15431544 const cond = try self.resolveInst(inst.condition);
15441545
1545 // TODO deal with liveness / deaths condbr's then_entry_deaths and else_entry_deaths
15461546 const reloc: Reloc = switch (arch) {
15471547 .i386, .x86_64 => reloc: {
15481548 try self.code.ensureCapacity(self.code.items.len + 6);
......@@ -1595,9 +1595,117 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
15951595 },
15961596 else => return self.fail(inst.base.src, "TODO implement condbr {}", .{ self.target.cpu.arch }),
15971597 };
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 }
15981614 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
15991632 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 }
16001641 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
16011709 return MCValue.unreach;
16021710 }
16031711
......@@ -1671,11 +1779,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16711779 switch (reloc) {
16721780 .rel32 => |pos| {
16731781 const amt = self.code.items.len - (pos + 4);
1674 // If it wouldn't jump at all, elide it.
1675 if (amt == 0) {
1676 self.code.items.len -= 5;
1677 return;
1678 }
1782 // Here it would be tempting to implement testing for amt == 0 and then elide the
1783 // jump. However, that will cause a problem because other jumps may assume that they
1784 // can jump to this code. Or maybe I didn't understand something when I was debugging.
1785 // It could be worth another look. Anyway, that's why that isn't done here. Probably the
1786 // best place to elide jumps will be in semantic analysis, by inlining blocks that only
1787 // only have 1 break instruction.
16791788 const s32_amt = math.cast(i32, amt) catch
16801789 return self.fail(src, "unable to perform relocation: jump too far", .{});
16811790 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
......@@ -2280,8 +2389,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22802389 }
22812390
22822391 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
22832396 // 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| {
22852398 const branch = &self.branch_stack.items[0];
22862399 const gop = try branch.inst_table.getOrPut(self.gpa, inst);
22872400 if (!gop.found_existing) {
......@@ -2290,6 +2403,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
22902403 return gop.entry.value;
22912404 }
22922405
2406 return self.getResolvedInstValue(inst);
2407 }
2408
2409 fn getResolvedInstValue(self: *Self, inst: *ir.Inst) MCValue {
22932410 // Treat each stack item as a "layer" on top of the previous one.
22942411 var i: usize = self.branch_stack.items.len;
22952412 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 {
16401640 else => false,
16411641 };
16421642 if (is_fn) {
1643 //if (mem.eql(u8, mem.spanZ(decl.name), "add")) {
1644 // typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*);
1645 //}
1643 {
1644 //if (mem.eql(u8, mem.spanZ(decl.name), "add")) {
1645 //}
1646 std.debug.print("\n{}\n", .{decl.name});
1647 typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*);
1648 }
16461649
16471650 // For functions we need to add a prologue to the debug line program.
16481651 try dbg_line_buffer.ensureCapacity(26);
src-self-hosted/type.zig+2-2
......@@ -771,8 +771,8 @@ pub const Type = extern union {
771771 .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,
772772 .array_u8 => self.arrayLen() != 0,
773773 .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,
775 .int_unsigned => self.cast(Payload.IntUnsigned).?.bits == 0,
774 .int_signed => self.cast(Payload.IntSigned).?.bits != 0,
775 .int_unsigned => self.cast(Payload.IntUnsigned).?.bits != 0,
776776
777777 .error_union => {
778778 const payload = self.cast(Payload.ErrorUnion).?;