authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-03-19 20:44:57+01:00
committergravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-03-19 20:44:57+01:00
loga153732d5a86f7d8c5ca5bc91cfad1ccf0f8f573
tree97f065f4df0b8b6f94ed3a2dc2ebed01152757af
parent7cdc47a4e08cd32e1f68ca4aad3ca22b11e4e7b2
signature Commit is signed but in an unrecognized format.

stage2 RISCV64: implement add, sub for registers


3 files changed, 212 insertions(+), 18 deletions(-)

src/arch/riscv64/CodeGen.zig+188-18
......@@ -409,14 +409,17 @@ fn gen(self: *Self) !void {
409409 });
410410
411411 // exitlude jumps
412 if (self.exitlude_jump_relocs.items.len == 1) {
413 // There is only one relocation. Hence,
414 // this relocation must be at the end of
415 // the code. Therefore, we can just delete
416 // the space initially reserved for the
417 // jump
418 self.mir_instructions.len -= 1;
419 } 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| {
420423 _ = jmp_reloc;
421424 return self.fail("TODO add branches in RISCV64", .{});
422425 }
......@@ -489,10 +492,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
489492
490493 switch (air_tags[inst]) {
491494 // zig fmt: off
492 .add, .ptr_add => try self.airAdd(inst),
495 .add, .ptr_add => try self.airBinOp(inst),
493496 .addwrap => try self.airAddWrap(inst),
494497 .add_sat => try self.airAddSat(inst),
495 .sub, .ptr_sub => try self.airSub(inst),
498 .sub, .ptr_sub => try self.airBinOp(inst),
496499 .subwrap => try self.airSubWrap(inst),
497500 .sub_sat => try self.airSubSat(inst),
498501 .mul => try self.airMul(inst),
......@@ -916,9 +919,182 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
916919 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
917920}
918921
919fn airAdd(self: *Self, inst: Air.Inst.Index) !void {
922/// Don't call this function directly. Use binOp instead.
923///
924/// Calling this function signals an intention to generate a Mir
925/// instruction of the form
926///
927/// op dest, lhs, rhs
928///
929/// Asserts that generating an instruction of that form is possible.
930fn binOpRegister(
931 self: *Self,
932 tag: Air.Inst.Tag,
933 maybe_inst: ?Air.Inst.Index,
934 lhs: MCValue,
935 rhs: MCValue,
936 lhs_ty: Type,
937 rhs_ty: Type,
938) !MCValue {
939 const lhs_is_register = lhs == .register;
940 const rhs_is_register = rhs == .register;
941
942 if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
943 if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register});
944
945 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
946
947 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
948 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
949 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
950 break :inst Air.refToIndex(bin_op.lhs).?;
951 } else null;
952
953 const reg = try self.register_manager.allocReg(track_inst);
954 self.register_manager.freezeRegs(&.{reg});
955
956 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
957
958 break :blk reg;
959 };
960 defer self.register_manager.unfreezeRegs(&.{lhs_reg});
961
962 const rhs_reg = if (rhs_is_register) rhs.register else blk: {
963 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
964 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
965 break :inst Air.refToIndex(bin_op.rhs).?;
966 } else null;
967
968 const reg = try self.register_manager.allocReg(track_inst);
969 self.register_manager.freezeRegs(&.{reg});
970
971 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
972
973 break :blk reg;
974 };
975 defer self.register_manager.unfreezeRegs(&.{rhs_reg});
976
977 const dest_reg = if (maybe_inst) |inst| blk: {
978 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
979
980 if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) {
981 break :blk lhs_reg;
982 } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) {
983 break :blk rhs_reg;
984 } else {
985 break :blk try self.register_manager.allocReg(inst);
986 }
987 } else try self.register_manager.allocReg(null);
988
989 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
990 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
991
992 const mir_tag: Mir.Inst.Tag = switch (tag) {
993 .add => .add,
994 .sub => .sub,
995 else => unreachable,
996 };
997 const mir_data: Mir.Inst.Data = switch (tag) {
998 .add,
999 .sub,
1000 => .{ .r_type = .{
1001 .rd = dest_reg,
1002 .rs1 = lhs_reg,
1003 .rs2 = rhs_reg,
1004 } },
1005 else => unreachable,
1006 };
1007
1008 _ = try self.addInst(.{
1009 .tag = mir_tag,
1010 .data = mir_data,
1011 });
1012
1013 return MCValue{ .register = dest_reg };
1014}
1015
1016/// For all your binary operation needs, this function will generate
1017/// the corresponding Mir instruction(s). Returns the location of the
1018/// result.
1019///
1020/// If the binary operation itself happens to be an Air instruction,
1021/// pass the corresponding index in the inst parameter. That helps
1022/// this function do stuff like reusing operands.
1023///
1024/// This function does not do any lowering to Mir itself, but instead
1025/// looks at the lhs and rhs and determines which kind of lowering
1026/// would be best suitable and then delegates the lowering to other
1027/// functions.
1028fn binOp(
1029 self: *Self,
1030 tag: Air.Inst.Tag,
1031 maybe_inst: ?Air.Inst.Index,
1032 lhs: MCValue,
1033 rhs: MCValue,
1034 lhs_ty: Type,
1035 rhs_ty: Type,
1036) InnerError!MCValue {
1037 switch (tag) {
1038 // Arithmetic operations on integers and floats
1039 .add,
1040 .sub,
1041 => {
1042 switch (lhs_ty.zigTypeTag()) {
1043 .Float => return self.fail("TODO binary operations on floats", .{}),
1044 .Vector => return self.fail("TODO binary operations on vectors", .{}),
1045 .Int => {
1046 assert(lhs_ty.eql(rhs_ty));
1047 const int_info = lhs_ty.intInfo(self.target.*);
1048 if (int_info.bits <= 64) {
1049 // TODO immediate operands
1050 return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1051 } else {
1052 return self.fail("TODO binary operations on int with bits > 64", .{});
1053 }
1054 },
1055 else => unreachable,
1056 }
1057 },
1058 .ptr_add,
1059 .ptr_sub,
1060 => {
1061 switch (lhs_ty.zigTypeTag()) {
1062 .Pointer => {
1063 const ptr_ty = lhs_ty;
1064 const elem_ty = switch (ptr_ty.ptrSize()) {
1065 .One => ptr_ty.childType().childType(), // ptr to array, so get array element type
1066 else => ptr_ty.childType(),
1067 };
1068 const elem_size = elem_ty.abiSize(self.target.*);
1069
1070 if (elem_size == 1) {
1071 const base_tag: Air.Inst.Tag = switch (tag) {
1072 .ptr_add => .add,
1073 .ptr_sub => .sub,
1074 else => unreachable,
1075 };
1076
1077 return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1078 } else {
1079 return self.fail("TODO ptr_add with elem_size > 1", .{});
1080 }
1081 },
1082 else => unreachable,
1083 }
1084 },
1085 else => unreachable,
1086 }
1087}
1088
1089fn airBinOp(self: *Self, inst: Air.Inst.Index) !void {
1090 const tag = self.air.instructions.items(.tag)[inst];
9201091 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
921 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add for {}", .{self.target.cpu.arch});
1092 const lhs = try self.resolveInst(bin_op.lhs);
1093 const rhs = try self.resolveInst(bin_op.rhs);
1094 const lhs_ty = self.air.typeOf(bin_op.lhs);
1095 const rhs_ty = self.air.typeOf(bin_op.rhs);
1096
1097 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty);
9221098 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
9231099}
9241100
......@@ -934,12 +1110,6 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
9341110 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
9351111}
9361112
937fn airSub(self: *Self, inst: Air.Inst.Index) !void {
938 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
939 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub for {}", .{self.target.cpu.arch});
940 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
941}
942
9431113fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {
9441114 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
9451115 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch});
src/arch/riscv64/Emit.zig+14
......@@ -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),
......@@ -133,6 +136,17 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
133136 }
134137}
135138
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
136150fn mirIType(emit: *Emit, inst: Mir.Inst.Index) !void {
137151 const tag = emit.mir.instructions.items(.tag)[inst];
138152 const i_type = emit.mir.instructions.items(.data)[inst].i_type;
src/arch/riscv64/Mir.zig+10
......@@ -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,
......@@ -40,6 +41,7 @@ pub const Inst = struct {
4041 nop,
4142 ret,
4243 sd,
44 sub,
4345 };
4446
4547 /// The position of an MIR instruction within the `Mir` instructions array.
......@@ -84,6 +86,14 @@ pub const Inst = struct {
8486 rs1: Register,
8587 imm12: i12,
8688 },
89 /// R-Type
90 ///
91 /// Used by e.g. add
92 r_type: struct {
93 rd: Register,
94 rs1: Register,
95 rs2: Register,
96 },
8797 /// U-Type
8898 ///
8999 /// Used by e.g. lui