authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-08 05:35:41+00:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-08 05:35:41+00:00
logab9df5b04b862800ffee720635b6bec0185b5054
tree25595e708e831ef7e9c8efa0457336fd4c5710d0
parentb55d0193e41311532e7b3603b9386863626afcd2

stage2: machine code for condbr jumps


4 files changed, 109 insertions(+), 42 deletions(-)

lib/std/math.zig-5
...@@ -1047,19 +1047,14 @@ pub fn order(a: var, b: var) Order {...@@ -1047,19 +1047,14 @@ pub fn order(a: var, b: var) Order {
1047pub const CompareOperator = enum {1047pub const CompareOperator = enum {
1048 /// Less than (`<`)1048 /// Less than (`<`)
1049 lt,1049 lt,
1050
1051 /// Less than or equal (`<=`)1050 /// Less than or equal (`<=`)
1052 lte,1051 lte,
1053
1054 /// Equal (`==`)1052 /// Equal (`==`)
1055 eq,1053 eq,
1056
1057 /// Greater than or equal (`>=`)1054 /// Greater than or equal (`>=`)
1058 gte,1055 gte,
1059
1060 /// Greater than (`>`)1056 /// Greater than (`>`)
1061 gt,1057 gt,
1062
1063 /// Not equal (`!=`)1058 /// Not equal (`!=`)
1064 neq,1059 neq,
1065};1060};
src-self-hosted/codegen.zig+94-30
...@@ -12,6 +12,18 @@ const Target = std.Target;...@@ -12,6 +12,18 @@ const Target = std.Target;
12const Allocator = mem.Allocator;12const Allocator = mem.Allocator;
13const trace = @import("tracy.zig").trace;13const trace = @import("tracy.zig").trace;
1414
15/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
16pub const BlockData = struct {
17 relocs: std.ArrayListUnmanaged(Reloc) = .{},
18};
19
20pub const Reloc = union(enum) {
21 /// The value is an offset into the `Function` `code` from the beginning.
22 /// To perform the reloc, write 32-bit signed little-endian integer
23 /// which is a relative jump, based on the address following the reloc.
24 rel32: usize,
25};
26
15pub const Result = union(enum) {27pub const Result = union(enum) {
16 /// The `code` parameter passed to `generateSymbol` has the value appended.28 /// The `code` parameter passed to `generateSymbol` has the value appended.
17 appended: void,29 appended: void,
...@@ -290,9 +302,12 @@ const Function = struct {...@@ -290,9 +302,12 @@ const Function = struct {
290 memory: u64,302 memory: u64,
291 /// The value is one of the stack variables.303 /// The value is one of the stack variables.
292 stack_offset: u64,304 stack_offset: u64,
293 /// The value is the compare flag, with this operator305 /// The value is in the compare flags assuming an unsigned operation,
294 /// applied on top of it.306 /// with this operator applied on top of it.
295 compare_flag: std.math.CompareOperator,307 compare_flags_unsigned: std.math.CompareOperator,
308 /// The value is in the compare flags assuming a signed operation,
309 /// with this operator applied on top of it.
310 compare_flags_signed: std.math.CompareOperator,
296311
297 fn isMemory(mcv: MCValue) bool {312 fn isMemory(mcv: MCValue) bool {
298 return switch (mcv) {313 return switch (mcv) {
...@@ -317,7 +332,8 @@ const Function = struct {...@@ -317,7 +332,8 @@ const Function = struct {
317 .immediate,332 .immediate,
318 .embedded_in_code,333 .embedded_in_code,
319 .memory,334 .memory,
320 .compare_flag,335 .compare_flags_unsigned,
336 .compare_flags_signed,
321 => false,337 => false,
322338
323 .register,339 .register,
...@@ -513,7 +529,8 @@ const Function = struct {...@@ -513,7 +529,8 @@ const Function = struct {
513 switch (dst_mcv) {529 switch (dst_mcv) {
514 .none => unreachable,530 .none => unreachable,
515 .dead, .unreach, .immediate => unreachable,531 .dead, .unreach, .immediate => unreachable,
516 .compare_flag => unreachable,532 .compare_flags_unsigned => unreachable,
533 .compare_flags_signed => unreachable,
517 .register => |dst_reg_usize| {534 .register => |dst_reg_usize| {
518 const dst_reg = @intToEnum(Reg(.x86_64), @intCast(u8, dst_reg_usize));535 const dst_reg = @intToEnum(Reg(.x86_64), @intCast(u8, dst_reg_usize));
519 switch (src_mcv) {536 switch (src_mcv) {
...@@ -546,8 +563,11 @@ const Function = struct {...@@ -546,8 +563,11 @@ const Function = struct {
546 .embedded_in_code, .memory, .stack_offset => {563 .embedded_in_code, .memory, .stack_offset => {
547 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});564 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});
548 },565 },
549 .compare_flag => {566 .compare_flags_unsigned => {
550 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag", .{});567 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
568 },
569 .compare_flags_signed => {
570 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
551 },571 },
552 }572 }
553 },573 },
...@@ -650,7 +670,12 @@ const Function = struct {...@@ -650,7 +670,12 @@ const Function = struct {
650 const src_mcv = try self.limitImmediateType(inst.args.rhs, i32);670 const src_mcv = try self.limitImmediateType(inst.args.rhs, i32);
651671
652 try self.genX8664BinMathCode(inst.base.src, dst_mcv, src_mcv, 7, 0x38);672 try self.genX8664BinMathCode(inst.base.src, dst_mcv, src_mcv, 7, 0x38);
653 return MCValue{.compare_flag = inst.args.op};673 const info = inst.args.lhs.ty.intInfo(self.target.*);
674 if (info.signed) {
675 return MCValue{.compare_flags_signed = inst.args.op};
676 } else {
677 return MCValue{.compare_flags_unsigned = inst.args.op};
678 }
654 },679 },
655 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),680 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
656 }681 }
...@@ -658,8 +683,34 @@ const Function = struct {...@@ -658,8 +683,34 @@ const Function = struct {
658683
659 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue {684 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue {
660 switch (arch) {685 switch (arch) {
686 .i386, .x86_64 => {
687 try self.code.ensureCapacity(self.code.items.len + 6);
688
689 const cond = try self.resolveInst(inst.args.condition);
690 switch (cond) {
691 .compare_flags_signed => |cmp_op| {
692 // Here we map to the opposite opcode because the jump is to the false branch.
693 const opcode: u8 = switch (cmp_op) {
694 .gte => 0x8c,
695 .gt => 0x8e,
696 .neq => 0x84,
697 .lt => 0x8d,
698 .lte => 0x8f,
699 .eq => 0x85,
700 };
701 self.code.appendSliceAssumeCapacity(&[_]u8{0x0f, opcode});
702 const reloc = Reloc{ .rel32 = self.code.items.len };
703 self.code.items.len += 4;
704 try self.genBody(inst.args.true_body, arch);
705 try self.performReloc(inst.base.src, reloc);
706 try self.genBody(inst.args.false_body, arch);
707 },
708 else => return self.fail(inst.base.src, "TODO implement condbr {} when condition not already in the compare flags", .{self.target.cpu.arch}),
709 }
710 },
661 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),711 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),
662 }712 }
713 return MCValue.unreach;
663 }714 }
664715
665 fn genIsNull(self: *Function, inst: *ir.Inst.IsNull, comptime arch: std.Target.Cpu.Arch) !MCValue {716 fn genIsNull(self: *Function, inst: *ir.Inst.IsNull, comptime arch: std.Target.Cpu.Arch) !MCValue {
...@@ -676,33 +727,43 @@ const Function = struct {...@@ -676,33 +727,43 @@ const Function = struct {
676 }727 }
677 }728 }
678729
679 fn genRelativeFwdJump(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch, amount: u32) !void {
680 switch (arch) {
681 .i386, .x86_64 => {
682 // TODO x86 treats the operands as signed
683 if (amount <= std.math.maxInt(u8)) {
684 try self.code.resize(self.code.items.len + 2);
685 self.code.items[self.code.items.len - 2] = 0xeb;
686 self.code.items[self.code.items.len - 1] = @intCast(u8, amount);
687 } else {
688 try self.code.resize(self.code.items.len + 5);
689 self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
690 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
691 mem.writeIntLittle(u32, imm_ptr, amount);
692 }
693 },
694 else => return self.fail(src, "TODO implement relative forward jump for {}", .{self.target.cpu.arch}),
695 }
696 }
697
698 fn genBlock(self: *Function, inst: *ir.Inst.Block, comptime arch: std.Target.Cpu.Arch) !MCValue {730 fn genBlock(self: *Function, inst: *ir.Inst.Block, comptime arch: std.Target.Cpu.Arch) !MCValue {
731 if (inst.base.ty.hasCodeGenBits()) {
732 return self.fail(inst.base.src, "TODO codegen Block with non-void type", .{});
733 }
699 // A block is nothing but a setup to be able to jump to the end.734 // A block is nothing but a setup to be able to jump to the end.
735 defer inst.codegen.relocs.deinit(self.gpa);
700 try self.genBody(inst.args.body, arch);736 try self.genBody(inst.args.body, arch);
701 return self.fail(inst.base.src, "TODO process jump relocs after block end", .{});737
738 for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);
739
740 return MCValue.none;
741 }
742
743 fn performReloc(self: *Function, src: usize, reloc: Reloc) !void {
744 switch (reloc) {
745 .rel32 => |pos| {
746 const amt = self.code.items.len - (pos + 4);
747 const s32_amt = std.math.cast(i32, amt) catch
748 return self.fail(src, "unable to perform relocation: jump too far", .{});
749 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
750 },
751 }
702 }752 }
703753
704 fn genBreakVoid(self: *Function, inst: *ir.Inst.BreakVoid, comptime arch: std.Target.Cpu.Arch) !MCValue {754 fn genBreakVoid(self: *Function, inst: *ir.Inst.BreakVoid, comptime arch: std.Target.Cpu.Arch) !MCValue {
755 // Emit a jump with a relocation. It will be patched up after the block ends.
756 try inst.args.block.codegen.relocs.ensureCapacity(self.gpa, inst.args.block.codegen.relocs.items.len + 1);
757
705 switch (arch) {758 switch (arch) {
759 .i386, .x86_64 => {
760 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
761 // which is available if the jump is 127 bytes or less forward.
762 try self.code.resize(self.code.items.len + 5);
763 self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
764 // Leave the jump offset undefined
765 inst.args.block.codegen.relocs.appendAssumeCapacity(.{ .rel32 = self.code.items.len - 4 });
766 },
706 else => return self.fail(inst.base.src, "TODO implement breakvoid for {}", .{self.target.cpu.arch}),767 else => return self.fail(inst.base.src, "TODO implement breakvoid for {}", .{self.target.cpu.arch}),
707 }768 }
708 return .none;769 return .none;
...@@ -776,8 +837,11 @@ const Function = struct {...@@ -776,8 +837,11 @@ const Function = struct {
776 .dead => unreachable,837 .dead => unreachable,
777 .none => unreachable,838 .none => unreachable,
778 .unreach => unreachable,839 .unreach => unreachable,
779 .compare_flag => |op| {840 .compare_flags_unsigned => |op| {
780 return self.fail(src, "TODO set register with compare flag value", .{});841 return self.fail(src, "TODO set register with compare flags value (unsigned)", .{});
842 },
843 .compare_flags_signed => |op| {
844 return self.fail(src, "TODO set register with compare flags value (signed)", .{});
781 },845 },
782 .immediate => |x| {846 .immediate => |x| {
783 if (reg.size() != 64) {847 if (reg.size() != 64) {
src-self-hosted/ir.zig+3
...@@ -3,6 +3,7 @@ const Value = @import("value.zig").Value;...@@ -3,6 +3,7 @@ const Value = @import("value.zig").Value;
3const Type = @import("type.zig").Type;3const Type = @import("type.zig").Type;
4const Module = @import("Module.zig");4const Module = @import("Module.zig");
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const codegen = @import("codegen.zig");
67
7/// These are in-memory, analyzed instructions. See `zir.Inst` for the representation8/// These are in-memory, analyzed instructions. See `zir.Inst` for the representation
8/// of instructions that correspond to the ZIR text format.9/// of instructions that correspond to the ZIR text format.
...@@ -157,6 +158,8 @@ pub const Inst = struct {...@@ -157,6 +158,8 @@ pub const Inst = struct {
157 args: struct {158 args: struct {
158 body: Body,159 body: Body,
159 },160 },
161 /// This memory is reserved for codegen code to do whatever it needs to here.
162 codegen: codegen.BlockData = .{},
160 };163 };
161164
162 pub const Breakpoint = struct {165 pub const Breakpoint = struct {
src-self-hosted/zir.zig+12-7
...@@ -1589,11 +1589,9 @@ const EmitZIR = struct {...@@ -1589,11 +1589,9 @@ const EmitZIR = struct {
1589 const old_inst = inst.cast(ir.Inst.Block).?;1589 const old_inst = inst.cast(ir.Inst.Block).?;
1590 const new_inst = try self.arena.allocator.create(Inst.Block);1590 const new_inst = try self.arena.allocator.create(Inst.Block);
15911591
1592 var block_body = std.ArrayList(*Inst).init(self.allocator);1592 // We do this now so that the break instructions within the block
1593 defer block_body.deinit();1593 // can find it.
15941594 try inst_table.put(&old_inst.base, &new_inst.base);
1595 try self.emitBody(old_inst.args.body, inst_table, &block_body);
1596
1597 new_inst.* = .{1595 new_inst.* = .{
1598 .base = .{1596 .base = .{
1599 .src = inst.src,1597 .src = inst.src,
...@@ -1601,10 +1599,17 @@ const EmitZIR = struct {...@@ -1601,10 +1599,17 @@ const EmitZIR = struct {
1601 },1599 },
1602 .positionals = .{1600 .positionals = .{
1603 .label = try self.autoName(),1601 .label = try self.autoName(),
1604 .body = .{ .instructions = block_body.toOwnedSlice() },1602 .body = undefined,
1605 },1603 },
1606 .kw_args = .{},1604 .kw_args = .{},
1607 };1605 };
1606
1607 var block_body = std.ArrayList(*Inst).init(self.allocator);
1608 defer block_body.deinit();
1609
1610 try self.emitBody(old_inst.args.body, inst_table, &block_body);
1611 new_inst.positionals.body = .{ .instructions = block_body.toOwnedSlice() };
1612
1608 break :blk &new_inst.base;1613 break :blk &new_inst.base;
1609 },1614 },
1610 .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),1615 .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),
...@@ -1811,7 +1816,7 @@ const EmitZIR = struct {...@@ -1811,7 +1816,7 @@ const EmitZIR = struct {
1811 },1816 },
1812 };1817 };
1813 try instructions.append(new_inst);1818 try instructions.append(new_inst);
1814 try inst_table.putNoClobber(inst, new_inst);1819 try inst_table.put(inst, new_inst);
1815 }1820 }
1816 }1821 }
18171822