| author | |
| committer | |
| log | 09d2b6c4e1b2edd1d011ef4f947cdd835313a2e5 |
| tree | a0d9836cd8d0d81ec5e441b80938f3bf3a99e53f |
| parent | 13d1798ea0c2665f09508ac505259e761f8b4e22 |
| parent | 9985699943edb4bf4f3dae9b57e0e2017c23c4bf |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
7 files changed, 285 insertions(+), 167 deletions(-)
lib/std/zig/system/NativeTargetInfo.zig+1| ... | ... | @@ -931,6 +931,7 @@ pub fn getExternalExecutor( |
| 931 | 931 | .riscv64 => Executor{ .qemu = "qemu-riscv64" }, |
| 932 | 932 | .s390x => Executor{ .qemu = "qemu-s390x" }, |
| 933 | 933 | .sparc => Executor{ .qemu = "qemu-sparc" }, |
| 934 | .sparcv9 => Executor{ .qemu = "qemu-sparc64" }, | |
| 934 | 935 | .x86_64 => Executor{ .qemu = "qemu-x86_64" }, |
| 935 | 936 | else => return bad_result, |
| 936 | 937 | }; |
src/arch/sparcv9/CodeGen.zig+166-61| ... | ... | @@ -1,5 +1,7 @@ |
| 1 | 1 | //! SPARCv9 codegen. |
| 2 | 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. | |
| 3 | 5 | const std = @import("std"); |
| 4 | 6 | const assert = std.debug.assert; |
| 5 | 7 | const log = std.log.scoped(.codegen); |
| ... | ... | @@ -338,16 +340,15 @@ fn gen(self: *Self) !void { |
| 338 | 340 | if (cc != .Naked) { |
| 339 | 341 | // TODO Finish function prologue and epilogue for sparcv9. |
| 340 | 342 | |
| 341 | // TODO Backpatch stack offset | |
| 342 | // save %sp, -176, %sp | |
| 343 | _ = try self.addInst(.{ | |
| 343 | // save %sp, stack_save_area, %sp | |
| 344 | const save_inst = try self.addInst(.{ | |
| 344 | 345 | .tag = .save, |
| 345 | 346 | .data = .{ |
| 346 | 347 | .arithmetic_3op = .{ |
| 347 | 348 | .is_imm = true, |
| 348 | 349 | .rd = .sp, |
| 349 | 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 | 381 | return self.fail("TODO add branches in sparcv9", .{}); |
| 381 | 382 | } |
| 382 | 383 | |
| 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 | 406 | // return %i7 + 8 |
| 384 | 407 | _ = try self.addInst(.{ |
| 385 | 408 | .tag = .@"return", |
| ... | ... | @@ -392,7 +415,11 @@ fn gen(self: *Self) !void { |
| 392 | 415 | }, |
| 393 | 416 | }); |
| 394 | 417 | |
| 395 | // TODO Find a way to fill this slot | |
| 418 | // 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 | 423 | // nop |
| 397 | 424 | _ = try self.addInst(.{ |
| 398 | 425 | .tag = .nop, |
| ... | ... | @@ -753,15 +780,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 753 | 780 | // TODO Copy registers to the stack |
| 754 | 781 | const mcv = result; |
| 755 | 782 | |
| 756 | _ = try self.addInst(.{ | |
| 757 | .tag = .dbg_arg, | |
| 758 | .data = .{ | |
| 759 | .dbg_arg_info = .{ | |
| 760 | .air_inst = inst, | |
| 761 | .arg_index = arg_index, | |
| 762 | }, | |
| 763 | }, | |
| 764 | }); | |
| 783 | try self.genArgDbgInfo(inst, mcv, @intCast(u32, arg_index)); | |
| 765 | 784 | |
| 766 | 785 | if (self.liveness.isUnused(inst)) |
| 767 | 786 | return self.finishAirBookkeeping(); |
| ... | ... | @@ -884,7 +903,20 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 884 | 903 | |
| 885 | 904 | _ = try self.addInst(.{ |
| 886 | 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 | 921 | } else if (func_value.castTag(.extern_fn)) |_| { |
| 890 | 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 | 931 | |
| 900 | 932 | _ = try self.addInst(.{ |
| 901 | 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 | } |
| 905 | 950 | |
| ... | ... | @@ -995,11 +1040,29 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 995 | 1040 | |
| 996 | 1041 | // Common helper functions |
| 997 | 1042 | |
| 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. | |
| 1045 | fn 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 | ||
| 998 | 1063 | fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { |
| 999 | 1064 | const gpa = self.gpa; |
| 1000 | ||
| 1001 | 1065 | try self.mir_instructions.ensureUnusedCapacity(gpa, 1); |
| 1002 | ||
| 1003 | 1066 | const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len); |
| 1004 | 1067 | self.mir_instructions.appendAssumeCapacity(inst); |
| 1005 | 1068 | return result_index; |
| ... | ... | @@ -1125,6 +1188,40 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live |
| 1125 | 1188 | self.finishAirBookkeeping(); |
| 1126 | 1189 | } |
| 1127 | 1190 | |
| 1191 | fn 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 | ||
| 1128 | 1225 | fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_type: type, off: off_type, abi_size: u64) !void { |
| 1129 | 1226 | assert(off_type == Register or off_type == i13); |
| 1130 | 1227 | |
| ... | ... | @@ -1132,48 +1229,17 @@ fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_ty |
| 1132 | 1229 | const rs2_or_imm = if (is_imm) .{ .imm = off } else .{ .rs2 = off }; |
| 1133 | 1230 | |
| 1134 | 1231 | switch (abi_size) { |
| 1135 | 1 => { | |
| 1136 | _ = try self.addInst(.{ | |
| 1137 | .tag = .ldub, | |
| 1138 | .data = .{ | |
| 1139 | .arithmetic_3op = .{ | |
| 1140 | .is_imm = is_imm, | |
| 1141 | .rd = value_reg, | |
| 1142 | .rs1 = addr_reg, | |
| 1143 | .rs2_or_imm = rs2_or_imm, | |
| 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 => { | |
| 1232 | 1, 2, 4, 8 => { | |
| 1233 | const tag: Mir.Inst.Tag = switch (abi_size) { | |
| 1234 | 1 => .ldub, | |
| 1235 | 2 => .lduh, | |
| 1236 | 4 => .lduw, | |
| 1237 | 8 => .ldx, | |
| 1238 | else => unreachable, // unexpected abi size | |
| 1239 | }; | |
| 1240 | ||
| 1175 | 1241 | _ = try self.addInst(.{ |
| 1176 | .tag = .ldx, | |
| 1242 | .tag = tag, | |
| 1177 | 1243 | .data = .{ |
| 1178 | 1244 | .arithmetic_3op = .{ |
| 1179 | 1245 | .is_imm = is_imm, |
| ... | ... | @@ -1335,7 +1401,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 1335 | 1401 | try self.genLoad(reg, reg, i13, 0, ty.abiSize(self.target.*)); |
| 1336 | 1402 | }, |
| 1337 | 1403 | .stack_offset => |off| { |
| 1338 | const simm13 = math.cast(u12, off) catch | |
| 1404 | const real_offset = off + abi.stack_bias + abi.stack_save_area; | |
| 1405 | const simm13 = math.cast(i13, real_offset) catch | |
| 1339 | 1406 | return self.fail("TODO larger stack offsets", .{}); |
| 1340 | 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 | 1432 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 1366 | 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 | 1441 | .memory, .stack_offset => return self.fail("TODO implement memcpy", .{}), |
| 1370 | 1442 | } |
| 1371 | 1443 | } |
| 1372 | 1444 | |
| 1445 | fn 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 | ||
| 1373 | 1478 | fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { |
| 1374 | 1479 | if (typed_value.val.isUndef()) |
| 1375 | 1480 | return MCValue{ .undef = {} }; |
src/arch/sparcv9/Emit.zig+31-55| ... | ... | @@ -45,7 +45,6 @@ pub fn emitMir( |
| 45 | 45 | for (mir_tags) |tag, index| { |
| 46 | 46 | const inst = @intCast(u32, index); |
| 47 | 47 | switch (tag) { |
| 48 | .dbg_arg => try emit.mirDbgArg(inst), | |
| 49 | 48 | .dbg_line => try emit.mirDbgLine(inst), |
| 50 | 49 | .dbg_prologue_end => try emit.mirDebugPrologueEnd(), |
| 51 | 50 | .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(), |
| ... | ... | @@ -56,8 +55,7 @@ pub fn emitMir( |
| 56 | 55 | |
| 57 | 56 | .call => @panic("TODO implement sparcv9 call"), |
| 58 | 57 | |
| 59 | .jmpl => @panic("TODO implement sparcv9 jmpl"), | |
| 60 | .jmpl_i => @panic("TODO implement sparcv9 jmpl to reg"), | |
| 58 | .jmpl => try emit.mirArithmetic3Op(inst), | |
| 61 | 59 | |
| 62 | 60 | .ldub => try emit.mirArithmetic3Op(inst), |
| 63 | 61 | .lduh => try emit.mirArithmetic3Op(inst), |
| ... | ... | @@ -77,6 +75,11 @@ pub fn emitMir( |
| 77 | 75 | |
| 78 | 76 | .sllx => @panic("TODO implement sparcv9 sllx"), |
| 79 | 77 | |
| 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 | 83 | .sub => try emit.mirArithmetic3Op(inst), |
| 81 | 84 | |
| 82 | 85 | .tcc => try emit.mirTrap(inst), |
| ... | ... | @@ -88,17 +91,6 @@ pub fn deinit(emit: *Emit) void { |
| 88 | 91 | emit.* = undefined; |
| 89 | 92 | } |
| 90 | 93 | |
| 91 | fn 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 | ||
| 102 | 94 | fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 103 | 95 | const tag = emit.mir.instructions.items(.tag)[inst]; |
| 104 | 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 | 101 | } |
| 110 | 102 | } |
| 111 | 103 | |
| 112 | fn mirDebugPrologueEnd(self: *Emit) !void { | |
| 113 | switch (self.debug_output) { | |
| 104 | fn mirDebugPrologueEnd(emit: *Emit) !void { | |
| 105 | switch (emit.debug_output) { | |
| 114 | 106 | .dwarf => |dbg_out| { |
| 115 | 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 | 110 | .plan9 => {}, |
| 119 | 111 | .none => {}, |
| 120 | 112 | } |
| 121 | 113 | } |
| 122 | 114 | |
| 123 | fn mirDebugEpilogueBegin(self: *Emit) !void { | |
| 124 | switch (self.debug_output) { | |
| 115 | fn mirDebugEpilogueBegin(emit: *Emit) !void { | |
| 116 | switch (emit.debug_output) { | |
| 125 | 117 | .dwarf => |dbg_out| { |
| 126 | 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 | 121 | .plan9 => {}, |
| 130 | 122 | .none => {}, |
| ... | ... | @@ -163,6 +155,7 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 163 | 155 | const imm = data.rs2_or_imm.imm; |
| 164 | 156 | switch (tag) { |
| 165 | 157 | .add => try emit.writeInstruction(Instruction.add(i13, rs1, imm, rd)), |
| 158 | .jmpl => try emit.writeInstruction(Instruction.jmpl(i13, rs1, imm, rd)), | |
| 166 | 159 | .ldub => try emit.writeInstruction(Instruction.ldub(i13, rs1, imm, rd)), |
| 167 | 160 | .lduh => try emit.writeInstruction(Instruction.lduh(i13, rs1, imm, rd)), |
| 168 | 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 | 163 | .@"or" => try emit.writeInstruction(Instruction.@"or"(i13, rs1, imm, rd)), |
| 171 | 164 | .save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)), |
| 172 | 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 | 170 | .sub => try emit.writeInstruction(Instruction.sub(i13, rs1, imm, rd)), |
| 174 | 171 | else => unreachable, |
| 175 | 172 | } |
| ... | ... | @@ -177,6 +174,7 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 177 | 174 | const rs2 = data.rs2_or_imm.rs2; |
| 178 | 175 | switch (tag) { |
| 179 | 176 | .add => try emit.writeInstruction(Instruction.add(Register, rs1, rs2, rd)), |
| 177 | .jmpl => try emit.writeInstruction(Instruction.jmpl(Register, rs1, rs2, rd)), | |
| 180 | 178 | .ldub => try emit.writeInstruction(Instruction.ldub(Register, rs1, rs2, rd)), |
| 181 | 179 | .lduh => try emit.writeInstruction(Instruction.lduh(Register, rs1, rs2, rd)), |
| 182 | 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 | 182 | .@"or" => try emit.writeInstruction(Instruction.@"or"(Register, rs1, rs2, rd)), |
| 185 | 183 | .save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)), |
| 186 | 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 | 189 | .sub => try emit.writeInstruction(Instruction.sub(Register, rs1, rs2, rd)), |
| 188 | 190 | else => unreachable, |
| 189 | 191 | } |
| ... | ... | @@ -230,10 +232,10 @@ fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 230 | 232 | |
| 231 | 233 | // Common helper functions |
| 232 | 234 | |
| 233 | fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { | |
| 234 | const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line); | |
| 235 | const delta_pc: usize = self.code.items.len - self.prev_di_pc; | |
| 236 | switch (self.debug_output) { | |
| 235 | fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void { | |
| 236 | const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line); | |
| 237 | const delta_pc: usize = emit.code.items.len - emit.prev_di_pc; | |
| 238 | switch (emit.debug_output) { | |
| 237 | 239 | .dwarf => |dbg_out| { |
| 238 | 240 | // TODO Look into using the DWARF special opcodes to compress this data. |
| 239 | 241 | // 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 | 248 | leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; |
| 247 | 249 | } |
| 248 | 250 | dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); |
| 249 | self.prev_di_pc = self.code.items.len; | |
| 250 | self.prev_di_line = line; | |
| 251 | self.prev_di_column = column; | |
| 252 | self.prev_di_pc = self.code.items.len; | |
| 251 | emit.prev_di_pc = emit.code.items.len; | |
| 252 | emit.prev_di_line = line; | |
| 253 | emit.prev_di_column = column; | |
| 254 | emit.prev_di_pc = emit.code.items.len; | |
| 253 | 255 | }, |
| 254 | .plan9 => |dbg_out| { | |
| 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 => {}, | |
| 256 | else => {}, | |
| 281 | 257 | } |
| 282 | 258 | } |
| 283 | 259 |
src/arch/sparcv9/Mir.zig+24-37| ... | ... | @@ -28,8 +28,6 @@ pub const Inst = struct { |
| 28 | 28 | data: Data, |
| 29 | 29 | |
| 30 | 30 | pub const Tag = enum(u16) { |
| 31 | /// Pseudo-instruction: Argument | |
| 32 | dbg_arg, | |
| 33 | 31 | /// Pseudo-instruction: End of prologue |
| 34 | 32 | dbg_prologue_end, |
| 35 | 33 | /// Pseudo-instruction: Beginning of epilogue |
| ... | ... | @@ -41,27 +39,24 @@ pub const Inst = struct { |
| 41 | 39 | // in The SPARC Architecture Manual, Version 9. |
| 42 | 40 | |
| 43 | 41 | /// A.2 Add |
| 44 | /// Those uses the arithmetic_3op field. | |
| 42 | /// This uses the arithmetic_3op field. | |
| 45 | 43 | // TODO add other operations. |
| 46 | 44 | add, |
| 47 | 45 | |
| 48 | 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 | 48 | bpcc, |
| 51 | 49 | |
| 52 | 50 | /// A.8 Call and Link |
| 53 | /// It uses the branch_link field. | |
| 51 | /// This uses the branch_link field. | |
| 54 | 52 | call, |
| 55 | 53 | |
| 56 | 54 | /// A.24 Jump and Link |
| 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. | |
| 55 | /// This uses the arithmetic_3op field. | |
| 60 | 56 | jmpl, |
| 61 | jmpl_i, | |
| 62 | 57 | |
| 63 | 58 | /// A.27 Load Integer |
| 64 | /// Those uses the arithmetic_3op field. | |
| 59 | /// This uses the arithmetic_3op field. | |
| 65 | 60 | /// Note that the ldd variant of this instruction is deprecated, so do not emit |
| 66 | 61 | /// it unless specifically requested (e.g. by inline assembly). |
| 67 | 62 | // TODO add other operations. |
| ... | ... | @@ -71,39 +66,49 @@ pub const Inst = struct { |
| 71 | 66 | ldx, |
| 72 | 67 | |
| 73 | 68 | /// A.31 Logical Operations |
| 74 | /// Those uses the arithmetic_3op field. | |
| 69 | /// This uses the arithmetic_3op field. | |
| 75 | 70 | // TODO add other operations. |
| 76 | 71 | @"or", |
| 77 | 72 | |
| 78 | 73 | /// A.40 No Operation |
| 79 | /// It uses the nop field. | |
| 74 | /// This uses the nop field. | |
| 80 | 75 | nop, |
| 81 | 76 | |
| 82 | 77 | /// A.45 RETURN |
| 83 | /// It uses the arithmetic_2op field. | |
| 78 | /// This uses the arithmetic_2op field. | |
| 84 | 79 | @"return", |
| 85 | 80 | |
| 86 | 81 | /// A.46 SAVE and RESTORE |
| 87 | /// Those uses the arithmetic_3op field. | |
| 82 | /// This uses the arithmetic_3op field. | |
| 88 | 83 | save, |
| 89 | 84 | restore, |
| 90 | 85 | |
| 91 | 86 | /// A.48 SETHI |
| 92 | /// It uses the sethi field. | |
| 87 | /// This uses the sethi field. | |
| 93 | 88 | sethi, |
| 94 | 89 | |
| 95 | 90 | /// A.49 Shift |
| 96 | /// Those uses the shift field. | |
| 91 | /// This uses the shift field. | |
| 97 | 92 | // TODO add other operations. |
| 98 | 93 | sllx, |
| 99 | 94 | |
| 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 | 105 | /// A.56 Subtract |
| 101 | /// Those uses the arithmetic_3op field. | |
| 106 | /// This uses the arithmetic_3op field. | |
| 102 | 107 | // TODO add other operations. |
| 103 | 108 | sub, |
| 104 | 109 | |
| 105 | 110 | /// A.61 Trap on Integer Condition Codes (Tcc) |
| 106 | /// It uses the trap field. | |
| 111 | /// This uses the trap field. | |
| 107 | 112 | tcc, |
| 108 | 113 | }; |
| 109 | 114 | |
| ... | ... | @@ -115,14 +120,6 @@ pub const Inst = struct { |
| 115 | 120 | /// how to interpret the data within. |
| 116 | 121 | // TODO this is a quick-n-dirty solution that needs to be cleaned up. |
| 117 | 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 | 123 | /// Debug info: line and column |
| 127 | 124 | /// |
| 128 | 125 | /// Used by e.g. dbg_line |
| ... | ... | @@ -167,13 +164,6 @@ pub const Inst = struct { |
| 167 | 164 | link: Register = .o7, |
| 168 | 165 | }, |
| 169 | 166 | |
| 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 | 167 | /// Branch with prediction. |
| 178 | 168 | /// Used by e.g. bpcc |
| 179 | 169 | branch_predict: struct { |
| ... | ... | @@ -234,10 +224,7 @@ pub const Inst = struct { |
| 234 | 224 | // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks. |
| 235 | 225 | comptime { |
| 236 | 226 | if (builtin.mode != .Debug) { |
| 237 | // TODO clean up the definition of Data before enabling this. | |
| 238 | // I'll do that after the PoC backend can produce usable binaries. | |
| 239 | ||
| 240 | // assert(@sizeOf(Data) == 8); | |
| 227 | assert(@sizeOf(Data) == 8); | |
| 241 | 228 | } |
| 242 | 229 | } |
| 243 | 230 | }; |
src/arch/sparcv9/abi.zig+12| ... | ... | @@ -1,6 +1,18 @@ |
| 1 | 1 | const bits = @import("bits.zig"); |
| 2 | 2 | const Register = bits.Register; |
| 3 | 3 | |
| 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. | |
| 9 | pub 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. | |
| 14 | pub const stack_save_area = 176; | |
| 15 | ||
| 4 | 16 | // There are no callee-preserved registers since the windowing |
| 5 | 17 | // mechanism already takes care of them. |
| 6 | 18 | // 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 | 271 | rd: u5, |
| 272 | 272 | op3: u6, |
| 273 | 273 | rs1: u5, |
| 274 | // See Errata 58 of SPARCv9 specification | |
| 275 | // https://sparc.org/errata-for-v9/#58 | |
| 274 | 276 | i: u1 = 0b1, |
| 275 | 277 | reserved: u8 = 0b00000000, |
| 276 | 278 | rs2: u5, |
| ... | ... | @@ -977,10 +979,10 @@ pub const Instruction = union(enum) { |
| 977 | 979 | }; |
| 978 | 980 | } |
| 979 | 981 | |
| 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 | 983 | return switch (s2) { |
| 982 | Register => format3a(0b10, 0b00_0010, rs1, rs2, rd), | |
| 983 | i13 => format3b(0b10, 0b00_0010, rs1, rs2, rd), | |
| 984 | Register => format3a(0b10, 0b11_1000, rs1, rs2, rd), | |
| 985 | i13 => format3b(0b10, 0b11_1000, rs1, rs2, rd), | |
| 984 | 986 | else => unreachable, |
| 985 | 987 | }; |
| 986 | 988 | } |
| ... | ... | @@ -1017,6 +1019,14 @@ pub const Instruction = union(enum) { |
| 1017 | 1019 | }; |
| 1018 | 1020 | } |
| 1019 | 1021 | |
| 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 | 1030 | pub fn nop() Instruction { |
| 1021 | 1031 | return sethi(0, .g0); |
| 1022 | 1032 | } |
| ... | ... | @@ -1049,6 +1059,38 @@ pub const Instruction = union(enum) { |
| 1049 | 1059 | return format2a(0b100, imm, rd); |
| 1050 | 1060 | } |
| 1051 | 1061 | |
| 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 | 1094 | pub fn sub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction { |
| 1053 | 1095 | return switch (s2) { |
| 1054 | 1096 | Register => format3a(0b10, 0b00_0100, rs1, rs2, rd), |
test/cases/sparcv9-linux/hello_world.zig+6-11| ... | ... | @@ -1,23 +1,18 @@ |
| 1 | 1 | const msg = "Hello, World!\n"; |
| 2 | 2 | |
| 3 | pub export fn _start() noreturn { | |
| 3 | fn length() usize { | |
| 4 | return msg.len; | |
| 5 | } | |
| 6 | ||
| 7 | pub fn main() void { | |
| 4 | 8 | asm volatile ("ta 0x6d" |
| 5 | 9 | : |
| 6 | 10 | : [number] "{g1}" (4), |
| 7 | 11 | [arg1] "{o0}" (1), |
| 8 | 12 | [arg2] "{o1}" (@ptrToInt(msg)), |
| 9 | [arg3] "{o2}" (msg.len), | |
| 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), | |
| 13 | [arg3] "{o2}" (length()), | |
| 17 | 14 | : "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", "memory" |
| 18 | 15 | ); |
| 19 | ||
| 20 | unreachable; | |
| 21 | 16 | } |
| 22 | 17 | |
| 23 | 18 | // run |