authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-02-09 23:19:28+01:00
committergravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-02-14 22:09:44+01:00
logedb2a75982a011dc883678ca57efa8c3f6be5466
tree29494934861fa302a54e08019b42b9e6848ca8f0
parent8bfc4b2f9cf4798fe7731288c652145803ef1998
signaturelock-open Commit is signed but in an unrecognized format.

stage2 AArch64: Implement binOp for add, sub


3 files changed, 249 insertions(+), 11 deletions(-)

src/arch/aarch64/CodeGen.zig+244-10
...@@ -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 {
511511
512 switch (air_tags[inst]) {512 switch (air_tags[inst]) {
513 // zig fmt: off513 // 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}
952952
953fn 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.
961fn 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.
1051fn 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.
1130fn 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
1187fn 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}
9581198
...@@ -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}
9701210
971fn 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
977fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {1211fn 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});
src/arch/aarch64/Emit.zig+3-1
...@@ -93,6 +93,7 @@ pub fn emitMir(...@@ -93,6 +93,7 @@ pub fn emitMir(
9393
94 .add_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),94 .add_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
95 .cmp_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),95 .cmp_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
96 .sub_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
9697
97 .cset => try emit.mirConditionalSelect(inst),98 .cset => try emit.mirConditionalSelect(inst),
9899
...@@ -584,8 +585,9 @@ fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -584,8 +585,9 @@ fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
584 const imm6 = rrr_imm6_shift.imm6;585 const imm6 = rrr_imm6_shift.imm6;
585586
586 switch (tag) {587 switch (tag) {
587 .cmp_shifted_register => try emit.writeInstruction(Instruction.subsShiftedRegister(rd, rn, rm, shift, imm6)),
588 .add_shifted_register => try emit.writeInstruction(Instruction.addShiftedRegister(rd, rn, rm, shift, imm6)),588 .add_shifted_register => try emit.writeInstruction(Instruction.addShiftedRegister(rd, rn, rm, shift, imm6)),
589 .cmp_shifted_register => try emit.writeInstruction(Instruction.subsShiftedRegister(rd, rn, rm, shift, imm6)),
590 .sub_shifted_register => try emit.writeInstruction(Instruction.subShiftedRegister(rd, rn, rm, shift, imm6)),
589 else => unreachable,591 else => unreachable,
590 }592 }
591}593}
src/arch/aarch64/Mir.zig+2
...@@ -116,6 +116,8 @@ pub const Inst = struct {...@@ -116,6 +116,8 @@ pub const Inst = struct {
116 strh_register,116 strh_register,
117 /// Subtract (immediate)117 /// Subtract (immediate)
118 sub_immediate,118 sub_immediate,
119 /// Subtract (shifted register)
120 sub_shifted_register,
119 /// Supervisor Call121 /// Supervisor Call
120 svc,122 svc,
121 };123 };