| ... | ... | @@ -409,14 +409,17 @@ fn gen(self: *Self) !void { |
| 409 | 409 | }); |
| 410 | 410 | |
| 411 | 411 | // exitlude jumps |
| 412 | | if (self.exitlude_jump_relocs.items.len == 1) { |
| 413 | | // There is only one relocation. Hence, |
| 414 | | // this relocation must be at the end of |
| 415 | | // the code. Therefore, we can just delete |
| 416 | | // the space initially reserved for the |
| 417 | | // jump |
| 418 | | self.mir_instructions.len -= 1; |
| 419 | | } else for (self.exitlude_jump_relocs.items) |jmp_reloc| { |
| 412 | if (self.exitlude_jump_relocs.items.len > 0 and |
| 413 | self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2) |
| 414 | { |
| 415 | // If the last Mir instruction (apart from the |
| 416 | // dbg_epilogue_begin) is the last exitlude jump |
| 417 | // relocation (which would just jump one instruction |
| 418 | // further), it can be safely removed |
| 419 | self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop()); |
| 420 | } |
| 421 | |
| 422 | for (self.exitlude_jump_relocs.items) |jmp_reloc| { |
| 420 | 423 | _ = jmp_reloc; |
| 421 | 424 | return self.fail("TODO add branches in RISCV64", .{}); |
| 422 | 425 | } |
| ... | ... | @@ -489,10 +492,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 489 | 492 | |
| 490 | 493 | switch (air_tags[inst]) { |
| 491 | 494 | // zig fmt: off |
| 492 | | .add, .ptr_add => try self.airAdd(inst), |
| 495 | .add, .ptr_add => try self.airBinOp(inst), |
| 493 | 496 | .addwrap => try self.airAddWrap(inst), |
| 494 | 497 | .add_sat => try self.airAddSat(inst), |
| 495 | | .sub, .ptr_sub => try self.airSub(inst), |
| 498 | .sub, .ptr_sub => try self.airBinOp(inst), |
| 496 | 499 | .subwrap => try self.airSubWrap(inst), |
| 497 | 500 | .sub_sat => try self.airSubSat(inst), |
| 498 | 501 | .mul => try self.airMul(inst), |
| ... | ... | @@ -916,9 +919,182 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 916 | 919 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 917 | 920 | } |
| 918 | 921 | |
| 919 | | fn airAdd(self: *Self, inst: Air.Inst.Index) !void { |
| 922 | /// Don't call this function directly. Use binOp instead. |
| 923 | /// |
| 924 | /// Calling this function signals an intention to generate a Mir |
| 925 | /// instruction of the form |
| 926 | /// |
| 927 | /// op dest, lhs, rhs |
| 928 | /// |
| 929 | /// Asserts that generating an instruction of that form is possible. |
| 930 | fn binOpRegister( |
| 931 | self: *Self, |
| 932 | tag: Air.Inst.Tag, |
| 933 | maybe_inst: ?Air.Inst.Index, |
| 934 | lhs: MCValue, |
| 935 | rhs: MCValue, |
| 936 | lhs_ty: Type, |
| 937 | rhs_ty: Type, |
| 938 | ) !MCValue { |
| 939 | const lhs_is_register = lhs == .register; |
| 940 | const rhs_is_register = rhs == .register; |
| 941 | |
| 942 | if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); |
| 943 | if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); |
| 944 | |
| 945 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 946 | |
| 947 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| 948 | const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { |
| 949 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 950 | break :inst Air.refToIndex(bin_op.lhs).?; |
| 951 | } else null; |
| 952 | |
| 953 | const reg = try self.register_manager.allocReg(track_inst); |
| 954 | self.register_manager.freezeRegs(&.{reg}); |
| 955 | |
| 956 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 957 | |
| 958 | break :blk reg; |
| 959 | }; |
| 960 | defer self.register_manager.unfreezeRegs(&.{lhs_reg}); |
| 961 | |
| 962 | const rhs_reg = if (rhs_is_register) rhs.register else blk: { |
| 963 | const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { |
| 964 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 965 | break :inst Air.refToIndex(bin_op.rhs).?; |
| 966 | } else null; |
| 967 | |
| 968 | const reg = try self.register_manager.allocReg(track_inst); |
| 969 | self.register_manager.freezeRegs(&.{reg}); |
| 970 | |
| 971 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 972 | |
| 973 | break :blk reg; |
| 974 | }; |
| 975 | defer self.register_manager.unfreezeRegs(&.{rhs_reg}); |
| 976 | |
| 977 | const dest_reg = if (maybe_inst) |inst| blk: { |
| 978 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 979 | |
| 980 | if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { |
| 981 | break :blk lhs_reg; |
| 982 | } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { |
| 983 | break :blk rhs_reg; |
| 984 | } else { |
| 985 | break :blk try self.register_manager.allocReg(inst); |
| 986 | } |
| 987 | } else try self.register_manager.allocReg(null); |
| 988 | |
| 989 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 990 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 991 | |
| 992 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 993 | .add => .add, |
| 994 | .sub => .sub, |
| 995 | else => unreachable, |
| 996 | }; |
| 997 | const mir_data: Mir.Inst.Data = switch (tag) { |
| 998 | .add, |
| 999 | .sub, |
| 1000 | => .{ .r_type = .{ |
| 1001 | .rd = dest_reg, |
| 1002 | .rs1 = lhs_reg, |
| 1003 | .rs2 = rhs_reg, |
| 1004 | } }, |
| 1005 | else => unreachable, |
| 1006 | }; |
| 1007 | |
| 1008 | _ = try self.addInst(.{ |
| 1009 | .tag = mir_tag, |
| 1010 | .data = mir_data, |
| 1011 | }); |
| 1012 | |
| 1013 | return MCValue{ .register = dest_reg }; |
| 1014 | } |
| 1015 | |
| 1016 | /// For all your binary operation needs, this function will generate |
| 1017 | /// the corresponding Mir instruction(s). Returns the location of the |
| 1018 | /// result. |
| 1019 | /// |
| 1020 | /// If the binary operation itself happens to be an Air instruction, |
| 1021 | /// pass the corresponding index in the inst parameter. That helps |
| 1022 | /// this function do stuff like reusing operands. |
| 1023 | /// |
| 1024 | /// This function does not do any lowering to Mir itself, but instead |
| 1025 | /// looks at the lhs and rhs and determines which kind of lowering |
| 1026 | /// would be best suitable and then delegates the lowering to other |
| 1027 | /// functions. |
| 1028 | fn binOp( |
| 1029 | self: *Self, |
| 1030 | tag: Air.Inst.Tag, |
| 1031 | maybe_inst: ?Air.Inst.Index, |
| 1032 | lhs: MCValue, |
| 1033 | rhs: MCValue, |
| 1034 | lhs_ty: Type, |
| 1035 | rhs_ty: Type, |
| 1036 | ) InnerError!MCValue { |
| 1037 | switch (tag) { |
| 1038 | // Arithmetic operations on integers and floats |
| 1039 | .add, |
| 1040 | .sub, |
| 1041 | => { |
| 1042 | switch (lhs_ty.zigTypeTag()) { |
| 1043 | .Float => return self.fail("TODO binary operations on floats", .{}), |
| 1044 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1045 | .Int => { |
| 1046 | assert(lhs_ty.eql(rhs_ty)); |
| 1047 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1048 | if (int_info.bits <= 64) { |
| 1049 | // TODO immediate operands |
| 1050 | return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| 1051 | } else { |
| 1052 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 1053 | } |
| 1054 | }, |
| 1055 | else => unreachable, |
| 1056 | } |
| 1057 | }, |
| 1058 | .ptr_add, |
| 1059 | .ptr_sub, |
| 1060 | => { |
| 1061 | switch (lhs_ty.zigTypeTag()) { |
| 1062 | .Pointer => { |
| 1063 | const ptr_ty = lhs_ty; |
| 1064 | const elem_ty = switch (ptr_ty.ptrSize()) { |
| 1065 | .One => ptr_ty.childType().childType(), // ptr to array, so get array element type |
| 1066 | else => ptr_ty.childType(), |
| 1067 | }; |
| 1068 | const elem_size = elem_ty.abiSize(self.target.*); |
| 1069 | |
| 1070 | if (elem_size == 1) { |
| 1071 | const base_tag: Air.Inst.Tag = switch (tag) { |
| 1072 | .ptr_add => .add, |
| 1073 | .ptr_sub => .sub, |
| 1074 | else => unreachable, |
| 1075 | }; |
| 1076 | |
| 1077 | return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| 1078 | } else { |
| 1079 | return self.fail("TODO ptr_add with elem_size > 1", .{}); |
| 1080 | } |
| 1081 | }, |
| 1082 | else => unreachable, |
| 1083 | } |
| 1084 | }, |
| 1085 | else => unreachable, |
| 1086 | } |
| 1087 | } |
| 1088 | |
| 1089 | fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| 1090 | const tag = self.air.instructions.items(.tag)[inst]; |
| 920 | 1091 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 921 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add for {}", .{self.target.cpu.arch}); |
| 1092 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1093 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1094 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 1095 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 1096 | |
| 1097 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); |
| 922 | 1098 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 923 | 1099 | } |
| 924 | 1100 | |
| ... | ... | @@ -934,12 +1110,6 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { |
| 934 | 1110 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 935 | 1111 | } |
| 936 | 1112 | |
| 937 | | fn airSub(self: *Self, inst: Air.Inst.Index) !void { |
| 938 | | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 939 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub for {}", .{self.target.cpu.arch}); |
| 940 | | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 941 | | } |
| 942 | | |
| 943 | 1113 | fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void { |
| 944 | 1114 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 945 | 1115 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch}); |