authorgravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-04-04 21:28:55+07:00
committergravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-04-14 22:18:06+07:00
log42f4bd34216ae1ae03df0a56502919109e030136
tree891e138924fc1f965f9c2b4b3774468692de00fd
parent1972a2b08063841bdd6dd411b4fb0c1b16225067

stage2: sparcv9: Add breakpoint, ret, and calling mechanism


3 files changed, 436 insertions(+), 103 deletions(-)

src/arch/sparcv9/CodeGen.zig+341-100
......@@ -453,7 +453,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
453453 .bitcast => @panic("TODO try self.airBitCast(inst)"),
454454 .block => try self.airBlock(inst),
455455 .br => @panic("TODO try self.airBr(inst)"),
456 .breakpoint => @panic("TODO try self.airBreakpoint()"),
456 .breakpoint => try self.airBreakpoint(),
457457 .ret_addr => @panic("TODO try self.airRetAddr(inst)"),
458458 .frame_addr => @panic("TODO try self.airFrameAddress(inst)"),
459459 .fence => @panic("TODO try self.airFence()"),
......@@ -476,7 +476,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
476476 .loop => @panic("TODO try self.airLoop(inst)"),
477477 .not => @panic("TODO try self.airNot(inst)"),
478478 .ptrtoint => @panic("TODO try self.airPtrToInt(inst)"),
479 .ret => @panic("TODO try self.airRet(inst)"),
479 .ret => try self.airRet(inst),
480480 .ret_load => try self.airRetLoad(inst),
481481 .store => try self.airStore(inst),
482482 .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"),
......@@ -667,6 +667,21 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
667667 return self.finishAir(inst, result, .{ .none, .none, .none });
668668}
669669
670fn airBreakpoint(self: *Self) !void {
671 // ta 0x01
672 _ = try self.addInst(.{
673 .tag = .tcc,
674 .data = .{
675 .trap = .{
676 .is_imm = true,
677 .cond = 0b1000, // TODO need to look into changing this into an enum
678 .rs2_or_imm = .{ .imm = 0x01 },
679 },
680 },
681 });
682 return self.finishAirBookkeeping();
683}
684
670685fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) !void {
671686 if (modifier == .always_tail) return self.fail("TODO implement tail calls for {}", .{self.target.cpu.arch});
672687
......@@ -695,10 +710,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
695710 .unreach => unreachable,
696711 .dead => unreachable,
697712 .memory => unreachable,
698 .compare_flags_signed => unreachable,
699 .compare_flags_unsigned => unreachable,
700 .got_load => unreachable,
701 .direct_load => unreachable,
702713 .register => |reg| {
703714 try self.register_manager.getReg(reg, null);
704715 try self.genSetReg(arg_ty, reg, arg_mcv);
......@@ -712,6 +723,44 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
712723 }
713724 }
714725
726 // Due to incremental compilation, how function calls are generated depends
727 // on linking.
728 if (self.air.value(callee)) |func_value| {
729 if (self.bin_file.tag == link.File.Elf.base_tag) {
730 if (func_value.castTag(.function)) |func_payload| {
731 const func = func_payload.data;
732 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
733 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
734 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
735 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
736 break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
737 } else unreachable;
738
739 try self.genSetReg(Type.initTag(.usize), .o7, .{ .memory = got_addr });
740
741 _ = try self.addInst(.{
742 .tag = .jmpl,
743 .data = .{ .branch_link_indirect = .{ .reg = .o7 } },
744 });
745 } else if (func_value.castTag(.extern_fn)) |_| {
746 return self.fail("TODO implement calling extern functions", .{});
747 } else {
748 return self.fail("TODO implement calling bitcasted functions", .{});
749 }
750 } else @panic("TODO SPARCv9 currently does not support non-ELF binaries");
751 } else {
752 assert(ty.zigTypeTag() == .Pointer);
753 const mcv = try self.resolveInst(callee);
754 try self.genSetReg(ty, .o7, mcv);
755
756 _ = try self.addInst(.{
757 .tag = .jmpl,
758 .data = .{ .branch_link_indirect = .{ .reg = .o7 } },
759 });
760 }
761
762 // TODO handle return value
763
715764 return self.fail("TODO implement call for {}", .{self.target.cpu.arch});
716765}
717766
......@@ -759,8 +808,17 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
759808 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
760809}
761810
811fn airRet(self: *Self, inst: Air.Inst.Index) !void {
812 const un_op = self.air.instructions.items(.data)[inst].un_op;
813 const operand = try self.resolveInst(un_op);
814 try self.ret(operand);
815 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
816}
817
762818fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
763 _ = inst;
819 const un_op = self.air.instructions.items(.data)[inst].un_op;
820 const ptr = try self.resolveInst(un_op);
821 _ = ptr;
764822 return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch});
765823 //return self.finishAir(inst, .dead, .{ un_op, .none, .none });
766824}
......@@ -832,6 +890,37 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
832890 return self.allocMem(inst, abi_size, abi_align);
833891}
834892
893fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
894 const elem_ty = self.air.typeOfIndex(inst);
895 const target = self.target.*;
896 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
897 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)});
898 };
899 const abi_align = elem_ty.abiAlignment(self.target.*);
900 if (abi_align > self.stack_align)
901 self.stack_align = abi_align;
902
903 if (reg_ok) {
904 // Make sure the type can fit in a register before we try to allocate one.
905 if (abi_size <= 8) {
906 if (self.register_manager.tryAllocReg(inst)) |reg| {
907 return MCValue{ .register = reg };
908 }
909 }
910 }
911 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
912 return MCValue{ .stack_offset = stack_offset };
913}
914
915/// Copies a value to a register without tracking the register. The register is not considered
916/// allocated. A second call to `copyToTmpRegister` may return the same register.
917/// This can have a side effect of spilling instructions to the stack to free up a register.
918fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
919 const reg = try self.register_manager.allocReg(null);
920 try self.genSetReg(ty, reg, mcv);
921 return reg;
922}
923
835924fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
836925 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
837926 try table.ensureUnusedCapacity(self.gpa, additional_count);
......@@ -885,37 +974,216 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live
885974 self.finishAirBookkeeping();
886975}
887976
977fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, off: i13, abi_size: u64) !void {
978 _ = value_reg;
979 _ = addr_reg;
980 _ = off;
981
982 switch (abi_size) {
983 1, 2, 4, 8 => return self.fail("TODO: A.27 Load Integer", .{}),
984 3, 5, 6, 7 => return self.fail("TODO: genLoad for more abi_sizes", .{}),
985 else => unreachable,
986 }
987}
988
989fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
990 switch (mcv) {
991 .dead => unreachable,
992 .unreach, .none => return, // Nothing to do.
993 .undef => {
994 if (!self.wantSafety())
995 return; // The already existing value will do just fine.
996 // Write the debug undefined value.
997 return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
998 },
999 .ptr_stack_offset => |off| {
1000 const simm13 = math.cast(u12, off) catch
1001 return self.fail("TODO larger stack offsets", .{});
1002
1003 _ = try self.addInst(.{
1004 .tag = .add,
1005 .data = .{
1006 .arithmetic_3op = .{
1007 .is_imm = true,
1008 .rd = reg,
1009 .rs1 = .sp,
1010 .rs2_or_imm = .{ .imm = simm13 },
1011 },
1012 },
1013 });
1014 },
1015 .immediate => |x| {
1016 if (x <= math.maxInt(u12)) {
1017 _ = try self.addInst(.{
1018 .tag = .@"or",
1019 .data = .{
1020 .arithmetic_3op = .{
1021 .is_imm = true,
1022 .rd = reg,
1023 .rs1 = .g0,
1024 .rs2_or_imm = .{ .imm = @truncate(u12, x) },
1025 },
1026 },
1027 });
1028 } else if (x <= math.maxInt(u32)) {
1029 _ = try self.addInst(.{
1030 .tag = .sethi,
1031 .data = .{
1032 .sethi = .{
1033 .rd = reg,
1034 .imm = @truncate(u22, x >> 10),
1035 },
1036 },
1037 });
1038
1039 _ = try self.addInst(.{
1040 .tag = .@"or",
1041 .data = .{
1042 .arithmetic_3op = .{
1043 .is_imm = true,
1044 .rd = reg,
1045 .rs1 = reg,
1046 .rs2_or_imm = .{ .imm = @truncate(u10, x) },
1047 },
1048 },
1049 });
1050 } else if (x <= math.maxInt(u44)) {
1051 try self.genSetReg(ty, reg, .{ .immediate = @truncate(u32, x >> 12) });
1052
1053 _ = try self.addInst(.{
1054 .tag = .sllx,
1055 .data = .{
1056 .shift = .{
1057 .is_imm = true,
1058 .width = .shift64,
1059 .rd = reg,
1060 .rs1 = reg,
1061 .rs2_or_imm = .{ .imm = 12 },
1062 },
1063 },
1064 });
1065
1066 _ = try self.addInst(.{
1067 .tag = .@"or",
1068 .data = .{
1069 .arithmetic_3op = .{
1070 .is_imm = true,
1071 .rd = reg,
1072 .rs1 = reg,
1073 .rs2_or_imm = .{ .imm = @truncate(u12, x) },
1074 },
1075 },
1076 });
1077 } else {
1078 // Need to allocate a temporary register to load 64-bit immediates.
1079 const tmp_reg = try self.register_manager.allocReg(null);
1080
1081 try self.genSetReg(ty, tmp_reg, .{ .immediate = @truncate(u32, x) });
1082 try self.genSetReg(ty, reg, .{ .immediate = @truncate(u32, x >> 32) });
1083
1084 _ = try self.addInst(.{
1085 .tag = .sllx,
1086 .data = .{
1087 .shift = .{
1088 .is_imm = true,
1089 .width = .shift64,
1090 .rd = reg,
1091 .rs1 = reg,
1092 .rs2_or_imm = .{ .imm = 32 },
1093 },
1094 },
1095 });
1096
1097 _ = try self.addInst(.{
1098 .tag = .@"or",
1099 .data = .{
1100 .arithmetic_3op = .{
1101 .is_imm = false,
1102 .rd = reg,
1103 .rs1 = reg,
1104 .rs2_or_imm = .{ .rs2 = tmp_reg },
1105 },
1106 },
1107 });
1108 }
1109 },
1110 .register => |src_reg| {
1111 // If the registers are the same, nothing to do.
1112 if (src_reg.id() == reg.id())
1113 return;
1114
1115 // or %g0, src, dst (aka mov src, dst)
1116 _ = try self.addInst(.{
1117 .tag = .@"or",
1118 .data = .{
1119 .arithmetic_3op = .{
1120 .is_imm = false,
1121 .rd = reg,
1122 .rs1 = .g0,
1123 .rs2_or_imm = .{ .rs2 = src_reg },
1124 },
1125 },
1126 });
1127 },
1128 .memory => |addr| {
1129 // The value is in memory at a hard-coded address.
1130 // If the type is a pointer, it means the pointer address is at this memory location.
1131 try self.genSetReg(ty, reg, .{ .immediate = addr });
1132 try self.genLoad(reg, reg, 0, ty.abiSize(self.target.*));
1133 },
1134 .stack_offset => |off| {
1135 const simm13 = math.cast(u12, off) catch
1136 return self.fail("TODO larger stack offsets", .{});
1137 try self.genLoad(reg, .sp, simm13, ty.abiSize(self.target.*));
1138 },
1139 }
1140}
1141
1142fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
1143 const abi_size = ty.abiSize(self.target.*);
1144 switch (mcv) {
1145 .dead => unreachable,
1146 .unreach, .none => return, // Nothing to do.
1147 .undef => {
1148 if (!self.wantSafety())
1149 return; // The already existing value will do just fine.
1150 // TODO Upgrade this to a memset call when we have that available.
1151 switch (ty.abiSize(self.target.*)) {
1152 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
1153 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
1154 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
1155 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
1156 else => return self.fail("TODO implement memset", .{}),
1157 }
1158 },
1159 .immediate,
1160 .ptr_stack_offset,
1161 => {
1162 const reg = try self.copyToTmpRegister(ty, mcv);
1163 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
1164 },
1165 .register => return self.fail("TODO implement storing types abi_size={}", .{abi_size}),
1166 .memory, .stack_offset => return self.fail("TODO implement memcpy", .{}),
1167 }
1168}
1169
8881170fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
8891171 if (typed_value.val.isUndef())
8901172 return MCValue{ .undef = {} };
8911173
8921174 if (typed_value.val.castTag(.decl_ref)) |payload| {
893 return self.lowerDeclRef(typed_value, payload.data);
1175 _ = payload;
1176 return self.fail("TODO implement lowerDeclRef", .{});
1177 // return self.lowerDeclRef(typed_value, payload.data);
8941178 }
8951179 if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
896 return self.lowerDeclRef(typed_value, payload.data.decl);
1180 _ = payload;
1181 return self.fail("TODO implement lowerDeclRef", .{});
1182 // return self.lowerDeclRef(typed_value, payload.data.decl);
8971183 }
8981184 const target = self.target.*;
8991185
9001186 switch (typed_value.ty.zigTypeTag()) {
901 .Pointer => switch (typed_value.ty.ptrSize()) {
902 .Slice => {
903 return self.lowerUnnamedConst(typed_value);
904 },
905 else => {
906 switch (typed_value.val.tag()) {
907 .int_u64 => {
908 return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) };
909 },
910 .slice => {
911 return self.lowerUnnamedConst(typed_value);
912 },
913 else => {
914 return self.fail("TODO codegen more kinds of const pointers: {}", .{typed_value.val.tag()});
915 },
916 }
917 },
918 },
9191187 .Int => {
9201188 const info = typed_value.ty.intInfo(self.target.*);
9211189 if (info.bits <= 64) {
......@@ -929,83 +1197,11 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
9291197
9301198 return MCValue{ .immediate = unsigned };
9311199 } else {
932 return self.lowerUnnamedConst(typed_value);
1200 return self.fail("TODO implement int genTypedValue of > 64 bits", .{});
9331201 }
9341202 },
935 .Bool => {
936 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
937 },
9381203 .ComptimeInt => unreachable, // semantic analysis prevents this
9391204 .ComptimeFloat => unreachable, // semantic analysis prevents this
940 .Optional => {
941 if (typed_value.ty.isPtrLikeOptional()) {
942 if (typed_value.val.isNull())
943 return MCValue{ .immediate = 0 };
944
945 var buf: Type.Payload.ElemType = undefined;
946 return self.genTypedValue(.{
947 .ty = typed_value.ty.optionalChild(&buf),
948 .val = typed_value.val,
949 });
950 } else if (typed_value.ty.abiSize(self.target.*) == 1) {
951 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
952 }
953 return self.fail("TODO non pointer optionals", .{});
954 },
955 .Enum => {
956 if (typed_value.val.castTag(.enum_field_index)) |field_index| {
957 switch (typed_value.ty.tag()) {
958 .enum_simple => {
959 return MCValue{ .immediate = field_index.data };
960 },
961 .enum_full, .enum_nonexhaustive => {
962 const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data;
963 if (enum_full.values.count() != 0) {
964 const tag_val = enum_full.values.keys()[field_index.data];
965 return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val });
966 } else {
967 return MCValue{ .immediate = field_index.data };
968 }
969 },
970 else => unreachable,
971 }
972 } else {
973 var int_tag_buffer: Type.Payload.Bits = undefined;
974 const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer);
975 return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val });
976 }
977 },
978 .ErrorSet => {
979 const err_name = typed_value.val.castTag(.@"error").?.data.name;
980 const module = self.bin_file.options.module.?;
981 const global_error_set = module.global_error_set;
982 const error_index = global_error_set.get(err_name).?;
983 return MCValue{ .immediate = error_index };
984 },
985 .ErrorUnion => {
986 const error_type = typed_value.ty.errorUnionSet();
987 const payload_type = typed_value.ty.errorUnionPayload();
988
989 if (typed_value.val.castTag(.eu_payload)) |pl| {
990 if (!payload_type.hasRuntimeBits()) {
991 // We use the error type directly as the type.
992 return MCValue{ .immediate = 0 };
993 }
994
995 _ = pl;
996 return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty.fmtDebug()});
997 } else {
998 if (!payload_type.hasRuntimeBits()) {
999 // We use the error type directly as the type.
1000 return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val });
1001 }
1002
1003 return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty.fmtDebug()});
1004 }
1005 },
1006 .Struct => {
1007 return self.lowerUnnamedConst(typed_value);
1008 },
10091205 else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}),
10101206 }
10111207}
......@@ -1171,6 +1367,18 @@ fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
11711367 }
11721368}
11731369
1370fn ret(self: *Self, mcv: MCValue) !void {
1371 const ret_ty = self.fn_type.fnReturnType();
1372 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
1373
1374 // Just add space for an instruction, patch this later
1375 const index = try self.addInst(.{
1376 .tag = .nop,
1377 .data = .{ .nop = {} },
1378 });
1379 try self.exitlude_jump_relocs.append(self.gpa, index);
1380}
1381
11741382fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
11751383 if (!self.liveness.operandDies(inst, op_index))
11761384 return false;
......@@ -1201,3 +1409,36 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind
12011409
12021410 return true;
12031411}
1412
1413/// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
1414fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
1415 switch (loc) {
1416 .none => return,
1417 .register => |reg| return self.genSetReg(ty, reg, val),
1418 .stack_offset => |off| return self.genSetStack(ty, off, val),
1419 .memory => {
1420 return self.fail("TODO implement setRegOrMem for memory", .{});
1421 },
1422 else => unreachable,
1423 }
1424}
1425
1426pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
1427 const stack_mcv = try self.allocRegOrMem(inst, false);
1428 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
1429 const reg_mcv = self.getResolvedInstValue(inst);
1430 assert(reg == reg_mcv.register);
1431 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1432 try branch.inst_table.put(self.gpa, inst, stack_mcv);
1433 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);
1434}
1435
1436/// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`.
1437fn wantSafety(self: *Self) bool {
1438 return switch (self.bin_file.options.optimize_mode) {
1439 .Debug => true,
1440 .ReleaseSafe => true,
1441 .ReleaseFast => false,
1442 .ReleaseSmall => false,
1443 };
1444}
src/arch/sparcv9/Emit.zig+13
......@@ -47,11 +47,16 @@ pub fn emitMir(
4747 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),
4848 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
4949
50 .add => @panic("TODO implement sparcv9 add"),
51
5052 .bpcc => @panic("TODO implement sparcv9 bpcc"),
5153
5254 .call => @panic("TODO implement sparcv9 call"),
5355
5456 .jmpl => @panic("TODO implement sparcv9 jmpl"),
57 .jmpl_i => @panic("TODO implement sparcv9 jmpl to reg"),
58
59 .@"or" => @panic("TODO implement sparcv9 or"),
5560
5661 .nop => @panic("TODO implement sparcv9 nop"),
5762
......@@ -59,6 +64,14 @@ pub fn emitMir(
5964
6065 .save => @panic("TODO implement sparcv9 save"),
6166 .restore => @panic("TODO implement sparcv9 restore"),
67
68 .sethi => @panic("TODO implement sparcv9 sethi"),
69
70 .sllx => @panic("TODO implement sparcv9 sllx"),
71
72 .sub => @panic("TODO implement sparcv9 sub"),
73
74 .tcc => @panic("TODO implement sparcv9 tcc"),
6275 }
6376 }
6477}
src/arch/sparcv9/Mir.zig+82-3
......@@ -40,6 +40,11 @@ pub const Inst = struct {
4040 // All the real instructions are ordered by their section number
4141 // in The SPARC Architecture Manual, Version 9.
4242
43 /// A.2 Add
44 /// Those uses the arithmetic_3op field.
45 // TODO add other operations.
46 add,
47
4348 /// A.7 Branch on Integer Condition Codes with Prediction (BPcc)
4449 /// It uses the branch_predict field.
4550 bpcc,
......@@ -49,8 +54,16 @@ pub const Inst = struct {
4954 call,
5055
5156 /// A.24 Jump and Link
52 /// It uses the branch_link field.
57 /// jmpl (far direct jump) uses the branch_link field,
58 /// while jmpl_i (indirect jump) uses the branch_link_indirect field.
59 /// Those two MIR instructions will be lowered into SPARCv9 jmpl instruction.
5360 jmpl,
61 jmpl_i,
62
63 /// A.31 Logical Operations
64 /// Those uses the arithmetic_3op field.
65 // TODO add other operations.
66 @"or",
5467
5568 /// A.40 No Operation
5669 /// It uses the nop field.
......@@ -64,6 +77,24 @@ pub const Inst = struct {
6477 /// Those uses the arithmetic_3op field.
6578 save,
6679 restore,
80
81 /// A.48 SETHI
82 /// It uses the sethi field.
83 sethi,
84
85 /// A.49 Shift
86 /// Those uses the shift field.
87 // TODO add other operations.
88 sllx,
89
90 /// A.56 Subtract
91 /// Those uses the arithmetic_3op field.
92 // TODO add other operations.
93 sub,
94
95 /// A.61 Trap on Integer Condition Codes (Tcc)
96 /// It uses the trap field.
97 tcc,
6798 };
6899
69100 /// The position of an MIR instruction within the `Mir` instructions array.
......@@ -72,6 +103,7 @@ pub const Inst = struct {
72103 /// All instructions have a 8-byte payload, which is contained within
73104 /// this union. `Tag` determines which union field is active, as well as
74105 /// how to interpret the data within.
106 // TODO this is a quick-n-dirty solution that needs to be cleaned up.
75107 pub const Data = union {
76108 /// Debug info: argument
77109 ///
......@@ -122,14 +154,21 @@ pub const Inst = struct {
122154 /// Used by e.g. call
123155 branch_link: struct {
124156 inst: Index,
125 link: Register,
157 link: Register = .o7,
158 },
159
160 /// Indirect branch and link (always unconditional).
161 /// Used by e.g. jmpl_i
162 branch_link_indirect: struct {
163 reg: Register,
164 link: Register = .o7,
126165 },
127166
128167 /// Branch with prediction.
129168 /// Used by e.g. bpcc
130169 branch_predict: struct {
131170 annul: bool,
132 pt: bool,
171 pt: bool = true,
133172 ccr: Instruction.CCR,
134173 cond: Instruction.Condition,
135174 inst: Index,
......@@ -139,6 +178,46 @@ pub const Inst = struct {
139178 ///
140179 /// Used by e.g. flushw
141180 nop: void,
181
182 /// SETHI operands.
183 ///
184 /// Used by sethi
185 sethi: struct {
186 rd: Register,
187 imm: u22,
188 },
189
190 /// Shift operands.
191 /// if is_imm true then it uses the imm field of rs2_or_imm,
192 /// otherwise it uses rs2 field.
193 ///
194 /// Used by e.g. add, sub
195 shift: struct {
196 is_imm: bool,
197 width: Instruction.ShiftWidth,
198 rd: Register,
199 rs1: Register,
200 rs2_or_imm: union {
201 rs2: Register,
202 imm: u6,
203 },
204 },
205
206 /// Trap.
207 /// if is_imm true then it uses the imm field of rs2_or_imm,
208 /// otherwise it uses rs2 field.
209 ///
210 /// Used by e.g. tcc
211 trap: struct {
212 is_imm: bool = true,
213 cond: Instruction.Condition,
214 ccr: Instruction.CCR = .icc,
215 rs1: Register = .g0,
216 rs2_or_imm: union {
217 rs2: Register,
218 imm: u8,
219 },
220 },
142221 };
143222};
144223