| ... | ... | @@ -111,11 +111,6 @@ const MCValue = union(enum) { |
| 111 | 111 | /// A pointer-sized integer that fits in a register. |
| 112 | 112 | /// If the type is a pointer, this is the pointer address in virtual address space. |
| 113 | 113 | immediate: u64, |
| 114 | | /// The constant was emitted into the code, at this offset. |
| 115 | | /// If the type is a pointer, it means the pointer address is embedded in the code. |
| 116 | | embedded_in_code: usize, |
| 117 | | /// The value is a pointer to a constant which was emitted into the code, at this offset. |
| 118 | | ptr_embedded_in_code: usize, |
| 119 | 114 | /// The value is in a target-specific register. |
| 120 | 115 | register: Register, |
| 121 | 116 | /// The value is in memory at a hard-coded address. |
| ... | ... | @@ -129,7 +124,7 @@ const MCValue = union(enum) { |
| 129 | 124 | |
| 130 | 125 | fn isMemory(mcv: MCValue) bool { |
| 131 | 126 | return switch (mcv) { |
| 132 | | .embedded_in_code, .memory, .stack_offset => true, |
| 127 | .memory, .stack_offset => true, |
| 133 | 128 | else => false, |
| 134 | 129 | }; |
| 135 | 130 | } |
| ... | ... | @@ -148,10 +143,8 @@ const MCValue = union(enum) { |
| 148 | 143 | .dead => unreachable, |
| 149 | 144 | |
| 150 | 145 | .immediate, |
| 151 | | .embedded_in_code, |
| 152 | 146 | .memory, |
| 153 | 147 | .ptr_stack_offset, |
| 154 | | .ptr_embedded_in_code, |
| 155 | 148 | .undef, |
| 156 | 149 | => false, |
| 157 | 150 | |
| ... | ... | @@ -416,14 +409,17 @@ fn gen(self: *Self) !void { |
| 416 | 409 | }); |
| 417 | 410 | |
| 418 | 411 | // exitlude jumps |
| 419 | | if (self.exitlude_jump_relocs.items.len == 1) { |
| 420 | | // There is only one relocation. Hence, |
| 421 | | // this relocation must be at the end of |
| 422 | | // the code. Therefore, we can just delete |
| 423 | | // the space initially reserved for the |
| 424 | | // jump |
| 425 | | self.mir_instructions.len -= 1; |
| 426 | | } 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| { |
| 427 | 423 | _ = jmp_reloc; |
| 428 | 424 | return self.fail("TODO add branches in RISCV64", .{}); |
| 429 | 425 | } |
| ... | ... | @@ -496,10 +492,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 496 | 492 | |
| 497 | 493 | switch (air_tags[inst]) { |
| 498 | 494 | // zig fmt: off |
| 499 | | .add, .ptr_add => try self.airAdd(inst), |
| 495 | .add, .ptr_add => try self.airBinOp(inst), |
| 500 | 496 | .addwrap => try self.airAddWrap(inst), |
| 501 | 497 | .add_sat => try self.airAddSat(inst), |
| 502 | | .sub, .ptr_sub => try self.airSub(inst), |
| 498 | .sub, .ptr_sub => try self.airBinOp(inst), |
| 503 | 499 | .subwrap => try self.airSubWrap(inst), |
| 504 | 500 | .sub_sat => try self.airSubSat(inst), |
| 505 | 501 | .mul => try self.airMul(inst), |
| ... | ... | @@ -927,9 +923,182 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 927 | 923 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 928 | 924 | } |
| 929 | 925 | |
| 930 | | fn airAdd(self: *Self, inst: Air.Inst.Index) !void { |
| 926 | /// Don't call this function directly. Use binOp instead. |
| 927 | /// |
| 928 | /// Calling this function signals an intention to generate a Mir |
| 929 | /// instruction of the form |
| 930 | /// |
| 931 | /// op dest, lhs, rhs |
| 932 | /// |
| 933 | /// Asserts that generating an instruction of that form is possible. |
| 934 | fn binOpRegister( |
| 935 | self: *Self, |
| 936 | tag: Air.Inst.Tag, |
| 937 | maybe_inst: ?Air.Inst.Index, |
| 938 | lhs: MCValue, |
| 939 | rhs: MCValue, |
| 940 | lhs_ty: Type, |
| 941 | rhs_ty: Type, |
| 942 | ) !MCValue { |
| 943 | const lhs_is_register = lhs == .register; |
| 944 | const rhs_is_register = rhs == .register; |
| 945 | |
| 946 | if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); |
| 947 | if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); |
| 948 | |
| 949 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 950 | |
| 951 | const lhs_reg = if (lhs_is_register) lhs.register else blk: { |
| 952 | const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { |
| 953 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 954 | break :inst Air.refToIndex(bin_op.lhs).?; |
| 955 | } else null; |
| 956 | |
| 957 | const reg = try self.register_manager.allocReg(track_inst); |
| 958 | self.register_manager.freezeRegs(&.{reg}); |
| 959 | |
| 960 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 961 | |
| 962 | break :blk reg; |
| 963 | }; |
| 964 | defer self.register_manager.unfreezeRegs(&.{lhs_reg}); |
| 965 | |
| 966 | const rhs_reg = if (rhs_is_register) rhs.register else blk: { |
| 967 | const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { |
| 968 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 969 | break :inst Air.refToIndex(bin_op.rhs).?; |
| 970 | } else null; |
| 971 | |
| 972 | const reg = try self.register_manager.allocReg(track_inst); |
| 973 | self.register_manager.freezeRegs(&.{reg}); |
| 974 | |
| 975 | if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); |
| 976 | |
| 977 | break :blk reg; |
| 978 | }; |
| 979 | defer self.register_manager.unfreezeRegs(&.{rhs_reg}); |
| 980 | |
| 981 | const dest_reg = if (maybe_inst) |inst| blk: { |
| 982 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 983 | |
| 984 | if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { |
| 985 | break :blk lhs_reg; |
| 986 | } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { |
| 987 | break :blk rhs_reg; |
| 988 | } else { |
| 989 | break :blk try self.register_manager.allocReg(inst); |
| 990 | } |
| 991 | } else try self.register_manager.allocReg(null); |
| 992 | |
| 993 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 994 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| 995 | |
| 996 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 997 | .add => .add, |
| 998 | .sub => .sub, |
| 999 | else => unreachable, |
| 1000 | }; |
| 1001 | const mir_data: Mir.Inst.Data = switch (tag) { |
| 1002 | .add, |
| 1003 | .sub, |
| 1004 | => .{ .r_type = .{ |
| 1005 | .rd = dest_reg, |
| 1006 | .rs1 = lhs_reg, |
| 1007 | .rs2 = rhs_reg, |
| 1008 | } }, |
| 1009 | else => unreachable, |
| 1010 | }; |
| 1011 | |
| 1012 | _ = try self.addInst(.{ |
| 1013 | .tag = mir_tag, |
| 1014 | .data = mir_data, |
| 1015 | }); |
| 1016 | |
| 1017 | return MCValue{ .register = dest_reg }; |
| 1018 | } |
| 1019 | |
| 1020 | /// For all your binary operation needs, this function will generate |
| 1021 | /// the corresponding Mir instruction(s). Returns the location of the |
| 1022 | /// result. |
| 1023 | /// |
| 1024 | /// If the binary operation itself happens to be an Air instruction, |
| 1025 | /// pass the corresponding index in the inst parameter. That helps |
| 1026 | /// this function do stuff like reusing operands. |
| 1027 | /// |
| 1028 | /// This function does not do any lowering to Mir itself, but instead |
| 1029 | /// looks at the lhs and rhs and determines which kind of lowering |
| 1030 | /// would be best suitable and then delegates the lowering to other |
| 1031 | /// functions. |
| 1032 | fn binOp( |
| 1033 | self: *Self, |
| 1034 | tag: Air.Inst.Tag, |
| 1035 | maybe_inst: ?Air.Inst.Index, |
| 1036 | lhs: MCValue, |
| 1037 | rhs: MCValue, |
| 1038 | lhs_ty: Type, |
| 1039 | rhs_ty: Type, |
| 1040 | ) InnerError!MCValue { |
| 1041 | switch (tag) { |
| 1042 | // Arithmetic operations on integers and floats |
| 1043 | .add, |
| 1044 | .sub, |
| 1045 | => { |
| 1046 | switch (lhs_ty.zigTypeTag()) { |
| 1047 | .Float => return self.fail("TODO binary operations on floats", .{}), |
| 1048 | .Vector => return self.fail("TODO binary operations on vectors", .{}), |
| 1049 | .Int => { |
| 1050 | assert(lhs_ty.eql(rhs_ty)); |
| 1051 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1052 | if (int_info.bits <= 64) { |
| 1053 | // TODO immediate operands |
| 1054 | return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| 1055 | } else { |
| 1056 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 1057 | } |
| 1058 | }, |
| 1059 | else => unreachable, |
| 1060 | } |
| 1061 | }, |
| 1062 | .ptr_add, |
| 1063 | .ptr_sub, |
| 1064 | => { |
| 1065 | switch (lhs_ty.zigTypeTag()) { |
| 1066 | .Pointer => { |
| 1067 | const ptr_ty = lhs_ty; |
| 1068 | const elem_ty = switch (ptr_ty.ptrSize()) { |
| 1069 | .One => ptr_ty.childType().childType(), // ptr to array, so get array element type |
| 1070 | else => ptr_ty.childType(), |
| 1071 | }; |
| 1072 | const elem_size = elem_ty.abiSize(self.target.*); |
| 1073 | |
| 1074 | if (elem_size == 1) { |
| 1075 | const base_tag: Air.Inst.Tag = switch (tag) { |
| 1076 | .ptr_add => .add, |
| 1077 | .ptr_sub => .sub, |
| 1078 | else => unreachable, |
| 1079 | }; |
| 1080 | |
| 1081 | return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); |
| 1082 | } else { |
| 1083 | return self.fail("TODO ptr_add with elem_size > 1", .{}); |
| 1084 | } |
| 1085 | }, |
| 1086 | else => unreachable, |
| 1087 | } |
| 1088 | }, |
| 1089 | else => unreachable, |
| 1090 | } |
| 1091 | } |
| 1092 | |
| 1093 | fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| 1094 | const tag = self.air.instructions.items(.tag)[inst]; |
| 931 | 1095 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 932 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add for {}", .{self.target.cpu.arch}); |
| 1096 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1097 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1098 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 1099 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 1100 | |
| 1101 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); |
| 933 | 1102 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 934 | 1103 | } |
| 935 | 1104 | |
| ... | ... | @@ -945,12 +1114,6 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { |
| 945 | 1114 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 946 | 1115 | } |
| 947 | 1116 | |
| 948 | | fn airSub(self: *Self, inst: Air.Inst.Index) !void { |
| 949 | | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 950 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub for {}", .{self.target.cpu.arch}); |
| 951 | | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 952 | | } |
| 953 | | |
| 954 | 1117 | fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void { |
| 955 | 1118 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 956 | 1119 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch}); |
| ... | ... | @@ -1283,12 +1446,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 1283 | 1446 | .dead => unreachable, |
| 1284 | 1447 | .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), |
| 1285 | 1448 | .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), |
| 1286 | | .ptr_embedded_in_code => |off| { |
| 1287 | | try self.setRegOrMem(elem_ty, dst_mcv, .{ .embedded_in_code = off }); |
| 1288 | | }, |
| 1289 | | .embedded_in_code => { |
| 1290 | | return self.fail("TODO implement loading from MCValue.embedded_in_code", .{}); |
| 1291 | | }, |
| 1292 | 1449 | .register => { |
| 1293 | 1450 | return self.fail("TODO implement loading from MCValue.register", .{}); |
| 1294 | 1451 | }, |
| ... | ... | @@ -1331,27 +1488,19 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 1331 | 1488 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1332 | 1489 | } |
| 1333 | 1490 | |
| 1334 | | fn airStore(self: *Self, inst: Air.Inst.Index) !void { |
| 1335 | | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1336 | | const ptr = try self.resolveInst(bin_op.lhs); |
| 1337 | | const value = try self.resolveInst(bin_op.rhs); |
| 1338 | | const elem_ty = self.air.typeOf(bin_op.rhs); |
| 1491 | fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) !void { |
| 1492 | _ = ptr_ty; |
| 1493 | |
| 1339 | 1494 | switch (ptr) { |
| 1340 | 1495 | .none => unreachable, |
| 1341 | 1496 | .undef => unreachable, |
| 1342 | 1497 | .unreach => unreachable, |
| 1343 | 1498 | .dead => unreachable, |
| 1344 | 1499 | .immediate => |imm| { |
| 1345 | | try self.setRegOrMem(elem_ty, .{ .memory = imm }, value); |
| 1500 | try self.setRegOrMem(value_ty, .{ .memory = imm }, value); |
| 1346 | 1501 | }, |
| 1347 | 1502 | .ptr_stack_offset => |off| { |
| 1348 | | try self.genSetStack(elem_ty, off, value); |
| 1349 | | }, |
| 1350 | | .ptr_embedded_in_code => |off| { |
| 1351 | | try self.setRegOrMem(elem_ty, .{ .embedded_in_code = off }, value); |
| 1352 | | }, |
| 1353 | | .embedded_in_code => { |
| 1354 | | return self.fail("TODO implement storing to MCValue.embedded_in_code", .{}); |
| 1503 | try self.genSetStack(value_ty, off, value); |
| 1355 | 1504 | }, |
| 1356 | 1505 | .register => { |
| 1357 | 1506 | return self.fail("TODO implement storing to MCValue.register", .{}); |
| ... | ... | @@ -1363,6 +1512,17 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void { |
| 1363 | 1512 | return self.fail("TODO implement storing to MCValue.stack_offset", .{}); |
| 1364 | 1513 | }, |
| 1365 | 1514 | } |
| 1515 | } |
| 1516 | |
| 1517 | fn airStore(self: *Self, inst: Air.Inst.Index) !void { |
| 1518 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1519 | const ptr = try self.resolveInst(bin_op.lhs); |
| 1520 | const value = try self.resolveInst(bin_op.rhs); |
| 1521 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| 1522 | const value_ty = self.air.typeOf(bin_op.rhs); |
| 1523 | |
| 1524 | try self.store(ptr, value, ptr_ty, value_ty); |
| 1525 | |
| 1366 | 1526 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1367 | 1527 | } |
| 1368 | 1528 | |
| ... | ... | @@ -1508,7 +1668,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 1508 | 1668 | .immediate => unreachable, |
| 1509 | 1669 | .unreach => unreachable, |
| 1510 | 1670 | .dead => unreachable, |
| 1511 | | .embedded_in_code => unreachable, |
| 1512 | 1671 | .memory => unreachable, |
| 1513 | 1672 | .register => |reg| { |
| 1514 | 1673 | try self.register_manager.getReg(reg, null); |
| ... | ... | @@ -1520,9 +1679,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 1520 | 1679 | .ptr_stack_offset => { |
| 1521 | 1680 | return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); |
| 1522 | 1681 | }, |
| 1523 | | .ptr_embedded_in_code => { |
| 1524 | | return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); |
| 1525 | | }, |
| 1526 | 1682 | } |
| 1527 | 1683 | } |
| 1528 | 1684 | |
| ... | ... | @@ -2052,7 +2208,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2052 | 2208 | switch (mcv) { |
| 2053 | 2209 | .dead => unreachable, |
| 2054 | 2210 | .ptr_stack_offset => unreachable, |
| 2055 | | .ptr_embedded_in_code => unreachable, |
| 2056 | 2211 | .unreach, .none => return, // Nothing to do. |
| 2057 | 2212 | .undef => { |
| 2058 | 2213 | if (!self.wantSafety()) |
| ... | ... | @@ -2098,6 +2253,20 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2098 | 2253 | return self.fail("TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf |
| 2099 | 2254 | } |
| 2100 | 2255 | }, |
| 2256 | .register => |src_reg| { |
| 2257 | // If the registers are the same, nothing to do. |
| 2258 | if (src_reg.id() == reg.id()) |
| 2259 | return; |
| 2260 | |
| 2261 | // mov reg, src_reg |
| 2262 | _ = try self.addInst(.{ |
| 2263 | .tag = .mv, |
| 2264 | .data = .{ .rr = .{ |
| 2265 | .rd = reg, |
| 2266 | .rs = src_reg, |
| 2267 | } }, |
| 2268 | }); |
| 2269 | }, |
| 2101 | 2270 | .memory => |addr| { |
| 2102 | 2271 | // The value is in memory at a hard-coded address. |
| 2103 | 2272 | // If the type is a pointer, it means the pointer address is at this memory location. |
| ... | ... | @@ -2321,27 +2490,6 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { |
| 2321 | 2490 | } |
| 2322 | 2491 | } |
| 2323 | 2492 | |
| 2324 | | /// If the MCValue is an immediate, and it does not fit within this type, |
| 2325 | | /// we put it in a register. |
| 2326 | | /// A potential opportunity for future optimization here would be keeping track |
| 2327 | | /// of the fact that the instruction is available both as an immediate |
| 2328 | | /// and as a register. |
| 2329 | | fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCValue { |
| 2330 | | const mcv = try self.resolveInst(operand); |
| 2331 | | const ti = @typeInfo(T).Int; |
| 2332 | | switch (mcv) { |
| 2333 | | .immediate => |imm| { |
| 2334 | | // This immediate is unsigned. |
| 2335 | | const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed)); |
| 2336 | | if (imm >= math.maxInt(U)) { |
| 2337 | | return MCValue{ .register = try self.copyToTmpRegister(Type.initTag(.usize), mcv) }; |
| 2338 | | } |
| 2339 | | }, |
| 2340 | | else => {}, |
| 2341 | | } |
| 2342 | | return mcv; |
| 2343 | | } |
| 2344 | | |
| 2345 | 2493 | fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue { |
| 2346 | 2494 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 2347 | 2495 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |