| author | |
| committer | |
| log | 4a0f38bb7671750be1590e815def72e3b4a34ccf |
| tree | aeb31a69e29d856a92c02ce3433e788dc8d82cde |
| parent | 33aab2c1bbe55cdd3d2d08dc429260d06898d36d |
Also fix compile errors when not using -Dskip-non-native9 files changed, 482 insertions(+), 372 deletions(-)
src/codegen.zig+28-26| ... | ... | @@ -642,7 +642,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 642 | 642 | |
| 643 | 643 | try self.dbgSetPrologueEnd(); |
| 644 | 644 | |
| 645 | try self.genBody(self.mod_fn.body); | |
| 645 | try self.genBody(self.air.getMainBody()); | |
| 646 | 646 | |
| 647 | 647 | // Backpatch push callee saved regs |
| 648 | 648 | var saved_regs = Instruction.RegisterList{ |
| ... | ... | @@ -703,7 +703,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 703 | 703 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldm(.al, .sp, true, saved_regs).toU32()); |
| 704 | 704 | } else { |
| 705 | 705 | try self.dbgSetPrologueEnd(); |
| 706 | try self.genBody(self.mod_fn.body); | |
| 706 | try self.genBody(self.air.getMainBody()); | |
| 707 | 707 | try self.dbgSetEpilogueBegin(); |
| 708 | 708 | } |
| 709 | 709 | }, |
| ... | ... | @@ -727,7 +727,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 727 | 727 | |
| 728 | 728 | try self.dbgSetPrologueEnd(); |
| 729 | 729 | |
| 730 | try self.genBody(self.mod_fn.body); | |
| 730 | try self.genBody(self.air.getMainBody()); | |
| 731 | 731 | |
| 732 | 732 | // Backpatch stack offset |
| 733 | 733 | const stack_end = self.max_end_stack; |
| ... | ... | @@ -779,13 +779,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 779 | 779 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.ret(null).toU32()); |
| 780 | 780 | } else { |
| 781 | 781 | try self.dbgSetPrologueEnd(); |
| 782 | try self.genBody(self.mod_fn.body); | |
| 782 | try self.genBody(self.air.getMainBody()); | |
| 783 | 783 | try self.dbgSetEpilogueBegin(); |
| 784 | 784 | } |
| 785 | 785 | }, |
| 786 | 786 | else => { |
| 787 | 787 | try self.dbgSetPrologueEnd(); |
| 788 | try self.genBody(self.mod_fn.body); | |
| 788 | try self.genBody(self.air.getMainBody()); | |
| 789 | 789 | try self.dbgSetEpilogueBegin(); |
| 790 | 790 | }, |
| 791 | 791 | } |
| ... | ... | @@ -1492,7 +1492,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1492 | 1492 | }; |
| 1493 | 1493 | } |
| 1494 | 1494 | |
| 1495 | fn genArmBinOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, op: ir.Inst.Tag) !MCValue { | |
| 1495 | fn genArmBinOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, op: Air.Inst.Tag) !MCValue { | |
| 1496 | 1496 | const lhs = try self.resolveInst(op_lhs); |
| 1497 | 1497 | const rhs = try self.resolveInst(op_rhs); |
| 1498 | 1498 | |
| ... | ... | @@ -1514,14 +1514,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1514 | 1514 | if (reuse_lhs) { |
| 1515 | 1515 | // Allocate 0 or 1 registers |
| 1516 | 1516 | if (!rhs_is_register and rhs_should_be_register) { |
| 1517 | rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_rhs, &.{lhs.register}) }; | |
| 1517 | rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_rhs).?, &.{lhs.register}) }; | |
| 1518 | 1518 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv); |
| 1519 | 1519 | } |
| 1520 | 1520 | dst_mcv = lhs; |
| 1521 | 1521 | } else if (reuse_rhs) { |
| 1522 | 1522 | // Allocate 0 or 1 registers |
| 1523 | 1523 | if (!lhs_is_register and lhs_should_be_register) { |
| 1524 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_lhs, &.{rhs.register}) }; | |
| 1524 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?, &.{rhs.register}) }; | |
| 1525 | 1525 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv); |
| 1526 | 1526 | } |
| 1527 | 1527 | dst_mcv = rhs; |
| ... | ... | @@ -1542,7 +1542,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1542 | 1542 | lhs_mcv = dst_mcv; |
| 1543 | 1543 | } else { |
| 1544 | 1544 | // Move LHS and RHS to register |
| 1545 | const regs = try self.register_manager.allocRegs(2, .{ inst, op_rhs }, &.{}); | |
| 1545 | const regs = try self.register_manager.allocRegs(2, .{ inst, Air.refToIndex(op_rhs).? }, &.{}); | |
| 1546 | 1546 | lhs_mcv = MCValue{ .register = regs[0] }; |
| 1547 | 1547 | rhs_mcv = MCValue{ .register = regs[1] }; |
| 1548 | 1548 | dst_mcv = lhs_mcv; |
| ... | ... | @@ -1572,10 +1572,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1572 | 1572 | |
| 1573 | 1573 | // Move the operands to the newly allocated registers |
| 1574 | 1574 | if (lhs_mcv == .register and !lhs_is_register) { |
| 1575 | try self.genSetReg(op_lhs.ty, lhs_mcv.register, lhs); | |
| 1575 | try self.genSetReg(self.air.typeOf(op_lhs), lhs_mcv.register, lhs); | |
| 1576 | 1576 | } |
| 1577 | 1577 | if (rhs_mcv == .register and !rhs_is_register) { |
| 1578 | try self.genSetReg(op_rhs.ty, rhs_mcv.register, rhs); | |
| 1578 | try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs); | |
| 1579 | 1579 | } |
| 1580 | 1580 | |
| 1581 | 1581 | try self.genArmBinOpCode( |
| ... | ... | @@ -1594,7 +1594,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1594 | 1594 | lhs_mcv: MCValue, |
| 1595 | 1595 | rhs_mcv: MCValue, |
| 1596 | 1596 | swap_lhs_and_rhs: bool, |
| 1597 | op: ir.Inst.Tag, | |
| 1597 | op: Air.Inst.Tag, | |
| 1598 | 1598 | ) !void { |
| 1599 | 1599 | assert(lhs_mcv == .register or rhs_mcv == .register); |
| 1600 | 1600 | |
| ... | ... | @@ -1665,14 +1665,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1665 | 1665 | if (reuse_lhs) { |
| 1666 | 1666 | // Allocate 0 or 1 registers |
| 1667 | 1667 | if (!rhs_is_register) { |
| 1668 | rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_rhs, &.{lhs.register}) }; | |
| 1668 | rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_rhs).?, &.{lhs.register}) }; | |
| 1669 | 1669 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv); |
| 1670 | 1670 | } |
| 1671 | 1671 | dst_mcv = lhs; |
| 1672 | 1672 | } else if (reuse_rhs) { |
| 1673 | 1673 | // Allocate 0 or 1 registers |
| 1674 | 1674 | if (!lhs_is_register) { |
| 1675 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(op_lhs, &.{rhs.register}) }; | |
| 1675 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?, &.{rhs.register}) }; | |
| 1676 | 1676 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv); |
| 1677 | 1677 | } |
| 1678 | 1678 | dst_mcv = rhs; |
| ... | ... | @@ -1690,7 +1690,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1690 | 1690 | lhs_mcv = dst_mcv; |
| 1691 | 1691 | } else { |
| 1692 | 1692 | // Move LHS and RHS to register |
| 1693 | const regs = try self.register_manager.allocRegs(2, .{ inst, op_rhs }, &.{}); | |
| 1693 | const regs = try self.register_manager.allocRegs(2, .{ inst, Air.refToIndex(op_rhs).? }, &.{}); | |
| 1694 | 1694 | lhs_mcv = MCValue{ .register = regs[0] }; |
| 1695 | 1695 | rhs_mcv = MCValue{ .register = regs[1] }; |
| 1696 | 1696 | dst_mcv = lhs_mcv; |
| ... | ... | @@ -1701,10 +1701,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1701 | 1701 | |
| 1702 | 1702 | // Move the operands to the newly allocated registers |
| 1703 | 1703 | if (!lhs_is_register) { |
| 1704 | try self.genSetReg(op_lhs.ty, lhs_mcv.register, lhs); | |
| 1704 | try self.genSetReg(self.air.typeOf(op_lhs), lhs_mcv.register, lhs); | |
| 1705 | 1705 | } |
| 1706 | 1706 | if (!rhs_is_register) { |
| 1707 | try self.genSetReg(op_rhs.ty, rhs_mcv.register, rhs); | |
| 1707 | try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs); | |
| 1708 | 1708 | } |
| 1709 | 1709 | |
| 1710 | 1710 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.mul(.al, dst_mcv.register, lhs_mcv.register, rhs_mcv.register).toU32()); |
| ... | ... | @@ -2704,9 +2704,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2704 | 2704 | }, |
| 2705 | 2705 | .aarch64 => { |
| 2706 | 2706 | for (info.args) |mc_arg, arg_i| { |
| 2707 | const arg = inst.args[arg_i]; | |
| 2707 | const arg = args[arg_i]; | |
| 2708 | 2708 | const arg_ty = self.air.typeOf(arg); |
| 2709 | const arg_mcv = try self.resolveInst(inst.args[arg_i]); | |
| 2709 | const arg_mcv = try self.resolveInst(args[arg_i]); | |
| 2710 | 2710 | |
| 2711 | 2711 | switch (mc_arg) { |
| 2712 | 2712 | .none => continue, |
| ... | ... | @@ -2733,7 +2733,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2733 | 2733 | }, |
| 2734 | 2734 | } |
| 2735 | 2735 | } |
| 2736 | if (inst.func.value()) |func_value| { | |
| 2736 | if (self.air.value(callee)) |func_value| { | |
| 2737 | 2737 | if (func_value.castTag(.function)) |func_payload| { |
| 2738 | 2738 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 2739 | 2739 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| ... | ... | @@ -2899,15 +2899,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2899 | 2899 | // Allocate registers |
| 2900 | 2900 | if (rhs_should_be_register) { |
| 2901 | 2901 | if (!lhs_is_register and !rhs_is_register) { |
| 2902 | const regs = try self.register_manager.allocRegs(2, .{ bin_op.rhs, bin_op.lhs }, &.{}); | |
| 2902 | const regs = try self.register_manager.allocRegs(2, .{ | |
| 2903 | Air.refToIndex(bin_op.rhs).?, Air.refToIndex(bin_op.lhs).?, | |
| 2904 | }, &.{}); | |
| 2903 | 2905 | lhs_mcv = MCValue{ .register = regs[0] }; |
| 2904 | 2906 | rhs_mcv = MCValue{ .register = regs[1] }; |
| 2905 | 2907 | } else if (!rhs_is_register) { |
| 2906 | rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(bin_op.rhs, &.{}) }; | |
| 2908 | rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(bin_op.rhs).?, &.{}) }; | |
| 2907 | 2909 | } |
| 2908 | 2910 | } |
| 2909 | 2911 | if (!lhs_is_register) { |
| 2910 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(bin_op.lhs, &.{}) }; | |
| 2912 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(bin_op.lhs).?, &.{}) }; | |
| 2911 | 2913 | } |
| 2912 | 2914 | |
| 2913 | 2915 | // Move the operands to the newly allocated registers |
| ... | ... | @@ -3538,7 +3540,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3538 | 3540 | |
| 3539 | 3541 | break :result MCValue{ .register = reg }; |
| 3540 | 3542 | } else { |
| 3541 | break :result MCValue.none; | |
| 3543 | break :result MCValue{ .none = {} }; | |
| 3542 | 3544 | } |
| 3543 | 3545 | }, |
| 3544 | 3546 | .aarch64 => result: { |
| ... | ... | @@ -3576,7 +3578,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3576 | 3578 | return self.fail("unrecognized register: '{s}'", .{reg_name}); |
| 3577 | 3579 | break :result MCValue{ .register = reg }; |
| 3578 | 3580 | } else { |
| 3579 | break :result MCValue.none; | |
| 3581 | break :result MCValue{ .none = {} }; | |
| 3580 | 3582 | } |
| 3581 | 3583 | }, |
| 3582 | 3584 | .riscv64 => result: { |
| ... | ... | @@ -3612,7 +3614,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3612 | 3614 | return self.fail("unrecognized register: '{s}'", .{reg_name}); |
| 3613 | 3615 | break :result MCValue{ .register = reg }; |
| 3614 | 3616 | } else { |
| 3615 | break :result MCValue.none; | |
| 3617 | break :result MCValue{ .none = {} }; | |
| 3616 | 3618 | } |
| 3617 | 3619 | }, |
| 3618 | 3620 | .x86_64, .i386 => result: { |
src/codegen/c.zig+3| ... | ... | @@ -974,6 +974,9 @@ fn airArg(o: *Object) CValue { |
| 974 | 974 | |
| 975 | 975 | fn airLoad(o: *Object, inst: Air.Inst.Index) !CValue { |
| 976 | 976 | const ty_op = o.air.instructions.items(.data)[inst].ty_op; |
| 977 | const is_volatile = o.air.typeOf(ty_op.operand).isVolatilePtr(); | |
| 978 | if (!is_volatile and o.liveness.isUnused(inst)) | |
| 979 | return CValue.none; | |
| 977 | 980 | const inst_ty = o.air.typeOfIndex(inst); |
| 978 | 981 | const operand = try o.resolveInst(ty_op.operand); |
| 979 | 982 | const writer = o.writer(); |
src/codegen/llvm.zig+206-153| ... | ... | @@ -10,7 +10,7 @@ const math = std.math; |
| 10 | 10 | const Module = @import("../Module.zig"); |
| 11 | 11 | const TypedValue = @import("../TypedValue.zig"); |
| 12 | 12 | const Air = @import("../Air.zig"); |
| 13 | const Inst = ir.Inst; | |
| 13 | const Liveness = @import("../Liveness.zig"); | |
| 14 | 14 | |
| 15 | 15 | const Value = @import("../value.zig").Value; |
| 16 | 16 | const Type = @import("../type.zig").Type; |
| ... | ... | @@ -355,6 +355,7 @@ pub const DeclGen = struct { |
| 355 | 355 | builder.positionBuilderAtEnd(entry_block); |
| 356 | 356 | |
| 357 | 357 | var fg: FuncGen = .{ |
| 358 | .gpa = self.gpa, | |
| 358 | 359 | .dg = self, |
| 359 | 360 | .builder = builder, |
| 360 | 361 | .args = args, |
| ... | ... | @@ -593,29 +594,29 @@ pub const DeclGen = struct { |
| 593 | 594 | }; |
| 594 | 595 | |
| 595 | 596 | pub const FuncGen = struct { |
| 597 | gpa: *Allocator, | |
| 596 | 598 | dg: *DeclGen, |
| 597 | 599 | |
| 598 | 600 | builder: *const llvm.Builder, |
| 599 | 601 | |
| 600 | /// This stores the LLVM values used in a function, such that they can be | |
| 601 | /// referred to in other instructions. This table is cleared before every function is generated. | |
| 602 | /// TODO: Change this to a stack of Branch. Currently we store all the values from all the blocks | |
| 603 | /// in here, however if a block ends, the instructions can be thrown away. | |
| 604 | func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.Value), | |
| 602 | /// This stores the LLVM values used in a function, such that they can be referred to | |
| 603 | /// in other instructions. This table is cleared before every function is generated. | |
| 604 | func_inst_table: std.AutoHashMapUnmanaged(Air.Inst.Index, *const llvm.Value), | |
| 605 | 605 | |
| 606 | /// These fields are used to refer to the LLVM value of the function paramaters in an Arg instruction. | |
| 606 | /// These fields are used to refer to the LLVM value of the function paramaters | |
| 607 | /// in an Arg instruction. | |
| 607 | 608 | args: []*const llvm.Value, |
| 608 | 609 | arg_index: usize, |
| 609 | 610 | |
| 610 | 611 | entry_block: *const llvm.BasicBlock, |
| 611 | /// This fields stores the last alloca instruction, such that we can append more alloca instructions | |
| 612 | /// to the top of the function. | |
| 612 | /// This fields stores the last alloca instruction, such that we can append | |
| 613 | /// more alloca instructions to the top of the function. | |
| 613 | 614 | latest_alloca_inst: ?*const llvm.Value, |
| 614 | 615 | |
| 615 | 616 | llvm_func: *const llvm.Value, |
| 616 | 617 | |
| 617 | 618 | /// This data structure is used to implement breaking to blocks. |
| 618 | blocks: std.AutoHashMapUnmanaged(*Inst.Block, struct { | |
| 619 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, struct { | |
| 619 | 620 | parent_bb: *const llvm.BasicBlock, |
| 620 | 621 | break_bbs: *BreakBasicBlocks, |
| 621 | 622 | break_vals: *BreakValues, |
| ... | ... | @@ -626,9 +627,9 @@ pub const FuncGen = struct { |
| 626 | 627 | |
| 627 | 628 | fn deinit(self: *FuncGen) void { |
| 628 | 629 | self.builder.dispose(); |
| 629 | self.func_inst_table.deinit(self.gpa()); | |
| 630 | self.gpa().free(self.args); | |
| 631 | self.blocks.deinit(self.gpa()); | |
| 630 | self.func_inst_table.deinit(self.gpa); | |
| 631 | self.gpa.free(self.args); | |
| 632 | self.blocks.deinit(self.gpa); | |
| 632 | 633 | } |
| 633 | 634 | |
| 634 | 635 | fn todo(self: *FuncGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { |
| ... | ... | @@ -644,13 +645,9 @@ pub const FuncGen = struct { |
| 644 | 645 | return self.dg.object.context; |
| 645 | 646 | } |
| 646 | 647 | |
| 647 | fn gpa(self: *FuncGen) *Allocator { | |
| 648 | return self.dg.gpa; | |
| 649 | } | |
| 650 | ||
| 651 | fn resolveInst(self: *FuncGen, inst: *ir.Inst) !*const llvm.Value { | |
| 652 | if (inst.value()) |val| { | |
| 653 | return self.dg.genTypedValue(.{ .ty = inst.ty, .val = val }, self); | |
| 648 | fn resolveInst(self: *FuncGen, inst: Air.Inst.Ref) !*const llvm.Value { | |
| 649 | if (self.air.value(inst)) |val| { | |
| 650 | return self.dg.genTypedValue(.{ .ty = self.air.typeOf(inst), .val = val }, self); | |
| 654 | 651 | } |
| 655 | 652 | if (self.func_inst_table.get(inst)) |value| return value; |
| 656 | 653 | |
| ... | ... | @@ -658,51 +655,57 @@ pub const FuncGen = struct { |
| 658 | 655 | } |
| 659 | 656 | |
| 660 | 657 | fn genBody(self: *FuncGen, body: ir.Body) error{ OutOfMemory, CodegenFail }!void { |
| 658 | const air_tags = self.air.instructions.items(.tag); | |
| 661 | 659 | for (body.instructions) |inst| { |
| 662 | const opt_value = switch (inst.tag) { | |
| 663 | .add => try self.genAdd(inst.castTag(.add).?), | |
| 664 | .alloc => try self.genAlloc(inst.castTag(.alloc).?), | |
| 665 | .arg => try self.genArg(inst.castTag(.arg).?), | |
| 666 | .bitcast => try self.genBitCast(inst.castTag(.bitcast).?), | |
| 667 | .block => try self.genBlock(inst.castTag(.block).?), | |
| 668 | .br => try self.genBr(inst.castTag(.br).?), | |
| 669 | .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), | |
| 670 | .br_void => try self.genBrVoid(inst.castTag(.br_void).?), | |
| 671 | .call => try self.genCall(inst.castTag(.call).?), | |
| 672 | .cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?, .eq), | |
| 673 | .cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?, .gt), | |
| 674 | .cmp_gte => try self.genCmp(inst.castTag(.cmp_gte).?, .gte), | |
| 675 | .cmp_lt => try self.genCmp(inst.castTag(.cmp_lt).?, .lt), | |
| 676 | .cmp_lte => try self.genCmp(inst.castTag(.cmp_lte).?, .lte), | |
| 677 | .cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?, .neq), | |
| 678 | .condbr => try self.genCondBr(inst.castTag(.condbr).?), | |
| 679 | .intcast => try self.genIntCast(inst.castTag(.intcast).?), | |
| 680 | .is_non_null => try self.genIsNonNull(inst.castTag(.is_non_null).?, false), | |
| 681 | .is_non_null_ptr => try self.genIsNonNull(inst.castTag(.is_non_null_ptr).?, true), | |
| 682 | .is_null => try self.genIsNull(inst.castTag(.is_null).?, false), | |
| 683 | .is_null_ptr => try self.genIsNull(inst.castTag(.is_null_ptr).?, true), | |
| 684 | .load => try self.genLoad(inst.castTag(.load).?), | |
| 685 | .loop => try self.genLoop(inst.castTag(.loop).?), | |
| 686 | .not => try self.genNot(inst.castTag(.not).?), | |
| 687 | .ret => try self.genRet(inst.castTag(.ret).?), | |
| 688 | .retvoid => self.genRetVoid(inst.castTag(.retvoid).?), | |
| 689 | .store => try self.genStore(inst.castTag(.store).?), | |
| 690 | .sub => try self.genSub(inst.castTag(.sub).?), | |
| 691 | .unreach => self.genUnreach(inst.castTag(.unreach).?), | |
| 692 | .optional_payload => try self.genOptionalPayload(inst.castTag(.optional_payload).?, false), | |
| 693 | .optional_payload_ptr => try self.genOptionalPayload(inst.castTag(.optional_payload_ptr).?, true), | |
| 660 | const opt_value = switch (air_tags[inst]) { | |
| 661 | .add => try self.airAdd(inst), | |
| 662 | .sub => try self.airSub(inst), | |
| 663 | ||
| 664 | .cmp_eq => try self.airCmp(inst, .eq), | |
| 665 | .cmp_gt => try self.airCmp(inst, .gt), | |
| 666 | .cmp_gte => try self.airCmp(inst, .gte), | |
| 667 | .cmp_lt => try self.airCmp(inst, .lt), | |
| 668 | .cmp_lte => try self.airCmp(inst, .lte), | |
| 669 | .cmp_neq => try self.airCmp(inst, .neq), | |
| 670 | ||
| 671 | .is_non_null => try self.airIsNonNull(inst, false), | |
| 672 | .is_non_null_ptr => try self.airIsNonNull(inst, true), | |
| 673 | .is_null => try self.airIsNull(inst, false), | |
| 674 | .is_null_ptr => try self.airIsNull(inst, true), | |
| 675 | ||
| 676 | .alloc => try self.airAlloc(inst), | |
| 677 | .arg => try self.airArg(inst), | |
| 678 | .bitcast => try self.airBitCast(inst), | |
| 679 | .block => try self.airBlock(inst), | |
| 680 | .br => try self.airBr(inst), | |
| 681 | .breakpoint => try self.airBreakpoint(inst), | |
| 682 | .call => try self.airCall(inst), | |
| 683 | .cond_br => try self.airCondBr(inst), | |
| 684 | .intcast => try self.airIntCast(inst), | |
| 685 | .load => try self.airLoad(inst), | |
| 686 | .loop => try self.airLoop(inst), | |
| 687 | .not => try self.airNot(inst), | |
| 688 | .ret => try self.airRet(inst), | |
| 689 | .store => try self.airStore(inst), | |
| 690 | .unreach => self.airUnreach(inst), | |
| 691 | .optional_payload => try self.airOptionalPayload(inst, false), | |
| 692 | .optional_payload_ptr => try self.airOptionalPayload(inst, true), | |
| 694 | 693 | .dbg_stmt => blk: { |
| 695 | 694 | // TODO: implement debug info |
| 696 | 695 | break :blk null; |
| 697 | 696 | }, |
| 698 | else => |tag| return self.todo("implement TZIR instruction: {}", .{tag}), | |
| 697 | else => |tag| return self.todo("implement AIR instruction: {}", .{tag}), | |
| 699 | 698 | }; |
| 700 | if (opt_value) |val| try self.func_inst_table.putNoClobber(self.gpa(), inst, val); | |
| 699 | if (opt_value) |val| try self.func_inst_table.putNoClobber(self.gpa, inst, val); | |
| 701 | 700 | } |
| 702 | 701 | } |
| 703 | 702 | |
| 704 | fn genCall(self: *FuncGen, inst: *Inst.Call) !?*const llvm.Value { | |
| 705 | if (inst.func.value()) |func_value| { | |
| 703 | fn airCall(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 704 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | |
| 705 | const extra = self.air.extraData(Air.Call, pl_op.payload); | |
| 706 | const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); | |
| 707 | ||
| 708 | if (self.air.value(pl_op.operand)) |func_value| { | |
| 706 | 709 | const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn| |
| 707 | 710 | extern_fn.data |
| 708 | 711 | else if (func_value.castTag(.function)) |func_payload| |
| ... | ... | @@ -714,12 +717,10 @@ pub const FuncGen = struct { |
| 714 | 717 | const zig_fn_type = fn_decl.ty; |
| 715 | 718 | const llvm_fn = try self.dg.resolveLLVMFunction(fn_decl); |
| 716 | 719 | |
| 717 | const num_args = inst.args.len; | |
| 718 | ||
| 719 | const llvm_param_vals = try self.gpa().alloc(*const llvm.Value, num_args); | |
| 720 | defer self.gpa().free(llvm_param_vals); | |
| 720 | const llvm_param_vals = try self.gpa.alloc(*const llvm.Value, args.len); | |
| 721 | defer self.gpa.free(llvm_param_vals); | |
| 721 | 722 | |
| 722 | for (inst.args) |arg, i| { | |
| 723 | for (args) |arg, i| { | |
| 723 | 724 | llvm_param_vals[i] = try self.resolveInst(arg); |
| 724 | 725 | } |
| 725 | 726 | |
| ... | ... | @@ -727,8 +728,8 @@ pub const FuncGen = struct { |
| 727 | 728 | // Do we need that? |
| 728 | 729 | const call = self.builder.buildCall( |
| 729 | 730 | llvm_fn, |
| 730 | if (num_args == 0) null else llvm_param_vals.ptr, | |
| 731 | @intCast(c_uint, num_args), | |
| 731 | if (args.len == 0) null else llvm_param_vals.ptr, | |
| 732 | @intCast(c_uint, args.len), | |
| 732 | 733 | "", |
| 733 | 734 | ); |
| 734 | 735 | |
| ... | ... | @@ -746,31 +747,31 @@ pub const FuncGen = struct { |
| 746 | 747 | } |
| 747 | 748 | } |
| 748 | 749 | |
| 749 | fn genRetVoid(self: *FuncGen, inst: *Inst.NoOp) ?*const llvm.Value { | |
| 750 | _ = inst; | |
| 751 | _ = self.builder.buildRetVoid(); | |
| 752 | return null; | |
| 753 | } | |
| 754 | ||
| 755 | fn genRet(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { | |
| 756 | if (!inst.operand.ty.hasCodeGenBits()) { | |
| 757 | // TODO: in astgen these instructions should turn into `retvoid` instructions. | |
| 750 | fn airRet(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 751 | const un_op = self.air.instructions.items(.data)[inst].un_op; | |
| 752 | if (!self.air.typeOf(un_op).hasCodeGenBits()) { | |
| 758 | 753 | _ = self.builder.buildRetVoid(); |
| 759 | 754 | return null; |
| 760 | 755 | } |
| 761 | _ = self.builder.buildRet(try self.resolveInst(inst.operand)); | |
| 756 | const operand = try self.resolveInst(un_op); | |
| 757 | _ = self.builder.buildRet(operand); | |
| 762 | 758 | return null; |
| 763 | 759 | } |
| 764 | 760 | |
| 765 | fn genCmp(self: *FuncGen, inst: *Inst.BinOp, op: math.CompareOperator) !?*const llvm.Value { | |
| 766 | const lhs = try self.resolveInst(inst.lhs); | |
| 767 | const rhs = try self.resolveInst(inst.rhs); | |
| 761 | fn airCmp(self: *FuncGen, inst: Air.Inst.Index, op: math.CompareOperator) !?*const llvm.Value { | |
| 762 | if (self.liveness.isUnused(inst)) | |
| 763 | return null; | |
| 764 | ||
| 765 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 766 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 767 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 768 | const inst_ty = self.air.typeOfIndex(inst); | |
| 768 | 769 | |
| 769 | if (!inst.base.ty.isInt()) | |
| 770 | if (inst.base.ty.tag() != .bool) | |
| 771 | return self.todo("implement 'genCmp' for type {}", .{inst.base.ty}); | |
| 770 | if (!inst_ty.isInt()) | |
| 771 | if (inst_ty.tag() != .bool) | |
| 772 | return self.todo("implement 'airCmp' for type {}", .{inst_ty}); | |
| 772 | 773 | |
| 773 | const is_signed = inst.base.ty.isSignedInt(); | |
| 774 | const is_signed = inst_ty.isSignedInt(); | |
| 774 | 775 | const operation = switch (op) { |
| 775 | 776 | .eq => .EQ, |
| 776 | 777 | .neq => .NE, |
| ... | ... | @@ -783,32 +784,36 @@ pub const FuncGen = struct { |
| 783 | 784 | return self.builder.buildICmp(operation, lhs, rhs, ""); |
| 784 | 785 | } |
| 785 | 786 | |
| 786 | fn genBlock(self: *FuncGen, inst: *Inst.Block) !?*const llvm.Value { | |
| 787 | fn airBlock(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 788 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | |
| 789 | const extra = self.air.extraData(Air.Block, ty_pl.payload); | |
| 790 | const body = self.air.extra[extra.end..][0..extra.data.body_len]; | |
| 787 | 791 | const parent_bb = self.context().createBasicBlock("Block"); |
| 788 | 792 | |
| 789 | 793 | // 5 breaks to a block seems like a reasonable default. |
| 790 | var break_bbs = try BreakBasicBlocks.initCapacity(self.gpa(), 5); | |
| 791 | var break_vals = try BreakValues.initCapacity(self.gpa(), 5); | |
| 792 | try self.blocks.putNoClobber(self.gpa(), inst, .{ | |
| 794 | var break_bbs = try BreakBasicBlocks.initCapacity(self.gpa, 5); | |
| 795 | var break_vals = try BreakValues.initCapacity(self.gpa, 5); | |
| 796 | try self.blocks.putNoClobber(self.gpa, inst, .{ | |
| 793 | 797 | .parent_bb = parent_bb, |
| 794 | 798 | .break_bbs = &break_bbs, |
| 795 | 799 | .break_vals = &break_vals, |
| 796 | 800 | }); |
| 797 | 801 | defer { |
| 798 | 802 | assert(self.blocks.remove(inst)); |
| 799 | break_bbs.deinit(self.gpa()); | |
| 800 | break_vals.deinit(self.gpa()); | |
| 803 | break_bbs.deinit(self.gpa); | |
| 804 | break_vals.deinit(self.gpa); | |
| 801 | 805 | } |
| 802 | 806 | |
| 803 | try self.genBody(inst.body); | |
| 807 | try self.genBody(body); | |
| 804 | 808 | |
| 805 | 809 | self.llvm_func.appendExistingBasicBlock(parent_bb); |
| 806 | 810 | self.builder.positionBuilderAtEnd(parent_bb); |
| 807 | 811 | |
| 808 | 812 | // If the block does not return a value, we dont have to create a phi node. |
| 809 | if (!inst.base.ty.hasCodeGenBits()) return null; | |
| 813 | const inst_ty = self.air.typeOfIndex(inst); | |
| 814 | if (!inst_ty.hasCodeGenBits()) return null; | |
| 810 | 815 | |
| 811 | const phi_node = self.builder.buildPhi(try self.dg.getLLVMType(inst.base.ty), ""); | |
| 816 | const phi_node = self.builder.buildPhi(try self.dg.getLLVMType(inst_ty), ""); | |
| 812 | 817 | phi_node.addIncoming( |
| 813 | 818 | break_vals.items.ptr, |
| 814 | 819 | break_bbs.items.ptr, |
| ... | ... | @@ -817,35 +822,30 @@ pub const FuncGen = struct { |
| 817 | 822 | return phi_node; |
| 818 | 823 | } |
| 819 | 824 | |
| 820 | fn genBr(self: *FuncGen, inst: *Inst.Br) !?*const llvm.Value { | |
| 821 | var block = self.blocks.get(inst.block).?; | |
| 825 | fn airBr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 826 | const branch = self.air.instructions.items(.data)[inst].br; | |
| 827 | const block = self.blocks.get(branch.block_inst).?; | |
| 822 | 828 | |
| 823 | 829 | // If the break doesn't break a value, then we don't have to add |
| 824 | 830 | // the values to the lists. |
| 825 | if (!inst.operand.ty.hasCodeGenBits()) { | |
| 826 | // TODO: in astgen these instructions should turn into `br_void` instructions. | |
| 827 | _ = self.builder.buildBr(block.parent_bb); | |
| 828 | } else { | |
| 829 | const val = try self.resolveInst(inst.operand); | |
| 831 | if (self.air.typeOf(branch.result).hasCodeGenBits()) { | |
| 832 | const val = try self.resolveInst(branch.result); | |
| 830 | 833 | |
| 831 | 834 | // For the phi node, we need the basic blocks and the values of the |
| 832 | 835 | // break instructions. |
| 833 | try block.break_bbs.append(self.gpa(), self.builder.getInsertBlock()); | |
| 834 | try block.break_vals.append(self.gpa(), val); | |
| 835 | ||
| 836 | _ = self.builder.buildBr(block.parent_bb); | |
| 836 | try block.break_bbs.append(self.gpa, self.builder.getInsertBlock()); | |
| 837 | try block.break_vals.append(self.gpa, val); | |
| 837 | 838 | } |
| 838 | return null; | |
| 839 | } | |
| 840 | ||
| 841 | fn genBrVoid(self: *FuncGen, inst: *Inst.BrVoid) !?*const llvm.Value { | |
| 842 | var block = self.blocks.get(inst.block).?; | |
| 843 | 839 | _ = self.builder.buildBr(block.parent_bb); |
| 844 | 840 | return null; |
| 845 | 841 | } |
| 846 | 842 | |
| 847 | fn genCondBr(self: *FuncGen, inst: *Inst.CondBr) !?*const llvm.Value { | |
| 848 | const condition_value = try self.resolveInst(inst.condition); | |
| 843 | fn airCondBr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 844 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | |
| 845 | const cond = try self.resolveInst(pl_op.operand); | |
| 846 | const extra = self.air.extraData(Air.CondBr, pl_op.payload); | |
| 847 | const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len]; | |
| 848 | const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; | |
| 849 | 849 | |
| 850 | 850 | const then_block = self.context().appendBasicBlock(self.llvm_func, "Then"); |
| 851 | 851 | const else_block = self.context().appendBasicBlock(self.llvm_func, "Else"); |
| ... | ... | @@ -854,38 +854,51 @@ pub const FuncGen = struct { |
| 854 | 854 | defer self.builder.positionBuilderAtEnd(prev_block); |
| 855 | 855 | |
| 856 | 856 | self.builder.positionBuilderAtEnd(then_block); |
| 857 | try self.genBody(inst.then_body); | |
| 857 | try self.genBody(then_body); | |
| 858 | 858 | |
| 859 | 859 | self.builder.positionBuilderAtEnd(else_block); |
| 860 | try self.genBody(inst.else_body); | |
| 860 | try self.genBody(else_body); | |
| 861 | 861 | } |
| 862 | _ = self.builder.buildCondBr(condition_value, then_block, else_block); | |
| 862 | _ = self.builder.buildCondBr(cond, then_block, else_block); | |
| 863 | 863 | return null; |
| 864 | 864 | } |
| 865 | 865 | |
| 866 | fn genLoop(self: *FuncGen, inst: *Inst.Loop) !?*const llvm.Value { | |
| 866 | fn airLoop(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 867 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | |
| 868 | const loop = self.air.extraData(Air.Block, ty_pl.payload); | |
| 869 | const body = self.air.extra[loop.end..][0..loop.data.body_len]; | |
| 867 | 870 | const loop_block = self.context().appendBasicBlock(self.llvm_func, "Loop"); |
| 868 | 871 | _ = self.builder.buildBr(loop_block); |
| 869 | 872 | |
| 870 | 873 | self.builder.positionBuilderAtEnd(loop_block); |
| 871 | try self.genBody(inst.body); | |
| 874 | try self.genBody(body); | |
| 872 | 875 | |
| 873 | 876 | _ = self.builder.buildBr(loop_block); |
| 874 | 877 | return null; |
| 875 | 878 | } |
| 876 | 879 | |
| 877 | fn genNot(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { | |
| 878 | return self.builder.buildNot(try self.resolveInst(inst.operand), ""); | |
| 880 | fn airNot(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 881 | if (self.liveness.isUnused(inst)) | |
| 882 | return null; | |
| 883 | ||
| 884 | const un_op = self.air.instructions.items(.data)[inst].un_op; | |
| 885 | const operand = try self.resolveInst(un_op); | |
| 886 | ||
| 887 | return self.builder.buildNot(operand, ""); | |
| 879 | 888 | } |
| 880 | 889 | |
| 881 | fn genUnreach(self: *FuncGen, inst: *Inst.NoOp) ?*const llvm.Value { | |
| 890 | fn airUnreach(self: *FuncGen, inst: Air.Inst.Index) ?*const llvm.Value { | |
| 882 | 891 | _ = inst; |
| 883 | 892 | _ = self.builder.buildUnreachable(); |
| 884 | 893 | return null; |
| 885 | 894 | } |
| 886 | 895 | |
| 887 | fn genIsNonNull(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { | |
| 888 | const operand = try self.resolveInst(inst.operand); | |
| 896 | fn airIsNonNull(self: *FuncGen, inst: Air.Inst.Index, operand_is_ptr: bool) !?*const llvm.Value { | |
| 897 | if (self.liveness.isUnused(inst)) | |
| 898 | return null; | |
| 899 | ||
| 900 | const un_op = self.air.instructions.items(.data)[inst].un_op; | |
| 901 | const operand = try self.resolveInst(un_op); | |
| 889 | 902 | |
| 890 | 903 | if (operand_is_ptr) { |
| 891 | 904 | const index_type = self.context().intType(32); |
| ... | ... | @@ -901,12 +914,23 @@ pub const FuncGen = struct { |
| 901 | 914 | } |
| 902 | 915 | } |
| 903 | 916 | |
| 904 | fn genIsNull(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { | |
| 905 | return self.builder.buildNot((try self.genIsNonNull(inst, operand_is_ptr)).?, ""); | |
| 917 | fn airIsNull(self: *FuncGen, inst: Air.Inst.Index, operand_is_ptr: bool) !?*const llvm.Value { | |
| 918 | if (self.liveness.isUnused(inst)) | |
| 919 | return null; | |
| 920 | ||
| 921 | return self.builder.buildNot((try self.airIsNonNull(inst, operand_is_ptr)).?, ""); | |
| 906 | 922 | } |
| 907 | 923 | |
| 908 | fn genOptionalPayload(self: *FuncGen, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { | |
| 909 | const operand = try self.resolveInst(inst.operand); | |
| 924 | fn airOptionalPayload( | |
| 925 | self: *FuncGen, | |
| 926 | inst: Air.Inst.Index, | |
| 927 | operand_is_ptr: bool, | |
| 928 | ) !?*const llvm.Value { | |
| 929 | if (self.liveness.isUnused(inst)) | |
| 930 | return null; | |
| 931 | ||
| 932 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 933 | const operand = try self.resolveInst(ty_op.operand); | |
| 910 | 934 | |
| 911 | 935 | if (operand_is_ptr) { |
| 912 | 936 | const index_type = self.context().intType(32); |
| ... | ... | @@ -922,61 +946,83 @@ pub const FuncGen = struct { |
| 922 | 946 | } |
| 923 | 947 | } |
| 924 | 948 | |
| 925 | fn genAdd(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value { | |
| 926 | const lhs = try self.resolveInst(inst.lhs); | |
| 927 | const rhs = try self.resolveInst(inst.rhs); | |
| 949 | fn airAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 950 | if (self.liveness.isUnused(inst)) | |
| 951 | return null; | |
| 952 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 953 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 954 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 955 | const inst_ty = self.air.typeOfIndex(inst); | |
| 928 | 956 | |
| 929 | if (!inst.base.ty.isInt()) | |
| 930 | return self.todo("implement 'genAdd' for type {}", .{inst.base.ty}); | |
| 957 | if (!inst_ty.isInt()) | |
| 958 | return self.todo("implement 'airAdd' for type {}", .{inst_ty}); | |
| 931 | 959 | |
| 932 | return if (inst.base.ty.isSignedInt()) | |
| 960 | return if (inst_ty.isSignedInt()) | |
| 933 | 961 | self.builder.buildNSWAdd(lhs, rhs, "") |
| 934 | 962 | else |
| 935 | 963 | self.builder.buildNUWAdd(lhs, rhs, ""); |
| 936 | 964 | } |
| 937 | 965 | |
| 938 | fn genSub(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value { | |
| 939 | const lhs = try self.resolveInst(inst.lhs); | |
| 940 | const rhs = try self.resolveInst(inst.rhs); | |
| 966 | fn airSub(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 967 | if (self.liveness.isUnused(inst)) | |
| 968 | return null; | |
| 969 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 970 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 971 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 972 | const inst_ty = self.air.typeOfIndex(inst); | |
| 941 | 973 | |
| 942 | if (!inst.base.ty.isInt()) | |
| 943 | return self.todo("implement 'genSub' for type {}", .{inst.base.ty}); | |
| 974 | if (!inst_ty.isInt()) | |
| 975 | return self.todo("implement 'airSub' for type {}", .{inst_ty}); | |
| 944 | 976 | |
| 945 | return if (inst.base.ty.isSignedInt()) | |
| 977 | return if (inst_ty.isSignedInt()) | |
| 946 | 978 | self.builder.buildNSWSub(lhs, rhs, "") |
| 947 | 979 | else |
| 948 | 980 | self.builder.buildNUWSub(lhs, rhs, ""); |
| 949 | 981 | } |
| 950 | 982 | |
| 951 | fn genIntCast(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { | |
| 952 | const val = try self.resolveInst(inst.operand); | |
| 983 | fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 984 | if (self.liveness.isUnused(inst)) | |
| 985 | return null; | |
| 986 | ||
| 987 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 988 | const operand = try self.resolveInst(ty_op.operand); | |
| 989 | const inst_ty = self.air.typeOfIndex(inst); | |
| 953 | 990 | |
| 954 | const signed = inst.base.ty.isSignedInt(); | |
| 991 | const signed = inst_ty.isSignedInt(); | |
| 955 | 992 | // TODO: Should we use intcast here or just a simple bitcast? |
| 956 | 993 | // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes |
| 957 | return self.builder.buildIntCast2(val, try self.dg.getLLVMType(inst.base.ty), llvm.Bool.fromBool(signed), ""); | |
| 994 | return self.builder.buildIntCast2(operand, try self.dg.getLLVMType(inst_ty), llvm.Bool.fromBool(signed), ""); | |
| 958 | 995 | } |
| 959 | 996 | |
| 960 | fn genBitCast(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { | |
| 961 | const val = try self.resolveInst(inst.operand); | |
| 962 | const dest_type = try self.dg.getLLVMType(inst.base.ty); | |
| 997 | fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 998 | if (self.liveness.isUnused(inst)) | |
| 999 | return null; | |
| 1000 | ||
| 1001 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 1002 | const operand = try self.resolveInst(ty_op.operand); | |
| 1003 | const inst_ty = self.air.typeOfIndex(inst); | |
| 1004 | const dest_type = try self.dg.getLLVMType(inst_ty); | |
| 963 | 1005 | |
| 964 | return self.builder.buildBitCast(val, dest_type, ""); | |
| 1006 | return self.builder.buildBitCast(operand, dest_type, ""); | |
| 965 | 1007 | } |
| 966 | 1008 | |
| 967 | fn genArg(self: *FuncGen, inst: *Inst.Arg) !?*const llvm.Value { | |
| 1009 | fn airArg(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 968 | 1010 | const arg_val = self.args[self.arg_index]; |
| 969 | 1011 | self.arg_index += 1; |
| 970 | 1012 | |
| 971 | const ptr_val = self.buildAlloca(try self.dg.getLLVMType(inst.base.ty)); | |
| 1013 | const inst_ty = self.air.typeOfIndex(inst); | |
| 1014 | const ptr_val = self.buildAlloca(try self.dg.getLLVMType(inst_ty)); | |
| 972 | 1015 | _ = self.builder.buildStore(arg_val, ptr_val); |
| 973 | 1016 | return self.builder.buildLoad(ptr_val, ""); |
| 974 | 1017 | } |
| 975 | 1018 | |
| 976 | fn genAlloc(self: *FuncGen, inst: *Inst.NoOp) !?*const llvm.Value { | |
| 1019 | fn airAlloc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 1020 | if (self.liveness.isUnused(inst)) | |
| 1021 | return null; | |
| 977 | 1022 | // buildAlloca expects the pointee type, not the pointer type, so assert that |
| 978 | 1023 | // a Payload.PointerSimple is passed to the alloc instruction. |
| 979 | const pointee_type = inst.base.ty.castPointer().?.data; | |
| 1024 | const inst_ty = self.air.typeOfIndex(inst); | |
| 1025 | const pointee_type = inst_ty.castPointer().?.data; | |
| 980 | 1026 | |
| 981 | 1027 | // TODO: figure out a way to get the name of the var decl. |
| 982 | 1028 | // TODO: set alignment and volatile |
| ... | ... | @@ -1007,19 +1053,26 @@ pub const FuncGen = struct { |
| 1007 | 1053 | return val; |
| 1008 | 1054 | } |
| 1009 | 1055 | |
| 1010 | fn genStore(self: *FuncGen, inst: *Inst.BinOp) !?*const llvm.Value { | |
| 1011 | const val = try self.resolveInst(inst.rhs); | |
| 1012 | const ptr = try self.resolveInst(inst.lhs); | |
| 1013 | _ = self.builder.buildStore(val, ptr); | |
| 1056 | fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 1057 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 1058 | const dest_ptr = try self.resolveInst(bin_op.lhs); | |
| 1059 | const src_operand = try self.resolveInst(bin_op.rhs); | |
| 1060 | // TODO set volatile on this store properly | |
| 1061 | _ = self.builder.buildStore(src_operand, dest_ptr); | |
| 1014 | 1062 | return null; |
| 1015 | 1063 | } |
| 1016 | 1064 | |
| 1017 | fn genLoad(self: *FuncGen, inst: *Inst.UnOp) !?*const llvm.Value { | |
| 1018 | const ptr_val = try self.resolveInst(inst.operand); | |
| 1019 | return self.builder.buildLoad(ptr_val, ""); | |
| 1065 | fn airLoad(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 1066 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 1067 | const is_volatile = self.air.typeOf(ty_op.operand).isVolatilePtr(); | |
| 1068 | if (!is_volatile and self.liveness.isUnused(inst)) | |
| 1069 | return null; | |
| 1070 | const ptr = try self.resolveInst(ty_op.operand); | |
| 1071 | // TODO set volatile on this load properly | |
| 1072 | return self.builder.buildLoad(ptr, ""); | |
| 1020 | 1073 | } |
| 1021 | 1074 | |
| 1022 | fn genBreakpoint(self: *FuncGen, inst: *Inst.NoOp) !?*const llvm.Value { | |
| 1075 | fn airBreakpoint(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | |
| 1023 | 1076 | _ = inst; |
| 1024 | 1077 | const llvn_fn = self.getIntrinsic("llvm.debugtrap"); |
| 1025 | 1078 | _ = self.builder.buildCall(llvn_fn, null, 0, ""); |
src/codegen/spirv.zig+78-74| ... | ... | @@ -13,6 +13,7 @@ const Type = @import("../type.zig").Type; |
| 13 | 13 | const Value = @import("../value.zig").Value; |
| 14 | 14 | const LazySrcLoc = Module.LazySrcLoc; |
| 15 | 15 | const Air = @import("../Air.zig"); |
| 16 | const Liveness = @import("../Liveness.zig"); | |
| 16 | 17 | |
| 17 | 18 | pub const Word = u32; |
| 18 | 19 | pub const ResultId = u32; |
| ... | ... | @@ -247,6 +248,7 @@ pub const DeclGen = struct { |
| 247 | 248 | return .{ |
| 248 | 249 | .spv = spv, |
| 249 | 250 | .air = undefined, |
| 251 | .liveness = undefined, | |
| 250 | 252 | .args = std.ArrayList(ResultId).init(spv.gpa), |
| 251 | 253 | .next_arg_index = undefined, |
| 252 | 254 | .inst_results = InstMap.init(spv.gpa), |
| ... | ... | @@ -259,11 +261,12 @@ pub const DeclGen = struct { |
| 259 | 261 | } |
| 260 | 262 | |
| 261 | 263 | /// Generate the code for `decl`. If a reportable error occured during code generation, |
| 262 | /// a message is returned by this function. Callee owns the memory. If this function returns such | |
| 263 | /// a reportable error, it is valid to be called again for a different decl. | |
| 264 | pub fn gen(self: *DeclGen, decl: *Decl, air: Air) !?*Module.ErrorMsg { | |
| 264 | /// a message is returned by this function. Callee owns the memory. If this function | |
| 265 | /// returns such a reportable error, it is valid to be called again for a different decl. | |
| 266 | pub fn gen(self: *DeclGen, decl: *Decl, air: Air, liveness: Liveness) !?*Module.ErrorMsg { | |
| 265 | 267 | // Reset internal resources, we don't want to re-allocate these. |
| 266 | self.air = &air; | |
| 268 | self.air = air; | |
| 269 | self.liveness = liveness; | |
| 267 | 270 | self.args.items.len = 0; |
| 268 | 271 | self.next_arg_index = 0; |
| 269 | 272 | self.inst_results.clearRetainingCapacity(); |
| ... | ... | @@ -297,12 +300,12 @@ pub const DeclGen = struct { |
| 297 | 300 | return error.AnalysisFail; |
| 298 | 301 | } |
| 299 | 302 | |
| 300 | fn resolve(self: *DeclGen, inst: Air.Inst.Index) !ResultId { | |
| 301 | if (inst.value()) |val| { | |
| 302 | return self.genConstant(inst.ty, val); | |
| 303 | fn resolve(self: *DeclGen, inst: Air.Inst.Ref) !ResultId { | |
| 304 | if (self.air.value(inst)) |val| { | |
| 305 | return self.genConstant(self.air.typeOf(inst), val); | |
| 303 | 306 | } |
| 304 | ||
| 305 | return self.inst_results.get(inst).?; // Instruction does not dominate all uses! | |
| 307 | const index = Air.refToIndex(inst).?; | |
| 308 | return self.inst_results.get(index).?; // Assertion means instruction does not dominate usage. | |
| 306 | 309 | } |
| 307 | 310 | |
| 308 | 311 | fn beginSPIRVBlock(self: *DeclGen, label_id: ResultId) !void { |
| ... | ... | @@ -663,40 +666,40 @@ pub const DeclGen = struct { |
| 663 | 666 | const air_tags = self.air.instructions.items(.tag); |
| 664 | 667 | const result_id = switch (air_tags[inst]) { |
| 665 | 668 | // zig fmt: off |
| 666 | .add, .addwrap => try self.genArithOp(inst, .{.OpFAdd, .OpIAdd, .OpIAdd}), | |
| 667 | .sub, .subwrap => try self.genArithOp(inst, .{.OpFSub, .OpISub, .OpISub}), | |
| 668 | .mul, .mulwrap => try self.genArithOp(inst, .{.OpFMul, .OpIMul, .OpIMul}), | |
| 669 | .div => try self.genArithOp(inst, .{.OpFDiv, .OpSDiv, .OpUDiv}), | |
| 670 | ||
| 671 | .bit_and => try self.genBinOpSimple(inst, .OpBitwiseAnd), | |
| 672 | .bit_or => try self.genBinOpSimple(inst, .OpBitwiseOr), | |
| 673 | .xor => try self.genBinOpSimple(inst, .OpBitwiseXor), | |
| 674 | .bool_and => try self.genBinOpSimple(inst, .OpLogicalAnd), | |
| 675 | .bool_or => try self.genBinOpSimple(inst, .OpLogicalOr), | |
| 676 | ||
| 677 | .not => try self.genNot(inst), | |
| 678 | ||
| 679 | .cmp_eq => try self.genCmp(inst, .{.OpFOrdEqual, .OpLogicalEqual, .OpIEqual}), | |
| 680 | .cmp_neq => try self.genCmp(inst, .{.OpFOrdNotEqual, .OpLogicalNotEqual, .OpINotEqual}), | |
| 681 | .cmp_gt => try self.genCmp(inst, .{.OpFOrdGreaterThan, .OpSGreaterThan, .OpUGreaterThan}), | |
| 682 | .cmp_gte => try self.genCmp(inst, .{.OpFOrdGreaterThanEqual, .OpSGreaterThanEqual, .OpUGreaterThanEqual}), | |
| 683 | .cmp_lt => try self.genCmp(inst, .{.OpFOrdLessThan, .OpSLessThan, .OpULessThan}), | |
| 684 | .cmp_lte => try self.genCmp(inst, .{.OpFOrdLessThanEqual, .OpSLessThanEqual, .OpULessThanEqual}), | |
| 685 | ||
| 686 | .arg => self.genArg(), | |
| 687 | .alloc => try self.genAlloc(inst), | |
| 688 | .block => (try self.genBlock(inst)) orelse return, | |
| 689 | .load => try self.genLoad(inst), | |
| 690 | ||
| 691 | .br => return self.genBr(inst), | |
| 669 | .add, .addwrap => try self.airArithOp(inst, .{.OpFAdd, .OpIAdd, .OpIAdd}), | |
| 670 | .sub, .subwrap => try self.airArithOp(inst, .{.OpFSub, .OpISub, .OpISub}), | |
| 671 | .mul, .mulwrap => try self.airArithOp(inst, .{.OpFMul, .OpIMul, .OpIMul}), | |
| 672 | .div => try self.airArithOp(inst, .{.OpFDiv, .OpSDiv, .OpUDiv}), | |
| 673 | ||
| 674 | .bit_and => try self.airBinOpSimple(inst, .OpBitwiseAnd), | |
| 675 | .bit_or => try self.airBinOpSimple(inst, .OpBitwiseOr), | |
| 676 | .xor => try self.airBinOpSimple(inst, .OpBitwiseXor), | |
| 677 | .bool_and => try self.airBinOpSimple(inst, .OpLogicalAnd), | |
| 678 | .bool_or => try self.airBinOpSimple(inst, .OpLogicalOr), | |
| 679 | ||
| 680 | .not => try self.airNot(inst), | |
| 681 | ||
| 682 | .cmp_eq => try self.airCmp(inst, .{.OpFOrdEqual, .OpLogicalEqual, .OpIEqual}), | |
| 683 | .cmp_neq => try self.airCmp(inst, .{.OpFOrdNotEqual, .OpLogicalNotEqual, .OpINotEqual}), | |
| 684 | .cmp_gt => try self.airCmp(inst, .{.OpFOrdGreaterThan, .OpSGreaterThan, .OpUGreaterThan}), | |
| 685 | .cmp_gte => try self.airCmp(inst, .{.OpFOrdGreaterThanEqual, .OpSGreaterThanEqual, .OpUGreaterThanEqual}), | |
| 686 | .cmp_lt => try self.airCmp(inst, .{.OpFOrdLessThan, .OpSLessThan, .OpULessThan}), | |
| 687 | .cmp_lte => try self.airCmp(inst, .{.OpFOrdLessThanEqual, .OpSLessThanEqual, .OpULessThanEqual}), | |
| 688 | ||
| 689 | .arg => self.airArg(), | |
| 690 | .alloc => try self.airAlloc(inst), | |
| 691 | .block => (try self.airBlock(inst)) orelse return, | |
| 692 | .load => try self.airLoad(inst), | |
| 693 | ||
| 694 | .br => return self.airBr(inst), | |
| 692 | 695 | .breakpoint => return, |
| 693 | .cond_br => return self.genCondBr(inst), | |
| 696 | .cond_br => return self.airCondBr(inst), | |
| 694 | 697 | .constant => unreachable, |
| 695 | .dbg_stmt => return self.genDbgStmt(inst), | |
| 696 | .loop => return self.genLoop(inst), | |
| 697 | .ret => return self.genRet(inst), | |
| 698 | .store => return self.genStore(inst), | |
| 699 | .unreach => return self.genUnreach(), | |
| 698 | .dbg_stmt => return self.airDbgStmt(inst), | |
| 699 | .loop => return self.airLoop(inst), | |
| 700 | .ret => return self.airRet(inst), | |
| 701 | .store => return self.airStore(inst), | |
| 702 | .unreach => return self.airUnreach(), | |
| 700 | 703 | // zig fmt: on |
| 701 | 704 | |
| 702 | 705 | else => |tag| return self.fail("TODO: SPIR-V backend: implement AIR tag {s}", .{ |
| ... | ... | @@ -707,21 +710,22 @@ pub const DeclGen = struct { |
| 707 | 710 | try self.inst_results.putNoClobber(inst, result_id); |
| 708 | 711 | } |
| 709 | 712 | |
| 710 | fn genBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, opcode: Opcode) !ResultId { | |
| 713 | fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, opcode: Opcode) !ResultId { | |
| 711 | 714 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 712 | 715 | const lhs_id = try self.resolve(bin_op.lhs); |
| 713 | 716 | const rhs_id = try self.resolve(bin_op.rhs); |
| 714 | 717 | const result_id = self.spv.allocResultId(); |
| 718 | const result_type_id = try self.genType(self.air.typeOfIndex(inst)); | |
| 715 | 719 | try writeInstruction(&self.code, opcode, &[_]Word{ |
| 716 | 720 | result_type_id, result_id, lhs_id, rhs_id, |
| 717 | 721 | }); |
| 718 | 722 | return result_id; |
| 719 | 723 | } |
| 720 | 724 | |
| 721 | fn genArithOp(self: *DeclGen, inst: Air.Inst.Index, ops: [3]Opcode) !ResultId { | |
| 725 | fn airArithOp(self: *DeclGen, inst: Air.Inst.Index, ops: [3]Opcode) !ResultId { | |
| 722 | 726 | // LHS and RHS are guaranteed to have the same type, and AIR guarantees |
| 723 | 727 | // the result to be the same as the LHS and RHS, which matches SPIR-V. |
| 724 | const ty = self.air.getType(inst); | |
| 728 | const ty = self.air.typeOfIndex(inst); | |
| 725 | 729 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 726 | 730 | const lhs_id = try self.resolve(bin_op.lhs); |
| 727 | 731 | const rhs_id = try self.resolve(bin_op.rhs); |
| ... | ... | @@ -729,8 +733,8 @@ pub const DeclGen = struct { |
| 729 | 733 | const result_id = self.spv.allocResultId(); |
| 730 | 734 | const result_type_id = try self.genType(ty); |
| 731 | 735 | |
| 732 | assert(self.air.getType(bin_op.lhs).eql(ty)); | |
| 733 | assert(self.air.getType(bin_op.rhs).eql(ty)); | |
| 736 | assert(self.air.typeOf(bin_op.lhs).eql(ty)); | |
| 737 | assert(self.air.typeOf(bin_op.rhs).eql(ty)); | |
| 734 | 738 | |
| 735 | 739 | // Binary operations are generally applicable to both scalar and vector operations |
| 736 | 740 | // in SPIR-V, but int and float versions of operations require different opcodes. |
| ... | ... | @@ -744,8 +748,8 @@ pub const DeclGen = struct { |
| 744 | 748 | return self.fail("TODO: SPIR-V backend: binary operations for strange integers", .{}); |
| 745 | 749 | }, |
| 746 | 750 | .integer => switch (info.signedness) { |
| 747 | .signed => 1, | |
| 748 | .unsigned => 2, | |
| 751 | .signed => @as(usize, 1), | |
| 752 | .unsigned => @as(usize, 2), | |
| 749 | 753 | }, |
| 750 | 754 | .float => 0, |
| 751 | 755 | else => unreachable, |
| ... | ... | @@ -759,14 +763,14 @@ pub const DeclGen = struct { |
| 759 | 763 | return result_id; |
| 760 | 764 | } |
| 761 | 765 | |
| 762 | fn genCmp(self: *DeclGen, inst: Air.Inst.Index, ops: [3]Opcode) !ResultId { | |
| 766 | fn airCmp(self: *DeclGen, inst: Air.Inst.Index, ops: [3]Opcode) !ResultId { | |
| 763 | 767 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 764 | 768 | const lhs_id = try self.resolve(bin_op.lhs); |
| 765 | 769 | const rhs_id = try self.resolve(bin_op.rhs); |
| 766 | 770 | const result_id = self.spv.allocResultId(); |
| 767 | 771 | const result_type_id = try self.genType(Type.initTag(.bool)); |
| 768 | const op_ty = self.air.getType(bin_op.lhs); | |
| 769 | assert(op_ty.eql(self.air.getType(bin_op.rhs))); | |
| 772 | const op_ty = self.air.typeOf(bin_op.lhs); | |
| 773 | assert(op_ty.eql(self.air.typeOf(bin_op.rhs))); | |
| 770 | 774 | |
| 771 | 775 | // Comparisons are generally applicable to both scalar and vector operations in SPIR-V, |
| 772 | 776 | // but int and float versions of operations require different opcodes. |
| ... | ... | @@ -782,10 +786,9 @@ pub const DeclGen = struct { |
| 782 | 786 | .float => 0, |
| 783 | 787 | .bool => 1, |
| 784 | 788 | .integer => switch (info.signedness) { |
| 785 | .signed => 1, | |
| 786 | .unsigned => 2, | |
| 789 | .signed => @as(usize, 1), | |
| 790 | .unsigned => @as(usize, 2), | |
| 787 | 791 | }, |
| 788 | else => unreachable, | |
| 789 | 792 | }; |
| 790 | 793 | const opcode = ops[opcode_index]; |
| 791 | 794 | |
| ... | ... | @@ -793,7 +796,7 @@ pub const DeclGen = struct { |
| 793 | 796 | return result_id; |
| 794 | 797 | } |
| 795 | 798 | |
| 796 | fn genNot(self: *DeclGen, inst: Air.Inst.Index) !ResultId { | |
| 799 | fn airNot(self: *DeclGen, inst: Air.Inst.Index) !ResultId { | |
| 797 | 800 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 798 | 801 | const operand_id = try self.resolve(ty_op.operand); |
| 799 | 802 | const result_id = self.spv.allocResultId(); |
| ... | ... | @@ -803,8 +806,8 @@ pub const DeclGen = struct { |
| 803 | 806 | return result_id; |
| 804 | 807 | } |
| 805 | 808 | |
| 806 | fn genAlloc(self: *DeclGen, inst: Air.Inst.Index) !ResultId { | |
| 807 | const ty = self.air.getType(inst); | |
| 809 | fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !ResultId { | |
| 810 | const ty = self.air.typeOfIndex(inst); | |
| 808 | 811 | const storage_class = spec.StorageClass.Function; |
| 809 | 812 | const result_type_id = try self.genPointerType(ty, storage_class); |
| 810 | 813 | const result_id = self.spv.allocResultId(); |
| ... | ... | @@ -816,12 +819,12 @@ pub const DeclGen = struct { |
| 816 | 819 | return result_id; |
| 817 | 820 | } |
| 818 | 821 | |
| 819 | fn genArg(self: *DeclGen) ResultId { | |
| 822 | fn airArg(self: *DeclGen) ResultId { | |
| 820 | 823 | defer self.next_arg_index += 1; |
| 821 | 824 | return self.args.items[self.next_arg_index]; |
| 822 | 825 | } |
| 823 | 826 | |
| 824 | fn genBlock(self: *DeclGen, inst: Air.Inst.Index) !?ResultId { | |
| 827 | fn airBlock(self: *DeclGen, inst: Air.Inst.Index) !?ResultId { | |
| 825 | 828 | // In IR, a block doesn't really define an entry point like a block, but more like a scope that breaks can jump out of and |
| 826 | 829 | // "return" a value from. This cannot be directly modelled in SPIR-V, so in a block instruction, we're going to split up |
| 827 | 830 | // the current block by first generating the code of the block, then a label, and then generate the rest of the current |
| ... | ... | @@ -841,7 +844,7 @@ pub const DeclGen = struct { |
| 841 | 844 | incoming_blocks.deinit(self.spv.gpa); |
| 842 | 845 | } |
| 843 | 846 | |
| 844 | const ty = self.air.getType(inst); | |
| 847 | const ty = self.air.typeOfIndex(inst); | |
| 845 | 848 | const inst_datas = self.air.instructions.items(.data); |
| 846 | 849 | const extra = self.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload); |
| 847 | 850 | const body = self.air.extra[extra.end..][0..extra.data.body_len]; |
| ... | ... | @@ -872,10 +875,10 @@ pub const DeclGen = struct { |
| 872 | 875 | return result_id; |
| 873 | 876 | } |
| 874 | 877 | |
| 875 | fn genBr(self: *DeclGen, inst: Air.Inst.Index) !void { | |
| 878 | fn airBr(self: *DeclGen, inst: Air.Inst.Index) !void { | |
| 876 | 879 | const br = self.air.instructions.items(.data)[inst].br; |
| 877 | 880 | const block = self.blocks.get(br.block_inst).?; |
| 878 | const operand_ty = self.air.getType(br.operand); | |
| 881 | const operand_ty = self.air.typeOf(br.operand); | |
| 879 | 882 | |
| 880 | 883 | if (operand_ty.hasCodeGenBits()) { |
| 881 | 884 | const operand_id = try self.resolve(br.operand); |
| ... | ... | @@ -886,7 +889,7 @@ pub const DeclGen = struct { |
| 886 | 889 | try writeInstruction(&self.code, .OpBranch, &[_]Word{block.label_id}); |
| 887 | 890 | } |
| 888 | 891 | |
| 889 | fn genCondBr(self: *DeclGen, inst: *Inst.CondBr) !void { | |
| 892 | fn airCondBr(self: *DeclGen, inst: Air.Inst.Index) !void { | |
| 890 | 893 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 891 | 894 | const cond_br = self.air.extraData(Air.CondBr, pl_op.payload); |
| 892 | 895 | const then_body = self.air.extra[cond_br.end..][0..cond_br.data.then_body_len]; |
| ... | ... | @@ -912,16 +915,16 @@ pub const DeclGen = struct { |
| 912 | 915 | try self.genBody(else_body); |
| 913 | 916 | } |
| 914 | 917 | |
| 915 | fn genDbgStmt(self: *DeclGen, inst: Air.Inst.Index) !void { | |
| 918 | fn airDbgStmt(self: *DeclGen, inst: Air.Inst.Index) !void { | |
| 916 | 919 | const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; |
| 917 | 920 | const src_fname_id = try self.spv.resolveSourceFileName(self.decl); |
| 918 | 921 | try writeInstruction(&self.code, .OpLine, &[_]Word{ src_fname_id, dbg_stmt.line, dbg_stmt.column }); |
| 919 | 922 | } |
| 920 | 923 | |
| 921 | fn genLoad(self: *DeclGen, inst: Air.Inst.Index) !ResultId { | |
| 924 | fn airLoad(self: *DeclGen, inst: Air.Inst.Index) !ResultId { | |
| 922 | 925 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 923 | 926 | const operand_id = try self.resolve(ty_op.operand); |
| 924 | const ty = self.air.getType(inst); | |
| 927 | const ty = self.air.typeOfIndex(inst); | |
| 925 | 928 | |
| 926 | 929 | const result_type_id = try self.genType(ty); |
| 927 | 930 | const result_id = self.spv.allocResultId(); |
| ... | ... | @@ -936,8 +939,9 @@ pub const DeclGen = struct { |
| 936 | 939 | return result_id; |
| 937 | 940 | } |
| 938 | 941 | |
| 939 | fn genLoop(self: *DeclGen, inst: Air.Inst.Index) !void { | |
| 940 | const loop = self.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload); | |
| 942 | fn airLoop(self: *DeclGen, inst: Air.Inst.Index) !void { | |
| 943 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | |
| 944 | const loop = self.air.extraData(Air.Block, ty_pl.payload); | |
| 941 | 945 | const body = self.air.extra[loop.end..][0..loop.data.body_len]; |
| 942 | 946 | const loop_label_id = self.spv.allocResultId(); |
| 943 | 947 | |
| ... | ... | @@ -952,9 +956,9 @@ pub const DeclGen = struct { |
| 952 | 956 | try writeInstruction(&self.code, .OpBranch, &[_]Word{loop_label_id}); |
| 953 | 957 | } |
| 954 | 958 | |
| 955 | fn genRet(self: *DeclGen, inst: Air.Inst.Index) !void { | |
| 956 | const operand = inst_datas[inst].un_op; | |
| 957 | const operand_ty = self.air.getType(operand); | |
| 959 | fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void { | |
| 960 | const operand = self.air.instructions.items(.data)[inst].un_op; | |
| 961 | const operand_ty = self.air.typeOf(operand); | |
| 958 | 962 | if (operand_ty.hasCodeGenBits()) { |
| 959 | 963 | const operand_id = try self.resolve(operand); |
| 960 | 964 | try writeInstruction(&self.code, .OpReturnValue, &[_]Word{operand_id}); |
| ... | ... | @@ -963,11 +967,11 @@ pub const DeclGen = struct { |
| 963 | 967 | } |
| 964 | 968 | } |
| 965 | 969 | |
| 966 | fn genStore(self: *DeclGen, inst: Air.Inst.Index) !void { | |
| 970 | fn airStore(self: *DeclGen, inst: Air.Inst.Index) !void { | |
| 967 | 971 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 968 | 972 | const dst_ptr_id = try self.resolve(bin_op.lhs); |
| 969 | 973 | const src_val_id = try self.resolve(bin_op.rhs); |
| 970 | const lhs_ty = self.air.getType(bin_op.lhs); | |
| 974 | const lhs_ty = self.air.typeOf(bin_op.lhs); | |
| 971 | 975 | |
| 972 | 976 | const operands = if (lhs_ty.isVolatilePtr()) |
| 973 | 977 | &[_]Word{ dst_ptr_id, src_val_id, @bitCast(u32, spec.MemoryAccess{ .Volatile = true }) } |
| ... | ... | @@ -977,7 +981,7 @@ pub const DeclGen = struct { |
| 977 | 981 | try writeInstruction(&self.code, .OpStore, operands); |
| 978 | 982 | } |
| 979 | 983 | |
| 980 | fn genUnreach(self: *DeclGen) !void { | |
| 984 | fn airUnreach(self: *DeclGen) !void { | |
| 981 | 985 | try writeInstruction(&self.code, .OpUnreachable, &[_]Word{}); |
| 982 | 986 | } |
| 983 | 987 | }; |
src/codegen/wasm.zig+4-4| ... | ... | @@ -774,7 +774,7 @@ pub const Context = struct { |
| 774 | 774 | } |
| 775 | 775 | } |
| 776 | 776 | return Result{ .externally_managed = payload.data }; |
| 777 | } else return self.fail(.{ .node_offset = 0 }, "TODO implement gen for more kinds of arrays", .{}); | |
| 777 | } else return self.fail("TODO implement gen for more kinds of arrays", .{}); | |
| 778 | 778 | }, |
| 779 | 779 | .Int => { |
| 780 | 780 | const info = typed_value.ty.intInfo(self.target); |
| ... | ... | @@ -783,9 +783,9 @@ pub const Context = struct { |
| 783 | 783 | try self.code.append(@intCast(u8, int_byte)); |
| 784 | 784 | return Result.appended; |
| 785 | 785 | } |
| 786 | return self.fail(.{ .node_offset = 0 }, "TODO: Implement codegen for int type: '{}'", .{typed_value.ty}); | |
| 786 | return self.fail("TODO: Implement codegen for int type: '{}'", .{typed_value.ty}); | |
| 787 | 787 | }, |
| 788 | else => |tag| return self.fail(.{ .node_offset = 0 }, "TODO: Implement zig type codegen for type: '{s}'", .{tag}), | |
| 788 | else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}), | |
| 789 | 789 | } |
| 790 | 790 | } |
| 791 | 791 | |
| ... | ... | @@ -883,7 +883,7 @@ pub const Context = struct { |
| 883 | 883 | } |
| 884 | 884 | |
| 885 | 885 | fn genAlloc(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| 886 | const elem_type = self.air.getType(inst).elemType(); | |
| 886 | const elem_type = self.air.typeOfIndex(inst).elemType(); | |
| 887 | 887 | return self.allocLocal(elem_type); |
| 888 | 888 | } |
| 889 | 889 |
src/link/Coff.zig+10-5| ... | ... | @@ -657,11 +657,16 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void { |
| 657 | 657 | } |
| 658 | 658 | |
| 659 | 659 | pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { |
| 660 | if (build_options.skip_non_native and builtin.object_format != .coff and builtin.object_format != .pe) { | |
| 660 | if (build_options.skip_non_native and | |
| 661 | builtin.object_format != .coff and | |
| 662 | builtin.object_format != .pe) | |
| 663 | { | |
| 661 | 664 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 662 | 665 | } |
| 663 | 666 | if (build_options.have_llvm) { |
| 664 | if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(module, func, air, liveness); | |
| 667 | if (self.llvm_object) |llvm_object| { | |
| 668 | return llvm_object.updateFunc(module, func, air, liveness); | |
| 669 | } | |
| 665 | 670 | } |
| 666 | 671 | const tracy = trace(@src()); |
| 667 | 672 | defer tracy.end(); |
| ... | ... | @@ -669,6 +674,7 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live |
| 669 | 674 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 670 | 675 | defer code_buffer.deinit(); |
| 671 | 676 | |
| 677 | const decl = func.owner_decl; | |
| 672 | 678 | const res = try codegen.generateFunction( |
| 673 | 679 | &self.base, |
| 674 | 680 | decl.srcLoc(), |
| ... | ... | @@ -679,7 +685,6 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live |
| 679 | 685 | .none, |
| 680 | 686 | ); |
| 681 | 687 | const code = switch (res) { |
| 682 | .externally_managed => |x| x, | |
| 683 | 688 | .appended => code_buffer.items, |
| 684 | 689 | .fail => |em| { |
| 685 | 690 | decl.analysis = .codegen_failure; |
| ... | ... | @@ -725,10 +730,10 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { |
| 725 | 730 | }, |
| 726 | 731 | }; |
| 727 | 732 | |
| 728 | return self.finishUpdateDecl(module, func.owner_decl, code); | |
| 733 | return self.finishUpdateDecl(module, decl, code); | |
| 729 | 734 | } |
| 730 | 735 | |
| 731 | fn finishUpdateDecl(self: *Coff, decl: *Module.Decl, code: []const u8) !void { | |
| 736 | fn finishUpdateDecl(self: *Coff, module: *Module, decl: *Module.Decl, code: []const u8) !void { | |
| 732 | 737 | const required_alignment = decl.ty.abiAlignment(self.base.options.target); |
| 733 | 738 | const curr_size = decl.link.coff.size; |
| 734 | 739 | if (curr_size != 0) { |
src/link/MachO.zig+138-107| ... | ... | @@ -1150,9 +1150,13 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv |
| 1150 | 1150 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 1151 | 1151 | defer code_buffer.deinit(); |
| 1152 | 1152 | |
| 1153 | var debug_buffers = if (self.d_sym) |*ds| try ds.initDeclDebugBuffers(self.base.allocator, module, decl) else null; | |
| 1153 | var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined; | |
| 1154 | const debug_buffers = if (self.d_sym) |*ds| blk: { | |
| 1155 | debug_buffers_buf = try ds.initDeclDebugBuffers(self.base.allocator, module, decl); | |
| 1156 | break :blk &debug_buffers_buf; | |
| 1157 | } else null; | |
| 1154 | 1158 | defer { |
| 1155 | if (debug_buffers) |*dbg| { | |
| 1159 | if (debug_buffers) |dbg| { | |
| 1156 | 1160 | dbg.dbg_line_buffer.deinit(); |
| 1157 | 1161 | dbg.dbg_info_buffer.deinit(); |
| 1158 | 1162 | var it = dbg.dbg_info_type_relocs.valueIterator(); |
| ... | ... | @@ -1163,7 +1167,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv |
| 1163 | 1167 | } |
| 1164 | 1168 | } |
| 1165 | 1169 | |
| 1166 | const res = if (debug_buffers) |*dbg| | |
| 1170 | const res = if (debug_buffers) |dbg| | |
| 1167 | 1171 | try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{ |
| 1168 | 1172 | .dwarf = .{ |
| 1169 | 1173 | .dbg_line = &dbg.dbg_line_buffer, |
| ... | ... | @@ -1172,9 +1176,109 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv |
| 1172 | 1176 | }, |
| 1173 | 1177 | }) |
| 1174 | 1178 | else |
| 1175 | try codegen.generateSymbol(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); | |
| 1179 | try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); | |
| 1180 | switch (res) { | |
| 1181 | .appended => {}, | |
| 1182 | .fail => |em| { | |
| 1183 | // Clear any PIE fixups for this decl. | |
| 1184 | self.pie_fixups.shrinkRetainingCapacity(0); | |
| 1185 | // Clear any stub fixups for this decl. | |
| 1186 | self.stub_fixups.shrinkRetainingCapacity(0); | |
| 1187 | decl.analysis = .codegen_failure; | |
| 1188 | try module.failed_decls.put(module.gpa, decl, em); | |
| 1189 | return; | |
| 1190 | }, | |
| 1191 | } | |
| 1192 | const symbol = try self.placeDecl(decl, code_buffer.items.len); | |
| 1193 | ||
| 1194 | // Calculate displacements to target addr (if any). | |
| 1195 | while (self.pie_fixups.popOrNull()) |fixup| { | |
| 1196 | assert(fixup.size == 4); | |
| 1197 | const this_addr = symbol.n_value + fixup.offset; | |
| 1198 | const target_addr = fixup.target_addr; | |
| 1199 | ||
| 1200 | switch (self.base.options.target.cpu.arch) { | |
| 1201 | .x86_64 => { | |
| 1202 | const displacement = try math.cast(u32, target_addr - this_addr - 4); | |
| 1203 | mem.writeIntLittle(u32, code_buffer.items[fixup.offset..][0..4], displacement); | |
| 1204 | }, | |
| 1205 | .aarch64 => { | |
| 1206 | // TODO optimize instruction based on jump length (use ldr(literal) + nop if possible). | |
| 1207 | { | |
| 1208 | const inst = code_buffer.items[fixup.offset..][0..4]; | |
| 1209 | const parsed = mem.bytesAsValue(meta.TagPayload( | |
| 1210 | aarch64.Instruction, | |
| 1211 | aarch64.Instruction.pc_relative_address, | |
| 1212 | ), inst); | |
| 1213 | const this_page = @intCast(i32, this_addr >> 12); | |
| 1214 | const target_page = @intCast(i32, target_addr >> 12); | |
| 1215 | const pages = @bitCast(u21, @intCast(i21, target_page - this_page)); | |
| 1216 | parsed.immhi = @truncate(u19, pages >> 2); | |
| 1217 | parsed.immlo = @truncate(u2, pages); | |
| 1218 | } | |
| 1219 | { | |
| 1220 | const inst = code_buffer.items[fixup.offset + 4 ..][0..4]; | |
| 1221 | const parsed = mem.bytesAsValue(meta.TagPayload( | |
| 1222 | aarch64.Instruction, | |
| 1223 | aarch64.Instruction.load_store_register, | |
| 1224 | ), inst); | |
| 1225 | const narrowed = @truncate(u12, target_addr); | |
| 1226 | const offset = try math.divExact(u12, narrowed, 8); | |
| 1227 | parsed.offset = offset; | |
| 1228 | } | |
| 1229 | }, | |
| 1230 | else => unreachable, // unsupported target architecture | |
| 1231 | } | |
| 1232 | } | |
| 1233 | ||
| 1234 | // Resolve stubs (if any) | |
| 1235 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1236 | const stubs = text_segment.sections.items[self.stubs_section_index.?]; | |
| 1237 | for (self.stub_fixups.items) |fixup| { | |
| 1238 | const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2; | |
| 1239 | const text_addr = symbol.n_value + fixup.start; | |
| 1240 | switch (self.base.options.target.cpu.arch) { | |
| 1241 | .x86_64 => { | |
| 1242 | assert(stub_addr >= text_addr + fixup.len); | |
| 1243 | const displacement = try math.cast(u32, stub_addr - text_addr - fixup.len); | |
| 1244 | const placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; | |
| 1245 | mem.writeIntSliceLittle(u32, placeholder, displacement); | |
| 1246 | }, | |
| 1247 | .aarch64 => { | |
| 1248 | assert(stub_addr >= text_addr); | |
| 1249 | const displacement = try math.cast(i28, stub_addr - text_addr); | |
| 1250 | const placeholder = code_buffer.items[fixup.start..][0..fixup.len]; | |
| 1251 | mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(displacement).toU32()); | |
| 1252 | }, | |
| 1253 | else => unreachable, // unsupported target architecture | |
| 1254 | } | |
| 1255 | if (!fixup.already_defined) { | |
| 1256 | try self.writeStub(fixup.symbol); | |
| 1257 | try self.writeStubInStubHelper(fixup.symbol); | |
| 1258 | try self.writeLazySymbolPointer(fixup.symbol); | |
| 1259 | ||
| 1260 | self.rebase_info_dirty = true; | |
| 1261 | self.lazy_binding_info_dirty = true; | |
| 1262 | } | |
| 1263 | } | |
| 1264 | self.stub_fixups.shrinkRetainingCapacity(0); | |
| 1265 | ||
| 1266 | try self.writeCode(symbol, code_buffer.items); | |
| 1176 | 1267 | |
| 1177 | return self.finishUpdateDecl(module, decl, res); | |
| 1268 | if (debug_buffers) |db| { | |
| 1269 | try self.d_sym.?.commitDeclDebugInfo( | |
| 1270 | self.base.allocator, | |
| 1271 | module, | |
| 1272 | decl, | |
| 1273 | db, | |
| 1274 | self.base.options.target, | |
| 1275 | ); | |
| 1276 | } | |
| 1277 | ||
| 1278 | // Since we updated the vaddr and the size, each corresponding export symbol also | |
| 1279 | // needs to be updated. | |
| 1280 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; | |
| 1281 | try self.updateDeclExports(module, decl, decl_exports); | |
| 1178 | 1282 | } |
| 1179 | 1283 | |
| 1180 | 1284 | pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| ... | ... | @@ -1194,9 +1298,13 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1194 | 1298 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 1195 | 1299 | defer code_buffer.deinit(); |
| 1196 | 1300 | |
| 1197 | var debug_buffers = if (self.d_sym) |*ds| try ds.initDeclDebugBuffers(self.base.allocator, module, decl) else null; | |
| 1301 | var debug_buffers_buf: DebugSymbols.DeclDebugBuffers = undefined; | |
| 1302 | const debug_buffers = if (self.d_sym) |*ds| blk: { | |
| 1303 | debug_buffers_buf = try ds.initDeclDebugBuffers(self.base.allocator, module, decl); | |
| 1304 | break :blk &debug_buffers_buf; | |
| 1305 | } else null; | |
| 1198 | 1306 | defer { |
| 1199 | if (debug_buffers) |*dbg| { | |
| 1307 | if (debug_buffers) |dbg| { | |
| 1200 | 1308 | dbg.dbg_line_buffer.deinit(); |
| 1201 | 1309 | dbg.dbg_info_buffer.deinit(); |
| 1202 | 1310 | var it = dbg.dbg_info_type_relocs.valueIterator(); |
| ... | ... | @@ -1207,7 +1315,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1207 | 1315 | } |
| 1208 | 1316 | } |
| 1209 | 1317 | |
| 1210 | const res = if (debug_buffers) |*dbg| | |
| 1318 | const res = if (debug_buffers) |dbg| | |
| 1211 | 1319 | try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ |
| 1212 | 1320 | .ty = decl.ty, |
| 1213 | 1321 | .val = decl.val, |
| ... | ... | @@ -1224,33 +1332,37 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1224 | 1332 | .val = decl.val, |
| 1225 | 1333 | }, &code_buffer, .none); |
| 1226 | 1334 | |
| 1227 | return self.finishUpdateDecl(module, decl, res); | |
| 1228 | } | |
| 1229 | ||
| 1230 | fn finishUpdateDecl(self: *MachO, module: *Module, decl: *Module.Decl, res: codegen.Result) !void { | |
| 1231 | 1335 | const code = switch (res) { |
| 1232 | 1336 | .externally_managed => |x| x, |
| 1233 | 1337 | .appended => code_buffer.items, |
| 1234 | 1338 | .fail => |em| { |
| 1235 | // Clear any PIE fixups for this decl. | |
| 1236 | self.pie_fixups.shrinkRetainingCapacity(0); | |
| 1237 | // Clear any stub fixups for this decl. | |
| 1238 | self.stub_fixups.shrinkRetainingCapacity(0); | |
| 1239 | 1339 | decl.analysis = .codegen_failure; |
| 1240 | 1340 | try module.failed_decls.put(module.gpa, decl, em); |
| 1241 | 1341 | return; |
| 1242 | 1342 | }, |
| 1243 | 1343 | }; |
| 1344 | const symbol = try self.placeDecl(decl, code.len); | |
| 1345 | assert(self.pie_fixups.items.len == 0); | |
| 1346 | assert(self.stub_fixups.items.len == 0); | |
| 1244 | 1347 | |
| 1348 | try self.writeCode(symbol, code); | |
| 1349 | ||
| 1350 | // Since we updated the vaddr and the size, each corresponding export symbol also | |
| 1351 | // needs to be updated. | |
| 1352 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; | |
| 1353 | try self.updateDeclExports(module, decl, decl_exports); | |
| 1354 | } | |
| 1355 | ||
| 1356 | fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 { | |
| 1245 | 1357 | const required_alignment = decl.ty.abiAlignment(self.base.options.target); |
| 1246 | 1358 | assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes() |
| 1247 | 1359 | const symbol = &self.locals.items[decl.link.macho.local_sym_index]; |
| 1248 | 1360 | |
| 1249 | 1361 | if (decl.link.macho.size != 0) { |
| 1250 | 1362 | const capacity = decl.link.macho.capacity(self.*); |
| 1251 | const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment); | |
| 1363 | const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment); | |
| 1252 | 1364 | if (need_realloc) { |
| 1253 | const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment); | |
| 1365 | const vaddr = try self.growTextBlock(&decl.link.macho, code_len, required_alignment); | |
| 1254 | 1366 | |
| 1255 | 1367 | log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr }); |
| 1256 | 1368 | |
| ... | ... | @@ -1265,10 +1377,10 @@ fn finishUpdateDecl(self: *MachO, module: *Module, decl: *Module.Decl, res: code |
| 1265 | 1377 | } |
| 1266 | 1378 | |
| 1267 | 1379 | symbol.n_value = vaddr; |
| 1268 | } else if (code.len < decl.link.macho.size) { | |
| 1269 | self.shrinkTextBlock(&decl.link.macho, code.len); | |
| 1380 | } else if (code_len < decl.link.macho.size) { | |
| 1381 | self.shrinkTextBlock(&decl.link.macho, code_len); | |
| 1270 | 1382 | } |
| 1271 | decl.link.macho.size = code.len; | |
| 1383 | decl.link.macho.size = code_len; | |
| 1272 | 1384 | |
| 1273 | 1385 | const new_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)}); |
| 1274 | 1386 | defer self.base.allocator.free(new_name); |
| ... | ... | @@ -1286,7 +1398,7 @@ fn finishUpdateDecl(self: *MachO, module: *Module, decl: *Module.Decl, res: code |
| 1286 | 1398 | defer self.base.allocator.free(decl_name); |
| 1287 | 1399 | |
| 1288 | 1400 | const name_str_index = try self.makeString(decl_name); |
| 1289 | const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment); | |
| 1401 | const addr = try self.allocateTextBlock(&decl.link.macho, code_len, required_alignment); | |
| 1290 | 1402 | |
| 1291 | 1403 | log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, addr }); |
| 1292 | 1404 | |
| ... | ... | @@ -1311,96 +1423,15 @@ fn finishUpdateDecl(self: *MachO, module: *Module, decl: *Module.Decl, res: code |
| 1311 | 1423 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); |
| 1312 | 1424 | } |
| 1313 | 1425 | |
| 1314 | // Calculate displacements to target addr (if any). | |
| 1315 | while (self.pie_fixups.popOrNull()) |fixup| { | |
| 1316 | assert(fixup.size == 4); | |
| 1317 | const this_addr = symbol.n_value + fixup.offset; | |
| 1318 | const target_addr = fixup.target_addr; | |
| 1319 | ||
| 1320 | switch (self.base.options.target.cpu.arch) { | |
| 1321 | .x86_64 => { | |
| 1322 | const displacement = try math.cast(u32, target_addr - this_addr - 4); | |
| 1323 | mem.writeIntLittle(u32, code_buffer.items[fixup.offset..][0..4], displacement); | |
| 1324 | }, | |
| 1325 | .aarch64 => { | |
| 1326 | // TODO optimize instruction based on jump length (use ldr(literal) + nop if possible). | |
| 1327 | { | |
| 1328 | const inst = code_buffer.items[fixup.offset..][0..4]; | |
| 1329 | var parsed = mem.bytesAsValue(meta.TagPayload( | |
| 1330 | aarch64.Instruction, | |
| 1331 | aarch64.Instruction.pc_relative_address, | |
| 1332 | ), inst); | |
| 1333 | const this_page = @intCast(i32, this_addr >> 12); | |
| 1334 | const target_page = @intCast(i32, target_addr >> 12); | |
| 1335 | const pages = @bitCast(u21, @intCast(i21, target_page - this_page)); | |
| 1336 | parsed.immhi = @truncate(u19, pages >> 2); | |
| 1337 | parsed.immlo = @truncate(u2, pages); | |
| 1338 | } | |
| 1339 | { | |
| 1340 | const inst = code_buffer.items[fixup.offset + 4 ..][0..4]; | |
| 1341 | var parsed = mem.bytesAsValue(meta.TagPayload( | |
| 1342 | aarch64.Instruction, | |
| 1343 | aarch64.Instruction.load_store_register, | |
| 1344 | ), inst); | |
| 1345 | const narrowed = @truncate(u12, target_addr); | |
| 1346 | const offset = try math.divExact(u12, narrowed, 8); | |
| 1347 | parsed.offset = offset; | |
| 1348 | } | |
| 1349 | }, | |
| 1350 | else => unreachable, // unsupported target architecture | |
| 1351 | } | |
| 1352 | } | |
| 1353 | ||
| 1354 | // Resolve stubs (if any) | |
| 1355 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1356 | const stubs = text_segment.sections.items[self.stubs_section_index.?]; | |
| 1357 | for (self.stub_fixups.items) |fixup| { | |
| 1358 | const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2; | |
| 1359 | const text_addr = symbol.n_value + fixup.start; | |
| 1360 | switch (self.base.options.target.cpu.arch) { | |
| 1361 | .x86_64 => { | |
| 1362 | assert(stub_addr >= text_addr + fixup.len); | |
| 1363 | const displacement = try math.cast(u32, stub_addr - text_addr - fixup.len); | |
| 1364 | var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)]; | |
| 1365 | mem.writeIntSliceLittle(u32, placeholder, displacement); | |
| 1366 | }, | |
| 1367 | .aarch64 => { | |
| 1368 | assert(stub_addr >= text_addr); | |
| 1369 | const displacement = try math.cast(i28, stub_addr - text_addr); | |
| 1370 | var placeholder = code_buffer.items[fixup.start..][0..fixup.len]; | |
| 1371 | mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(displacement).toU32()); | |
| 1372 | }, | |
| 1373 | else => unreachable, // unsupported target architecture | |
| 1374 | } | |
| 1375 | if (!fixup.already_defined) { | |
| 1376 | try self.writeStub(fixup.symbol); | |
| 1377 | try self.writeStubInStubHelper(fixup.symbol); | |
| 1378 | try self.writeLazySymbolPointer(fixup.symbol); | |
| 1379 | ||
| 1380 | self.rebase_info_dirty = true; | |
| 1381 | self.lazy_binding_info_dirty = true; | |
| 1382 | } | |
| 1383 | } | |
| 1384 | self.stub_fixups.shrinkRetainingCapacity(0); | |
| 1426 | return symbol; | |
| 1427 | } | |
| 1385 | 1428 | |
| 1429 | fn writeCode(self: *MachO, symbol: *macho.nlist_64, code: []const u8) !void { | |
| 1430 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | |
| 1386 | 1431 | const text_section = text_segment.sections.items[self.text_section_index.?]; |
| 1387 | 1432 | const section_offset = symbol.n_value - text_section.addr; |
| 1388 | 1433 | const file_offset = text_section.offset + section_offset; |
| 1389 | 1434 | try self.base.file.?.pwriteAll(code, file_offset); |
| 1390 | ||
| 1391 | if (debug_buffers) |*db| { | |
| 1392 | try self.d_sym.?.commitDeclDebugInfo( | |
| 1393 | self.base.allocator, | |
| 1394 | module, | |
| 1395 | decl, | |
| 1396 | db, | |
| 1397 | self.base.options.target, | |
| 1398 | ); | |
| 1399 | } | |
| 1400 | ||
| 1401 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. | |
| 1402 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; | |
| 1403 | try self.updateDeclExports(module, decl, decl_exports); | |
| 1404 | 1435 | } |
| 1405 | 1436 | |
| 1406 | 1437 | pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void { |
src/link/SpirV.zig+13-3| ... | ... | @@ -51,7 +51,12 @@ base: link.File, |
| 51 | 51 | /// This linker backend does not try to incrementally link output SPIR-V code. |
| 52 | 52 | /// Instead, it tracks all declarations in this table, and iterates over it |
| 53 | 53 | /// in the flush function. |
| 54 | decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, void) = .{}, | |
| 54 | decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, DeclGenContext) = .{}, | |
| 55 | ||
| 56 | const DeclGenContext = struct { | |
| 57 | air: Air, | |
| 58 | liveness: Liveness, | |
| 59 | }; | |
| 55 | 60 | |
| 56 | 61 | pub fn createEmpty(gpa: *Allocator, options: link.Options) !*SpirV { |
| 57 | 62 | const spirv = try gpa.create(SpirV); |
| ... | ... | @@ -181,10 +186,15 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void { |
| 181 | 186 | var decl_gen = codegen.DeclGen.init(&spv); |
| 182 | 187 | defer decl_gen.deinit(); |
| 183 | 188 | |
| 184 | for (self.decl_table.keys()) |decl| { | |
| 189 | var it = self.decl_table.iterator(); | |
| 190 | while (it.next()) |entry| { | |
| 191 | const decl = entry.key_ptr.*; | |
| 185 | 192 | if (!decl.has_tv) continue; |
| 186 | 193 | |
| 187 | if (try decl_gen.gen(decl)) |msg| { | |
| 194 | const air = entry.value_ptr.air; | |
| 195 | const liveness = entry.value_ptr.liveness; | |
| 196 | ||
| 197 | if (try decl_gen.gen(decl, air, liveness)) |msg| { | |
| 188 | 198 | try module.failed_decls.put(module.gpa, decl, msg); |
| 189 | 199 | return; // TODO: Attempt to generate more decls? |
| 190 | 200 | } |
src/link/Wasm.zig+2| ... | ... | @@ -250,6 +250,8 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { |
| 250 | 250 | |
| 251 | 251 | var context = codegen.Context{ |
| 252 | 252 | .gpa = self.base.allocator, |
| 253 | .air = undefined, | |
| 254 | .liveness = undefined, | |
| 253 | 255 | .values = .{}, |
| 254 | 256 | .code = fn_data.code.toManaged(self.base.allocator), |
| 255 | 257 | .func_type_data = fn_data.functype.toManaged(self.base.allocator), |