authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-03-21 12:36:47+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-03-21 12:36:47+01:00
logb48d8cce52369e7625f9dab8a37efe9c3a33186b
tree1ff1d7af0c7a98a74f651dab3505be75e499438b
parent3d8d6c0a6d7a29396725467672023b5ec3adbce6
parenta153732d5a86f7d8c5ca5bc91cfad1ccf0f8f573
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11235 from joachimschmidt557/stage2-riscv

stage2 RISCV64: remove MCValue.embedded_in_code

3 files changed, 262 insertions(+), 71 deletions(-)

src/arch/riscv64/CodeGen.zig+219-71
......@@ -111,11 +111,6 @@ const MCValue = union(enum) {
111111 /// A pointer-sized integer that fits in a register.
112112 /// If the type is a pointer, this is the pointer address in virtual address space.
113113 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,
119114 /// The value is in a target-specific register.
120115 register: Register,
121116 /// The value is in memory at a hard-coded address.
......@@ -129,7 +124,7 @@ const MCValue = union(enum) {
129124
130125 fn isMemory(mcv: MCValue) bool {
131126 return switch (mcv) {
132 .embedded_in_code, .memory, .stack_offset => true,
127 .memory, .stack_offset => true,
133128 else => false,
134129 };
135130 }
......@@ -148,10 +143,8 @@ const MCValue = union(enum) {
148143 .dead => unreachable,
149144
150145 .immediate,
151 .embedded_in_code,
152146 .memory,
153147 .ptr_stack_offset,
154 .ptr_embedded_in_code,
155148 .undef,
156149 => false,
157150
......@@ -416,14 +409,17 @@ fn gen(self: *Self) !void {
416409 });
417410
418411 // 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| {
427423 _ = jmp_reloc;
428424 return self.fail("TODO add branches in RISCV64", .{});
429425 }
......@@ -496,10 +492,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
496492
497493 switch (air_tags[inst]) {
498494 // zig fmt: off
499 .add, .ptr_add => try self.airAdd(inst),
495 .add, .ptr_add => try self.airBinOp(inst),
500496 .addwrap => try self.airAddWrap(inst),
501497 .add_sat => try self.airAddSat(inst),
502 .sub, .ptr_sub => try self.airSub(inst),
498 .sub, .ptr_sub => try self.airBinOp(inst),
503499 .subwrap => try self.airSubWrap(inst),
504500 .sub_sat => try self.airSubSat(inst),
505501 .mul => try self.airMul(inst),
......@@ -927,9 +923,182 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
927923 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
928924}
929925
930fn 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.
934fn 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.
1032fn 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
1093fn airBinOp(self: *Self, inst: Air.Inst.Index) !void {
1094 const tag = self.air.instructions.items(.tag)[inst];
9311095 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);
9331102 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
9341103}
9351104
......@@ -945,12 +1114,6 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
9451114 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
9461115}
9471116
948fn 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
9541117fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {
9551118 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
9561119 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
12831446 .dead => unreachable,
12841447 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
12851448 .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 },
12921449 .register => {
12931450 return self.fail("TODO implement loading from MCValue.register", .{});
12941451 },
......@@ -1331,27 +1488,19 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
13311488 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
13321489}
13331490
1334fn 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);
1491fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) !void {
1492 _ = ptr_ty;
1493
13391494 switch (ptr) {
13401495 .none => unreachable,
13411496 .undef => unreachable,
13421497 .unreach => unreachable,
13431498 .dead => unreachable,
13441499 .immediate => |imm| {
1345 try self.setRegOrMem(elem_ty, .{ .memory = imm }, value);
1500 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
13461501 },
13471502 .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);
13551504 },
13561505 .register => {
13571506 return self.fail("TODO implement storing to MCValue.register", .{});
......@@ -1363,6 +1512,17 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void {
13631512 return self.fail("TODO implement storing to MCValue.stack_offset", .{});
13641513 },
13651514 }
1515}
1516
1517fn 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
13661526 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
13671527}
13681528
......@@ -1508,7 +1668,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
15081668 .immediate => unreachable,
15091669 .unreach => unreachable,
15101670 .dead => unreachable,
1511 .embedded_in_code => unreachable,
15121671 .memory => unreachable,
15131672 .register => |reg| {
15141673 try self.register_manager.getReg(reg, null);
......@@ -1520,9 +1679,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
15201679 .ptr_stack_offset => {
15211680 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
15221681 },
1523 .ptr_embedded_in_code => {
1524 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
1525 },
15261682 }
15271683 }
15281684
......@@ -2052,7 +2208,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
20522208 switch (mcv) {
20532209 .dead => unreachable,
20542210 .ptr_stack_offset => unreachable,
2055 .ptr_embedded_in_code => unreachable,
20562211 .unreach, .none => return, // Nothing to do.
20572212 .undef => {
20582213 if (!self.wantSafety())
......@@ -2098,6 +2253,20 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
20982253 return self.fail("TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf
20992254 }
21002255 },
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 },
21012270 .memory => |addr| {
21022271 // The value is in memory at a hard-coded address.
21032272 // 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 {
23212490 }
23222491}
23232492
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.
2329fn 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
23452493fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue {
23462494 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
23472495 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
src/arch/riscv64/Emit.zig+25
......@@ -43,6 +43,9 @@ pub fn emitMir(
4343 for (mir_tags) |tag, index| {
4444 const inst = @intCast(u32, index);
4545 switch (tag) {
46 .add => try emit.mirRType(inst),
47 .sub => try emit.mirRType(inst),
48
4649 .addi => try emit.mirIType(inst),
4750 .jalr => try emit.mirIType(inst),
4851 .ld => try emit.mirIType(inst),
......@@ -56,6 +59,8 @@ pub fn emitMir(
5659 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),
5760 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
5861
62 .mv => try emit.mirRR(inst),
63
5964 .nop => try emit.mirNop(inst),
6065 .ret => try emit.mirNop(inst),
6166
......@@ -131,6 +136,17 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
131136 }
132137}
133138
139fn mirRType(emit: *Emit, inst: Mir.Inst.Index) !void {
140 const tag = emit.mir.instructions.items(.tag)[inst];
141 const r_type = emit.mir.instructions.items(.data)[inst].r_type;
142
143 switch (tag) {
144 .add => try emit.writeInstruction(Instruction.add(r_type.rd, r_type.rs1, r_type.rs2)),
145 .sub => try emit.writeInstruction(Instruction.sub(r_type.rd, r_type.rs1, r_type.rs2)),
146 else => unreachable,
147 }
148}
149
134150fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {
135151 const tag = emit.mir.instructions.items(.tag)[inst];
136152 const i_type = emit.mir.instructions.items(.data)[inst].i_type;
......@@ -186,6 +202,15 @@ fn mirDebugEpilogueBegin(self: *Emit) !void {
186202 }
187203}
188204
205fn mirRR(emit: *Emit, inst: Mir.Inst.Index) !void {
206 const tag = emit.mir.instructions.items(.tag)[inst];
207 const rr = emit.mir.instructions.items(.data)[inst].rr;
208
209 switch (tag) {
210 .mv => try emit.writeInstruction(Instruction.addi(rr.rd, rr.rs, 0)),
211 else => unreachable,
212 }
213}
189214fn mirUType(emit: *Emit, inst: Mir.Inst.Index) !void {
190215 const tag = emit.mir.instructions.items(.tag)[inst];
191216 const u_type = emit.mir.instructions.items(.data)[inst].u_type;
src/arch/riscv64/Mir.zig+18
......@@ -24,6 +24,7 @@ pub const Inst = struct {
2424 data: Data,
2525
2626 pub const Tag = enum(u16) {
27 add,
2728 addi,
2829 /// Pseudo-instruction: End of prologue
2930 dbg_prologue_end,
......@@ -36,9 +37,11 @@ pub const Inst = struct {
3637 jalr,
3738 ld,
3839 lui,
40 mv,
3941 nop,
4042 ret,
4143 sd,
44 sub,
4245 };
4346
4447 /// The position of an MIR instruction within the `Mir` instructions array.
......@@ -68,6 +71,13 @@ pub const Inst = struct {
6871 ///
6972 /// Used by e.g. blr
7073 reg: Register,
74 /// Two registers
75 ///
76 /// Used by e.g. mv
77 rr: struct {
78 rd: Register,
79 rs: Register,
80 },
7181 /// I-Type
7282 ///
7383 /// Used by e.g. jalr
......@@ -76,6 +86,14 @@ pub const Inst = struct {
7686 rs1: Register,
7787 imm12: i12,
7888 },
89 /// R-Type
90 ///
91 /// Used by e.g. add
92 r_type: struct {
93 rd: Register,
94 rs1: Register,
95 rs2: Register,
96 },
7997 /// U-Type
8098 ///
8199 /// Used by e.g. lui