| ... | @@ -511,10 +511,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -511,10 +511,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 511 | | 511 | |
| 512 | switch (air_tags[inst]) { | 512 | switch (air_tags[inst]) { |
| 513 | // zig fmt: off | 513 | // zig fmt: off |
| 514 | .add, .ptr_add => try self.airAdd(inst), | 514 | .add, .ptr_add => try self.airBinOp(inst), |
| 515 | .addwrap => try self.airAddWrap(inst), | 515 | .addwrap => try self.airAddWrap(inst), |
| 516 | .add_sat => try self.airAddSat(inst), | 516 | .add_sat => try self.airAddSat(inst), |
| 517 | .sub, .ptr_sub => try self.airSub(inst), | 517 | .sub, .ptr_sub => try self.airBinOp(inst), |
| 518 | .subwrap => try self.airSubWrap(inst), | 518 | .subwrap => try self.airSubWrap(inst), |
| 519 | .sub_sat => try self.airSubSat(inst), | 519 | .sub_sat => try self.airSubSat(inst), |
| 520 | .mul => try self.airMul(inst), | 520 | .mul => try self.airMul(inst), |
| ... | @@ -950,9 +950,249 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -950,9 +950,249 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 950 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 950 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 951 | } | 951 | } |
| 952 | | 952 | |
| 953 | fn airAdd(self: *Self, inst: Air.Inst.Index) !void { | 953 | /// Don't call this function directly. Use binOp instead. |
| | 954 | /// |
| | 955 | /// Calling this function signals an intention to generate a Mir |
| | 956 | /// instruction of the form |
| | 957 | /// |
| | 958 | /// op dest, lhs, rhs |
| | 959 | /// |
| | 960 | /// Asserts that generating an instruction of that form is possible. |
| | 961 | fn binOpRegister( |
| | 962 | self: *Self, |
| | 963 | tag: Air.Inst.Tag, |
| | 964 | maybe_inst: ?Air.Inst.Index, |
| | 965 | lhs: MCValue, |
| | 966 | rhs: MCValue, |
| | 967 | lhs_ty: Type, |
| | 968 | rhs_ty: Type, |
| | 969 | ) !MCValue { |
| | 970 | const lhs_is_register = lhs == .register; |
| | 971 | const rhs_is_register = rhs == .register; |
| | 972 | |
| | 973 | if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); |
| | 974 | if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); |
| | 975 | |
| | 976 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| | 977 | const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { |
| | 978 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| | 979 | break :inst Air.refToIndex(bin_op.lhs).?; |
| | 980 | } else null; |
| | 981 | const reg = try self.register_manager.allocReg(track_inst); |
| | 982 | self.register_manager.freezeRegs(&.{reg}); |
| | 983 | break :blk reg; |
| | 984 | }; |
| | 985 | defer self.register_manager.unfreezeRegs(&.{lhs_reg}); |
| | 986 | |
| | 987 | const rhs_reg = if (rhs_is_register) rhs.register else blk: { |
| | 988 | const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { |
| | 989 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| | 990 | break :inst Air.refToIndex(bin_op.rhs).?; |
| | 991 | } else null; |
| | 992 | const reg = try self.register_manager.allocReg(track_inst); |
| | 993 | self.register_manager.freezeRegs(&.{reg}); |
| | 994 | break :blk reg; |
| | 995 | }; |
| | 996 | defer self.register_manager.unfreezeRegs(&.{rhs_reg}); |
| | 997 | |
| | 998 | const dest_reg = if (maybe_inst) |inst| blk: { |
| | 999 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| | 1000 | |
| | 1001 | if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { |
| | 1002 | break :blk lhs_reg; |
| | 1003 | } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { |
| | 1004 | break :blk rhs_reg; |
| | 1005 | } else { |
| | 1006 | break :blk try self.register_manager.allocReg(inst); |
| | 1007 | } |
| | 1008 | } else try self.register_manager.allocReg(null); |
| | 1009 | |
| | 1010 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| | 1011 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| | 1012 | |
| | 1013 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| | 1014 | .add => .add_shifted_register, |
| | 1015 | .sub => .sub_shifted_register, |
| | 1016 | else => unreachable, |
| | 1017 | }; |
| | 1018 | const mir_data: Mir.Inst.Data = switch (tag) { |
| | 1019 | .add, |
| | 1020 | .sub, |
| | 1021 | => .{ .rrr_imm6_shift = .{ |
| | 1022 | .rd = dest_reg, |
| | 1023 | .rn = lhs_reg, |
| | 1024 | .rm = rhs_reg, |
| | 1025 | .imm6 = 0, |
| | 1026 | .shift = .lsl, |
| | 1027 | } }, |
| | 1028 | else => unreachable, |
| | 1029 | }; |
| | 1030 | |
| | 1031 | _ = try self.addInst(.{ |
| | 1032 | .tag = mir_tag, |
| | 1033 | .data = mir_data, |
| | 1034 | }); |
| | 1035 | |
| | 1036 | return MCValue{ .register = dest_reg }; |
| | 1037 | } |
| | 1038 | |
| | 1039 | /// Don't call this function directly. Use binOp instead. |
| | 1040 | /// |
| | 1041 | /// Calling this function signals an intention to generate a Mir |
| | 1042 | /// instruction of the form |
| | 1043 | /// |
| | 1044 | /// op dest, lhs, #rhs_imm |
| | 1045 | /// |
| | 1046 | /// Set lhs_and_rhs_swapped to true iff inst.bin_op.lhs corresponds to |
| | 1047 | /// rhs and vice versa. This parameter is only used when maybe_inst != |
| | 1048 | /// null. |
| | 1049 | /// |
| | 1050 | /// Asserts that generating an instruction of that form is possible. |
| | 1051 | fn binOpImmediate( |
| | 1052 | self: *Self, |
| | 1053 | tag: Air.Inst.Tag, |
| | 1054 | maybe_inst: ?Air.Inst.Index, |
| | 1055 | lhs: MCValue, |
| | 1056 | rhs: MCValue, |
| | 1057 | lhs_ty: Type, |
| | 1058 | lhs_and_rhs_swapped: bool, |
| | 1059 | ) !MCValue { |
| | 1060 | const lhs_is_register = lhs == .register; |
| | 1061 | |
| | 1062 | if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); |
| | 1063 | |
| | 1064 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| | 1065 | const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { |
| | 1066 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| | 1067 | break :inst Air.refToIndex( |
| | 1068 | if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, |
| | 1069 | ).?; |
| | 1070 | } else null; |
| | 1071 | const reg = try self.register_manager.allocReg(track_inst); |
| | 1072 | self.register_manager.freezeRegs(&.{reg}); |
| | 1073 | break :blk reg; |
| | 1074 | }; |
| | 1075 | defer self.register_manager.unfreezeRegs(&.{lhs_reg}); |
| | 1076 | |
| | 1077 | const dest_reg = if (maybe_inst) |inst| blk: { |
| | 1078 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| | 1079 | |
| | 1080 | if (lhs_is_register and self.reuseOperand( |
| | 1081 | inst, |
| | 1082 | if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, |
| | 1083 | if (lhs_and_rhs_swapped) 1 else 0, |
| | 1084 | lhs, |
| | 1085 | )) { |
| | 1086 | break :blk lhs_reg; |
| | 1087 | } else { |
| | 1088 | break :blk try self.register_manager.allocReg(inst); |
| | 1089 | } |
| | 1090 | } else try self.register_manager.allocReg(null); |
| | 1091 | |
| | 1092 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| | 1093 | |
| | 1094 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| | 1095 | .add => .add_immediate, |
| | 1096 | .sub => .sub_immediate, |
| | 1097 | else => unreachable, |
| | 1098 | }; |
| | 1099 | const mir_data: Mir.Inst.Data = switch (tag) { |
| | 1100 | .add, |
| | 1101 | .sub, |
| | 1102 | => .{ .rr_imm12_sh = .{ |
| | 1103 | .rd = dest_reg, |
| | 1104 | .rn = lhs_reg, |
| | 1105 | .imm12 = @intCast(u12, rhs.immediate), |
| | 1106 | } }, |
| | 1107 | else => unreachable, |
| | 1108 | }; |
| | 1109 | |
| | 1110 | _ = try self.addInst(.{ |
| | 1111 | .tag = mir_tag, |
| | 1112 | .data = mir_data, |
| | 1113 | }); |
| | 1114 | |
| | 1115 | return MCValue{ .register = dest_reg }; |
| | 1116 | } |
| | 1117 | |
| | 1118 | /// For all your binary operation needs, this function will generate |
| | 1119 | /// the corresponding Mir instruction(s). Returns the location of the |
| | 1120 | /// result. |
| | 1121 | /// |
| | 1122 | /// If the binary operation itself happens to be an Air instruction, |
| | 1123 | /// pass the corresponding index in the inst parameter. That helps |
| | 1124 | /// this function do stuff like reusing operands. |
| | 1125 | /// |
| | 1126 | /// This function does not do any lowering to Mir itself, but instead |
| | 1127 | /// looks at the lhs and rhs and determines which kind of lowering |
| | 1128 | /// would be best suitable and then delegates the lowering to other |
| | 1129 | /// functions. |
| | 1130 | fn binOp( |
| | 1131 | self: *Self, |
| | 1132 | tag: Air.Inst.Tag, |
| | 1133 | maybe_inst: ?Air.Inst.Index, |
| | 1134 | lhs: MCValue, |
| | 1135 | rhs: MCValue, |
| | 1136 | lhs_ty: Type, |
| | 1137 | rhs_ty: Type, |
| | 1138 | ) !MCValue { |
| | 1139 | switch (tag) { |
| | 1140 | .add, |
| | 1141 | .sub, |
| | 1142 | => { |
| | 1143 | switch (lhs_ty.zigTypeTag()) { |
| | 1144 | .Float => return self.fail("TODO binary operations on floats", .{}), |
| | 1145 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| | 1146 | .Int => { |
| | 1147 | assert(lhs_ty.eql(rhs_ty)); |
| | 1148 | const int_info = lhs_ty.intInfo(self.target.*); |
| | 1149 | if (int_info.bits <= 64) { |
| | 1150 | // Only say yes if the operation is |
| | 1151 | // commutative, i.e. we can swap both of the |
| | 1152 | // operands |
| | 1153 | const lhs_immediate_ok = switch (tag) { |
| | 1154 | .add => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), |
| | 1155 | .sub => false, |
| | 1156 | else => unreachable, |
| | 1157 | }; |
| | 1158 | const rhs_immediate_ok = switch (tag) { |
| | 1159 | .add, |
| | 1160 | .sub, |
| | 1161 | => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), |
| | 1162 | else => unreachable, |
| | 1163 | }; |
| | 1164 | |
| | 1165 | if (rhs_immediate_ok) { |
| | 1166 | return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false); |
| | 1167 | } else if (lhs_immediate_ok) { |
| | 1168 | // swap lhs and rhs |
| | 1169 | return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true); |
| | 1170 | } else { |
| | 1171 | return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| | 1172 | } |
| | 1173 | } else { |
| | 1174 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| | 1175 | } |
| | 1176 | }, |
| | 1177 | else => unreachable, |
| | 1178 | } |
| | 1179 | }, |
| | 1180 | .ptr_add, |
| | 1181 | .ptr_sub, |
| | 1182 | => return self.fail("TODO ptr_add, ptr_sub", .{}), |
| | 1183 | else => unreachable, |
| | 1184 | } |
| | 1185 | } |
| | 1186 | |
| | 1187 | fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| | 1188 | const tag = self.air.instructions.items(.tag)[inst]; |
| 954 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1189 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 955 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add for {}", .{self.target.cpu.arch}); | 1190 | const lhs = try self.resolveInst(bin_op.lhs); |
| | 1191 | const rhs = try self.resolveInst(bin_op.rhs); |
| | 1192 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| | 1193 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| | 1194 | |
| | 1195 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); |
| 956 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1196 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 957 | } | 1197 | } |
| 958 | | 1198 | |
| ... | @@ -968,12 +1208,6 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -968,12 +1208,6 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { |
| 968 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1208 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 969 | } | 1209 | } |
| 970 | | 1210 | |
| 971 | fn airSub(self: *Self, inst: Air.Inst.Index) !void { | | |
| 972 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 973 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub for {}", .{self.target.cpu.arch}); | | |
| 974 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 975 | } | | |
| 976 | | | |
| 977 | fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void { | 1211 | fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void { |
| 978 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1212 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 979 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch}); | 1213 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch}); |