diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2022-03-21 18:21:31 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-03-21 18:21:31 -0400 |
| commit | 916a65cb7bc96e5faa9337ab541d1ae230b48259 (patch) | |
| tree | 6d33498f2e6e83fea34317df31cb15331beb2b32 /src | |
| parent | 18a43b61f92ae937e39fc5dbc3de581e26709151 (diff) | |
| parent | 731dda18dde439631ed93afb0c0a199dc8842726 (diff) | |
| download | zig-916a65cb7bc96e5faa9337ab541d1ae230b48259.tar.gz zig-916a65cb7bc96e5faa9337ab541d1ae230b48259.zip | |
Merge pull request #11224 from koachan/sparc64-codegen
stage2 sparcv9: Add instruction encoder and placeholder codegen impl
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/sparcv9/CodeGen.zig | 39 | ||||
| -rw-r--r-- | src/arch/sparcv9/Emit.zig | 6 | ||||
| -rw-r--r-- | src/arch/sparcv9/Mir.zig | 11 | ||||
| -rw-r--r-- | src/arch/sparcv9/abi.zig | 12 | ||||
| -rw-r--r-- | src/arch/sparcv9/bits.zig | 1101 | ||||
| -rw-r--r-- | src/codegen.zig | 2 |
6 files changed, 1170 insertions, 1 deletions
diff --git a/src/arch/sparcv9/CodeGen.zig b/src/arch/sparcv9/CodeGen.zig new file mode 100644 index 0000000000..db5811dfda --- /dev/null +++ b/src/arch/sparcv9/CodeGen.zig @@ -0,0 +1,39 @@ +//! SPARCv9 codegen. +//! This lowers AIR into MIR. +const std = @import("std"); +const builtin = @import("builtin"); +const link = @import("../../link.zig"); +const Module = @import("../../Module.zig"); +const Air = @import("../../Air.zig"); +const Mir = @import("Mir.zig"); +const Emit = @import("Emit.zig"); +const Liveness = @import("../../Liveness.zig"); + +const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; +const FnResult = @import("../../codegen.zig").FnResult; +const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; + +const bits = @import("bits.zig"); +const abi = @import("abi.zig"); + +const Self = @This(); + +pub fn generate( + bin_file: *link.File, + src_loc: Module.SrcLoc, + module_fn: *Module.Fn, + air: Air, + liveness: Liveness, + code: *std.ArrayList(u8), + debug_output: DebugInfoOutput, +) GenerateSymbolError!FnResult { + _ = bin_file; + _ = src_loc; + _ = module_fn; + _ = air; + _ = liveness; + _ = code; + _ = debug_output; + + @panic("TODO implement SPARCv9 codegen"); +} diff --git a/src/arch/sparcv9/Emit.zig b/src/arch/sparcv9/Emit.zig new file mode 100644 index 0000000000..ba644ede7e --- /dev/null +++ b/src/arch/sparcv9/Emit.zig @@ -0,0 +1,6 @@ +//! This file contains the functionality for lowering SPARCv9 MIR into +//! machine code + +const Emit = @This(); +const Mir = @import("Mir.zig"); +const bits = @import("bits.zig"); diff --git a/src/arch/sparcv9/Mir.zig b/src/arch/sparcv9/Mir.zig new file mode 100644 index 0000000000..f0d3b1dfbd --- /dev/null +++ b/src/arch/sparcv9/Mir.zig @@ -0,0 +1,11 @@ +//! Machine Intermediate Representation. +//! This data is produced by SPARCv9 Codegen or SPARCv9 assembly parsing +//! These instructions have a 1:1 correspondence with machine code instructions +//! for the target. MIR can be lowered to source-annotated textual assembly code +//! instructions, or it can be lowered to machine code. +//! The main purpose of MIR is to postpone the assignment of offsets until Isel, +//! so that, for example, the smaller encodings of jump instructions can be used. + +const Mir = @This(); +const bits = @import("bits.zig"); +const Register = bits.Register; diff --git a/src/arch/sparcv9/abi.zig b/src/arch/sparcv9/abi.zig new file mode 100644 index 0000000000..5c9ea979fc --- /dev/null +++ b/src/arch/sparcv9/abi.zig @@ -0,0 +1,12 @@ +const bits = @import("bits.zig"); +const Register = bits.Register; + +// Register windowing mechanism will take care of preserving registers +// so no need to do it manually +pub const callee_preserved_regs = [_]Register{}; + +pub const c_abi_int_param_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 }; +pub const c_abi_int_param_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5" }; + +pub const c_abi_int_return_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5 }; +pub const c_abi_int_return_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5" }; diff --git a/src/arch/sparcv9/bits.zig b/src/arch/sparcv9/bits.zig new file mode 100644 index 0000000000..07cbf7fc91 --- /dev/null +++ b/src/arch/sparcv9/bits.zig @@ -0,0 +1,1101 @@ +const std = @import("std"); +const DW = std.dwarf; +const assert = std.debug.assert; +const testing = std.testing; + +/// General purpose registers in the SPARCv9 instruction set +pub const Register = enum(u6) { + // zig fmt: off + g0, g1, g2, g3, g4, g5, g6, g7, + o0, o1, o2, o3, o4, o5, o6, o7, + l0, l1, l2, l3, l4, l5, l6, l7, + @"i0", @"i1", @"i2", @"i3", @"i4", @"i5", @"i6", @"i7", + + sp = 46, // stack pointer (o6) + fp = 62, // frame pointer (i6) + // zig fmt: on + + pub fn id(self: Register) u5 { + return @truncate(u5, @enumToInt(self)); + } + + pub fn enc(self: Register) u5 { + // For integer registers, enc() == id(). + return self.id(); + } + + pub fn dwarfLocOp(reg: Register) u8 { + return @as(u8, reg.id()) + DW.OP.reg0; + } +}; + +test "Register.id" { + // SP + try testing.expectEqual(@as(u5, 14), Register.o6.id()); + try testing.expectEqual(Register.o6.id(), Register.sp.id()); + + // FP + try testing.expectEqual(@as(u5, 30), Register.@"i6".id()); + try testing.expectEqual(Register.@"i6".id(), Register.fp.id()); + + // x0 + try testing.expectEqual(@as(u5, 0), Register.g0.id()); + try testing.expectEqual(@as(u5, 8), Register.o0.id()); + try testing.expectEqual(@as(u5, 16), Register.l0.id()); + try testing.expectEqual(@as(u5, 24), Register.@"i0".id()); +} + +test "Register.enc" { + // x0 + try testing.expectEqual(@as(u5, 0), Register.g0.enc()); + try testing.expectEqual(@as(u5, 8), Register.o0.enc()); + try testing.expectEqual(@as(u5, 16), Register.l0.enc()); + try testing.expectEqual(@as(u5, 24), Register.@"i0".enc()); + + // For integer registers, enc() == id(). + try testing.expectEqual(Register.g0.enc(), Register.g0.id()); + try testing.expectEqual(Register.o0.enc(), Register.o0.id()); + try testing.expectEqual(Register.l0.enc(), Register.l0.id()); + try testing.expectEqual(Register.@"i0".enc(), Register.@"i0".id()); +} + +/// Scalar floating point registers in the SPARCv9 instruction set +pub const FloatingPointRegister = enum(u7) { + // SPARCv9 has 64 f32 registers, 32 f64 registers, and 16 f128 registers, + // which are aliased in this way: + // + // | %d0 | %d2 | + // %q0 | %f0 | %f1 | %f2 | %f3 | + // | %d4 | %d6 | + // %q4 | %f4 | %f5 | %f6 | %f7 | + // ... + // | %d60 | %d62 | + // %q60 | %f60 | %f61 | %f62 | %f63 | + // + // Though, since the instructions uses five-bit addressing, only %f0-%f31 + // is usable with f32 instructions. + + // zig fmt: off + + // 32-bit registers + @"f0", @"f1", @"f2", @"f3", @"f4", @"f5", @"f6", @"f7", + @"f8", @"f9", @"f10", @"f11", @"f12", @"f13", @"f14", @"f15", + @"f16", @"f17", @"f18", @"f19", @"f20", @"f21", @"f22", @"f23", + @"f24", @"f25", @"f26", @"f27", @"f28", @"f29", @"f30", @"f31", + + // 64-bit registers + d0, d2, d4, d6, d8, d10, d12, d14, + d16, d18, d20, d22, d24, d26, d28, d30, + d32, d34, d36, d38, d40, d42, d44, d46, + d48, d50, d52, d54, d56, d58, d60, d62, + + // 128-bit registers + q0, q4, q8, q12, q16, q20, q24, q28, + q32, q36, q40, q44, q48, q52, q56, q60, + // zig fmt: on + + pub fn id(self: FloatingPointRegister) u6 { + return switch (self.size()) { + 32 => @truncate(u6, @enumToInt(self)), + 64 => @truncate(u6, (@enumToInt(self) - 32) * 2), + 128 => @truncate(u6, (@enumToInt(self) - 64) * 4), + else => unreachable, + }; + } + + pub fn enc(self: FloatingPointRegister) u5 { + // Floating point registers use an encoding scheme to map from the 6-bit + // ID to 5-bit encoded value. + // (See section 5.1.4.1 of SPARCv9 ISA specification) + + const reg_id = self.id(); + return @truncate(u5, reg_id | (reg_id >> 5)); + } + + /// Returns the bit-width of the register. + pub fn size(self: FloatingPointRegister) u8 { + return switch (@enumToInt(self)) { + 0...31 => 32, + 32...63 => 64, + 64...79 => 128, + else => unreachable, + }; + } +}; + +test "FloatingPointRegister.id" { + // Low region + try testing.expectEqual(@as(u6, 0), FloatingPointRegister.q0.id()); + try testing.expectEqual(FloatingPointRegister.q0.id(), FloatingPointRegister.d0.id()); + try testing.expectEqual(FloatingPointRegister.d0.id(), FloatingPointRegister.@"f0".id()); + + try testing.expectEqual(@as(u6, 28), FloatingPointRegister.q28.id()); + try testing.expectEqual(FloatingPointRegister.q28.id(), FloatingPointRegister.d28.id()); + try testing.expectEqual(FloatingPointRegister.d28.id(), FloatingPointRegister.@"f28".id()); + + // High region + try testing.expectEqual(@as(u6, 32), FloatingPointRegister.q32.id()); + try testing.expectEqual(FloatingPointRegister.q32.id(), FloatingPointRegister.d32.id()); + + try testing.expectEqual(@as(u6, 60), FloatingPointRegister.q60.id()); + try testing.expectEqual(FloatingPointRegister.q60.id(), FloatingPointRegister.d60.id()); +} + +test "FloatingPointRegister.enc" { + // f registers + try testing.expectEqual(@as(u5, 0), FloatingPointRegister.@"f0".enc()); + try testing.expectEqual(@as(u5, 1), FloatingPointRegister.@"f1".enc()); + try testing.expectEqual(@as(u5, 31), FloatingPointRegister.@"f31".enc()); + + // d registers + try testing.expectEqual(@as(u5, 0), FloatingPointRegister.d0.enc()); + try testing.expectEqual(@as(u5, 1), FloatingPointRegister.d32.enc()); + try testing.expectEqual(@as(u5, 31), FloatingPointRegister.d62.enc()); + + // q registers + try testing.expectEqual(@as(u5, 0), FloatingPointRegister.q0.enc()); + try testing.expectEqual(@as(u5, 1), FloatingPointRegister.q32.enc()); + try testing.expectEqual(@as(u5, 29), FloatingPointRegister.q60.enc()); +} + +/// Represents an instruction in the SPARCv9 instruction set +pub const Instruction = union(enum) { + // Some of the instruction formats have several minor formats, here I + // name them with letters since there's no official naming scheme. + // TODO: need to rename the minor formats to a more descriptive name. + + // Format 1 (op = 1): CALL + format_1: packed struct { + op: u2 = 0b01, + disp30: u30, + }, + + // Format 2 (op = 0): SETHI & Branches (Bicc, BPcc, BPr, FBfcc, FBPfcc) + format_2a: packed struct { + op: u2 = 0b00, + rd: u5, + op2: u3, + imm22: u22, + }, + format_2b: packed struct { + op: u2 = 0b00, + a: u1, + cond: u4, + op2: u3, + disp22: u22, + }, + format_2c: packed struct { + op: u2 = 0b00, + a: u1, + cond: u4, + op2: u3, + cc1: u1, + cc0: u1, + p: u1, + disp19: u19, + }, + format_2d: packed struct { + op: u2 = 0b00, + a: u1, + fixed: u1 = 0b0, + rcond: u3, + op2: u3, + d16hi: u2, + p: u1, + rs1: u5, + d16lo: u14, + }, + + // Format 3 (op = 2 or 3): Arithmetic, Logical, MOVr, MEMBAR, Load, and Store + format_3a: packed struct { + op: u2, + rd: u5, + op3: u6, + rs1: u5, + i: u1 = 0b0, + reserved: u8 = 0b00000000, + rs2: u5, + }, + format_3b: struct { + op: u2, + rd: u5, + op3: u6, + rs1: u5, + i: u1 = 0b1, + simm13: u13, + }, + format_3c: packed struct { + op: u2, + reserved1: u5 = 0b00000, + op3: u6, + rs1: u5, + i: u1 = 0b0, + reserved2: u8 = 0b00000000, + rs2: u5, + }, + format_3d: struct { + op: u2, + reserved: u5 = 0b00000, + op3: u6, + rs1: u5, + i: u1 = 0b1, + simm13: u13, + }, + format_3e: packed struct { + op: u2, + rd: u5, + op3: u6, + rs1: u5, + i: u1 = 0b0, + rcond: u3, + reserved: u5 = 0b00000, + rs2: u5, + }, + format_3f: struct { + op: u2, + rd: u5, + op3: u6, + rs1: u5, + i: u1 = 0b1, + rcond: u3, + simm10: u10, + }, + format_3g: packed struct { + op: u2, + rd: u5, + op3: u6, + rs1: u5, + i: u1 = 0b1, + reserved: u8 = 0b00000000, + rs2: u5, + }, + format_3h: packed struct { + op: u2 = 0b10, + fixed1: u5 = 0b00000, + op3: u6 = 0b101000, + fixed2: u5 = 0b01111, + i: u1 = 0b1, + reserved: u6 = 0b000000, + cmask: u3, + mmask: u4, + }, + format_3i: packed struct { + op: u2, + rd: u5, + op3: u6, + rs1: u5, + i: u1 = 0b0, + imm_asi: u8, + rs2: u5, + }, + format_3j: packed struct { + op: u2, + impl_dep1: u5, + op3: u6, + impl_dep2: u19, + }, + format_3k: packed struct { + op: u2, + rd: u5, + op3: u6, + rs1: u5, + i: u1 = 0b0, + x: u1, + reserved: u7 = 0b0000000, + rs2: u5, + }, + format_3l: packed struct { + op: u2, + rd: u5, + op3: u6, + rs1: u5, + i: u1 = 0b1, + x: u1 = 0b0, + reserved: u7 = 0b0000000, + shcnt32: u5, + }, + format_3m: packed struct { + op: u2, + rd: u5, + op3: u6, + rs1: u5, + i: u1 = 0b1, + x: u1 = 0b1, + reserved: u6 = 0b000000, + shcnt64: u6, + }, + format_3n: packed struct { + op: u2, + rd: u5, + op3: u6, + reserved: u5 = 0b00000, + opf: u9, + rs2: u5, + }, + format_3o: packed struct { + op: u2, + fixed: u3 = 0b000, + cc1: u1, + cc0: u1, + op3: u6, + rs1: u5, + opf: u9, + rs2: u5, + }, + format_3p: packed struct { + op: u2, + rd: u5, + op3: u6, + rs1: u5, + opf: u9, + rs2: u5, + }, + format_3q: packed struct { + op: u2, + rd: u5, + op3: u6, + rs1: u5, + reserved: u14 = 0b00000000000000, + }, + format_3r: packed struct { + op: u2, + fcn: u5, + op3: u6, + reserved: u19 = 0b0000000000000000000, + }, + format_3s: packed struct { + op: u2, + rd: u5, + op3: u6, + reserved: u19 = 0b0000000000000000000, + }, + + //Format 4 (op = 2): MOVcc, FMOVr, FMOVcc, and Tcc + format_4a: packed struct { + op: u2 = 0b10, + rd: u5, + op3: u6, + rs1: u5, + i: u1 = 0b0, + cc1: u1, + cc0: u1, + reserved: u6 = 0b000000, + rs2: u5, + }, + format_4b: struct { + op: u2 = 0b10, + rd: u5, + op3: u6, + rs1: u5, + i: u1 = 0b1, + cc1: u1, + cc0: u1, + simm11: u11, + }, + format_4c: packed struct { + op: u2 = 0b10, + rd: u5, + op3: u6, + cc2: u1, + cond: u4, + i: u1 = 0b0, + cc1: u1, + cc0: u1, + reserved: u6 = 0b000000, + rs2: u5, + }, + format_4d: struct { + op: u2 = 0b10, + rd: u5, + op3: u6, + cc2: u1, + cond: u4, + i: u1 = 0b1, + cc1: u1, + cc0: u1, + simm11: u11, + }, + format_4e: packed struct { + op: u2 = 0b10, + rd: u5, + op3: u6, + rs1: u5, + i: u1 = 0b1, + cc1: u1, + cc0: u1, + reserved: u4 = 0b0000, + sw_trap: u7, + }, + format_4f: packed struct { + op: u2 = 0b10, + rd: u5, + op3: u6, + rs1: u5, + fixed: u1 = 0b0, + rcond: u3, + opf_low: u5, + rs2: u5, + }, + format_4g: packed struct { + op: u2 = 0b10, + rd: u5, + op3: u6, + fixed: u1 = 0b0, + cond: u4, + opf_cc: u3, + opf_low: u6, + rs2: u5, + }, + + pub const CCR = enum(u3) { + fcc0, + fcc1, + fcc2, + fcc3, + icc, + reserved1, + xcc, + reserved2, + }; + + pub const RCondition = enum(u3) { + reserved1, + eq_zero, + le_zero, + lt_zero, + reserved2, + ne_zero, + gt_zero, + ge_zero, + }; + + pub const ASI = enum(u8) { + asi_nucleus = 0x04, + asi_nucleus_little = 0x0c, + asi_as_if_user_primary = 0x10, + asi_as_if_user_secondary = 0x11, + asi_as_if_user_primary_little = 0x18, + asi_as_if_user_secondary_little = 0x19, + asi_primary = 0x80, + asi_secondary = 0x81, + asi_primary_nofault = 0x82, + asi_secondary_nofault = 0x83, + asi_primary_little = 0x88, + asi_secondary_little = 0x89, + asi_primary_nofault_little = 0x8a, + asi_secondary_nofault_little = 0x8b, + }; + + pub const ShiftWidth = enum(u1) { + shift32, + shift64, + }; + + pub const MemOrderingConstraint = packed struct { + store_store: bool = false, + load_store: bool = false, + store_load: bool = false, + load_load: bool = false, + }; + + pub const MemCompletionConstraint = packed struct { + sync: bool = false, + mem_issue: bool = false, + lookaside: bool = false, + }; + + // TODO: Need to define an enum for `cond` values + // This is kinda challenging since the cond values have different meanings + // depending on whether it's operating on integer or FP CCR. + pub const Condition = u4; + + pub fn toU32(self: Instruction) u32 { + // TODO: Remove this once packed structs work. + return switch (self) { + .format_1 => |v| @bitCast(u32, v), + .format_2a => |v| @bitCast(u32, v), + .format_2b => |v| @bitCast(u32, v), + .format_2c => |v| @bitCast(u32, v), + .format_2d => |v| @bitCast(u32, v), + .format_3a => |v| @bitCast(u32, v), + .format_3b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | @as(u32, v.simm13), + .format_3c => |v| @bitCast(u32, v), + .format_3d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.reserved) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | @as(u32, v.simm13), + .format_3e => |v| @bitCast(u32, v), + .format_3f => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.rcond) << 10) | @as(u32, v.simm10), + .format_3g => |v| @bitCast(u32, v), + .format_3h => |v| @bitCast(u32, v), + .format_3i => |v| @bitCast(u32, v), + .format_3j => |v| @bitCast(u32, v), + .format_3k => |v| @bitCast(u32, v), + .format_3l => |v| @bitCast(u32, v), + .format_3m => |v| @bitCast(u32, v), + .format_3n => |v| @bitCast(u32, v), + .format_3o => |v| @bitCast(u32, v), + .format_3p => |v| @bitCast(u32, v), + .format_3q => |v| @bitCast(u32, v), + .format_3r => |v| @bitCast(u32, v), + .format_3s => |v| @bitCast(u32, v), + .format_4a => |v| @bitCast(u32, v), + .format_4b => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.rs1) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | @as(u32, v.simm11), + .format_4c => |v| @bitCast(u32, v), + .format_4d => |v| (@as(u32, v.op) << 30) | (@as(u32, v.rd) << 25) | (@as(u32, v.op3) << 19) | (@as(u32, v.cc2) << 18) | (@as(u32, v.cond) << 14) | (@as(u32, v.i) << 13) | (@as(u32, v.cc1) << 12) | (@as(u32, v.cc0) << 11) | @as(u32, v.simm11), + .format_4e => |v| @bitCast(u32, v), + .format_4f => |v| @bitCast(u32, v), + .format_4g => |v| @bitCast(u32, v), + }; + } + + fn format1(disp: i32) Instruction { + const udisp = @bitCast(u32, disp); + + // In SPARC, branch target needs to be aligned to 4 bytes. + assert(udisp % 4 == 0); + + // Discard the last two bits since those are implicitly zero. + const udisp_truncated = @truncate(u30, udisp >> 2); + return Instruction{ + .format_1 = .{ + .disp30 = udisp_truncated, + }, + }; + } + + fn format2a(op2: u3, rd: Register, imm: u22) Instruction { + return Instruction{ + .format_2a = .{ + .rd = rd.enc(), + .op2 = op2, + .imm22 = imm, + }, + }; + } + + fn format2b(op2: u3, cond: Condition, annul: bool, disp: i24) Instruction { + const udisp = @bitCast(u24, disp); + + // In SPARC, branch target needs to be aligned to 4 bytes. + assert(udisp % 4 == 0); + + // Discard the last two bits since those are implicitly zero. + const udisp_truncated = @truncate(u22, udisp >> 2); + return Instruction{ + .format_2b = .{ + .a = @boolToInt(annul), + .cond = cond, + .op2 = op2, + .disp22 = udisp_truncated, + }, + }; + } + + fn format2c(op2: u3, cond: Condition, annul: bool, pt: bool, ccr: CCR, disp: i21) Instruction { + const udisp = @bitCast(u21, disp); + + // In SPARC, branch target needs to be aligned to 4 bytes. + assert(udisp % 4 == 0); + + // Discard the last two bits since those are implicitly zero. + const udisp_truncated = @truncate(u19, udisp >> 2); + + const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1); + const ccr_cc0 = @truncate(u1, @enumToInt(ccr)); + return Instruction{ + .format_2c = .{ + .a = @boolToInt(annul), + .cond = cond, + .op2 = op2, + .cc1 = ccr_cc1, + .cc0 = ccr_cc0, + .p = @boolToInt(pt), + .disp19 = udisp_truncated, + }, + }; + } + + fn format2d(op2: u3, rcond: RCondition, annul: bool, pt: bool, rs1: Register, disp: i18) Instruction { + const udisp = @bitCast(u18, disp); + + // In SPARC, branch target needs to be aligned to 4 bytes. + assert(udisp % 4 == 0); + + // Discard the last two bits since those are implicitly zero, + // and split it into low and high parts. + const udisp_truncated = @truncate(u16, udisp >> 2); + const udisp_hi = @truncate(u2, (udisp_truncated & 0b1100_0000_0000_0000) >> 14); + const udisp_lo = @truncate(u14, udisp_truncated & 0b0011_1111_1111_1111); + return Instruction{ + .format_2d = .{ + .a = @boolToInt(annul), + .rcond = @enumToInt(rcond), + .op2 = op2, + .p = @boolToInt(pt), + .rs1 = rs1.enc(), + .d16hi = udisp_hi, + .d16lo = udisp_lo, + }, + }; + } + + fn format3a(op: u2, op3: u6, rs1: Register, rs2: Register, rd: Register) Instruction { + return Instruction{ + .format_3a = .{ + .op = op, + .rd = rd.enc(), + .op3 = op3, + .rs1 = rs1.enc(), + .rs2 = rs2.enc(), + }, + }; + } + fn format3b(op: u2, op3: u6, rs1: Register, imm: i13, rd: Register) Instruction { + return Instruction{ + .format_3b = .{ + .op = op, + .rd = rd.enc(), + .op3 = op3, + .rs1 = rs1.enc(), + .simm13 = @bitCast(u13, imm), + }, + }; + } + fn format3c(op: u2, op3: u6, rs1: Register, rs2: Register) Instruction { + return Instruction{ + .format_3c = .{ + .op = op, + .op3 = op3, + .rs1 = rs1.enc(), + .rs2 = rs2.enc(), + }, + }; + } + fn format3d(op: u2, op3: u6, rs1: Register, imm: i13) Instruction { + return Instruction{ + .format_3d = .{ + .op = op, + .op3 = op3, + .rs1 = rs1.enc(), + .simm13 = @bitCast(u13, imm), + }, + }; + } + fn format3e(op: u2, op3: u6, rcond: RCondition, rs1: Register, rs2: Register, rd: Register) Instruction { + return Instruction{ + .format_3e = .{ + .op = op, + .rd = rd.enc(), + .op3 = op3, + .rs1 = rs1.enc(), + .rcond = @enumToInt(rcond), + .rs2 = rs2.enc(), + }, + }; + } + fn format3f(op: u2, op3: u6, rcond: RCondition, rs1: Register, imm: i10, rd: Register) Instruction { + return Instruction{ + .format_3f = .{ + .op = op, + .rd = rd.enc(), + .op3 = op3, + .rs1 = rs1.enc(), + .rcond = @enumToInt(rcond), + .simm10 = @bitCast(u10, imm), + }, + }; + } + fn format3g(op: u2, op3: u6, rs1: Register, rs2: Register, rd: Register) Instruction { + return Instruction{ + .format_3g = .{ + .op = op, + .rd = rd.enc(), + .op3 = op3, + .rs1 = rs1.enc(), + .rs2 = rs2.enc(), + }, + }; + } + fn format3h(cmask: MemCompletionConstraint, mmask: MemOrderingConstraint) Instruction { + return Instruction{ + .format_3h = .{ + .cmask = @bitCast(u3, cmask), + .mmask = @bitCast(u4, mmask), + }, + }; + } + fn format3i(op: u2, op3: u6, rs1: Register, rs2: Register, rd: Register, asi: ASI) Instruction { + return Instruction{ + .format_3i = .{ + .op = op, + .rd = rd.enc(), + .op3 = op3, + .rs1 = rs1.enc(), + .imm_asi = @enumToInt(asi), + .rs2 = rs2.enc(), + }, + }; + } + fn format3j(op: u2, op3: u6, impl_dep1: u5, impl_dep2: u19) Instruction { + return Instruction{ + .format_3j = .{ + .op = op, + .impl_dep1 = impl_dep1, + .op3 = op3, + .impl_dep2 = impl_dep2, + }, + }; + } + fn format3k(op: u2, op3: u6, sw: ShiftWidth, rs1: Register, rs2: Register, rd: Register) Instruction { + return Instruction{ + .format_3k = .{ + .op = op, + .rd = rd.enc(), + .op3 = op3, + .rs1 = rs1.enc(), + .x = @enumToInt(sw), + .rs2 = rs2.enc(), + }, + }; + } + fn format3l(op: u2, op3: u6, rs1: Register, shift_count: u5, rd: Register) Instruction { + return Instruction{ + .format_3l = .{ + .op = op, + .rd = rd.enc(), + .op3 = op3, + .rs1 = rs1.enc(), + .shcnt32 = shift_count, + }, + }; + } + fn format3m(op: u2, op3: u6, rs1: Register, shift_count: u6, rd: Register) Instruction { + return Instruction{ + .format_3m = .{ + .op = op, + .rd = rd.enc(), + .op3 = op3, + .rs1 = rs1.enc(), + .shcnt64 = shift_count, + }, + }; + } + fn format3n(op: u2, op3: u6, opf: u9, rs2: Register, rd: Register) Instruction { + return Instruction{ + .format_3n = .{ + .op = op, + .rd = rd.enc(), + .op3 = op3, + .opf = opf, + .rs2 = rs2.enc(), + }, + }; + } + fn format3o(op: u2, op3: u6, opf: u9, ccr: CCR, rs1: Register, rs2: Register) Instruction { + const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1); + const ccr_cc0 = @truncate(u1, @enumToInt(ccr)); + return Instruction{ + .format_3o = .{ + .op = op, + .cc1 = ccr_cc1, + .cc0 = ccr_cc0, + .op3 = op3, + .rs1 = rs1.enc(), + .opf = opf, + .rs2 = rs2.enc(), + }, + }; + } + fn format3p(op: u2, op3: u6, opf: u9, rs1: Register, rs2: Register, rd: Register) Instruction { + return Instruction{ + .format_3p = .{ + .op = op, + .rd = rd.enc(), + .op3 = op3, + .rs1 = rs1.enc(), + .opf = opf, + .rs2 = rs2.enc(), + }, + }; + } + fn format3q(op: u2, op3: u6, rs1: Register, rd: Register) Instruction { + return Instruction{ + .format_3q = .{ + .op = op, + .rd = rd.enc(), + .op3 = op3, + .rs1 = rs1.enc(), + }, + }; + } + fn format3r(op: u2, op3: u6, fcn: u5) Instruction { + return Instruction{ + .format_3r = .{ + .op = op, + .fcn = fcn, + .op3 = op3, + }, + }; + } + fn format3s(op: u2, op3: u6, rd: Register) Instruction { + return Instruction{ + .format_3s = .{ + .op = op, + .rd = rd.enc(), + .op3 = op3, + }, + }; + } + + fn format4a(op3: u6, ccr: CCR, rs1: Register, rs2: Register, rd: Register) Instruction { + const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1); + const ccr_cc0 = @truncate(u1, @enumToInt(ccr)); + return Instruction{ + .format_4a = .{ + .rd = rd.enc(), + .op3 = op3, + .rs1 = rs1.enc(), + .cc1 = ccr_cc1, + .cc0 = ccr_cc0, + .rs2 = rs2.enc(), + }, + }; + } + + fn format4b(op3: u6, ccr: CCR, rs1: Register, imm: i11, rd: Register) Instruction { + const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1); + const ccr_cc0 = @truncate(u1, @enumToInt(ccr)); + return Instruction{ + .format_4b = .{ + .rd = rd.enc(), + .op3 = op3, + .rs1 = rs1.enc(), + .cc1 = ccr_cc1, + .cc0 = ccr_cc0, + .simm11 = @bitCast(u11, imm), + }, + }; + } + + fn format4c(op3: u6, cond: Condition, ccr: CCR, rs2: Register, rd: Register) Instruction { + const ccr_cc2 = @truncate(u1, @enumToInt(ccr) >> 2); + const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1); + const ccr_cc0 = @truncate(u1, @enumToInt(ccr)); + return Instruction{ + .format_4c = .{ + .rd = rd.enc(), + .op3 = op3, + .cc2 = ccr_cc2, + .cond = cond, + .cc1 = ccr_cc1, + .cc0 = ccr_cc0, + .rs2 = rs2.enc(), + }, + }; + } + + fn format4d(op3: u6, cond: Condition, ccr: CCR, imm: i11, rd: Register) Instruction { + const ccr_cc2 = @truncate(u1, @enumToInt(ccr) >> 2); + const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1); + const ccr_cc0 = @truncate(u1, @enumToInt(ccr)); + return Instruction{ + .format_4d = .{ + .rd = rd.enc(), + .op3 = op3, + .cc2 = ccr_cc2, + .cond = cond, + .cc1 = ccr_cc1, + .cc0 = ccr_cc0, + .simm11 = @bitCast(u11, imm), + }, + }; + } + + fn format4e(op3: u6, ccr: CCR, rs1: Register, rd: Register, sw_trap: u7) Instruction { + const ccr_cc1 = @truncate(u1, @enumToInt(ccr) >> 1); + const ccr_cc0 = @truncate(u1, @enumToInt(ccr)); + return Instruction{ + .format_4e = .{ + .rd = rd.enc(), + .op3 = op3, + .rs1 = rs1.enc(), + .cc1 = ccr_cc1, + .cc0 = ccr_cc0, + .sw_trap = sw_trap, + }, + }; + } + + fn format4f( + op3: u6, + opf_low: u5, + rcond: RCondition, + rs1: Register, + rs2: Register, + rd: Register, + ) Instruction { + return Instruction{ + .format_4f = .{ + .rd = rd.enc(), + .op3 = op3, + .rs1 = rs1.enc(), + .rcond = @enumToInt(rcond), + .opf_low = opf_low, + .rs2 = rs2.enc(), + }, + }; + } + + fn format4g(op3: u6, opf_low: u6, opf_cc: u3, cond: Condition, rs2: Register, rd: Register) Instruction { + return Instruction{ + .format_4g = .{ + .rd = rd.enc(), + .op3 = op3, + .cond = cond, + .opf_cc = opf_cc, + .opf_low = opf_low, + .rs2 = rs2.enc(), + }, + }; + } +}; + +test "Serialize formats" { + const Testcase = struct { + inst: Instruction, + expected: u32, + }; + + // Note that the testcases might or might not be a valid instruction + // This is mostly just to check the behavior of the format packed structs + // since currently stage1 doesn't properly implement it in all cases + const testcases = [_]Testcase{ + .{ + .inst = Instruction.format1(4), + .expected = 0b01_000000000000000000000000000001, + }, + .{ + .inst = Instruction.format2a(4, .g0, 0), + .expected = 0b00_00000_100_0000000000000000000000, + }, + .{ + .inst = Instruction.format2b(6, 3, true, -4), + .expected = 0b00_1_0011_110_1111111111111111111111, + }, + .{ + .inst = Instruction.format2c(3, 0, false, true, .xcc, 8), + .expected = 0b00_0_0000_011_1_0_1_0000000000000000010, + }, + .{ + .inst = Instruction.format2d(7, .eq_zero, false, true, .o0, 20), + .expected = 0b00_0_0_001_111_00_1_01000_00000000000101, + }, + .{ + .inst = Instruction.format3a(3, 5, .g0, .o1, .l2), + .expected = 0b11_10010_000101_00000_0_00000000_01001, + }, + .{ + .inst = Instruction.format3b(3, 5, .g0, -1, .l2), + .expected = 0b11_10010_000101_00000_1_1111111111111, + }, + .{ + .inst = Instruction.format3c(3, 5, .g0, .o1), + .expected = 0b11_00000_000101_00000_0_00000000_01001, + }, + .{ + .inst = Instruction.format3d(3, 5, .g0, 0), + .expected = 0b11_00000_000101_00000_1_0000000000000, + }, + .{ + .inst = Instruction.format3e(3, 5, .ne_zero, .g0, .o1, .l2), + .expected = 0b11_10010_000101_00000_0_101_00000_01001, + }, + .{ + .inst = Instruction.format3f(3, 5, .ne_zero, .g0, -1, .l2), + .expected = 0b11_10010_000101_00000_1_101_1111111111, + }, + .{ + .inst = Instruction.format3g(3, 5, .g0, .o1, .l2), + .expected = 0b11_10010_000101_00000_1_00000000_01001, + }, + .{ + .inst = Instruction.format3h(.{}, .{}), + .expected = 0b10_00000_101000_01111_1_000000_000_0000, + }, + .{ + .inst = Instruction.format3i(3, 5, .g0, .o1, .l2, .asi_primary_little), + .expected = 0b11_10010_000101_00000_0_10001000_01001, + }, + .{ + .inst = Instruction.format3j(3, 5, 31, 0), + .expected = 0b11_11111_000101_0000000000000000000, + }, + .{ + .inst = Instruction.format3k(3, 5, .shift32, .g0, .o1, .l2), + .expected = 0b11_10010_000101_00000_0_0_0000000_01001, + }, + .{ + .inst = Instruction.format3l(3, 5, .g0, 31, .l2), + .expected = 0b11_10010_000101_00000_1_0_0000000_11111, + }, + .{ + .inst = Instruction.format3m(3, 5, .g0, 63, .l2), + .expected = 0b11_10010_000101_00000_1_1_000000_111111, + }, + .{ + .inst = Instruction.format3n(3, 5, 0, .o1, .l2), + .expected = 0b11_10010_000101_00000_000000000_01001, + }, + .{ + .inst = Instruction.format3o(3, 5, 0, .xcc, .o1, .l2), + .expected = 0b11_000_1_0_000101_01001_000000000_10010, + }, + .{ + .inst = Instruction.format3p(3, 5, 0, .g0, .o1, .l2), + .expected = 0b11_10010_000101_00000_000000000_01001, + }, + .{ + .inst = Instruction.format3q(3, 5, .g0, .o1), + .expected = 0b11_01001_000101_00000_00000000000000, + }, + .{ + .inst = Instruction.format3r(3, 5, 4), + .expected = 0b11_00100_000101_0000000000000000000, + }, + .{ + .inst = Instruction.format3s(3, 5, .g0), + .expected = 0b11_00000_000101_0000000000000000000, + }, + .{ + .inst = Instruction.format4a(8, .xcc, .g0, .o1, .l2), + .expected = 0b10_10010_001000_00000_0_1_0_000000_01001, + }, + .{ + .inst = Instruction.format4b(8, .xcc, .g0, -1, .l2), + .expected = 0b10_10010_001000_00000_1_1_0_11111111111, + }, + .{ + .inst = Instruction.format4c(8, 0, .xcc, .g0, .o1), + .expected = 0b10_01001_001000_1_0000_0_1_0_000000_00000, + }, + .{ + .inst = Instruction.format4d(8, 0, .xcc, 0, .l2), + .expected = 0b10_10010_001000_1_0000_1_1_0_00000000000, + }, + .{ + .inst = Instruction.format4e(8, .xcc, .g0, .o1, 0), + .expected = 0b10_01001_001000_00000_1_1_0_0000_0000000, + }, + .{ + .inst = Instruction.format4f(8, 4, .eq_zero, .g0, .o1, .l2), + .expected = 0b10_10010_001000_00000_0_001_00100_01001, + }, + .{ + .inst = Instruction.format4g(8, 4, 2, 0, .o1, .l2), + .expected = 0b10_10010_001000_0_0000_010_000100_01001, + }, + }; + + for (testcases) |case| { + const actual = case.inst.toU32(); + try testing.expectEqual(case.expected, actual); + } +} diff --git a/src/codegen.zig b/src/codegen.zig index 95b145e901..9861dfc06c 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -108,7 +108,7 @@ pub fn generateFunction( //.riscv32 => return Function(.riscv32).generate(bin_file, src_loc, func, air, liveness, code, debug_output), .riscv64 => return @import("arch/riscv64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.sparc => return Function(.sparc).generate(bin_file, src_loc, func, air, liveness, code, debug_output), - //.sparcv9 => return Function(.sparcv9).generate(bin_file, src_loc, func, air, liveness, code, debug_output), + .sparcv9 => return @import("arch/sparcv9/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.sparcel => return Function(.sparcel).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.s390x => return Function(.s390x).generate(bin_file, src_loc, func, air, liveness, code, debug_output), //.tce => return Function(.tce).generate(bin_file, src_loc, func, air, liveness, code, debug_output), |
