| ... | @@ -12,6 +12,18 @@ const Target = std.Target; | ... | @@ -12,6 +12,18 @@ const Target = std.Target; |
| 12 | const Allocator = mem.Allocator; | 12 | const Allocator = mem.Allocator; |
| 13 | const trace = @import("tracy.zig").trace; | 13 | const trace = @import("tracy.zig").trace; |
| 14 | | 14 | |
| | 15 | /// The codegen-related data that is stored in `ir.Inst.Block` instructions. |
| | 16 | pub const BlockData = struct { |
| | 17 | relocs: std.ArrayListUnmanaged(Reloc) = .{}, |
| | 18 | }; |
| | 19 | |
| | 20 | pub 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 | |
| 15 | pub const Result = union(enum) { | 27 | pub 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 operator | 305 | /// 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, |
| 296 | | 311 | |
| 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, |
| 322 | | 338 | |
| 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); |
| 651 | | 671 | |
| 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 { |
| 658 | | 683 | |
| 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 | } |
| 664 | | 715 | |
| 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 | } |
| 678 | | 729 | |
| 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 | } |
| 703 | | 753 | |
| 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) { |