| ... | @@ -502,18 +502,18 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -502,18 +502,18 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 502 | | 502 | |
| 503 | switch (air_tags[inst]) { | 503 | switch (air_tags[inst]) { |
| 504 | // zig fmt: off | 504 | // zig fmt: off |
| 505 | .add, .ptr_add => try self.airAdd(inst), | 505 | .add, .ptr_add => try self.airBinOp(inst), |
| 506 | .addwrap => try self.airAddWrap(inst), | 506 | .addwrap => try self.airAddWrap(inst), |
| 507 | .add_sat => try self.airAddSat(inst), | 507 | .add_sat => try self.airAddSat(inst), |
| 508 | .sub, .ptr_sub => try self.airSub(inst), | 508 | .sub, .ptr_sub => try self.airBinOp(inst), |
| 509 | .subwrap => try self.airSubWrap(inst), | 509 | .subwrap => try self.airSubWrap(inst), |
| 510 | .sub_sat => try self.airSubSat(inst), | 510 | .sub_sat => try self.airSubSat(inst), |
| 511 | .mul => try self.airMul(inst), | 511 | .mul => try self.airBinOp(inst), |
| 512 | .mulwrap => try self.airMulWrap(inst), | 512 | .mulwrap => try self.airMulWrap(inst), |
| 513 | .mul_sat => try self.airMulSat(inst), | 513 | .mul_sat => try self.airMulSat(inst), |
| 514 | .rem => try self.airRem(inst), | 514 | .rem => try self.airRem(inst), |
| 515 | .mod => try self.airMod(inst), | 515 | .mod => try self.airMod(inst), |
| 516 | .shl, .shl_exact => try self.airShl(inst), | 516 | .shl, .shl_exact => try self.airBinOp(inst), |
| 517 | .shl_sat => try self.airShlSat(inst), | 517 | .shl_sat => try self.airShlSat(inst), |
| 518 | .min => try self.airMin(inst), | 518 | .min => try self.airMin(inst), |
| 519 | .max => try self.airMax(inst), | 519 | .max => try self.airMax(inst), |
| ... | @@ -548,12 +548,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -548,12 +548,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 548 | .cmp_gt => try self.airCmp(inst, .gt), | 548 | .cmp_gt => try self.airCmp(inst, .gt), |
| 549 | .cmp_neq => try self.airCmp(inst, .neq), | 549 | .cmp_neq => try self.airCmp(inst, .neq), |
| 550 | | 550 | |
| 551 | .bool_and => try self.airBoolOp(inst), | 551 | .bool_and => try self.airBinOp(inst), |
| 552 | .bool_or => try self.airBoolOp(inst), | 552 | .bool_or => try self.airBinOp(inst), |
| 553 | .bit_and => try self.airBitAnd(inst), | 553 | .bit_and => try self.airBinOp(inst), |
| 554 | .bit_or => try self.airBitOr(inst), | 554 | .bit_or => try self.airBinOp(inst), |
| 555 | .xor => try self.airXor(inst), | 555 | .xor => try self.airBinOp(inst), |
| 556 | .shr, .shr_exact => try self.airShr(inst), | 556 | .shr, .shr_exact => try self.airBinOp(inst), |
| 557 | | 557 | |
| 558 | .alloc => try self.airAlloc(inst), | 558 | .alloc => try self.airAlloc(inst), |
| 559 | .ret_ptr => try self.airRetPtr(inst), | 559 | .ret_ptr => try self.airRetPtr(inst), |
| ... | @@ -887,6 +887,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -887,6 +887,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 887 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 887 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 888 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 888 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 889 | const operand = try self.resolveInst(ty_op.operand); | 889 | const operand = try self.resolveInst(ty_op.operand); |
| | 890 | const operand_ty = self.air.typeOf(ty_op.operand); |
| 890 | switch (operand) { | 891 | switch (operand) { |
| 891 | .dead => unreachable, | 892 | .dead => unreachable, |
| 892 | .unreach => unreachable, | 893 | .unreach => unreachable, |
| ... | @@ -917,7 +918,68 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -917,7 +918,68 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 917 | break :result r; | 918 | break :result r; |
| 918 | }, | 919 | }, |
| 919 | else => { | 920 | else => { |
| 920 | break :result try self.genBinOp(inst, ty_op.operand, .bool_true, .not); | 921 | switch (operand_ty.zigTypeTag()) { |
| | 922 | .Bool => { |
| | 923 | const op_reg = switch (operand) { |
| | 924 | .register => |r| r, |
| | 925 | else => try self.copyToTmpRegister(operand_ty, operand), |
| | 926 | }; |
| | 927 | self.register_manager.freezeRegs(&.{op_reg}); |
| | 928 | defer self.register_manager.unfreezeRegs(&.{op_reg}); |
| | 929 | |
| | 930 | const dest_reg = blk: { |
| | 931 | if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { |
| | 932 | break :blk op_reg; |
| | 933 | } |
| | 934 | |
| | 935 | break :blk try self.register_manager.allocReg(null); |
| | 936 | }; |
| | 937 | |
| | 938 | _ = try self.addInst(.{ |
| | 939 | .tag = .eor, |
| | 940 | .data = .{ .rr_op = .{ |
| | 941 | .rd = dest_reg, |
| | 942 | .rn = op_reg, |
| | 943 | .op = Instruction.Operand.fromU32(1).?, |
| | 944 | } }, |
| | 945 | }); |
| | 946 | |
| | 947 | break :result MCValue{ .register = dest_reg }; |
| | 948 | }, |
| | 949 | .Int => { |
| | 950 | const int_info = operand_ty.intInfo(self.target.*); |
| | 951 | if (int_info.bits <= 32) { |
| | 952 | const op_reg = switch (operand) { |
| | 953 | .register => |r| r, |
| | 954 | else => try self.copyToTmpRegister(operand_ty, operand), |
| | 955 | }; |
| | 956 | self.register_manager.freezeRegs(&.{op_reg}); |
| | 957 | defer self.register_manager.unfreezeRegs(&.{op_reg}); |
| | 958 | |
| | 959 | const dest_reg = blk: { |
| | 960 | if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { |
| | 961 | break :blk op_reg; |
| | 962 | } |
| | 963 | |
| | 964 | break :blk try self.register_manager.allocReg(null); |
| | 965 | }; |
| | 966 | |
| | 967 | _ = try self.addInst(.{ |
| | 968 | .tag = .mvn, |
| | 969 | .data = .{ .rr_op = .{ |
| | 970 | .rd = dest_reg, |
| | 971 | .rn = undefined, |
| | 972 | .op = Instruction.Operand.reg(op_reg, Instruction.Operand.Shift.none), |
| | 973 | } }, |
| | 974 | }); |
| | 975 | |
| | 976 | break :result MCValue{ .register = dest_reg }; |
| | 977 | } else { |
| | 978 | return self.fail("TODO ARM not on integers > u32/i32", .{}); |
| | 979 | } |
| | 980 | }, |
| | 981 | else => unreachable, |
| | 982 | } |
| 921 | }, | 983 | }, |
| 922 | } | 984 | } |
| 923 | }; | 985 | }; |
| ... | @@ -943,9 +1005,15 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -943,9 +1005,15 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 943 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1005 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 944 | } | 1006 | } |
| 945 | | 1007 | |
| 946 | fn airAdd(self: *Self, inst: Air.Inst.Index) !void { | 1008 | fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| | 1009 | const tag = self.air.instructions.items(.tag)[inst]; |
| 947 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1010 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 948 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .add); | 1011 | const lhs = try self.resolveInst(bin_op.lhs); |
| | 1012 | const rhs = try self.resolveInst(bin_op.rhs); |
| | 1013 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| | 1014 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| | 1015 | |
| | 1016 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); |
| 949 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1017 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 950 | } | 1018 | } |
| 951 | | 1019 | |
| ... | @@ -961,12 +1029,6 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -961,12 +1029,6 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { |
| 961 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1029 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 962 | } | 1030 | } |
| 963 | | 1031 | |
| 964 | fn airSub(self: *Self, inst: Air.Inst.Index) !void { | | |
| 965 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 966 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .sub); | | |
| 967 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 968 | } | | |
| 969 | | | |
| 970 | fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void { | 1032 | fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void { |
| 971 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1033 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 972 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch}); | 1034 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch}); |
| ... | @@ -979,12 +1041,6 @@ fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -979,12 +1041,6 @@ fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { |
| 979 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1041 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 980 | } | 1042 | } |
| 981 | | 1043 | |
| 982 | fn airMul(self: *Self, inst: Air.Inst.Index) !void { | | |
| 983 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 984 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genMul(inst, bin_op.lhs, bin_op.rhs); | | |
| 985 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 986 | } | | |
| 987 | | | |
| 988 | fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void { | 1044 | fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void { |
| 989 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1045 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 990 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch}); | 1046 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch}); |
| ... | @@ -1035,42 +1091,12 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1035,42 +1091,12 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void { |
| 1035 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1091 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1036 | } | 1092 | } |
| 1037 | | 1093 | |
| 1038 | fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void { | | |
| 1039 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 1040 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_and); | | |
| 1041 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1042 | } | | |
| 1043 | | | |
| 1044 | fn airBitOr(self: *Self, inst: Air.Inst.Index) !void { | | |
| 1045 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 1046 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_or); | | |
| 1047 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1048 | } | | |
| 1049 | | | |
| 1050 | fn airXor(self: *Self, inst: Air.Inst.Index) !void { | | |
| 1051 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 1052 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .xor); | | |
| 1053 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1054 | } | | |
| 1055 | | | |
| 1056 | fn airShl(self: *Self, inst: Air.Inst.Index) !void { | | |
| 1057 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 1058 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .shl); | | |
| 1059 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1060 | } | | |
| 1061 | | | |
| 1062 | fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { | 1094 | fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1063 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1095 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1064 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}); | 1096 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}); |
| 1065 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1097 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1066 | } | 1098 | } |
| 1067 | | 1099 | |
| 1068 | fn airShr(self: *Self, inst: Air.Inst.Index) !void { | | |
| 1069 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 1070 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .shr); | | |
| 1071 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 1072 | } | | |
| 1073 | | | |
| 1074 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { | 1100 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1075 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1101 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1076 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch}); | 1102 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch}); |
| ... | @@ -1252,7 +1278,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1252,7 +1278,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1252 | | 1278 | |
| 1253 | const slice_ty = self.air.typeOf(bin_op.lhs); | 1279 | const slice_ty = self.air.typeOf(bin_op.lhs); |
| 1254 | const elem_ty = slice_ty.childType(); | 1280 | const elem_ty = slice_ty.childType(); |
| 1255 | const elem_size = elem_ty.abiSize(self.target.*); | 1281 | const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*)); |
| 1256 | | 1282 | |
| 1257 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | 1283 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 1258 | const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); | 1284 | const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); |
| ... | @@ -1307,22 +1333,25 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1307,22 +1333,25 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1307 | else => { | 1333 | else => { |
| 1308 | const dst_mcv = try self.allocRegOrMem(inst, true); | 1334 | const dst_mcv = try self.allocRegOrMem(inst, true); |
| 1309 | | 1335 | |
| 1310 | const offset_mcv = try self.genMulConstant(bin_op.rhs, @intCast(u32, elem_size)); | 1336 | const offset_mcv = try self.binOp( |
| | 1337 | .mul, |
| | 1338 | null, |
| | 1339 | index_mcv, |
| | 1340 | .{ .immediate = elem_size }, |
| | 1341 | Type.usize, |
| | 1342 | Type.usize, |
| | 1343 | ); |
| 1311 | assert(offset_mcv == .register); // result of multiplication should always be register | 1344 | assert(offset_mcv == .register); // result of multiplication should always be register |
| 1312 | self.register_manager.freezeRegs(&.{offset_mcv.register}); | 1345 | self.register_manager.freezeRegs(&.{offset_mcv.register}); |
| 1313 | | 1346 | |
| 1314 | const addr_reg = try self.register_manager.allocReg(null); | 1347 | const addr_mcv = try self.binOp(.add, null, base_mcv, offset_mcv, Type.usize, Type.usize); |
| 1315 | self.register_manager.freezeRegs(&.{addr_reg}); | | |
| 1316 | defer self.register_manager.unfreezeRegs(&.{addr_reg}); | | |
| 1317 | | | |
| 1318 | try self.genBinOpCode(addr_reg, base_mcv, offset_mcv, false, .add, .unsigned); | | |
| 1319 | | 1348 | |
| 1320 | // At this point in time, neither the base register | 1349 | // At this point in time, neither the base register |
| 1321 | // nor the offset register contains any valuable data | 1350 | // nor the offset register contains any valuable data |
| 1322 | // anymore. | 1351 | // anymore. |
| 1323 | self.register_manager.unfreezeRegs(&.{ base_mcv.register, offset_mcv.register }); | 1352 | self.register_manager.unfreezeRegs(&.{ base_mcv.register, offset_mcv.register }); |
| 1324 | | 1353 | |
| 1325 | try self.load(dst_mcv, .{ .register = addr_reg }, slice_ptr_field_type); | 1354 | try self.load(dst_mcv, addr_mcv, slice_ptr_field_type); |
| 1326 | | 1355 | |
| 1327 | break :result dst_mcv; | 1356 | break :result dst_mcv; |
| 1328 | }, | 1357 | }, |
| ... | @@ -1499,31 +1528,10 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo | ... | @@ -1499,31 +1528,10 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 1499 | const tmp_reg = regs[3]; | 1528 | const tmp_reg = regs[3]; |
| 1500 | | 1529 | |
| 1501 | // sub dst_reg, fp, #off | 1530 | // sub dst_reg, fp, #off |
| 1502 | const adj_off = off + elem_size; | 1531 | try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = off }); |
| 1503 | const offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_off)) |x| x else { | | |
| 1504 | return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); | | |
| 1505 | }; | | |
| 1506 | _ = try self.addInst(.{ | | |
| 1507 | .tag = .sub, | | |
| 1508 | .data = .{ .rr_op = .{ | | |
| 1509 | .rd = dst_reg, | | |
| 1510 | .rn = .fp, | | |
| 1511 | .op = offset_op, | | |
| 1512 | } }, | | |
| 1513 | }); | | |
| 1514 | | 1532 | |
| 1515 | // mov len, #elem_size | 1533 | // mov len, #elem_size |
| 1516 | const len_op: Instruction.Operand = if (Instruction.Operand.fromU32(elem_size)) |x| x else { | 1534 | try self.genSetReg(Type.usize, len_reg, .{ .immediate = elem_size }); |
| 1517 | return self.fail("TODO load: set reg to elem_size with all possible sizes", .{}); | | |
| 1518 | }; | | |
| 1519 | _ = try self.addInst(.{ | | |
| 1520 | .tag = .mov, | | |
| 1521 | .data = .{ .rr_op = .{ | | |
| 1522 | .rd = len_reg, | | |
| 1523 | .rn = .r0, | | |
| 1524 | .op = len_op, | | |
| 1525 | } }, | | |
| 1526 | }); | | |
| 1527 | | 1535 | |
| 1528 | // memcpy(src, dst, len) | 1536 | // memcpy(src, dst, len) |
| 1529 | try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); | 1537 | try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); |
| ... | @@ -1673,16 +1681,16 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde | ... | @@ -1673,16 +1681,16 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde |
| 1673 | self.register_manager.freezeRegs(&.{addr_reg}); | 1681 | self.register_manager.freezeRegs(&.{addr_reg}); |
| 1674 | defer self.register_manager.unfreezeRegs(&.{addr_reg}); | 1682 | defer self.register_manager.unfreezeRegs(&.{addr_reg}); |
| 1675 | | 1683 | |
| 1676 | const dst_reg = try self.register_manager.allocReg(inst); | 1684 | const dest = try self.binOp( |
| 1677 | try self.genBinOpCode( | 1685 | .add, |
| 1678 | dst_reg, | 1686 | null, |
| 1679 | .{ .register = addr_reg }, | 1687 | .{ .register = addr_reg }, |
| 1680 | .{ .register = offset_reg }, | 1688 | .{ .register = offset_reg }, |
| 1681 | false, | 1689 | Type.usize, |
| 1682 | .add, | 1690 | Type.usize, |
| 1683 | .unsigned, | | |
| 1684 | ); | 1691 | ); |
| 1685 | break :result MCValue{ .register = dst_reg }; | 1692 | |
| | 1693 | break :result dest; |
| 1686 | }, | 1694 | }, |
| 1687 | } | 1695 | } |
| 1688 | }; | 1696 | }; |
| ... | @@ -1720,403 +1728,440 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1720,403 +1728,440 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1720 | return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); | 1728 | return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); |
| 1721 | } | 1729 | } |
| 1722 | | 1730 | |
| 1723 | fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool { | 1731 | /// Don't call this function directly. Use binOp instead. |
| 1724 | return switch (mcv) { | 1732 | /// |
| 1725 | .none => unreachable, | 1733 | /// Calling this function signals an intention to generate a Mir |
| 1726 | .undef => unreachable, | 1734 | /// instruction of the form |
| 1727 | .dead, .unreach => unreachable, | 1735 | /// |
| 1728 | .compare_flags_unsigned => unreachable, | 1736 | /// op dest, lhs, rhs |
| 1729 | .compare_flags_signed => unreachable, | 1737 | /// |
| 1730 | .ptr_stack_offset => unreachable, | 1738 | /// Asserts that generating an instruction of that form is possible. |
| 1731 | .ptr_embedded_in_code => unreachable, | 1739 | fn binOpRegister( |
| 1732 | .immediate => |imm| blk: { | | |
| 1733 | if (imm > std.math.maxInt(u32)) return self.fail("TODO ARM binary arithmetic immediate larger than u32", .{}); | | |
| 1734 | | | |
| 1735 | // Load immediate into register if it doesn't fit | | |
| 1736 | // in an operand | | |
| 1737 | break :blk Instruction.Operand.fromU32(@intCast(u32, imm)) == null; | | |
| 1738 | }, | | |
| 1739 | .register => true, | | |
| 1740 | .stack_offset, | | |
| 1741 | .stack_argument_offset, | | |
| 1742 | .embedded_in_code, | | |
| 1743 | .memory, | | |
| 1744 | => true, | | |
| 1745 | }; | | |
| 1746 | } | | |
| 1747 | | | |
| 1748 | fn genBinOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, op: Air.Inst.Tag) !MCValue { | | |
| 1749 | // In the case of bitshifts, the type of rhs is different | | |
| 1750 | // from the resulting type | | |
| 1751 | const ty = self.air.typeOf(op_lhs); | | |
| 1752 | | | |
| 1753 | switch (ty.zigTypeTag()) { | | |
| 1754 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), | | |
| 1755 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | | |
| 1756 | .Bool => { | | |
| 1757 | return self.genBinIntOp(inst, op_lhs, op_rhs, op, 1, .unsigned); | | |
| 1758 | }, | | |
| 1759 | .Int => { | | |
| 1760 | const int_info = ty.intInfo(self.target.*); | | |
| 1761 | return self.genBinIntOp(inst, op_lhs, op_rhs, op, int_info.bits, int_info.signedness); | | |
| 1762 | }, | | |
| 1763 | else => unreachable, | | |
| 1764 | } | | |
| 1765 | } | | |
| 1766 | | | |
| 1767 | fn genBinIntOp( | | |
| 1768 | self: *Self, | 1740 | self: *Self, |
| 1769 | inst: Air.Inst.Index, | 1741 | tag: Air.Inst.Tag, |
| 1770 | op_lhs: Air.Inst.Ref, | 1742 | maybe_inst: ?Air.Inst.Index, |
| 1771 | op_rhs: Air.Inst.Ref, | 1743 | lhs: MCValue, |
| 1772 | op: Air.Inst.Tag, | 1744 | rhs: MCValue, |
| 1773 | bits: u16, | 1745 | lhs_ty: Type, |
| 1774 | signedness: std.builtin.Signedness, | 1746 | rhs_ty: Type, |
| 1775 | ) !MCValue { | 1747 | ) !MCValue { |
| 1776 | if (bits > 32) { | | |
| 1777 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | | |
| 1778 | } | | |
| 1779 | | | |
| 1780 | const lhs = try self.resolveInst(op_lhs); | | |
| 1781 | const rhs = try self.resolveInst(op_rhs); | | |
| 1782 | | | |
| 1783 | const lhs_is_register = lhs == .register; | 1748 | const lhs_is_register = lhs == .register; |
| 1784 | const rhs_is_register = rhs == .register; | 1749 | const rhs_is_register = rhs == .register; |
| 1785 | const lhs_should_be_register = switch (op) { | | |
| 1786 | .shr, .shl => true, | | |
| 1787 | else => try self.armOperandShouldBeRegister(lhs), | | |
| 1788 | }; | | |
| 1789 | const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs); | | |
| 1790 | const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs); | | |
| 1791 | const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs); | | |
| 1792 | const can_swap_lhs_and_rhs = switch (op) { | | |
| 1793 | .shr, .shl => false, | | |
| 1794 | else => true, | | |
| 1795 | }; | | |
| 1796 | | 1750 | |
| 1797 | if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); | 1751 | if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); |
| 1798 | defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register}); | | |
| 1799 | if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); | 1752 | if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); |
| 1800 | defer if (rhs_is_register) self.register_manager.unfreezeRegs(&.{rhs.register}); | | |
| 1801 | | 1753 | |
| 1802 | // Destination must be a register | | |
| 1803 | var dst_mcv: MCValue = undefined; | | |
| 1804 | var lhs_mcv = lhs; | | |
| 1805 | var rhs_mcv = rhs; | | |
| 1806 | var swap_lhs_and_rhs = false; | | |
| 1807 | | | |
| 1808 | // Allocate registers for operands and/or destination | | |
| 1809 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 1754 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1810 | if (reuse_lhs) { | | |
| 1811 | // Allocate 0 or 1 registers | | |
| 1812 | if (!rhs_is_register and rhs_should_be_register) { | | |
| 1813 | rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_rhs).?) }; | | |
| 1814 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv); | | |
| 1815 | } | | |
| 1816 | dst_mcv = lhs; | | |
| 1817 | } else if (reuse_rhs and can_swap_lhs_and_rhs) { | | |
| 1818 | // Allocate 0 or 1 registers | | |
| 1819 | if (!lhs_is_register and lhs_should_be_register) { | | |
| 1820 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?) }; | | |
| 1821 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv); | | |
| 1822 | } | | |
| 1823 | dst_mcv = rhs; | | |
| 1824 | | 1755 | |
| 1825 | swap_lhs_and_rhs = true; | 1756 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| 1826 | } else { | 1757 | const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { |
| 1827 | // Allocate 1 or 2 registers | 1758 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1828 | if (lhs_should_be_register and rhs_should_be_register) { | 1759 | break :inst Air.refToIndex(bin_op.lhs).?; |
| 1829 | if (lhs_is_register and rhs_is_register) { | 1760 | } else null; |
| 1830 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) }; | | |
| 1831 | } else if (lhs_is_register) { | | |
| 1832 | // Move RHS to register | | |
| 1833 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) }; | | |
| 1834 | rhs_mcv = dst_mcv; | | |
| 1835 | } else if (rhs_is_register) { | | |
| 1836 | // Move LHS to register | | |
| 1837 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) }; | | |
| 1838 | lhs_mcv = dst_mcv; | | |
| 1839 | } else { | | |
| 1840 | // Move LHS and RHS to register | | |
| 1841 | const regs = try self.register_manager.allocRegs(2, .{ inst, Air.refToIndex(op_rhs).? }); | | |
| 1842 | lhs_mcv = MCValue{ .register = regs[0] }; | | |
| 1843 | rhs_mcv = MCValue{ .register = regs[1] }; | | |
| 1844 | dst_mcv = lhs_mcv; | | |
| 1845 | | 1761 | |
| 1846 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv); | 1762 | const reg = try self.register_manager.allocReg(track_inst); |
| 1847 | } | 1763 | self.register_manager.freezeRegs(&.{reg}); |
| 1848 | } else if (lhs_should_be_register) { | | |
| 1849 | // RHS is immediate | | |
| 1850 | if (lhs_is_register) { | | |
| 1851 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) }; | | |
| 1852 | } else { | | |
| 1853 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) }; | | |
| 1854 | lhs_mcv = dst_mcv; | | |
| 1855 | } | | |
| 1856 | } else if (rhs_should_be_register and can_swap_lhs_and_rhs) { | | |
| 1857 | // LHS is immediate | | |
| 1858 | if (rhs_is_register) { | | |
| 1859 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) }; | | |
| 1860 | } else { | | |
| 1861 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) }; | | |
| 1862 | rhs_mcv = dst_mcv; | | |
| 1863 | } | | |
| 1864 | | 1764 | |
| 1865 | swap_lhs_and_rhs = true; | 1765 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 1866 | } else unreachable; // binary operation on two immediates | | |
| 1867 | } | | |
| 1868 | | 1766 | |
| 1869 | // Move the operands to the newly allocated registers | 1767 | break :blk reg; |
| 1870 | if (lhs_mcv == .register and !lhs_is_register) { | 1768 | }; |
| 1871 | try self.genSetReg(self.air.typeOf(op_lhs), lhs_mcv.register, lhs); | 1769 | defer self.register_manager.unfreezeRegs(&.{lhs_reg}); |
| 1872 | } | | |
| 1873 | if (rhs_mcv == .register and !rhs_is_register) { | | |
| 1874 | try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs); | | |
| 1875 | } | | |
| 1876 | | 1770 | |
| 1877 | try self.genBinOpCode( | 1771 | const rhs_reg = if (rhs_is_register) rhs.register else blk: { |
| 1878 | dst_mcv.register, | 1772 | const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { |
| 1879 | lhs_mcv, | 1773 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1880 | rhs_mcv, | 1774 | break :inst Air.refToIndex(bin_op.rhs).?; |
| 1881 | swap_lhs_and_rhs, | 1775 | } else null; |
| 1882 | op, | | |
| 1883 | signedness, | | |
| 1884 | ); | | |
| 1885 | return dst_mcv; | | |
| 1886 | } | | |
| 1887 | | 1776 | |
| 1888 | fn genBinOpCode( | 1777 | const reg = try self.register_manager.allocReg(track_inst); |
| 1889 | self: *Self, | 1778 | self.register_manager.freezeRegs(&.{reg}); |
| 1890 | dst_reg: Register, | | |
| 1891 | lhs_mcv: MCValue, | | |
| 1892 | rhs_mcv: MCValue, | | |
| 1893 | swap_lhs_and_rhs: bool, | | |
| 1894 | op: Air.Inst.Tag, | | |
| 1895 | signedness: std.builtin.Signedness, | | |
| 1896 | ) !void { | | |
| 1897 | assert(lhs_mcv == .register or rhs_mcv == .register); | | |
| 1898 | | 1779 | |
| 1899 | const op1 = if (swap_lhs_and_rhs) rhs_mcv.register else lhs_mcv.register; | 1780 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 1900 | const op2 = if (swap_lhs_and_rhs) lhs_mcv else rhs_mcv; | | |
| 1901 | | 1781 | |
| 1902 | const operand = switch (op2) { | 1782 | break :blk reg; |
| 1903 | .none => unreachable, | | |
| 1904 | .undef => unreachable, | | |
| 1905 | .dead, .unreach => unreachable, | | |
| 1906 | .compare_flags_unsigned => unreachable, | | |
| 1907 | .compare_flags_signed => unreachable, | | |
| 1908 | .ptr_stack_offset => unreachable, | | |
| 1909 | .ptr_embedded_in_code => unreachable, | | |
| 1910 | .immediate => |imm| Instruction.Operand.fromU32(@intCast(u32, imm)).?, | | |
| 1911 | .register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none), | | |
| 1912 | .stack_offset, | | |
| 1913 | .stack_argument_offset, | | |
| 1914 | .embedded_in_code, | | |
| 1915 | .memory, | | |
| 1916 | => unreachable, | | |
| 1917 | }; | 1783 | }; |
| | 1784 | defer self.register_manager.unfreezeRegs(&.{rhs_reg}); |
| 1918 | | 1785 | |
| 1919 | switch (op) { | 1786 | const dest_reg = switch (tag) { |
| 1920 | .add, | 1787 | .cmp_eq => .r0, // cmp has no destination regardless |
| 1921 | .bool_and, | 1788 | else => if (maybe_inst) |inst| blk: { |
| | 1789 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| | 1790 | |
| | 1791 | if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { |
| | 1792 | break :blk lhs_reg; |
| | 1793 | } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { |
| | 1794 | break :blk rhs_reg; |
| | 1795 | } else { |
| | 1796 | break :blk try self.register_manager.allocReg(inst); |
| | 1797 | } |
| | 1798 | } else try self.register_manager.allocReg(null), |
| | 1799 | }; |
| | 1800 | |
| | 1801 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| | 1802 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| | 1803 | |
| | 1804 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| | 1805 | .add, .ptr_add => .add, |
| | 1806 | .sub, .ptr_sub => .sub, |
| | 1807 | .cmp_eq => .cmp, |
| | 1808 | .mul => .mul, |
| 1922 | .bit_and, | 1809 | .bit_and, |
| | 1810 | .bool_and, |
| | 1811 | => .@"and", |
| | 1812 | .bit_or, |
| 1923 | .bool_or, | 1813 | .bool_or, |
| | 1814 | => .orr, |
| | 1815 | .shl, |
| | 1816 | .shl_exact, |
| | 1817 | => .lsl, |
| | 1818 | .shr, |
| | 1819 | .shr_exact, |
| | 1820 | => switch (lhs_ty.intInfo(self.target.*).signedness) { |
| | 1821 | .signed => Mir.Inst.Tag.asr, |
| | 1822 | .unsigned => Mir.Inst.Tag.lsr, |
| | 1823 | }, |
| | 1824 | .xor => .eor, |
| | 1825 | else => unreachable, |
| | 1826 | }; |
| | 1827 | const mir_data: Mir.Inst.Data = switch (tag) { |
| | 1828 | .add, |
| | 1829 | .sub, |
| | 1830 | .cmp_eq, |
| | 1831 | .bit_and, |
| | 1832 | .bool_and, |
| 1924 | .bit_or, | 1833 | .bit_or, |
| 1925 | .not, | 1834 | .bool_or, |
| 1926 | .xor, | 1835 | .xor, |
| 1927 | => { | 1836 | .ptr_add, |
| 1928 | const tag: Mir.Inst.Tag = switch (op) { | 1837 | .ptr_sub, |
| 1929 | .add => .add, | 1838 | => .{ .rr_op = .{ |
| 1930 | .bool_and, .bit_and => .@"and", | 1839 | .rd = dest_reg, |
| 1931 | .bool_or, .bit_or => .orr, | 1840 | .rn = lhs_reg, |
| 1932 | .not, .xor => .eor, | 1841 | .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none), |
| 1933 | else => unreachable, | 1842 | } }, |
| 1934 | }; | 1843 | .shl, |
| 1935 | | 1844 | .shl_exact, |
| 1936 | _ = try self.addInst(.{ | 1845 | .shr, |
| 1937 | .tag = tag, | 1846 | .shr_exact, |
| 1938 | .data = .{ .rr_op = .{ | 1847 | => .{ .rr_shift = .{ |
| 1939 | .rd = dst_reg, | 1848 | .rd = dest_reg, |
| 1940 | .rn = op1, | 1849 | .rm = lhs_reg, |
| 1941 | .op = operand, | 1850 | .shift_amount = Instruction.ShiftAmount.reg(rhs_reg), |
| 1942 | } }, | 1851 | } }, |
| 1943 | }); | 1852 | .mul => .{ .rrr = .{ |
| 1944 | }, | 1853 | .rd = dest_reg, |
| 1945 | .sub => { | 1854 | .rn = lhs_reg, |
| 1946 | const tag: Mir.Inst.Tag = if (swap_lhs_and_rhs) .rsb else .sub; | 1855 | .rm = rhs_reg, |
| | 1856 | } }, |
| | 1857 | else => unreachable, |
| | 1858 | }; |
| 1947 | | 1859 | |
| 1948 | _ = try self.addInst(.{ | 1860 | _ = try self.addInst(.{ |
| 1949 | .tag = tag, | 1861 | .tag = mir_tag, |
| 1950 | .data = .{ .rr_op = .{ | 1862 | .data = mir_data, |
| 1951 | .rd = dst_reg, | 1863 | }); |
| 1952 | .rn = op1, | | |
| 1953 | .op = operand, | | |
| 1954 | } }, | | |
| 1955 | }); | | |
| 1956 | }, | | |
| 1957 | .cmp_eq => { | | |
| 1958 | _ = try self.addInst(.{ | | |
| 1959 | .tag = .cmp, | | |
| 1960 | .data = .{ .rr_op = .{ | | |
| 1961 | .rd = .r0, | | |
| 1962 | .rn = op1, | | |
| 1963 | .op = operand, | | |
| 1964 | } }, | | |
| 1965 | }); | | |
| 1966 | }, | | |
| 1967 | .shl, .shr => { | | |
| 1968 | assert(!swap_lhs_and_rhs); | | |
| 1969 | const shift_amount = switch (operand) { | | |
| 1970 | .register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)), | | |
| 1971 | .immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)), | | |
| 1972 | }; | | |
| 1973 | | 1864 | |
| 1974 | const tag: Mir.Inst.Tag = switch (op) { | 1865 | return MCValue{ .register = dest_reg }; |
| 1975 | .shl => .lsl, | 1866 | } |
| 1976 | .shr => switch (signedness) { | 1867 | |
| 1977 | .signed => Mir.Inst.Tag.asr, | 1868 | /// Don't call this function directly. Use binOp instead. |
| 1978 | .unsigned => Mir.Inst.Tag.lsr, | 1869 | /// |
| 1979 | }, | 1870 | /// Calling this function signals an intention to generate a Mir |
| 1980 | else => unreachable, | 1871 | /// instruction of the form |
| 1981 | }; | 1872 | /// |
| | 1873 | /// op dest, lhs, #rhs_imm |
| | 1874 | /// |
| | 1875 | /// Set lhs_and_rhs_swapped to true iff inst.bin_op.lhs corresponds to |
| | 1876 | /// rhs and vice versa. This parameter is only used when maybe_inst != |
| | 1877 | /// null. |
| | 1878 | /// |
| | 1879 | /// Asserts that generating an instruction of that form is possible. |
| | 1880 | fn binOpImmediate( |
| | 1881 | self: *Self, |
| | 1882 | tag: Air.Inst.Tag, |
| | 1883 | maybe_inst: ?Air.Inst.Index, |
| | 1884 | lhs: MCValue, |
| | 1885 | rhs: MCValue, |
| | 1886 | lhs_ty: Type, |
| | 1887 | lhs_and_rhs_swapped: bool, |
| | 1888 | ) !MCValue { |
| | 1889 | const lhs_is_register = lhs == .register; |
| 1982 | | 1890 | |
| 1983 | _ = try self.addInst(.{ | 1891 | if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); |
| 1984 | .tag = tag, | | |
| 1985 | .data = .{ .rr_shift = .{ | | |
| 1986 | .rd = dst_reg, | | |
| 1987 | .rm = op1, | | |
| 1988 | .shift_amount = shift_amount, | | |
| 1989 | } }, | | |
| 1990 | }); | | |
| 1991 | }, | | |
| 1992 | else => unreachable, // not a binary instruction | | |
| 1993 | } | | |
| 1994 | } | | |
| 1995 | | 1892 | |
| 1996 | fn genMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue { | 1893 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1997 | const lhs = try self.resolveInst(op_lhs); | | |
| 1998 | const rhs = try self.resolveInst(op_rhs); | | |
| 1999 | | 1894 | |
| 2000 | const lhs_is_register = lhs == .register; | 1895 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| 2001 | const rhs_is_register = rhs == .register; | 1896 | const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { |
| 2002 | const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs); | 1897 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2003 | const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs); | 1898 | break :inst Air.refToIndex( |
| | 1899 | if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, |
| | 1900 | ).?; |
| | 1901 | } else null; |
| 2004 | | 1902 | |
| 2005 | if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); | 1903 | const reg = try self.register_manager.allocReg(track_inst); |
| 2006 | defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register}); | 1904 | self.register_manager.freezeRegs(&.{reg}); |
| 2007 | if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); | | |
| 2008 | defer if (rhs_is_register) self.register_manager.unfreezeRegs(&.{rhs.register}); | | |
| 2009 | | 1905 | |
| 2010 | // Destination must be a register | 1906 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 2011 | // LHS must be a register | | |
| 2012 | // RHS must be a register | | |
| 2013 | var dst_mcv: MCValue = undefined; | | |
| 2014 | var lhs_mcv: MCValue = lhs; | | |
| 2015 | var rhs_mcv: MCValue = rhs; | | |
| 2016 | | 1907 | |
| 2017 | // Allocate registers for operands and/or destination | 1908 | break :blk reg; |
| 2018 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 1909 | }; |
| 2019 | if (reuse_lhs) { | 1910 | defer self.register_manager.unfreezeRegs(&.{lhs_reg}); |
| 2020 | // Allocate 0 or 1 registers | 1911 | |
| 2021 | if (!rhs_is_register) { | 1912 | const dest_reg = switch (tag) { |
| 2022 | rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_rhs).?) }; | 1913 | .cmp_eq => .r0, // cmp has no destination reg |
| 2023 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv); | 1914 | else => if (maybe_inst) |inst| blk: { |
| 2024 | } | 1915 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2025 | dst_mcv = lhs; | 1916 | |
| 2026 | } else if (reuse_rhs) { | 1917 | if (lhs_is_register and self.reuseOperand( |
| 2027 | // Allocate 0 or 1 registers | 1918 | inst, |
| 2028 | if (!lhs_is_register) { | 1919 | if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, |
| 2029 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?) }; | 1920 | if (lhs_and_rhs_swapped) 1 else 0, |
| 2030 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv); | 1921 | lhs, |
| 2031 | } | 1922 | )) { |
| 2032 | dst_mcv = rhs; | 1923 | break :blk lhs_reg; |
| 2033 | } else { | 1924 | } else { |
| 2034 | // Allocate 1 or 2 registers | 1925 | break :blk try self.register_manager.allocReg(inst); |
| 2035 | if (lhs_is_register and rhs_is_register) { | 1926 | } |
| 2036 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) }; | 1927 | } else try self.register_manager.allocReg(null), |
| 2037 | } else if (lhs_is_register) { | 1928 | }; |
| 2038 | // Move RHS to register | | |
| 2039 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) }; | | |
| 2040 | rhs_mcv = dst_mcv; | | |
| 2041 | } else if (rhs_is_register) { | | |
| 2042 | // Move LHS to register | | |
| 2043 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst) }; | | |
| 2044 | lhs_mcv = dst_mcv; | | |
| 2045 | } else { | | |
| 2046 | // Move LHS and RHS to register | | |
| 2047 | const regs = try self.register_manager.allocRegs(2, .{ inst, Air.refToIndex(op_rhs).? }); | | |
| 2048 | lhs_mcv = MCValue{ .register = regs[0] }; | | |
| 2049 | rhs_mcv = MCValue{ .register = regs[1] }; | | |
| 2050 | dst_mcv = lhs_mcv; | | |
| 2051 | | 1929 | |
| 2052 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv); | 1930 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 2053 | } | | |
| 2054 | } | | |
| 2055 | | 1931 | |
| 2056 | // Move the operands to the newly allocated registers | 1932 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 2057 | if (!lhs_is_register) { | 1933 | .add => .add, |
| 2058 | try self.genSetReg(self.air.typeOf(op_lhs), lhs_mcv.register, lhs); | 1934 | .sub => .sub, |
| 2059 | } | 1935 | .cmp_eq => .cmp, |
| 2060 | if (!rhs_is_register) { | 1936 | .bit_and, |
| 2061 | try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs); | 1937 | .bool_and, |
| 2062 | } | 1938 | => .@"and", |
| | 1939 | .bit_or, |
| | 1940 | .bool_or, |
| | 1941 | => .orr, |
| | 1942 | .shl, |
| | 1943 | .shl_exact, |
| | 1944 | => .lsl, |
| | 1945 | .shr, |
| | 1946 | .shr_exact, |
| | 1947 | => switch (lhs_ty.intInfo(self.target.*).signedness) { |
| | 1948 | .signed => Mir.Inst.Tag.asr, |
| | 1949 | .unsigned => Mir.Inst.Tag.lsr, |
| | 1950 | }, |
| | 1951 | .xor => .eor, |
| | 1952 | else => unreachable, |
| | 1953 | }; |
| | 1954 | const mir_data: Mir.Inst.Data = switch (tag) { |
| | 1955 | .add, |
| | 1956 | .sub, |
| | 1957 | .cmp_eq, |
| | 1958 | .bit_and, |
| | 1959 | .bool_and, |
| | 1960 | .bit_or, |
| | 1961 | .bool_or, |
| | 1962 | .xor, |
| | 1963 | => .{ .rr_op = .{ |
| | 1964 | .rd = dest_reg, |
| | 1965 | .rn = lhs_reg, |
| | 1966 | .op = Instruction.Operand.fromU32(rhs.immediate).?, |
| | 1967 | } }, |
| | 1968 | .shl, |
| | 1969 | .shl_exact, |
| | 1970 | .shr, |
| | 1971 | .shr_exact, |
| | 1972 | => .{ .rr_shift = .{ |
| | 1973 | .rd = dest_reg, |
| | 1974 | .rm = lhs_reg, |
| | 1975 | .shift_amount = Instruction.ShiftAmount.imm(@intCast(u5, rhs.immediate)), |
| | 1976 | } }, |
| | 1977 | else => unreachable, |
| | 1978 | }; |
| 2063 | | 1979 | |
| 2064 | _ = try self.addInst(.{ | 1980 | _ = try self.addInst(.{ |
| 2065 | .tag = .mul, | 1981 | .tag = mir_tag, |
| 2066 | .data = .{ .rrr = .{ | 1982 | .data = mir_data, |
| 2067 | .rd = dst_mcv.register, | | |
| 2068 | .rn = lhs_mcv.register, | | |
| 2069 | .rm = rhs_mcv.register, | | |
| 2070 | } }, | | |
| 2071 | }); | 1983 | }); |
| 2072 | return dst_mcv; | | |
| 2073 | } | | |
| 2074 | | 1984 | |
| 2075 | fn genMulConstant(self: *Self, op: Air.Inst.Ref, imm: u32) !MCValue { | 1985 | return MCValue{ .register = dest_reg }; |
| 2076 | const lhs = try self.resolveInst(op); | 1986 | } |
| 2077 | const rhs = MCValue{ .immediate = imm }; | 1987 | |
| | 1988 | /// For all your binary operation needs, this function will generate |
| | 1989 | /// the corresponding Mir instruction(s). Returns the location of the |
| | 1990 | /// result. |
| | 1991 | /// |
| | 1992 | /// If the binary operation itself happens to be an Air instruction, |
| | 1993 | /// pass the corresponding index in the inst parameter. That helps |
| | 1994 | /// this function do stuff like reusing operands. |
| | 1995 | /// |
| | 1996 | /// This function does not do any lowering to Mir itself, but instead |
| | 1997 | /// looks at the lhs and rhs and determines which kind of lowering |
| | 1998 | /// would be best suitable and then delegates the lowering to other |
| | 1999 | /// functions. |
| | 2000 | fn binOp( |
| | 2001 | self: *Self, |
| | 2002 | tag: Air.Inst.Tag, |
| | 2003 | maybe_inst: ?Air.Inst.Index, |
| | 2004 | lhs: MCValue, |
| | 2005 | rhs: MCValue, |
| | 2006 | lhs_ty: Type, |
| | 2007 | rhs_ty: Type, |
| | 2008 | ) !MCValue { |
| | 2009 | switch (tag) { |
| | 2010 | .add, |
| | 2011 | .sub, |
| | 2012 | .cmp_eq, |
| | 2013 | => { |
| | 2014 | switch (lhs_ty.zigTypeTag()) { |
| | 2015 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), |
| | 2016 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), |
| | 2017 | .Int => { |
| | 2018 | assert(lhs_ty.eql(rhs_ty)); |
| | 2019 | const int_info = lhs_ty.intInfo(self.target.*); |
| | 2020 | if (int_info.bits <= 32) { |
| | 2021 | // Only say yes if the operation is |
| | 2022 | // commutative, i.e. we can swap both of the |
| | 2023 | // operands |
| | 2024 | const lhs_immediate_ok = switch (tag) { |
| | 2025 | .add => lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null, |
| | 2026 | .sub, |
| | 2027 | .cmp_eq, |
| | 2028 | => false, |
| | 2029 | else => unreachable, |
| | 2030 | }; |
| | 2031 | const rhs_immediate_ok = switch (tag) { |
| | 2032 | .add, |
| | 2033 | .sub, |
| | 2034 | .cmp_eq, |
| | 2035 | => rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null, |
| | 2036 | else => unreachable, |
| | 2037 | }; |
| 2078 | | 2038 | |
| 2079 | const lhs_is_register = lhs == .register; | 2039 | if (rhs_immediate_ok) { |
| | 2040 | return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false); |
| | 2041 | } else if (lhs_immediate_ok) { |
| | 2042 | // swap lhs and rhs |
| | 2043 | return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true); |
| | 2044 | } else { |
| | 2045 | return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| | 2046 | } |
| | 2047 | } else { |
| | 2048 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| | 2049 | } |
| | 2050 | }, |
| | 2051 | else => unreachable, |
| | 2052 | } |
| | 2053 | }, |
| | 2054 | .mul => { |
| | 2055 | switch (lhs_ty.zigTypeTag()) { |
| | 2056 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), |
| | 2057 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), |
| | 2058 | .Int => { |
| | 2059 | assert(lhs_ty.eql(rhs_ty)); |
| | 2060 | const int_info = lhs_ty.intInfo(self.target.*); |
| | 2061 | if (int_info.bits <= 32) { |
| | 2062 | // TODO add optimisations for multiplication |
| | 2063 | // with immediates, for example a * 2 can be |
| | 2064 | // lowered to a << 1 |
| | 2065 | return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| | 2066 | } else { |
| | 2067 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| | 2068 | } |
| | 2069 | }, |
| | 2070 | else => unreachable, |
| | 2071 | } |
| | 2072 | }, |
| | 2073 | .bit_and, |
| | 2074 | .bit_or, |
| | 2075 | .xor, |
| | 2076 | => { |
| | 2077 | switch (lhs_ty.zigTypeTag()) { |
| | 2078 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), |
| | 2079 | .Int => { |
| | 2080 | assert(lhs_ty.eql(rhs_ty)); |
| | 2081 | const int_info = lhs_ty.intInfo(self.target.*); |
| | 2082 | if (int_info.bits <= 32) { |
| | 2083 | const lhs_immediate_ok = lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null; |
| | 2084 | const rhs_immediate_ok = rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null; |
| | 2085 | |
| | 2086 | if (rhs_immediate_ok) { |
| | 2087 | return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false); |
| | 2088 | } else if (lhs_immediate_ok) { |
| | 2089 | // swap lhs and rhs |
| | 2090 | return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true); |
| | 2091 | } else { |
| | 2092 | return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| | 2093 | } |
| | 2094 | } else { |
| | 2095 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| | 2096 | } |
| | 2097 | }, |
| | 2098 | else => unreachable, |
| | 2099 | } |
| | 2100 | }, |
| | 2101 | .shl, |
| | 2102 | .shr, |
| | 2103 | => { |
| | 2104 | switch (lhs_ty.zigTypeTag()) { |
| | 2105 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), |
| | 2106 | .Int => { |
| | 2107 | const int_info = lhs_ty.intInfo(self.target.*); |
| | 2108 | if (int_info.bits <= 32) { |
| | 2109 | const rhs_immediate_ok = rhs == .immediate; |
| | 2110 | |
| | 2111 | if (rhs_immediate_ok) { |
| | 2112 | return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false); |
| | 2113 | } else { |
| | 2114 | return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| | 2115 | } |
| | 2116 | } else { |
| | 2117 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| | 2118 | } |
| | 2119 | }, |
| | 2120 | else => unreachable, |
| | 2121 | } |
| | 2122 | }, |
| | 2123 | .bool_and, |
| | 2124 | .bool_or, |
| | 2125 | => { |
| | 2126 | switch (lhs_ty.zigTypeTag()) { |
| | 2127 | .Bool => { |
| | 2128 | const lhs_immediate_ok = lhs == .immediate; |
| | 2129 | const rhs_immediate_ok = rhs == .immediate; |
| | 2130 | |
| | 2131 | if (rhs_immediate_ok) { |
| | 2132 | return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false); |
| | 2133 | } else if (lhs_immediate_ok) { |
| | 2134 | // swap lhs and rhs |
| | 2135 | return try self.binOpImmediate(tag, maybe_inst, rhs, lhs, rhs_ty, true); |
| | 2136 | } else { |
| | 2137 | return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| | 2138 | } |
| | 2139 | }, |
| | 2140 | else => unreachable, |
| | 2141 | } |
| | 2142 | }, |
| | 2143 | .ptr_add, |
| | 2144 | .ptr_sub, |
| | 2145 | => { |
| | 2146 | switch (lhs_ty.zigTypeTag()) { |
| | 2147 | .Pointer => { |
| | 2148 | const ptr_ty = lhs_ty; |
| | 2149 | const pointee_ty = switch (ptr_ty.ptrSize()) { |
| | 2150 | .One => ptr_ty.childType().childType(), // ptr to array, so get array element type |
| | 2151 | else => ptr_ty.childType(), |
| | 2152 | }; |
| 2080 | | 2153 | |
| 2081 | if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); | 2154 | if (pointee_ty.abiSize(self.target.*) > 1) { |
| 2082 | defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register}); | 2155 | return self.fail("TODO ptr_add, ptr_sub with more element sizes", .{}); |
| 2083 | | 2156 | } |
| 2084 | // Destination must be a register | | |
| 2085 | // LHS must be a register | | |
| 2086 | // RHS must be a register | | |
| 2087 | var dst_mcv: MCValue = undefined; | | |
| 2088 | var lhs_mcv: MCValue = lhs; | | |
| 2089 | var rhs_mcv: MCValue = rhs; | | |
| 2090 | | | |
| 2091 | // Allocate registers for operands and/or destination | | |
| 2092 | // Allocate 1 or 2 registers | | |
| 2093 | if (lhs_is_register) { | | |
| 2094 | // Move RHS to register | | |
| 2095 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(null) }; | | |
| 2096 | rhs_mcv = dst_mcv; | | |
| 2097 | } else { | | |
| 2098 | // Move LHS and RHS to register | | |
| 2099 | const regs = try self.register_manager.allocRegs(2, .{ null, null }); | | |
| 2100 | lhs_mcv = MCValue{ .register = regs[0] }; | | |
| 2101 | rhs_mcv = MCValue{ .register = regs[1] }; | | |
| 2102 | dst_mcv = lhs_mcv; | | |
| 2103 | } | | |
| 2104 | | 2157 | |
| 2105 | // Move the operands to the newly allocated registers | 2158 | return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| 2106 | if (!lhs_is_register) { | 2159 | }, |
| 2107 | try self.genSetReg(self.air.typeOf(op), lhs_mcv.register, lhs); | 2160 | else => unreachable, |
| | 2161 | } |
| | 2162 | }, |
| | 2163 | else => unreachable, |
| 2108 | } | 2164 | } |
| 2109 | try self.genSetReg(Type.initTag(.usize), rhs_mcv.register, rhs); | | |
| 2110 | | | |
| 2111 | _ = try self.addInst(.{ | | |
| 2112 | .tag = .mul, | | |
| 2113 | .data = .{ .rrr = .{ | | |
| 2114 | .rd = dst_mcv.register, | | |
| 2115 | .rn = lhs_mcv.register, | | |
| 2116 | .rm = rhs_mcv.register, | | |
| 2117 | } }, | | |
| 2118 | }); | | |
| 2119 | return dst_mcv; | | |
| 2120 | } | 2165 | } |
| 2121 | | 2166 | |
| 2122 | fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, abi_size: u32) !void { | 2167 | fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, abi_size: u32) !void { |
| ... | @@ -2492,75 +2537,38 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { | ... | @@ -2492,75 +2537,38 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 2492 | const rhs = try self.resolveInst(bin_op.rhs); | 2537 | const rhs = try self.resolveInst(bin_op.rhs); |
| 2493 | const lhs_ty = self.air.typeOf(bin_op.lhs); | 2538 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 2494 | | 2539 | |
| 2495 | if (lhs_ty.abiSize(self.target.*) > 4) { | 2540 | switch (lhs_ty.zigTypeTag()) { |
| 2496 | return self.fail("TODO cmp for types with size > 4", .{}); | 2541 | .Vector => return self.fail("TODO ARM cmp vectors", .{}), |
| 2497 | } | 2542 | .Optional => return self.fail("TODO ARM cmp optionals", .{}), |
| 2498 | | 2543 | .Float => return self.fail("TODO ARM cmp floats", .{}), |
| 2499 | const signedness: std.builtin.Signedness = blk: { | 2544 | .Int, .Bool, .Pointer, .ErrorSet, .Enum => { |
| 2500 | // by default we tell the operand type is unsigned (i.e. bools and enum values) | 2545 | var int_buffer: Type.Payload.Bits = undefined; |
| 2501 | if (lhs_ty.zigTypeTag() != .Int) break :blk .unsigned; | 2546 | const int_ty = switch (lhs_ty.zigTypeTag()) { |
| 2502 | | 2547 | .Enum => lhs_ty.intTagType(&int_buffer), |
| 2503 | // incase of an actual integer, we emit the correct signedness | 2548 | .Int => lhs_ty, |
| 2504 | break :blk lhs_ty.intInfo(self.target.*).signedness; | 2549 | .Bool => Type.initTag(.u1), |
| 2505 | }; | 2550 | .Pointer => Type.usize, |
| 2506 | | 2551 | .ErrorSet => Type.initTag(.u16), |
| 2507 | try self.spillCompareFlagsIfOccupied(); | 2552 | else => unreachable, |
| 2508 | self.compare_flags_inst = inst; | 2553 | }; |
| 2509 | | | |
| 2510 | const lhs_is_register = lhs == .register; | | |
| 2511 | const rhs_is_register = rhs == .register; | | |
| 2512 | // lhs should always be a register | | |
| 2513 | const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs); | | |
| 2514 | | 2554 | |
| 2515 | if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); | 2555 | const int_info = int_ty.intInfo(self.target.*); |
| 2516 | defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register}); | 2556 | if (int_info.bits <= 32) { |
| 2517 | if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); | 2557 | try self.spillCompareFlagsIfOccupied(); |
| 2518 | defer if (rhs_is_register) self.register_manager.unfreezeRegs(&.{rhs.register}); | 2558 | self.compare_flags_inst = inst; |
| 2519 | | 2559 | |
| 2520 | var lhs_mcv = lhs; | 2560 | _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty); |
| 2521 | var rhs_mcv = rhs; | | |
| 2522 | | 2561 | |
| 2523 | // Allocate registers | 2562 | break :result switch (int_info.signedness) { |
| 2524 | if (rhs_should_be_register) { | 2563 | .signed => MCValue{ .compare_flags_signed = op }, |
| 2525 | if (!lhs_is_register and !rhs_is_register) { | 2564 | .unsigned => MCValue{ .compare_flags_unsigned = op }, |
| 2526 | const regs = try self.register_manager.allocRegs(2, .{ | 2565 | }; |
| 2527 | Air.refToIndex(bin_op.lhs).?, Air.refToIndex(bin_op.rhs).?, | 2566 | } else { |
| 2528 | }); | 2567 | return self.fail("TODO ARM cmp for ints > 32 bits", .{}); |
| 2529 | lhs_mcv = MCValue{ .register = regs[0] }; | 2568 | } |
| 2530 | rhs_mcv = MCValue{ .register = regs[1] }; | 2569 | }, |
| 2531 | } else if (!rhs_is_register) { | 2570 | else => unreachable, |
| 2532 | const track_inst = if (self.liveness.operandDies(inst, 1)) null else Air.refToIndex(bin_op.rhs).?; | | |
| 2533 | rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(track_inst) }; | | |
| 2534 | } else if (!lhs_is_register) { | | |
| 2535 | const track_inst = if (self.liveness.operandDies(inst, 0)) null else Air.refToIndex(bin_op.lhs).?; | | |
| 2536 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(track_inst) }; | | |
| 2537 | } | | |
| 2538 | } else { | | |
| 2539 | if (!lhs_is_register) { | | |
| 2540 | const track_inst = if (self.liveness.operandDies(inst, 0)) null else Air.refToIndex(bin_op.lhs).?; | | |
| 2541 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(track_inst) }; | | |
| 2542 | } | | |
| 2543 | } | | |
| 2544 | | | |
| 2545 | // Move the operands to the newly allocated registers | | |
| 2546 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | | |
| 2547 | if (lhs_mcv == .register and !lhs_is_register) { | | |
| 2548 | try self.genSetReg(lhs_ty, lhs_mcv.register, lhs); | | |
| 2549 | branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.lhs).?, lhs); | | |
| 2550 | } | | |
| 2551 | if (rhs_mcv == .register and !rhs_is_register) { | | |
| 2552 | try self.genSetReg(lhs_ty, rhs_mcv.register, rhs); | | |
| 2553 | branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.rhs).?, rhs); | | |
| 2554 | } | 2571 | } |
| 2555 | | | |
| 2556 | // The destination register is not present in the cmp instruction | | |
| 2557 | // The signedness of the integer does not matter for the cmp instruction | | |
| 2558 | try self.genBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq, undefined); | | |
| 2559 | | | |
| 2560 | break :result switch (signedness) { | | |
| 2561 | .signed => MCValue{ .compare_flags_signed = op }, | | |
| 2562 | .unsigned => MCValue{ .compare_flags_unsigned = op }, | | |
| 2563 | }; | | |
| 2564 | }; | 2572 | }; |
| 2565 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 2573 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2566 | } | 2574 | } |
| ... | @@ -2787,7 +2795,14 @@ fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue { | ... | @@ -2787,7 +2795,14 @@ fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2787 | else => .{ .register = try self.copyToTmpRegister(ty, operand) }, | 2795 | else => .{ .register = try self.copyToTmpRegister(ty, operand) }, |
| 2788 | }; | 2796 | }; |
| 2789 | | 2797 | |
| 2790 | try self.genBinOpCode(undefined, reg_mcv, .{ .immediate = 0 }, false, .cmp_eq, undefined); | 2798 | _ = try self.addInst(.{ |
| | 2799 | .tag = .cmp, |
| | 2800 | .data = .{ .rr_op = .{ |
| | 2801 | .rd = undefined, |
| | 2802 | .rn = reg_mcv.register, |
| | 2803 | .op = Instruction.Operand.fromU32(0).?, |
| | 2804 | } }, |
| | 2805 | }); |
| 2791 | | 2806 | |
| 2792 | return MCValue{ .compare_flags_unsigned = .eq }; | 2807 | return MCValue{ .compare_flags_unsigned = .eq }; |
| 2793 | } else { | 2808 | } else { |
| ... | @@ -2817,7 +2832,14 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { | ... | @@ -2817,7 +2832,14 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2817 | else => .{ .register = try self.copyToTmpRegister(error_type, operand) }, | 2832 | else => .{ .register = try self.copyToTmpRegister(error_type, operand) }, |
| 2818 | }; | 2833 | }; |
| 2819 | | 2834 | |
| 2820 | try self.genBinOpCode(undefined, reg_mcv, .{ .immediate = 0 }, false, .cmp_eq, undefined); | 2835 | _ = try self.addInst(.{ |
| | 2836 | .tag = .cmp, |
| | 2837 | .data = .{ .rr_op = .{ |
| | 2838 | .rd = undefined, |
| | 2839 | .rn = reg_mcv.register, |
| | 2840 | .op = Instruction.Operand.fromU32(0).?, |
| | 2841 | } }, |
| | 2842 | }); |
| 2821 | | 2843 | |
| 2822 | return MCValue{ .compare_flags_unsigned = .gt }; | 2844 | return MCValue{ .compare_flags_unsigned = .gt }; |
| 2823 | } else { | 2845 | } else { |
| ... | @@ -3028,17 +3050,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3028,17 +3050,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { |
| 3028 | return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); | 3050 | return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); |
| 3029 | } | 3051 | } |
| 3030 | | 3052 | |
| 3031 | fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void { | | |
| 3032 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | | |
| 3033 | const air_tags = self.air.instructions.items(.tag); | | |
| 3034 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (air_tags[inst]) { | | |
| 3035 | .bool_and => try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_and), | | |
| 3036 | .bool_or => try self.genBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_or), | | |
| 3037 | else => unreachable, // Not a boolean operation | | |
| 3038 | }; | | |
| 3039 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | | |
| 3040 | } | | |
| 3041 | | | |
| 3042 | fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { | 3053 | fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { |
| 3043 | const block_data = self.blocks.getPtr(block).?; | 3054 | const block_data = self.blocks.getPtr(block).?; |
| 3044 | | 3055 | |
| ... | @@ -3342,17 +3353,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro | ... | @@ -3342,17 +3353,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3342 | }); | 3353 | }); |
| 3343 | | 3354 | |
| 3344 | // mov len, #abi_size | 3355 | // mov len, #abi_size |
| 3345 | const len_op: Instruction.Operand = if (Instruction.Operand.fromU32(abi_size)) |x| x else { | 3356 | try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size }); |
| 3346 | return self.fail("TODO load: set reg to elem_size with all possible sizes", .{}); | | |
| 3347 | }; | | |
| 3348 | _ = try self.addInst(.{ | | |
| 3349 | .tag = .mov, | | |
| 3350 | .data = .{ .rr_op = .{ | | |
| 3351 | .rd = len_reg, | | |
| 3352 | .rn = .r0, | | |
| 3353 | .op = len_op, | | |
| 3354 | } }, | | |
| 3355 | }); | | |
| 3356 | | 3357 | |
| 3357 | // memcpy(src, dst, len) | 3358 | // memcpy(src, dst, len) |
| 3358 | try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); | 3359 | try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); |
| ... | @@ -3738,17 +3739,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I | ... | @@ -3738,17 +3739,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I |
| 3738 | }); | 3739 | }); |
| 3739 | | 3740 | |
| 3740 | // mov len, #abi_size | 3741 | // mov len, #abi_size |
| 3741 | const len_op: Instruction.Operand = if (Instruction.Operand.fromU32(abi_size)) |x| x else { | 3742 | try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size }); |
| 3742 | return self.fail("TODO load: set reg to elem_size with all possible sizes", .{}); | | |
| 3743 | }; | | |
| 3744 | _ = try self.addInst(.{ | | |
| 3745 | .tag = .mov, | | |
| 3746 | .data = .{ .rr_op = .{ | | |
| 3747 | .rd = len_reg, | | |
| 3748 | .rn = .r0, | | |
| 3749 | .op = len_op, | | |
| 3750 | } }, | | |
| 3751 | }); | | |
| 3752 | | 3743 | |
| 3753 | // memcpy(src, dst, len) | 3744 | // memcpy(src, dst, len) |
| 3754 | try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); | 3745 | try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); |