authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-03-09 09:02:06+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-03-11 20:05:49+01:00
logaa8fda799e64c02d44fe80d1297b5ec8ae6b7677
treecb92fb1724e6d02dc25240dc0d71184c6869701b
parent6e882d730b2ed1f2494e7b28f1fed2726e4a1ac0

x86_64: split up assemble() into more declarative single-purpose helpers


2 files changed, 67 insertions(+), 70 deletions(-)

src/arch/x86_64/CodeGen.zig+63-65
...@@ -33,9 +33,11 @@ const errUnionPayloadOffset = codegen.errUnionPayloadOffset;...@@ -33,9 +33,11 @@ const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
33const errUnionErrorOffset = codegen.errUnionErrorOffset;33const errUnionErrorOffset = codegen.errUnionErrorOffset;
3434
35const Condition = bits.Condition;35const Condition = bits.Condition;
36const Immediate = bits.Immediate;
37const Memory = bits.Memory;
38const Register = bits.Register;
36const RegisterManager = abi.RegisterManager;39const RegisterManager = abi.RegisterManager;
37const RegisterLock = RegisterManager.RegisterLock;40const RegisterLock = RegisterManager.RegisterLock;
38const Register = bits.Register;
3941
40const gp = abi.RegisterClass.gp;42const gp = abi.RegisterClass.gp;
41const sse = abi.RegisterClass.sse;43const sse = abi.RegisterClass.sse;
...@@ -398,47 +400,58 @@ fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {...@@ -398,47 +400,58 @@ fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 {
398 return result;400 return result;
399}401}
400402
401fn assemble(self: *Self, tag: Mir.Inst.Tag, args: struct {403fn asmNone(self: *Self, tag: Mir.Inst.Tag) !void {
402 op1: Mir.Operand = .none,404 _ = try self.addInst(.{
403 op2: Mir.Operand = .none,405 .tag = tag,
404 op3: Mir.Operand = .none,406 .ops = .none,
405 op4: Mir.Operand = .none,407 .data = undefined,
406}) !void {408 });
407 const ops: Mir.Inst.Ops = blk: {409}
408 if (args.op1 == .none and args.op2 == .none and args.op3 == .none and args.op4 == .none)
409 break :blk .none;
410
411 if (args.op1 == .reg and args.op2 == .reg)
412 break :blk .rr;
413 if (args.op1 == .reg and args.op2 == .imm) switch (args.op2.imm) {
414 .signed => break :blk .ri_s,
415 .unsigned => break :blk .ri_u,
416 };
417 if (args.op1 == .reg)
418 break :blk .r;
419 if (args.op1 == .imm) switch (args.op1.imm) {
420 .signed => break :blk .imm_s,
421 .unsigned => break :blk .imm_u, // TODO 64bits
422 };
423410
424 unreachable;411fn asmRegister(self: *Self, tag: Mir.Inst.Tag, reg: Register) !void {
425 };412 _ = try self.addInst(.{
413 .tag = tag,
414 .ops = .r,
415 .data = .{ .r = reg },
416 });
417}
418
419fn asmImmediate(self: *Self, tag: Mir.Inst.Tag, imm: Immediate) !void {
420 // TODO imm64
421 const ops: Mir.Inst.Ops = if (imm == .signed) .imm_s else .imm_u;
426 const data: Mir.Inst.Data = switch (ops) {422 const data: Mir.Inst.Data = switch (ops) {
427 .none => undefined,423 .imm_s => .{ .imm_s = imm.signed },
428 .imm_s => .{ .imm_s = args.op1.imm.signed },424 .imm_u => .{ .imm_u = @intCast(u32, imm.unsigned) },
429 .imm_u => .{ .imm_u = @intCast(u32, args.op1.imm.unsigned) },425 else => unreachable,
430 .r => .{ .r = args.op1.reg },426 };
431 .rr => .{ .rr = .{427 _ = try self.addInst(.{
432 .r1 = args.op1.reg,428 .tag = tag,
433 .r2 = args.op2.reg,429 .ops = ops,
430 .data = data,
431 });
432}
433
434fn asmRegisterRegister(self: *Self, tag: Mir.Inst.Tag, reg1: Register, reg2: Register) !void {
435 _ = try self.addInst(.{
436 .tag = tag,
437 .ops = .rr,
438 .data = .{ .rr = .{
439 .r1 = reg1,
440 .r2 = reg2,
434 } },441 } },
442 });
443}
444
445fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.Tag, reg: Register, imm: Immediate) !void {
446 const ops: Mir.Inst.Ops = if (imm == .signed) .ri_s else .ri_u;
447 const data: Mir.Inst.Data = switch (ops) {
435 .ri_s => .{ .ri_s = .{448 .ri_s => .{ .ri_s = .{
436 .r1 = args.op1.reg,449 .r1 = reg,
437 .imm = args.op2.imm.signed,450 .imm = imm.signed,
438 } },451 } },
439 .ri_u => .{ .ri_u = .{452 .ri_u => .{ .ri_u = .{
440 .r1 = args.op1.reg,453 .r1 = reg,
441 .imm = @intCast(u32, args.op2.imm.unsigned),454 .imm = @intCast(u32, imm.unsigned),
442 } },455 } },
443 else => unreachable,456 else => unreachable,
444 };457 };
...@@ -452,13 +465,8 @@ fn assemble(self: *Self, tag: Mir.Inst.Tag, args: struct {...@@ -452,13 +465,8 @@ fn assemble(self: *Self, tag: Mir.Inst.Tag, args: struct {
452fn gen(self: *Self) InnerError!void {465fn gen(self: *Self) InnerError!void {
453 const cc = self.fn_type.fnCallingConvention();466 const cc = self.fn_type.fnCallingConvention();
454 if (cc != .Naked) {467 if (cc != .Naked) {
455 try self.assemble(.push, .{468 try self.asmRegister(.push, .rbp);
456 .op1 = .{ .reg = .rbp },469 try self.asmRegisterRegister(.mov, .rbp, .rsp);
457 });
458 try self.assemble(.mov, .{
459 .op1 = .{ .reg = .rbp },
460 .op2 = .{ .reg = .rsp },
461 });
462470
463 // We want to subtract the aligned stack frame size from rsp here, but we don't471 // We want to subtract the aligned stack frame size from rsp here, but we don't
464 // yet know how big it will be, so we leave room for a 4-byte stack size.472 // yet know how big it will be, so we leave room for a 4-byte stack size.
...@@ -541,8 +549,8 @@ fn gen(self: *Self) InnerError!void {...@@ -541,8 +549,8 @@ fn gen(self: *Self) InnerError!void {
541 .data = undefined,549 .data = undefined,
542 });550 });
543551
544 try self.assemble(.pop, .{ .op1 = .{ .reg = .rbp } });552 try self.asmRegister(.pop, .rbp);
545 try self.assemble(.ret, .{});553 try self.asmNone(.ret);
546554
547 // Adjust the stack555 // Adjust the stack
548 if (self.max_end_stack > math.maxInt(i32)) {556 if (self.max_end_stack > math.maxInt(i32)) {
...@@ -5313,23 +5321,19 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -5313,23 +5321,19 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
5313 var iter = std.mem.tokenize(u8, asm_source, "\n\r");5321 var iter = std.mem.tokenize(u8, asm_source, "\n\r");
5314 while (iter.next()) |ins| {5322 while (iter.next()) |ins| {
5315 if (mem.eql(u8, ins, "syscall")) {5323 if (mem.eql(u8, ins, "syscall")) {
5316 try self.assemble(.syscall, .{});5324 try self.asmNone(.syscall);
5317 } else if (mem.indexOf(u8, ins, "push")) |_| {5325 } else if (mem.indexOf(u8, ins, "push")) |_| {
5318 const arg = ins[4..];5326 const arg = ins[4..];
5319 if (mem.indexOf(u8, arg, "$")) |l| {5327 if (mem.indexOf(u8, arg, "$")) |l| {
5320 const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch {5328 const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch {
5321 return self.fail("TODO implement more inline asm int parsing", .{});5329 return self.fail("TODO implement more inline asm int parsing", .{});
5322 };5330 };
5323 try self.assemble(.push, .{5331 try self.asmImmediate(.push, Immediate.u(n));
5324 .op1 = .{ .imm = Mir.Operand.Immediate.u(n) },
5325 });
5326 } else if (mem.indexOf(u8, arg, "%%")) |l| {5332 } else if (mem.indexOf(u8, arg, "%%")) |l| {
5327 const reg_name = ins[4 + l + 2 ..];5333 const reg_name = ins[4 + l + 2 ..];
5328 const reg = parseRegName(reg_name) orelse5334 const reg = parseRegName(reg_name) orelse
5329 return self.fail("unrecognized register: '{s}'", .{reg_name});5335 return self.fail("unrecognized register: '{s}'", .{reg_name});
5330 try self.assemble(.push, .{5336 try self.asmRegister(.push, reg);
5331 .op1 = .{ .reg = reg },
5332 });
5333 } else return self.fail("TODO more push operands", .{});5337 } else return self.fail("TODO more push operands", .{});
5334 } else if (mem.indexOf(u8, ins, "pop")) |_| {5338 } else if (mem.indexOf(u8, ins, "pop")) |_| {
5335 const arg = ins[3..];5339 const arg = ins[3..];
...@@ -5337,9 +5341,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {...@@ -5337,9 +5341,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
5337 const reg_name = ins[3 + l + 2 ..];5341 const reg_name = ins[3 + l + 2 ..];
5338 const reg = parseRegName(reg_name) orelse5342 const reg = parseRegName(reg_name) orelse
5339 return self.fail("unrecognized register: '{s}'", .{reg_name});5343 return self.fail("unrecognized register: '{s}'", .{reg_name});
5340 try self.assemble(.pop, .{5344 try self.asmRegister(.pop, reg);
5341 .op1 = .{ .reg = reg },
5342 });
5343 } else return self.fail("TODO more pop operands", .{});5345 } else return self.fail("TODO more pop operands", .{});
5344 } else {5346 } else {
5345 return self.fail("TODO implement support for more x86 assembly instructions", .{});5347 return self.fail("TODO implement support for more x86 assembly instructions", .{});
...@@ -6119,19 +6121,15 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -6119,19 +6121,15 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
6119 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit6121 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
6120 // register is the fastest way to zero a register.6122 // register is the fastest way to zero a register.
6121 if (x == 0) {6123 if (x == 0) {
6122 try self.assemble(.xor, .{6124 return self.asmRegisterRegister(.xor, reg.to32(), reg.to32());
6123 .op1 = .{ .reg = reg.to32() },
6124 .op2 = .{ .reg = reg.to32() },
6125 });
6126 return;
6127 }6125 }
6128 if (x <= math.maxInt(i32)) {6126 if (x <= math.maxInt(i32)) {
6129 // Next best case: if we set the lower four bytes, the upper four will be zeroed.6127 // Next best case: if we set the lower four bytes, the upper four will be zeroed.
6130 try self.assemble(.mov, .{6128 return self.asmRegisterImmediate(
6131 .op1 = .{ .reg = registerAlias(reg, abi_size) },6129 .mov,
6132 .op2 = .{ .imm = Mir.Operand.Immediate.u(@intCast(u32, x)) },6130 registerAlias(reg, abi_size),
6133 });6131 Immediate.u(@intCast(u32, x)),
6134 return;6132 );
6135 }6133 }
6136 // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls6134 // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls
6137 // this `movabs`, though this is officially just a different variant of the plain `mov`6135 // this `movabs`, though this is officially just a different variant of the plain `mov`
src/arch/x86_64/encoder.zig+4-5
...@@ -4,6 +4,8 @@ const math = std.math;...@@ -4,6 +4,8 @@ const math = std.math;
44
5const bits = @import("bits.zig");5const bits = @import("bits.zig");
6const Encoding = @import("Encoding.zig");6const Encoding = @import("Encoding.zig");
7const Immediate = bits.Immediate;
8const Memory = bits.Memory;
7const Register = bits.Register;9const Register = bits.Register;
810
9pub const Instruction = struct {11pub const Instruction = struct {
...@@ -21,9 +23,6 @@ pub const Instruction = struct {...@@ -21,9 +23,6 @@ pub const Instruction = struct {
21 mem: Memory,23 mem: Memory,
22 imm: Immediate,24 imm: Immediate,
2325
24 pub const Memory = bits.Memory;
25 pub const Immediate = bits.Immediate;
26
27 /// Returns the bitsize of the operand.26 /// Returns the bitsize of the operand.
28 pub fn bitSize(op: Operand) u64 {27 pub fn bitSize(op: Operand) u64 {
29 return switch (op) {28 return switch (op) {
...@@ -295,7 +294,7 @@ pub const Instruction = struct {...@@ -295,7 +294,7 @@ pub const Instruction = struct {
295 try encoder.opcode_1byte(prefix);294 try encoder.opcode_1byte(prefix);
296 }295 }
297296
298 fn encodeMemory(encoding: Encoding, mem: Operand.Memory, operand: Operand, encoder: anytype) !void {297 fn encodeMemory(encoding: Encoding, mem: Memory, operand: Operand, encoder: anytype) !void {
299 const operand_enc = switch (operand) {298 const operand_enc = switch (operand) {
300 .reg => |reg| reg.lowEnc(),299 .reg => |reg| reg.lowEnc(),
301 .none => encoding.modRmExt(),300 .none => encoding.modRmExt(),
...@@ -378,7 +377,7 @@ pub const Instruction = struct {...@@ -378,7 +377,7 @@ pub const Instruction = struct {
378 }377 }
379 }378 }
380379
381 fn encodeImm(imm: Operand.Immediate, kind: Encoding.Op, encoder: anytype) !void {380 fn encodeImm(imm: Immediate, kind: Encoding.Op, encoder: anytype) !void {
382 const raw = imm.asUnsigned(kind.bitSize());381 const raw = imm.asUnsigned(kind.bitSize());
383 switch (kind.bitSize()) {382 switch (kind.bitSize()) {
384 8 => try encoder.imm8(@intCast(u8, raw)),383 8 => try encoder.imm8(@intCast(u8, raw)),