authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-06 07:27:30+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-05-06 07:27:30+02:00
log09d2b6c4e1b2edd1d011ef4f947cdd835313a2e5
treea0d9836cd8d0d81ec5e441b80938f3bf3a99e53f
parent13d1798ea0c2665f09508ac505259e761f8b4e22
parent9985699943edb4bf4f3dae9b57e0e2017c23c4bf
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11487 from koachan/sparc64-codegen


7 files changed, 285 insertions(+), 167 deletions(-)

lib/std/zig/system/NativeTargetInfo.zig+1
...@@ -931,6 +931,7 @@ pub fn getExternalExecutor(...@@ -931,6 +931,7 @@ pub fn getExternalExecutor(
931 .riscv64 => Executor{ .qemu = "qemu-riscv64" },931 .riscv64 => Executor{ .qemu = "qemu-riscv64" },
932 .s390x => Executor{ .qemu = "qemu-s390x" },932 .s390x => Executor{ .qemu = "qemu-s390x" },
933 .sparc => Executor{ .qemu = "qemu-sparc" },933 .sparc => Executor{ .qemu = "qemu-sparc" },
934 .sparcv9 => Executor{ .qemu = "qemu-sparc64" },
934 .x86_64 => Executor{ .qemu = "qemu-x86_64" },935 .x86_64 => Executor{ .qemu = "qemu-x86_64" },
935 else => return bad_result,936 else => return bad_result,
936 };937 };
src/arch/sparcv9/CodeGen.zig+166-61
...@@ -1,5 +1,7 @@...@@ -1,5 +1,7 @@
1//! SPARCv9 codegen.1//! SPARCv9 codegen.
2//! This lowers AIR into MIR.2//! This lowers AIR into MIR.
3//! For now this only implements medium/low code model with absolute addressing.
4//! TODO add support for other code models.
3const std = @import("std");5const std = @import("std");
4const assert = std.debug.assert;6const assert = std.debug.assert;
5const log = std.log.scoped(.codegen);7const log = std.log.scoped(.codegen);
...@@ -338,16 +340,15 @@ fn gen(self: *Self) !void {...@@ -338,16 +340,15 @@ fn gen(self: *Self) !void {
338 if (cc != .Naked) {340 if (cc != .Naked) {
339 // TODO Finish function prologue and epilogue for sparcv9.341 // TODO Finish function prologue and epilogue for sparcv9.
340342
341 // TODO Backpatch stack offset343 // save %sp, stack_save_area, %sp
342 // save %sp, -176, %sp344 const save_inst = try self.addInst(.{
343 _ = try self.addInst(.{
344 .tag = .save,345 .tag = .save,
345 .data = .{346 .data = .{
346 .arithmetic_3op = .{347 .arithmetic_3op = .{
347 .is_imm = true,348 .is_imm = true,
348 .rd = .sp,349 .rd = .sp,
349 .rs1 = .sp,350 .rs1 = .sp,
350 .rs2_or_imm = .{ .imm = -176 },351 .rs2_or_imm = .{ .imm = -abi.stack_save_area },
351 },352 },
352 },353 },
353 });354 });
...@@ -380,6 +381,28 @@ fn gen(self: *Self) !void {...@@ -380,6 +381,28 @@ fn gen(self: *Self) !void {
380 return self.fail("TODO add branches in sparcv9", .{});381 return self.fail("TODO add branches in sparcv9", .{});
381 }382 }
382383
384 // Backpatch stack offset
385 const total_stack_size = self.max_end_stack + abi.stack_save_area; // TODO + self.saved_regs_stack_space;
386 const stack_size = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align);
387 if (math.cast(i13, stack_size)) |size| {
388 self.mir_instructions.set(save_inst, .{
389 .tag = .save,
390 .data = .{
391 .arithmetic_3op = .{
392 .is_imm = true,
393 .rd = .sp,
394 .rs1 = .sp,
395 .rs2_or_imm = .{ .imm = -size },
396 },
397 },
398 });
399 } else |_| {
400 // TODO for large stacks, replace the prologue with:
401 // setx stack_size, %g1
402 // save %sp, %g1, %sp
403 return self.fail("TODO SPARCv9: allow larger stacks", .{});
404 }
405
383 // return %i7 + 8406 // return %i7 + 8
384 _ = try self.addInst(.{407 _ = try self.addInst(.{
385 .tag = .@"return",408 .tag = .@"return",
...@@ -392,7 +415,11 @@ fn gen(self: *Self) !void {...@@ -392,7 +415,11 @@ fn gen(self: *Self) !void {
392 },415 },
393 });416 });
394417
395 // TODO Find a way to fill this slot418 // Branches in SPARC have a delay slot, that is, the instruction
419 // following it will unconditionally be executed.
420 // See: Section 3.2.3 Control Transfer in SPARCv9 manual.
421 // See also: https://arcb.csc.ncsu.edu/~mueller/codeopt/codeopt00/notes/delaybra.html
422 // TODO Find a way to fill this delay slot
396 // nop423 // nop
397 _ = try self.addInst(.{424 _ = try self.addInst(.{
398 .tag = .nop,425 .tag = .nop,
...@@ -753,15 +780,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -753,15 +780,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
753 // TODO Copy registers to the stack780 // TODO Copy registers to the stack
754 const mcv = result;781 const mcv = result;
755782
756 _ = try self.addInst(.{783 try self.genArgDbgInfo(inst, mcv, @intCast(u32, arg_index));
757 .tag = .dbg_arg,
758 .data = .{
759 .dbg_arg_info = .{
760 .air_inst = inst,
761 .arg_index = arg_index,
762 },
763 },
764 });
765784
766 if (self.liveness.isUnused(inst))785 if (self.liveness.isUnused(inst))
767 return self.finishAirBookkeeping();786 return self.finishAirBookkeeping();
...@@ -884,7 +903,20 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -884,7 +903,20 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
884903
885 _ = try self.addInst(.{904 _ = try self.addInst(.{
886 .tag = .jmpl,905 .tag = .jmpl,
887 .data = .{ .branch_link_indirect = .{ .reg = .o7 } },906 .data = .{
907 .arithmetic_3op = .{
908 .is_imm = false,
909 .rd = .o7,
910 .rs1 = .o7,
911 .rs2_or_imm = .{ .rs2 = .g0 },
912 },
913 },
914 });
915
916 // TODO Find a way to fill this delay slot
917 _ = try self.addInst(.{
918 .tag = .nop,
919 .data = .{ .nop = {} },
888 });920 });
889 } else if (func_value.castTag(.extern_fn)) |_| {921 } else if (func_value.castTag(.extern_fn)) |_| {
890 return self.fail("TODO implement calling extern functions", .{});922 return self.fail("TODO implement calling extern functions", .{});
...@@ -899,7 +931,20 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -899,7 +931,20 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
899931
900 _ = try self.addInst(.{932 _ = try self.addInst(.{
901 .tag = .jmpl,933 .tag = .jmpl,
902 .data = .{ .branch_link_indirect = .{ .reg = .o7 } },934 .data = .{
935 .arithmetic_3op = .{
936 .is_imm = false,
937 .rd = .o7,
938 .rs1 = .o7,
939 .rs2_or_imm = .{ .rs2 = .g0 },
940 },
941 },
942 });
943
944 // TODO Find a way to fill this delay slot
945 _ = try self.addInst(.{
946 .tag = .nop,
947 .data = .{ .nop = {} },
903 });948 });
904 }949 }
905950
...@@ -995,11 +1040,29 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -995,11 +1040,29 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
9951040
996// Common helper functions1041// Common helper functions
9971042
1043/// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
1044/// after codegen for this symbol is done.
1045fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
1046 switch (self.debug_output) {
1047 .dwarf => |dw| {
1048 assert(ty.hasRuntimeBits());
1049 const dbg_info = &dw.dbg_info;
1050 const index = dbg_info.items.len;
1051 try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4
1052 const mod = self.bin_file.options.module.?;
1053 const atom = switch (self.bin_file.tag) {
1054 .elf => &mod.declPtr(self.mod_fn.owner_decl).link.elf.dbg_info_atom,
1055 else => unreachable,
1056 };
1057 try dw.addTypeReloc(atom, ty, @intCast(u32, index), null);
1058 },
1059 else => {},
1060 }
1061}
1062
998fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {1063fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
999 const gpa = self.gpa;1064 const gpa = self.gpa;
1000
1001 try self.mir_instructions.ensureUnusedCapacity(gpa, 1);1065 try self.mir_instructions.ensureUnusedCapacity(gpa, 1);
1002
1003 const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len);1066 const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len);
1004 self.mir_instructions.appendAssumeCapacity(inst);1067 self.mir_instructions.appendAssumeCapacity(inst);
1005 return result_index;1068 return result_index;
...@@ -1125,6 +1188,40 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live...@@ -1125,6 +1188,40 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live
1125 self.finishAirBookkeeping();1188 self.finishAirBookkeeping();
1126}1189}
11271190
1191fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32) !void {
1192 const ty = self.air.instructions.items(.data)[inst].ty;
1193 const name = self.mod_fn.getParamName(arg_index);
1194 const name_with_null = name.ptr[0 .. name.len + 1];
1195
1196 switch (mcv) {
1197 .register => |reg| {
1198 switch (self.debug_output) {
1199 .dwarf => |dw| {
1200 const dbg_info = &dw.dbg_info;
1201 try dbg_info.ensureUnusedCapacity(3);
1202 dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter));
1203 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
1204 1, // ULEB128 dwarf expression length
1205 reg.dwarfLocOp(),
1206 });
1207 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
1208 try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
1209 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
1210 },
1211 else => {},
1212 }
1213 },
1214 .stack_offset => |offset| {
1215 _ = offset;
1216 switch (self.debug_output) {
1217 .dwarf => {},
1218 else => {},
1219 }
1220 },
1221 else => {},
1222 }
1223}
1224
1128fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_type: type, off: off_type, abi_size: u64) !void {1225fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_type: type, off: off_type, abi_size: u64) !void {
1129 assert(off_type == Register or off_type == i13);1226 assert(off_type == Register or off_type == i13);
11301227
...@@ -1132,48 +1229,17 @@ fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_ty...@@ -1132,48 +1229,17 @@ fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_ty
1132 const rs2_or_imm = if (is_imm) .{ .imm = off } else .{ .rs2 = off };1229 const rs2_or_imm = if (is_imm) .{ .imm = off } else .{ .rs2 = off };
11331230
1134 switch (abi_size) {1231 switch (abi_size) {
1135 1 => {1232 1, 2, 4, 8 => {
1136 _ = try self.addInst(.{1233 const tag: Mir.Inst.Tag = switch (abi_size) {
1137 .tag = .ldub,1234 1 => .ldub,
1138 .data = .{1235 2 => .lduh,
1139 .arithmetic_3op = .{1236 4 => .lduw,
1140 .is_imm = is_imm,1237 8 => .ldx,
1141 .rd = value_reg,1238 else => unreachable, // unexpected abi size
1142 .rs1 = addr_reg,1239 };
1143 .rs2_or_imm = rs2_or_imm,1240
1144 },
1145 },
1146 });
1147 },
1148 2 => {
1149 _ = try self.addInst(.{
1150 .tag = .lduh,
1151 .data = .{
1152 .arithmetic_3op = .{
1153 .is_imm = is_imm,
1154 .rd = value_reg,
1155 .rs1 = addr_reg,
1156 .rs2_or_imm = rs2_or_imm,
1157 },
1158 },
1159 });
1160 },
1161 4 => {
1162 _ = try self.addInst(.{
1163 .tag = .lduw,
1164 .data = .{
1165 .arithmetic_3op = .{
1166 .is_imm = is_imm,
1167 .rd = value_reg,
1168 .rs1 = addr_reg,
1169 .rs2_or_imm = rs2_or_imm,
1170 },
1171 },
1172 });
1173 },
1174 8 => {
1175 _ = try self.addInst(.{1241 _ = try self.addInst(.{
1176 .tag = .ldx,1242 .tag = tag,
1177 .data = .{1243 .data = .{
1178 .arithmetic_3op = .{1244 .arithmetic_3op = .{
1179 .is_imm = is_imm,1245 .is_imm = is_imm,
...@@ -1335,7 +1401,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -1335,7 +1401,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
1335 try self.genLoad(reg, reg, i13, 0, ty.abiSize(self.target.*));1401 try self.genLoad(reg, reg, i13, 0, ty.abiSize(self.target.*));
1336 },1402 },
1337 .stack_offset => |off| {1403 .stack_offset => |off| {
1338 const simm13 = math.cast(u12, off) catch1404 const real_offset = off + abi.stack_bias + abi.stack_save_area;
1405 const simm13 = math.cast(i13, real_offset) catch
1339 return self.fail("TODO larger stack offsets", .{});1406 return self.fail("TODO larger stack offsets", .{});
1340 try self.genLoad(reg, .sp, i13, simm13, ty.abiSize(self.target.*));1407 try self.genLoad(reg, .sp, i13, simm13, ty.abiSize(self.target.*));
1341 },1408 },
...@@ -1365,11 +1432,49 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -1365,11 +1432,49 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
1365 const reg = try self.copyToTmpRegister(ty, mcv);1432 const reg = try self.copyToTmpRegister(ty, mcv);
1366 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });1433 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
1367 },1434 },
1368 .register => return self.fail("TODO implement storing types abi_size={}", .{abi_size}),1435 .register => |reg| {
1436 const real_offset = stack_offset + abi.stack_bias + abi.stack_save_area;
1437 const simm13 = math.cast(i13, real_offset) catch
1438 return self.fail("TODO larger stack offsets", .{});
1439 return self.genStore(reg, .sp, i13, simm13, abi_size);
1440 },
1369 .memory, .stack_offset => return self.fail("TODO implement memcpy", .{}),1441 .memory, .stack_offset => return self.fail("TODO implement memcpy", .{}),
1370 }1442 }
1371}1443}
13721444
1445fn genStore(self: *Self, value_reg: Register, addr_reg: Register, comptime off_type: type, off: off_type, abi_size: u64) !void {
1446 assert(off_type == Register or off_type == i13);
1447
1448 const is_imm = (off_type == i13);
1449 const rs2_or_imm = if (is_imm) .{ .imm = off } else .{ .rs2 = off };
1450
1451 switch (abi_size) {
1452 1, 2, 4, 8 => {
1453 const tag: Mir.Inst.Tag = switch (abi_size) {
1454 1 => .stb,
1455 2 => .sth,
1456 4 => .stw,
1457 8 => .stx,
1458 else => unreachable, // unexpected abi size
1459 };
1460
1461 _ = try self.addInst(.{
1462 .tag = tag,
1463 .data = .{
1464 .arithmetic_3op = .{
1465 .is_imm = is_imm,
1466 .rd = value_reg,
1467 .rs1 = addr_reg,
1468 .rs2_or_imm = rs2_or_imm,
1469 },
1470 },
1471 });
1472 },
1473 3, 5, 6, 7 => return self.fail("TODO: genLoad for more abi_sizes", .{}),
1474 else => unreachable,
1475 }
1476}
1477
1373fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {1478fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
1374 if (typed_value.val.isUndef())1479 if (typed_value.val.isUndef())
1375 return MCValue{ .undef = {} };1480 return MCValue{ .undef = {} };
src/arch/sparcv9/Emit.zig+31-55
...@@ -45,7 +45,6 @@ pub fn emitMir(...@@ -45,7 +45,6 @@ pub fn emitMir(
45 for (mir_tags) |tag, index| {45 for (mir_tags) |tag, index| {
46 const inst = @intCast(u32, index);46 const inst = @intCast(u32, index);
47 switch (tag) {47 switch (tag) {
48 .dbg_arg => try emit.mirDbgArg(inst),
49 .dbg_line => try emit.mirDbgLine(inst),48 .dbg_line => try emit.mirDbgLine(inst),
50 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),49 .dbg_prologue_end => try emit.mirDebugPrologueEnd(),
51 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),50 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
...@@ -56,8 +55,7 @@ pub fn emitMir(...@@ -56,8 +55,7 @@ pub fn emitMir(
5655
57 .call => @panic("TODO implement sparcv9 call"),56 .call => @panic("TODO implement sparcv9 call"),
5857
59 .jmpl => @panic("TODO implement sparcv9 jmpl"),58 .jmpl => try emit.mirArithmetic3Op(inst),
60 .jmpl_i => @panic("TODO implement sparcv9 jmpl to reg"),
6159
62 .ldub => try emit.mirArithmetic3Op(inst),60 .ldub => try emit.mirArithmetic3Op(inst),
63 .lduh => try emit.mirArithmetic3Op(inst),61 .lduh => try emit.mirArithmetic3Op(inst),
...@@ -77,6 +75,11 @@ pub fn emitMir(...@@ -77,6 +75,11 @@ pub fn emitMir(
7775
78 .sllx => @panic("TODO implement sparcv9 sllx"),76 .sllx => @panic("TODO implement sparcv9 sllx"),
7977
78 .stb => try emit.mirArithmetic3Op(inst),
79 .sth => try emit.mirArithmetic3Op(inst),
80 .stw => try emit.mirArithmetic3Op(inst),
81 .stx => try emit.mirArithmetic3Op(inst),
82
80 .sub => try emit.mirArithmetic3Op(inst),83 .sub => try emit.mirArithmetic3Op(inst),
8184
82 .tcc => try emit.mirTrap(inst),85 .tcc => try emit.mirTrap(inst),
...@@ -88,17 +91,6 @@ pub fn deinit(emit: *Emit) void {...@@ -88,17 +91,6 @@ pub fn deinit(emit: *Emit) void {
88 emit.* = undefined;91 emit.* = undefined;
89}92}
9093
91fn mirDbgArg(emit: *Emit, inst: Mir.Inst.Index) !void {
92 const tag = emit.mir.instructions.items(.tag)[inst];
93 const dbg_arg_info = emit.mir.instructions.items(.data)[inst].dbg_arg_info;
94 _ = dbg_arg_info;
95
96 switch (tag) {
97 .dbg_arg => {}, // TODO try emit.genArgDbgInfo(dbg_arg_info.air_inst, dbg_arg_info.arg_index),
98 else => unreachable,
99 }
100}
101
102fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {94fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
103 const tag = emit.mir.instructions.items(.tag)[inst];95 const tag = emit.mir.instructions.items(.tag)[inst];
104 const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column;96 const dbg_line_column = emit.mir.instructions.items(.data)[inst].dbg_line_column;
...@@ -109,22 +101,22 @@ fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -109,22 +101,22 @@ fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
109 }101 }
110}102}
111103
112fn mirDebugPrologueEnd(self: *Emit) !void {104fn mirDebugPrologueEnd(emit: *Emit) !void {
113 switch (self.debug_output) {105 switch (emit.debug_output) {
114 .dwarf => |dbg_out| {106 .dwarf => |dbg_out| {
115 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);107 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
116 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);108 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
117 },109 },
118 .plan9 => {},110 .plan9 => {},
119 .none => {},111 .none => {},
120 }112 }
121}113}
122114
123fn mirDebugEpilogueBegin(self: *Emit) !void {115fn mirDebugEpilogueBegin(emit: *Emit) !void {
124 switch (self.debug_output) {116 switch (emit.debug_output) {
125 .dwarf => |dbg_out| {117 .dwarf => |dbg_out| {
126 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);118 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
127 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);119 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
128 },120 },
129 .plan9 => {},121 .plan9 => {},
130 .none => {},122 .none => {},
...@@ -163,6 +155,7 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -163,6 +155,7 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {
163 const imm = data.rs2_or_imm.imm;155 const imm = data.rs2_or_imm.imm;
164 switch (tag) {156 switch (tag) {
165 .add => try emit.writeInstruction(Instruction.add(i13, rs1, imm, rd)),157 .add => try emit.writeInstruction(Instruction.add(i13, rs1, imm, rd)),
158 .jmpl => try emit.writeInstruction(Instruction.jmpl(i13, rs1, imm, rd)),
166 .ldub => try emit.writeInstruction(Instruction.ldub(i13, rs1, imm, rd)),159 .ldub => try emit.writeInstruction(Instruction.ldub(i13, rs1, imm, rd)),
167 .lduh => try emit.writeInstruction(Instruction.lduh(i13, rs1, imm, rd)),160 .lduh => try emit.writeInstruction(Instruction.lduh(i13, rs1, imm, rd)),
168 .lduw => try emit.writeInstruction(Instruction.lduw(i13, rs1, imm, rd)),161 .lduw => try emit.writeInstruction(Instruction.lduw(i13, rs1, imm, rd)),
...@@ -170,6 +163,10 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -170,6 +163,10 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {
170 .@"or" => try emit.writeInstruction(Instruction.@"or"(i13, rs1, imm, rd)),163 .@"or" => try emit.writeInstruction(Instruction.@"or"(i13, rs1, imm, rd)),
171 .save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)),164 .save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)),
172 .restore => try emit.writeInstruction(Instruction.restore(i13, rs1, imm, rd)),165 .restore => try emit.writeInstruction(Instruction.restore(i13, rs1, imm, rd)),
166 .stb => try emit.writeInstruction(Instruction.stb(i13, rs1, imm, rd)),
167 .sth => try emit.writeInstruction(Instruction.sth(i13, rs1, imm, rd)),
168 .stw => try emit.writeInstruction(Instruction.stw(i13, rs1, imm, rd)),
169 .stx => try emit.writeInstruction(Instruction.stx(i13, rs1, imm, rd)),
173 .sub => try emit.writeInstruction(Instruction.sub(i13, rs1, imm, rd)),170 .sub => try emit.writeInstruction(Instruction.sub(i13, rs1, imm, rd)),
174 else => unreachable,171 else => unreachable,
175 }172 }
...@@ -177,6 +174,7 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -177,6 +174,7 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {
177 const rs2 = data.rs2_or_imm.rs2;174 const rs2 = data.rs2_or_imm.rs2;
178 switch (tag) {175 switch (tag) {
179 .add => try emit.writeInstruction(Instruction.add(Register, rs1, rs2, rd)),176 .add => try emit.writeInstruction(Instruction.add(Register, rs1, rs2, rd)),
177 .jmpl => try emit.writeInstruction(Instruction.jmpl(Register, rs1, rs2, rd)),
180 .ldub => try emit.writeInstruction(Instruction.ldub(Register, rs1, rs2, rd)),178 .ldub => try emit.writeInstruction(Instruction.ldub(Register, rs1, rs2, rd)),
181 .lduh => try emit.writeInstruction(Instruction.lduh(Register, rs1, rs2, rd)),179 .lduh => try emit.writeInstruction(Instruction.lduh(Register, rs1, rs2, rd)),
182 .lduw => try emit.writeInstruction(Instruction.lduw(Register, rs1, rs2, rd)),180 .lduw => try emit.writeInstruction(Instruction.lduw(Register, rs1, rs2, rd)),
...@@ -184,6 +182,10 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -184,6 +182,10 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {
184 .@"or" => try emit.writeInstruction(Instruction.@"or"(Register, rs1, rs2, rd)),182 .@"or" => try emit.writeInstruction(Instruction.@"or"(Register, rs1, rs2, rd)),
185 .save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)),183 .save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)),
186 .restore => try emit.writeInstruction(Instruction.restore(Register, rs1, rs2, rd)),184 .restore => try emit.writeInstruction(Instruction.restore(Register, rs1, rs2, rd)),
185 .stb => try emit.writeInstruction(Instruction.stb(Register, rs1, rs2, rd)),
186 .sth => try emit.writeInstruction(Instruction.sth(Register, rs1, rs2, rd)),
187 .stw => try emit.writeInstruction(Instruction.stw(Register, rs1, rs2, rd)),
188 .stx => try emit.writeInstruction(Instruction.stx(Register, rs1, rs2, rd)),
187 .sub => try emit.writeInstruction(Instruction.sub(Register, rs1, rs2, rd)),189 .sub => try emit.writeInstruction(Instruction.sub(Register, rs1, rs2, rd)),
188 else => unreachable,190 else => unreachable,
189 }191 }
...@@ -230,10 +232,10 @@ fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -230,10 +232,10 @@ fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void {
230232
231// Common helper functions233// Common helper functions
232234
233fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {235fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void {
234 const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);236 const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line);
235 const delta_pc: usize = self.code.items.len - self.prev_di_pc;237 const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
236 switch (self.debug_output) {238 switch (emit.debug_output) {
237 .dwarf => |dbg_out| {239 .dwarf => |dbg_out| {
238 // TODO Look into using the DWARF special opcodes to compress this data.240 // TODO Look into using the DWARF special opcodes to compress this data.
239 // It lets you emit single-byte opcodes that add different numbers to241 // It lets you emit single-byte opcodes that add different numbers to
...@@ -246,38 +248,12 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {...@@ -246,38 +248,12 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
246 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;248 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
247 }249 }
248 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);250 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);
249 self.prev_di_pc = self.code.items.len;251 emit.prev_di_pc = emit.code.items.len;
250 self.prev_di_line = line;252 emit.prev_di_line = line;
251 self.prev_di_column = column;253 emit.prev_di_column = column;
252 self.prev_di_pc = self.code.items.len;254 emit.prev_di_pc = emit.code.items.len;
253 },255 },
254 .plan9 => |dbg_out| {256 else => {},
255 if (delta_pc <= 0) return; // only do this when the pc changes
256 // we have already checked the target in the linker to make sure it is compatable
257 const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable;
258
259 // increasing the line number
260 try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
261 // increasing the pc
262 const d_pc_p9 = @intCast(i64, delta_pc) - quant;
263 if (d_pc_p9 > 0) {
264 // minus one because if its the last one, we want to leave space to change the line which is one quanta
265 try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant);
266 if (dbg_out.pcop_change_index.*) |pci|
267 dbg_out.dbg_line.items[pci] += 1;
268 dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1);
269 } else if (d_pc_p9 == 0) {
270 // we don't need to do anything, because adding the quant does it for us
271 } else unreachable;
272 if (dbg_out.start_line.* == null)
273 dbg_out.start_line.* = self.prev_di_line;
274 dbg_out.end_line.* = line;
275 // only do this if the pc changed
276 self.prev_di_line = line;
277 self.prev_di_column = column;
278 self.prev_di_pc = self.code.items.len;
279 },
280 .none => {},
281 }257 }
282}258}
283259
src/arch/sparcv9/Mir.zig+24-37
...@@ -28,8 +28,6 @@ pub const Inst = struct {...@@ -28,8 +28,6 @@ pub const Inst = struct {
28 data: Data,28 data: Data,
2929
30 pub const Tag = enum(u16) {30 pub const Tag = enum(u16) {
31 /// Pseudo-instruction: Argument
32 dbg_arg,
33 /// Pseudo-instruction: End of prologue31 /// Pseudo-instruction: End of prologue
34 dbg_prologue_end,32 dbg_prologue_end,
35 /// Pseudo-instruction: Beginning of epilogue33 /// Pseudo-instruction: Beginning of epilogue
...@@ -41,27 +39,24 @@ pub const Inst = struct {...@@ -41,27 +39,24 @@ pub const Inst = struct {
41 // in The SPARC Architecture Manual, Version 9.39 // in The SPARC Architecture Manual, Version 9.
4240
43 /// A.2 Add41 /// A.2 Add
44 /// Those uses the arithmetic_3op field.42 /// This uses the arithmetic_3op field.
45 // TODO add other operations.43 // TODO add other operations.
46 add,44 add,
4745
48 /// A.7 Branch on Integer Condition Codes with Prediction (BPcc)46 /// A.7 Branch on Integer Condition Codes with Prediction (BPcc)
49 /// It uses the branch_predict field.47 /// This uses the branch_predict field.
50 bpcc,48 bpcc,
5149
52 /// A.8 Call and Link50 /// A.8 Call and Link
53 /// It uses the branch_link field.51 /// This uses the branch_link field.
54 call,52 call,
5553
56 /// A.24 Jump and Link54 /// A.24 Jump and Link
57 /// jmpl (far direct jump) uses the branch_link field,55 /// This uses the arithmetic_3op 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.
60 jmpl,56 jmpl,
61 jmpl_i,
6257
63 /// A.27 Load Integer58 /// A.27 Load Integer
64 /// Those uses the arithmetic_3op field.59 /// This uses the arithmetic_3op field.
65 /// Note that the ldd variant of this instruction is deprecated, so do not emit60 /// Note that the ldd variant of this instruction is deprecated, so do not emit
66 /// it unless specifically requested (e.g. by inline assembly).61 /// it unless specifically requested (e.g. by inline assembly).
67 // TODO add other operations.62 // TODO add other operations.
...@@ -71,39 +66,49 @@ pub const Inst = struct {...@@ -71,39 +66,49 @@ pub const Inst = struct {
71 ldx,66 ldx,
7267
73 /// A.31 Logical Operations68 /// A.31 Logical Operations
74 /// Those uses the arithmetic_3op field.69 /// This uses the arithmetic_3op field.
75 // TODO add other operations.70 // TODO add other operations.
76 @"or",71 @"or",
7772
78 /// A.40 No Operation73 /// A.40 No Operation
79 /// It uses the nop field.74 /// This uses the nop field.
80 nop,75 nop,
8176
82 /// A.45 RETURN77 /// A.45 RETURN
83 /// It uses the arithmetic_2op field.78 /// This uses the arithmetic_2op field.
84 @"return",79 @"return",
8580
86 /// A.46 SAVE and RESTORE81 /// A.46 SAVE and RESTORE
87 /// Those uses the arithmetic_3op field.82 /// This uses the arithmetic_3op field.
88 save,83 save,
89 restore,84 restore,
9085
91 /// A.48 SETHI86 /// A.48 SETHI
92 /// It uses the sethi field.87 /// This uses the sethi field.
93 sethi,88 sethi,
9489
95 /// A.49 Shift90 /// A.49 Shift
96 /// Those uses the shift field.91 /// This uses the shift field.
97 // TODO add other operations.92 // TODO add other operations.
98 sllx,93 sllx,
9994
95 /// A.54 Store Integer
96 /// This uses the arithmetic_3op field.
97 /// Note that the std variant of this instruction is deprecated, so do not emit
98 /// it unless specifically requested (e.g. by inline assembly).
99 // TODO add other operations.
100 stb,
101 sth,
102 stw,
103 stx,
104
100 /// A.56 Subtract105 /// A.56 Subtract
101 /// Those uses the arithmetic_3op field.106 /// This uses the arithmetic_3op field.
102 // TODO add other operations.107 // TODO add other operations.
103 sub,108 sub,
104109
105 /// A.61 Trap on Integer Condition Codes (Tcc)110 /// A.61 Trap on Integer Condition Codes (Tcc)
106 /// It uses the trap field.111 /// This uses the trap field.
107 tcc,112 tcc,
108 };113 };
109114
...@@ -115,14 +120,6 @@ pub const Inst = struct {...@@ -115,14 +120,6 @@ pub const Inst = struct {
115 /// how to interpret the data within.120 /// how to interpret the data within.
116 // TODO this is a quick-n-dirty solution that needs to be cleaned up.121 // TODO this is a quick-n-dirty solution that needs to be cleaned up.
117 pub const Data = union {122 pub const Data = union {
118 /// Debug info: argument
119 ///
120 /// Used by e.g. dbg_arg
121 dbg_arg_info: struct {
122 air_inst: Air.Inst.Index,
123 arg_index: usize,
124 },
125
126 /// Debug info: line and column123 /// Debug info: line and column
127 ///124 ///
128 /// Used by e.g. dbg_line125 /// Used by e.g. dbg_line
...@@ -167,13 +164,6 @@ pub const Inst = struct {...@@ -167,13 +164,6 @@ pub const Inst = struct {
167 link: Register = .o7,164 link: Register = .o7,
168 },165 },
169166
170 /// Indirect branch and link (always unconditional).
171 /// Used by e.g. jmpl_i
172 branch_link_indirect: struct {
173 reg: Register,
174 link: Register = .o7,
175 },
176
177 /// Branch with prediction.167 /// Branch with prediction.
178 /// Used by e.g. bpcc168 /// Used by e.g. bpcc
179 branch_predict: struct {169 branch_predict: struct {
...@@ -234,10 +224,7 @@ pub const Inst = struct {...@@ -234,10 +224,7 @@ pub const Inst = struct {
234 // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.224 // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks.
235 comptime {225 comptime {
236 if (builtin.mode != .Debug) {226 if (builtin.mode != .Debug) {
237 // TODO clean up the definition of Data before enabling this.227 assert(@sizeOf(Data) == 8);
238 // I'll do that after the PoC backend can produce usable binaries.
239
240 // assert(@sizeOf(Data) == 8);
241 }228 }
242 }229 }
243};230};
src/arch/sparcv9/abi.zig+12
...@@ -1,6 +1,18 @@...@@ -1,6 +1,18 @@
1const bits = @import("bits.zig");1const bits = @import("bits.zig");
2const Register = bits.Register;2const Register = bits.Register;
33
4// SPARCv9 stack constants.
5// See: Registers and the Stack Frame, page 3P-8, SCD 2.4.1.
6
7// On SPARCv9, %sp points to top of stack + stack bias,
8// and %fp points to top of previous frame + stack bias.
9pub const stack_bias = 2047;
10
11// The first 176 bytes of the stack is reserved for register saving purposes.
12// SPARCv9 requires to reserve space in the stack for the first six arguments,
13// even though they are usually passed in registers.
14pub const stack_save_area = 176;
15
4// There are no callee-preserved registers since the windowing16// There are no callee-preserved registers since the windowing
5// mechanism already takes care of them.17// mechanism already takes care of them.
6// We still need to preserve %o0-%o5, %g1, %g4, and %g5 before calling18// We still need to preserve %o0-%o5, %g1, %g4, and %g5 before calling
src/arch/sparcv9/bits.zig+45-3
...@@ -271,6 +271,8 @@ pub const Instruction = union(enum) {...@@ -271,6 +271,8 @@ pub const Instruction = union(enum) {
271 rd: u5,271 rd: u5,
272 op3: u6,272 op3: u6,
273 rs1: u5,273 rs1: u5,
274 // See Errata 58 of SPARCv9 specification
275 // https://sparc.org/errata-for-v9/#58
274 i: u1 = 0b1,276 i: u1 = 0b1,
275 reserved: u8 = 0b00000000,277 reserved: u8 = 0b00000000,
276 rs2: u5,278 rs2: u5,
...@@ -977,10 +979,10 @@ pub const Instruction = union(enum) {...@@ -977,10 +979,10 @@ pub const Instruction = union(enum) {
977 };979 };
978 }980 }
979981
980 pub fn @"or"(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {982 pub fn jmpl(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
981 return switch (s2) {983 return switch (s2) {
982 Register => format3a(0b10, 0b00_0010, rs1, rs2, rd),984 Register => format3a(0b10, 0b11_1000, rs1, rs2, rd),
983 i13 => format3b(0b10, 0b00_0010, rs1, rs2, rd),985 i13 => format3b(0b10, 0b11_1000, rs1, rs2, rd),
984 else => unreachable,986 else => unreachable,
985 };987 };
986 }988 }
...@@ -1017,6 +1019,14 @@ pub const Instruction = union(enum) {...@@ -1017,6 +1019,14 @@ pub const Instruction = union(enum) {
1017 };1019 };
1018 }1020 }
10191021
1022 pub fn @"or"(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1023 return switch (s2) {
1024 Register => format3a(0b10, 0b00_0010, rs1, rs2, rd),
1025 i13 => format3b(0b10, 0b00_0010, rs1, rs2, rd),
1026 else => unreachable,
1027 };
1028 }
1029
1020 pub fn nop() Instruction {1030 pub fn nop() Instruction {
1021 return sethi(0, .g0);1031 return sethi(0, .g0);
1022 }1032 }
...@@ -1049,6 +1059,38 @@ pub const Instruction = union(enum) {...@@ -1049,6 +1059,38 @@ pub const Instruction = union(enum) {
1049 return format2a(0b100, imm, rd);1059 return format2a(0b100, imm, rd);
1050 }1060 }
10511061
1062 pub fn stb(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1063 return switch (s2) {
1064 Register => format3a(0b11, 0b00_0101, rs1, rs2, rd),
1065 i13 => format3b(0b11, 0b00_0101, rs1, rs2, rd),
1066 else => unreachable,
1067 };
1068 }
1069
1070 pub fn sth(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1071 return switch (s2) {
1072 Register => format3a(0b11, 0b00_0110, rs1, rs2, rd),
1073 i13 => format3b(0b11, 0b00_0110, rs1, rs2, rd),
1074 else => unreachable,
1075 };
1076 }
1077
1078 pub fn stw(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1079 return switch (s2) {
1080 Register => format3a(0b11, 0b00_0100, rs1, rs2, rd),
1081 i13 => format3b(0b11, 0b00_0100, rs1, rs2, rd),
1082 else => unreachable,
1083 };
1084 }
1085
1086 pub fn stx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1087 return switch (s2) {
1088 Register => format3a(0b11, 0b00_1110, rs1, rs2, rd),
1089 i13 => format3b(0b11, 0b00_1110, rs1, rs2, rd),
1090 else => unreachable,
1091 };
1092 }
1093
1052 pub fn sub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {1094 pub fn sub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1053 return switch (s2) {1095 return switch (s2) {
1054 Register => format3a(0b10, 0b00_0100, rs1, rs2, rd),1096 Register => format3a(0b10, 0b00_0100, rs1, rs2, rd),
test/cases/sparcv9-linux/hello_world.zig+6-11
...@@ -1,23 +1,18 @@...@@ -1,23 +1,18 @@
1const msg = "Hello, World!\n";1const msg = "Hello, World!\n";
22
3pub export fn _start() noreturn {3fn length() usize {
4 return msg.len;
5}
6
7pub fn main() void {
4 asm volatile ("ta 0x6d"8 asm volatile ("ta 0x6d"
5 :9 :
6 : [number] "{g1}" (4),10 : [number] "{g1}" (4),
7 [arg1] "{o0}" (1),11 [arg1] "{o0}" (1),
8 [arg2] "{o1}" (@ptrToInt(msg)),12 [arg2] "{o1}" (@ptrToInt(msg)),
9 [arg3] "{o2}" (msg.len),13 [arg3] "{o2}" (length()),
10 : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory"
11 );
12
13 asm volatile ("ta 0x6d"
14 :
15 : [number] "{g1}" (1),
16 [arg1] "{o0}" (0),
17 : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory"14 : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory"
18 );15 );
19
20 unreachable;
21}16}
2217
23// run18// run