authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-10-05 01:58:44-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-10-05 04:38:25-04:00
log54b2d6f072bde7f85c44bbcc4795bd980d79d45e
tree11fb8283d25a1175895233332720cf68504f2544
parentcc6694a323950d53e79afc76ba5a8000386f555b

x86_64: implement C abi for everything else


5 files changed, 151 insertions(+), 86 deletions(-)

src/arch/x86_64/CodeGen.zig+128-83
......@@ -10315,15 +10315,18 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError
1031510315 .off = -dst_reg_off.off,
1031610316 } },
1031710317 }),
10318 .register_pair => |dst_regs| switch (src_mcv) {
10319 .register_pair => |src_regs| for (dst_regs, src_regs) |dst_reg, src_reg|
10320 try self.genSetReg(dst_reg, Type.usize, .{ .register = src_reg }),
10321 else => for (dst_regs, 0..) |dst_reg, dst_reg_i| try self.genSetReg(
10318 .register_pair => |dst_regs| for (dst_regs, 0..) |dst_reg, dst_reg_i| switch (src_mcv) {
10319 .register_pair => |src_regs| try self.genSetReg(
10320 dst_reg,
10321 Type.usize,
10322 .{ .register = src_regs[dst_reg_i] },
10323 ),
10324 else => try self.genSetReg(
1032210325 dst_reg,
1032310326 Type.usize,
1032410327 src_mcv.address().offset(@intCast(dst_reg_i * 8)).deref(),
1032510328 ),
10326 .air_ref => |src_ref| try self.genCopy(ty, dst_mcv, try self.resolveInst(src_ref)),
10329 .air_ref => |src_ref| return self.genCopy(ty, dst_mcv, try self.resolveInst(src_ref)),
1032710330 },
1032810331 .indirect => |reg_off| try self.genSetMem(.{ .reg = reg_off.reg }, reg_off.off, ty, src_mcv),
1032910332 .memory, .load_direct, .load_got, .load_tlv => {
......@@ -12536,7 +12539,9 @@ fn resolveCallingConventionValues(
1253612539 result.stack_align = .@"8";
1253712540 },
1253812541 .C, .SysV, .Win64 => {
12539 var param_reg_i: usize = 0;
12542 var ret_int_reg_i: usize = 0;
12543 var ret_sse_reg_i: usize = 0;
12544 var param_int_reg_i: usize = 0;
1254012545 var param_sse_reg_i: usize = 0;
1254112546 result.stack_align = .@"16";
1254212547
......@@ -12556,97 +12561,134 @@ fn resolveCallingConventionValues(
1255612561 // TODO: is this even possible for C calling convention?
1255712562 result.return_value = InstTracking.init(.none);
1255812563 } else {
12564 var ret_tracking: [2]InstTracking = undefined;
12565 var ret_tracking_i: usize = 0;
12566
1255912567 const classes = switch (resolved_cc) {
1256012568 .SysV => mem.sliceTo(&abi.classifySystemV(ret_ty, mod, .ret), .none),
1256112569 .Win64 => &[1]abi.Class{abi.classifyWindows(ret_ty, mod)},
1256212570 else => unreachable,
1256312571 };
12564 for (
12565 classes,
12566 abi.getCAbiIntReturnRegs(resolved_cc)[0..classes.len],
12567 0..,
12568 ) |class, ret_reg, ret_reg_i| {
12569 result.return_value = switch (class) {
12570 .integer => switch (ret_reg_i) {
12571 0 => InstTracking.init(.{ .register = registerAlias(
12572 ret_reg,
12573 @intCast(@min(ret_ty.abiSize(mod), 8)),
12574 ) }),
12575 1 => InstTracking.init(.{ .register_pair = .{
12576 result.return_value.short.register,
12577 registerAlias(ret_reg, @intCast(ret_ty.abiSize(mod) - 8)),
12578 } }),
12579 else => return self.fail("TODO handle multiple classes per type", .{}),
12580 },
12581 .float, .sse => switch (ret_reg_i) {
12582 0 => InstTracking.init(.{ .register = .xmm0 }),
12583 else => return self.fail("TODO handle multiple classes per type", .{}),
12584 },
12585 .sseup => continue,
12586 .memory => switch (ret_reg_i) {
12587 0 => ret: {
12588 const ret_indirect_reg =
12589 abi.getCAbiIntParamRegs(resolved_cc)[param_reg_i];
12590 param_reg_i += 1;
12591 break :ret .{
12592 .short = .{ .indirect = .{ .reg = ret_reg } },
12593 .long = .{ .indirect = .{ .reg = ret_indirect_reg } },
12594 };
12595 },
12596 else => return self.fail("TODO handle multiple classes per type", .{}),
12597 },
12598 else => return self.fail("TODO handle calling convention class {s}", .{
12599 @tagName(class),
12600 }),
12601 };
12602 }
12572 for (classes) |class| switch (class) {
12573 .integer => {
12574 const ret_int_reg = registerAlias(
12575 abi.getCAbiIntReturnRegs(resolved_cc)[ret_int_reg_i],
12576 @intCast(@min(ret_ty.abiSize(mod), 8)),
12577 );
12578 ret_int_reg_i += 1;
12579
12580 ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = ret_int_reg });
12581 ret_tracking_i += 1;
12582 },
12583 .sse, .float, .float_combine, .win_i128 => {
12584 const ret_sse_reg = registerAlias(
12585 abi.getCAbiSseReturnRegs(resolved_cc)[ret_sse_reg_i],
12586 @intCast(ret_ty.abiSize(mod)),
12587 );
12588 ret_sse_reg_i += 1;
12589
12590 ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = ret_sse_reg });
12591 ret_tracking_i += 1;
12592 },
12593 .sseup => assert(ret_tracking[ret_tracking_i - 1].short.register.class() == .sse),
12594 .x87 => {
12595 ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = .st0 });
12596 ret_tracking_i += 1;
12597 },
12598 .x87up => assert(ret_tracking[ret_tracking_i - 1].short.register.class() == .x87),
12599 .complex_x87 => {
12600 ret_tracking[ret_tracking_i] =
12601 InstTracking.init(.{ .register_pair = .{ .st0, .st1 } });
12602 ret_tracking_i += 1;
12603 },
12604 .memory => {
12605 const ret_int_reg = abi.getCAbiIntReturnRegs(resolved_cc)[ret_int_reg_i].to64();
12606 ret_int_reg_i += 1;
12607 const ret_indirect_reg = abi.getCAbiIntParamRegs(resolved_cc)[param_int_reg_i];
12608 param_int_reg_i += 1;
12609
12610 ret_tracking[ret_tracking_i] = .{
12611 .short = .{ .indirect = .{ .reg = ret_int_reg } },
12612 .long = .{ .indirect = .{ .reg = ret_indirect_reg } },
12613 };
12614 ret_tracking_i += 1;
12615 },
12616 .none => unreachable,
12617 };
12618 result.return_value = switch (ret_tracking_i) {
12619 else => unreachable,
12620 1 => ret_tracking[0],
12621 2 => InstTracking.init(.{ .register_pair = .{
12622 ret_tracking[0].short.register, ret_tracking[1].short.register,
12623 } }),
12624 };
1260312625 }
1260412626
1260512627 // Input params
1260612628 for (param_types, result.args) |ty, *arg| {
1260712629 assert(ty.hasRuntimeBitsIgnoreComptime(mod));
12630 switch (resolved_cc) {
12631 .SysV => {},
12632 .Win64 => {
12633 param_int_reg_i = @max(param_int_reg_i, param_sse_reg_i);
12634 param_sse_reg_i = param_int_reg_i;
12635 },
12636 else => unreachable,
12637 }
12638
12639 var arg_mcv: [2]MCValue = undefined;
12640 var arg_mcv_i: usize = 0;
1260812641
1260912642 const classes = switch (self.target.os.tag) {
1261012643 .windows => &[1]abi.Class{abi.classifyWindows(ty, mod)},
1261112644 else => mem.sliceTo(&abi.classifySystemV(ty, mod, .arg), .none),
1261212645 };
12613 for (classes, 0..) |class, class_i| {
12614 switch (class) {
12615 .integer => if (param_reg_i < abi.getCAbiIntParamRegs(resolved_cc).len) {
12616 const param_reg = abi.getCAbiIntParamRegs(resolved_cc)[param_reg_i];
12617 param_reg_i += 1;
12618
12619 arg.* = switch (class_i) {
12620 0 => .{ .register = param_reg },
12621 1 => .{ .register_pair = .{ arg.register, param_reg } },
12622 else => return self.fail("TODO handle multiple classes per type", .{}),
12623 };
12624 } else break,
12625 .float, .sse => switch (self.target.os.tag) {
12626 .windows => if (param_reg_i < 4) {
12627 if (class_i > 0)
12628 return self.fail("TODO handle multiple classes per type", .{});
12629 arg.* = .{
12630 .register = @enumFromInt(@intFromEnum(Register.xmm0) + param_reg_i),
12631 };
12632 param_reg_i += 1;
12633 } else break,
12634 else => if (param_sse_reg_i < 8) {
12635 if (class_i > 0)
12636 return self.fail("TODO handle multiple classes per type", .{});
12637 arg.* = .{ .register = @enumFromInt(
12638 @intFromEnum(Register.xmm0) + param_sse_reg_i,
12639 ) };
12640 param_sse_reg_i += 1;
12641 } else break,
12642 },
12643 .sseup => {},
12644 .memory => break,
12645 else => return self.fail("TODO handle calling convention class {s}", .{
12646 @tagName(class),
12647 }),
12648 }
12649 } else continue;
12646 for (classes) |class| switch (class) {
12647 .integer => {
12648 const param_int_regs = abi.getCAbiIntParamRegs(resolved_cc);
12649 if (param_int_reg_i >= param_int_regs.len) break;
12650
12651 const param_int_reg = registerAlias(
12652 abi.getCAbiIntParamRegs(resolved_cc)[param_int_reg_i],
12653 @intCast(@min(ty.abiSize(mod), 8)),
12654 );
12655 param_int_reg_i += 1;
12656
12657 arg_mcv[arg_mcv_i] = .{ .register = param_int_reg };
12658 arg_mcv_i += 1;
12659 },
12660 .sse, .float, .float_combine => {
12661 const param_sse_regs = abi.getCAbiSseParamRegs(resolved_cc);
12662 if (param_sse_reg_i >= param_sse_regs.len) break;
12663
12664 const param_sse_reg = registerAlias(
12665 abi.getCAbiSseParamRegs(resolved_cc)[param_sse_reg_i],
12666 @intCast(ty.abiSize(mod)),
12667 );
12668 param_sse_reg_i += 1;
12669
12670 arg_mcv[arg_mcv_i] = .{ .register = param_sse_reg };
12671 arg_mcv_i += 1;
12672 },
12673 .sseup => assert(arg_mcv[arg_mcv_i - 1].register.class() == .sse),
12674 .x87, .x87up, .complex_x87, .memory => break,
12675 .none => unreachable,
12676 .win_i128 => {
12677 const param_int_reg =
12678 abi.getCAbiIntParamRegs(resolved_cc)[param_int_reg_i].to64();
12679 param_int_reg_i += 1;
12680
12681 arg_mcv[arg_mcv_i] = .{ .indirect = .{ .reg = param_int_reg } };
12682 arg_mcv_i += 1;
12683 },
12684 } else {
12685 arg.* = switch (arg_mcv_i) {
12686 else => unreachable,
12687 1 => arg_mcv[0],
12688 2 => .{ .register_pair = .{ arg_mcv[0].register, arg_mcv[1].register } },
12689 };
12690 continue;
12691 }
1265012692
1265112693 const param_size: u31 = @intCast(ty.abiSize(mod));
1265212694 const param_align: u31 = @intCast(ty.abiAlignment(mod).toByteUnitsOptional().?);
......@@ -12746,7 +12788,10 @@ fn registerAlias(reg: Register, size_bytes: u32) Register {
1274612788 reg
1274712789 else
1274812790 unreachable,
12749 .x87 => unreachable,
12791 .x87 => if (size_bytes == 16)
12792 reg
12793 else
12794 unreachable,
1275012795 .mmx => if (size_bytes <= 8)
1275112796 reg
1275212797 else
src/arch/x86_64/abi.zig+20
......@@ -447,7 +447,9 @@ pub const SysV = struct {
447447 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 } ++ sse_avx_regs;
448448
449449 pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
450 pub const c_abi_sse_param_regs = sse_avx_regs[0..8].*;
450451 pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
452 pub const c_abi_sse_return_regs = sse_avx_regs[0..2].*;
451453};
452454
453455pub const Win64 = struct {
......@@ -460,7 +462,9 @@ pub const Win64 = struct {
460462 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .r8, .r9, .r10, .r11 } ++ sse_avx_regs;
461463
462464 pub const c_abi_int_param_regs = [_]Register{ .rcx, .rdx, .r8, .r9 };
465 pub const c_abi_sse_param_regs = sse_avx_regs[0..4].*;
463466 pub const c_abi_int_return_regs = [_]Register{.rax};
467 pub const c_abi_sse_return_regs = sse_avx_regs[0..1].*;
464468};
465469
466470pub fn resolveCallingConvention(
......@@ -500,6 +504,14 @@ pub fn getCAbiIntParamRegs(cc: std.builtin.CallingConvention) []const Register {
500504 };
501505}
502506
507pub fn getCAbiSseParamRegs(cc: std.builtin.CallingConvention) []const Register {
508 return switch (cc) {
509 .SysV => &SysV.c_abi_sse_param_regs,
510 .Win64 => &Win64.c_abi_sse_param_regs,
511 else => unreachable,
512 };
513}
514
503515pub fn getCAbiIntReturnRegs(cc: std.builtin.CallingConvention) []const Register {
504516 return switch (cc) {
505517 .SysV => &SysV.c_abi_int_return_regs,
......@@ -508,6 +520,14 @@ pub fn getCAbiIntReturnRegs(cc: std.builtin.CallingConvention) []const Register
508520 };
509521}
510522
523pub fn getCAbiSseReturnRegs(cc: std.builtin.CallingConvention) []const Register {
524 return switch (cc) {
525 .SysV => &SysV.c_abi_sse_return_regs,
526 .Win64 => &Win64.c_abi_sse_return_regs,
527 else => unreachable,
528 };
529}
530
511531const gp_regs = [_]Register{
512532 .rax, .rcx, .rdx, .rbx, .rsi, .rdi, .r8, .r9, .r10, .r11, .r12, .r13, .r14, .r15,
513533};
test/behavior/bitcast.zig+1-1
......@@ -375,11 +375,11 @@ test "comptime @bitCast packed struct to int and back" {
375375
376376test "comptime bitcast with fields following f80" {
377377 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
378 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
379378 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
380379 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
381380 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
382381 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
382 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
383383
384384 const FloatT = extern struct { f: f80, x: u128 align(16) };
385385 const x: FloatT = .{ .f = 0.5, .x = 123 };
test/behavior/floatop.zig+1-1
......@@ -1379,7 +1379,7 @@ test "comptime fixed-width float zero divided by zero produces NaN" {
13791379 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
13801380 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
13811381 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1382 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1382 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
13831383
13841384 inline for (.{ f16, f32, f64, f80, f128 }) |F| {
13851385 try expect(math.isNan(@as(F, 0) / @as(F, 0)));
test/behavior/math.zig+1-1
......@@ -1538,9 +1538,9 @@ test "NaN comparison" {
15381538test "NaN comparison f80" {
15391539 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
15401540 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1541 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
15421541 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
15431542 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1543 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
15441544
15451545 try testNanEqNan(f80);
15461546 try comptime testNanEqNan(f80);