From 0310d88d7e6a855b3641b59c25f5b0f13726d14f Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 26 May 2022 13:08:39 +0700 Subject: [PATCH 1/4] stage2: sparc64: Add cmp and mov synthetic instructions --- src/arch/sparc64/CodeGen.zig | 43 ++++++++++++++++++++++-------------- src/arch/sparc64/Emit.zig | 8 +++++++ src/arch/sparc64/Mir.zig | 20 ++++++++++++++--- 3 files changed, 51 insertions(+), 20 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index ff066f78f2..8b6bcfcb06 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1677,7 +1677,7 @@ fn binOp( const mir_tag: Mir.Inst.Tag = switch (tag) { .add => .add, - .cmp_eq => .subcc, + .cmp_eq => .cmp, else => unreachable, }; @@ -1903,6 +1903,13 @@ fn binOpImmediate( .rs2_or_imm = .{ .imm = @intCast(u6, rhs.immediate) }, }, }, + .cmp => .{ + .arithmetic_2op = .{ + .is_imm = true, + .rs1 = lhs_reg, + .rs2_or_imm = .{ .imm = @intCast(i13, rhs.immediate) }, + }, + }, else => unreachable, }; @@ -2012,6 +2019,13 @@ fn binOpRegister( .rs2_or_imm = .{ .rs2 = rhs_reg }, }, }, + .cmp => .{ + .arithmetic_2op = .{ + .is_imm = false, + .rs1 = lhs_reg, + .rs2_or_imm = .{ .rs2 = rhs_reg }, + }, + }, else => unreachable, }; @@ -2303,12 +2317,11 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void .immediate => |x| { if (x <= math.maxInt(u12)) { _ = try self.addInst(.{ - .tag = .@"or", + .tag = .mov, .data = .{ - .arithmetic_3op = .{ + .arithmetic_2op = .{ .is_imm = true, - .rd = reg, - .rs1 = .g0, + .rs1 = reg, .rs2_or_imm = .{ .imm = @truncate(u12, x) }, }, }, @@ -2400,14 +2413,12 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void if (src_reg.id() == reg.id()) return; - // or %g0, src, dst (aka mov src, dst) _ = try self.addInst(.{ - .tag = .@"or", + .tag = .mov, .data = .{ - .arithmetic_3op = .{ + .arithmetic_2op = .{ .is_imm = false, - .rd = reg, - .rs1 = .g0, + .rs1 = reg, .rs2_or_imm = .{ .rs2 = src_reg }, }, }, @@ -2625,12 +2636,11 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { }; _ = try self.addInst(.{ - .tag = .subcc, - .data = .{ .arithmetic_3op = .{ + .tag = .cmp, + .data = .{ .arithmetic_2op = .{ .is_imm = true, .rs1 = reg_mcv.register, .rs2_or_imm = .{ .imm = 0 }, - .rd = .g0, } }, }); @@ -3163,12 +3173,11 @@ fn truncRegister( }, 64 => { _ = try self.addInst(.{ - .tag = .@"or", + .tag = .mov, .data = .{ - .arithmetic_3op = .{ + .arithmetic_2op = .{ .is_imm = true, - .rd = dest_reg, - .rs1 = .g0, + .rs1 = dest_reg, .rs2_or_imm = .{ .rs2 = operand_reg }, }, }, diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 6f30f785c5..dc1b4caedc 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -121,6 +121,10 @@ pub fn emitMir( .subcc => try emit.mirArithmetic3Op(inst), .tcc => try emit.mirTrap(inst), + + .cmp => try emit.mirArithmetic2Op(inst), + + .mov => try emit.mirArithmetic2Op(inst), } } } @@ -179,12 +183,16 @@ fn mirArithmetic2Op(emit: *Emit, inst: Mir.Inst.Index) !void { const imm = data.rs2_or_imm.imm; switch (tag) { .@"return" => try emit.writeInstruction(Instruction.@"return"(i13, rs1, imm)), + .cmp => try emit.writeInstruction(Instruction.subcc(i13, rs1, imm, .g0)), + .mov => try emit.writeInstruction(Instruction.@"or"(i13, .g0, imm, rs1)), else => unreachable, } } else { const rs2 = data.rs2_or_imm.rs2; switch (tag) { .@"return" => try emit.writeInstruction(Instruction.@"return"(Register, rs1, rs2)), + .cmp => try emit.writeInstruction(Instruction.subcc(Register, rs1, rs2, .g0)), + .mov => try emit.writeInstruction(Instruction.@"or"(Register, .g0, rs2, rs1)), else => unreachable, } } diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index 441e151cea..2ef66a1fa4 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -125,9 +125,23 @@ pub const Inst = struct { /// This uses the trap field. tcc, - // TODO add synthetic instructions - // TODO add cmp synthetic instruction to avoid wasting a register when - // comparing with subcc + // SPARCv9 synthetic instructions + // Note that the instructions that is added here are only those that + // will simplify backend development. Synthetic instructions that is + // only used to provide syntactic sugar in, e.g. inline assembly should + // be deconstructed inside the parser instead. + // See also: G.3 Synthetic Instructions + // TODO add more synthetic instructions + + /// Comparison + /// This uses the arithmetic_2op field. + cmp, // cmp rs1, rs2/imm -> subcc rs1, rs2/imm, %g0 + + /// Copy register/immediate contents to another register + /// This uses the arithmetic_2op field, with rs1 + /// being the *destination* register. + // TODO is it okay to abuse rs1 in this way? + mov, // mov rs2/imm, rs1 -> or %g0, rs2/imm, rs1 }; /// The position of an MIR instruction within the `Mir` instructions array. From 5fa971610e0f987938d43da9d3a40e2a31fe4605 Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 26 May 2022 13:09:56 +0700 Subject: [PATCH 2/4] stage2: sparc64: Change binOpImmediate immediates to u12 Sync with the check in binOp. --- src/arch/sparc64/CodeGen.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 8b6bcfcb06..228691721d 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1891,7 +1891,7 @@ fn binOpImmediate( .is_imm = true, .rd = dest_reg, .rs1 = lhs_reg, - .rs2_or_imm = .{ .imm = @intCast(i13, rhs.immediate) }, + .rs2_or_imm = .{ .imm = @intCast(u12, rhs.immediate) }, }, }, .sllx => .{ @@ -1907,7 +1907,7 @@ fn binOpImmediate( .arithmetic_2op = .{ .is_imm = true, .rs1 = lhs_reg, - .rs2_or_imm = .{ .imm = @intCast(i13, rhs.immediate) }, + .rs2_or_imm = .{ .imm = @intCast(u12, rhs.immediate) }, }, }, else => unreachable, From 3923722cc617251de91d5bc9a5d4f9c0ca93ee1a Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 26 May 2022 13:24:57 +0700 Subject: [PATCH 3/4] stage2: sparc64: Account for stack bias & reserved area in genSetReg genSetReg with ptr_stack_offset should add the bias and reserved area before emitting the instruction. --- src/arch/sparc64/CodeGen.zig | 12 ++++++------ src/arch/sparc64/abi.zig | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 228691721d..bf08e9a309 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -352,7 +352,7 @@ fn gen(self: *Self) !void { if (cc != .Naked) { // TODO Finish function prologue and epilogue for sparc64. - // save %sp, stack_save_area, %sp + // save %sp, stack_reserved_area, %sp const save_inst = try self.addInst(.{ .tag = .save, .data = .{ @@ -360,7 +360,7 @@ fn gen(self: *Self) !void { .is_imm = true, .rd = .sp, .rs1 = .sp, - .rs2_or_imm = .{ .imm = -abi.stack_save_area }, + .rs2_or_imm = .{ .imm = -abi.stack_reserved_area }, }, }, }); @@ -407,7 +407,7 @@ fn gen(self: *Self) !void { } // Backpatch stack offset - const total_stack_size = self.max_end_stack + abi.stack_save_area; // TODO + self.saved_regs_stack_space; + const total_stack_size = self.max_end_stack + abi.stack_reserved_area; // TODO + self.saved_regs_stack_space; const stack_size = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align); if (math.cast(i13, stack_size)) |size| { self.mir_instructions.set(save_inst, .{ @@ -2299,7 +2299,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); }, .ptr_stack_offset => |off| { - const simm13 = math.cast(u12, off) catch + const simm13 = math.cast(u12, off + abi.stack_bias + abi.stack_reserved_area) catch return self.fail("TODO larger stack offsets", .{}); _ = try self.addInst(.{ @@ -2431,7 +2431,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void try self.genLoad(reg, reg, i13, 0, ty.abiSize(self.target.*)); }, .stack_offset => |off| { - const real_offset = off + abi.stack_bias + abi.stack_save_area; + const real_offset = off + abi.stack_bias + abi.stack_reserved_area; const simm13 = math.cast(i13, real_offset) catch return self.fail("TODO larger stack offsets", .{}); try self.genLoad(reg, .sp, i13, simm13, ty.abiSize(self.target.*)); @@ -2465,7 +2465,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); }, .register => |reg| { - const real_offset = stack_offset + abi.stack_bias + abi.stack_save_area; + const real_offset = stack_offset + abi.stack_bias + abi.stack_reserved_area; const simm13 = math.cast(i13, real_offset) catch return self.fail("TODO larger stack offsets", .{}); return self.genStore(reg, .sp, i13, simm13, abi_size); diff --git a/src/arch/sparc64/abi.zig b/src/arch/sparc64/abi.zig index 1c6d40941f..99f83eee7b 100644 --- a/src/arch/sparc64/abi.zig +++ b/src/arch/sparc64/abi.zig @@ -3,17 +3,17 @@ const bits = @import("bits.zig"); const Register = bits.Register; const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; -// SPARCv9 stack constants. +// SPARCv9 SysV ABI stack constants. // See: Registers and the Stack Frame, page 3P-8, SCD 2.4.1. -// On SPARCv9, %sp points to top of stack + stack bias, -// and %fp points to top of previous frame + stack bias. +// The ABI specifies that %sp points to top of stack - stack bias, +// and %fp points to top of previous frame - stack bias. pub const stack_bias = 2047; -// The first 176 bytes of the stack is reserved for register saving purposes. -// SPARCv9 requires to reserve space in the stack for the first six arguments, -// even though they are usually passed in registers. -pub const stack_save_area = 176; +// The first 128 bytes of the stack is reserved for register saving purposes. +// The ABI also requires to reserve space in the stack for the first six +// outgoing arguments, even though they are usually passed in registers. +pub const stack_reserved_area = 128 + 48; // There are no callee-preserved registers since the windowing // mechanism already takes care of them. From c5b99267c0580b166d7ccc2bbf77e9c92566962f Mon Sep 17 00:00:00 2001 From: Koakuma Date: Thu, 26 May 2022 13:48:39 +0700 Subject: [PATCH 4/4] stage2: sparc64: Remove saved_regs_stack_space calculation SPARC does not have an explicit notion of saving/restoring registers. The usual windowing mechanism (save/restore/return) already takes care of that for us. --- src/arch/sparc64/CodeGen.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index bf08e9a309..f36d4ba45f 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -407,7 +407,7 @@ fn gen(self: *Self) !void { } // Backpatch stack offset - const total_stack_size = self.max_end_stack + abi.stack_reserved_area; // TODO + self.saved_regs_stack_space; + const total_stack_size = self.max_end_stack + abi.stack_reserved_area; const stack_size = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align); if (math.cast(i13, stack_size)) |size| { self.mir_instructions.set(save_inst, .{