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...@@ -10315,15 +10315,18 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError
10315 .off = -dst_reg_off.off,10315 .off = -dst_reg_off.off,
10316 } },10316 } },
10317 }),10317 }),
10318 .register_pair => |dst_regs| switch (src_mcv) {10318 .register_pair => |dst_regs| for (dst_regs, 0..) |dst_reg, dst_reg_i| switch (src_mcv) {
10319 .register_pair => |src_regs| for (dst_regs, src_regs) |dst_reg, src_reg|10319 .register_pair => |src_regs| try self.genSetReg(
10320 try self.genSetReg(dst_reg, Type.usize, .{ .register = src_reg }),10320 dst_reg,
10321 else => for (dst_regs, 0..) |dst_reg, dst_reg_i| try self.genSetReg(10321 Type.usize,
10322 .{ .register = src_regs[dst_reg_i] },
10323 ),
10324 else => try self.genSetReg(
10322 dst_reg,10325 dst_reg,
10323 Type.usize,10326 Type.usize,
10324 src_mcv.address().offset(@intCast(dst_reg_i * 8)).deref(),10327 src_mcv.address().offset(@intCast(dst_reg_i * 8)).deref(),
10325 ),10328 ),
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)),
10327 },10330 },
10328 .indirect => |reg_off| try self.genSetMem(.{ .reg = reg_off.reg }, reg_off.off, ty, src_mcv),10331 .indirect => |reg_off| try self.genSetMem(.{ .reg = reg_off.reg }, reg_off.off, ty, src_mcv),
10329 .memory, .load_direct, .load_got, .load_tlv => {10332 .memory, .load_direct, .load_got, .load_tlv => {
...@@ -12536,7 +12539,9 @@ fn resolveCallingConventionValues(...@@ -12536,7 +12539,9 @@ fn resolveCallingConventionValues(
12536 result.stack_align = .@"8";12539 result.stack_align = .@"8";
12537 },12540 },
12538 .C, .SysV, .Win64 => {12541 .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;
12540 var param_sse_reg_i: usize = 0;12545 var param_sse_reg_i: usize = 0;
12541 result.stack_align = .@"16";12546 result.stack_align = .@"16";
1254212547
...@@ -12556,97 +12561,134 @@ fn resolveCallingConventionValues(...@@ -12556,97 +12561,134 @@ fn resolveCallingConventionValues(
12556 // TODO: is this even possible for C calling convention?12561 // TODO: is this even possible for C calling convention?
12557 result.return_value = InstTracking.init(.none);12562 result.return_value = InstTracking.init(.none);
12558 } else {12563 } else {
12564 var ret_tracking: [2]InstTracking = undefined;
12565 var ret_tracking_i: usize = 0;
12566
12559 const classes = switch (resolved_cc) {12567 const classes = switch (resolved_cc) {
12560 .SysV => mem.sliceTo(&abi.classifySystemV(ret_ty, mod, .ret), .none),12568 .SysV => mem.sliceTo(&abi.classifySystemV(ret_ty, mod, .ret), .none),
12561 .Win64 => &[1]abi.Class{abi.classifyWindows(ret_ty, mod)},12569 .Win64 => &[1]abi.Class{abi.classifyWindows(ret_ty, mod)},
12562 else => unreachable,12570 else => unreachable,
12563 };12571 };
12564 for (12572 for (classes) |class| switch (class) {
12565 classes,12573 .integer => {
12566 abi.getCAbiIntReturnRegs(resolved_cc)[0..classes.len],12574 const ret_int_reg = registerAlias(
12567 0..,12575 abi.getCAbiIntReturnRegs(resolved_cc)[ret_int_reg_i],
12568 ) |class, ret_reg, ret_reg_i| {12576 @intCast(@min(ret_ty.abiSize(mod), 8)),
12569 result.return_value = switch (class) {12577 );
12570 .integer => switch (ret_reg_i) {12578 ret_int_reg_i += 1;
12571 0 => InstTracking.init(.{ .register = registerAlias(12579
12572 ret_reg,12580 ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = ret_int_reg });
12573 @intCast(@min(ret_ty.abiSize(mod), 8)),12581 ret_tracking_i += 1;
12574 ) }),12582 },
12575 1 => InstTracking.init(.{ .register_pair = .{12583 .sse, .float, .float_combine, .win_i128 => {
12576 result.return_value.short.register,12584 const ret_sse_reg = registerAlias(
12577 registerAlias(ret_reg, @intCast(ret_ty.abiSize(mod) - 8)),12585 abi.getCAbiSseReturnRegs(resolved_cc)[ret_sse_reg_i],
12578 } }),12586 @intCast(ret_ty.abiSize(mod)),
12579 else => return self.fail("TODO handle multiple classes per type", .{}),12587 );
12580 },12588 ret_sse_reg_i += 1;
12581 .float, .sse => switch (ret_reg_i) {12589
12582 0 => InstTracking.init(.{ .register = .xmm0 }),12590 ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = ret_sse_reg });
12583 else => return self.fail("TODO handle multiple classes per type", .{}),12591 ret_tracking_i += 1;
12584 },12592 },
12585 .sseup => continue,12593 .sseup => assert(ret_tracking[ret_tracking_i - 1].short.register.class() == .sse),
12586 .memory => switch (ret_reg_i) {12594 .x87 => {
12587 0 => ret: {12595 ret_tracking[ret_tracking_i] = InstTracking.init(.{ .register = .st0 });
12588 const ret_indirect_reg =12596 ret_tracking_i += 1;
12589 abi.getCAbiIntParamRegs(resolved_cc)[param_reg_i];12597 },
12590 param_reg_i += 1;12598 .x87up => assert(ret_tracking[ret_tracking_i - 1].short.register.class() == .x87),
12591 break :ret .{12599 .complex_x87 => {
12592 .short = .{ .indirect = .{ .reg = ret_reg } },12600 ret_tracking[ret_tracking_i] =
12593 .long = .{ .indirect = .{ .reg = ret_indirect_reg } },12601 InstTracking.init(.{ .register_pair = .{ .st0, .st1 } });
12594 };12602 ret_tracking_i += 1;
12595 },12603 },
12596 else => return self.fail("TODO handle multiple classes per type", .{}),12604 .memory => {
12597 },12605 const ret_int_reg = abi.getCAbiIntReturnRegs(resolved_cc)[ret_int_reg_i].to64();
12598 else => return self.fail("TODO handle calling convention class {s}", .{12606 ret_int_reg_i += 1;
12599 @tagName(class),12607 const ret_indirect_reg = abi.getCAbiIntParamRegs(resolved_cc)[param_int_reg_i];
12600 }),12608 param_int_reg_i += 1;
12601 };12609
12602 }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 };
12603 }12625 }
1260412626
12605 // Input params12627 // Input params
12606 for (param_types, result.args) |ty, *arg| {12628 for (param_types, result.args) |ty, *arg| {
12607 assert(ty.hasRuntimeBitsIgnoreComptime(mod));12629 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
12609 const classes = switch (self.target.os.tag) {12642 const classes = switch (self.target.os.tag) {
12610 .windows => &[1]abi.Class{abi.classifyWindows(ty, mod)},12643 .windows => &[1]abi.Class{abi.classifyWindows(ty, mod)},
12611 else => mem.sliceTo(&abi.classifySystemV(ty, mod, .arg), .none),12644 else => mem.sliceTo(&abi.classifySystemV(ty, mod, .arg), .none),
12612 };12645 };
12613 for (classes, 0..) |class, class_i| {12646 for (classes) |class| switch (class) {
12614 switch (class) {12647 .integer => {
12615 .integer => if (param_reg_i < abi.getCAbiIntParamRegs(resolved_cc).len) {12648 const param_int_regs = abi.getCAbiIntParamRegs(resolved_cc);
12616 const param_reg = abi.getCAbiIntParamRegs(resolved_cc)[param_reg_i];12649 if (param_int_reg_i >= param_int_regs.len) break;
12617 param_reg_i += 1;12650
1261812651 const param_int_reg = registerAlias(
12619 arg.* = switch (class_i) {12652 abi.getCAbiIntParamRegs(resolved_cc)[param_int_reg_i],
12620 0 => .{ .register = param_reg },12653 @intCast(@min(ty.abiSize(mod), 8)),
12621 1 => .{ .register_pair = .{ arg.register, param_reg } },12654 );
12622 else => return self.fail("TODO handle multiple classes per type", .{}),12655 param_int_reg_i += 1;
12623 };12656
12624 } else break,12657 arg_mcv[arg_mcv_i] = .{ .register = param_int_reg };
12625 .float, .sse => switch (self.target.os.tag) {12658 arg_mcv_i += 1;
12626 .windows => if (param_reg_i < 4) {12659 },
12627 if (class_i > 0)12660 .sse, .float, .float_combine => {
12628 return self.fail("TODO handle multiple classes per type", .{});12661 const param_sse_regs = abi.getCAbiSseParamRegs(resolved_cc);
12629 arg.* = .{12662 if (param_sse_reg_i >= param_sse_regs.len) break;
12630 .register = @enumFromInt(@intFromEnum(Register.xmm0) + param_reg_i),12663
12631 };12664 const param_sse_reg = registerAlias(
12632 param_reg_i += 1;12665 abi.getCAbiSseParamRegs(resolved_cc)[param_sse_reg_i],
12633 } else break,12666 @intCast(ty.abiSize(mod)),
12634 else => if (param_sse_reg_i < 8) {12667 );
12635 if (class_i > 0)12668 param_sse_reg_i += 1;
12636 return self.fail("TODO handle multiple classes per type", .{});12669
12637 arg.* = .{ .register = @enumFromInt(12670 arg_mcv[arg_mcv_i] = .{ .register = param_sse_reg };
12638 @intFromEnum(Register.xmm0) + param_sse_reg_i,12671 arg_mcv_i += 1;
12639 ) };12672 },
12640 param_sse_reg_i += 1;12673 .sseup => assert(arg_mcv[arg_mcv_i - 1].register.class() == .sse),
12641 } else break,12674 .x87, .x87up, .complex_x87, .memory => break,
12642 },12675 .none => unreachable,
12643 .sseup => {},12676 .win_i128 => {
12644 .memory => break,12677 const param_int_reg =
12645 else => return self.fail("TODO handle calling convention class {s}", .{12678 abi.getCAbiIntParamRegs(resolved_cc)[param_int_reg_i].to64();
12646 @tagName(class),12679 param_int_reg_i += 1;
12647 }),12680
12648 }12681 arg_mcv[arg_mcv_i] = .{ .indirect = .{ .reg = param_int_reg } };
12649 } else continue;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
12651 const param_size: u31 = @intCast(ty.abiSize(mod));12693 const param_size: u31 = @intCast(ty.abiSize(mod));
12652 const param_align: u31 = @intCast(ty.abiAlignment(mod).toByteUnitsOptional().?);12694 const param_align: u31 = @intCast(ty.abiAlignment(mod).toByteUnitsOptional().?);
...@@ -12746,7 +12788,10 @@ fn registerAlias(reg: Register, size_bytes: u32) Register {...@@ -12746,7 +12788,10 @@ fn registerAlias(reg: Register, size_bytes: u32) Register {
12746 reg12788 reg
12747 else12789 else
12748 unreachable,12790 unreachable,
12749 .x87 => unreachable,12791 .x87 => if (size_bytes == 16)
12792 reg
12793 else
12794 unreachable,
12750 .mmx => if (size_bytes <= 8)12795 .mmx => if (size_bytes <= 8)
12751 reg12796 reg
12752 else12797 else
src/arch/x86_64/abi.zig+20
...@@ -447,7 +447,9 @@ pub const SysV = struct {...@@ -447,7 +447,9 @@ pub const SysV = struct {
447 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 } ++ sse_avx_regs;447 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 } ++ sse_avx_regs;
448448
449 pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };449 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].*;
450 pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };451 pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
452 pub const c_abi_sse_return_regs = sse_avx_regs[0..2].*;
451};453};
452454
453pub const Win64 = struct {455pub const Win64 = struct {
...@@ -460,7 +462,9 @@ pub const Win64 = struct {...@@ -460,7 +462,9 @@ pub const Win64 = struct {
460 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .r8, .r9, .r10, .r11 } ++ sse_avx_regs;462 pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .r8, .r9, .r10, .r11 } ++ sse_avx_regs;
461463
462 pub const c_abi_int_param_regs = [_]Register{ .rcx, .rdx, .r8, .r9 };464 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].*;
463 pub const c_abi_int_return_regs = [_]Register{.rax};466 pub const c_abi_int_return_regs = [_]Register{.rax};
467 pub const c_abi_sse_return_regs = sse_avx_regs[0..1].*;
464};468};
465469
466pub fn resolveCallingConvention(470pub fn resolveCallingConvention(
...@@ -500,6 +504,14 @@ pub fn getCAbiIntParamRegs(cc: std.builtin.CallingConvention) []const Register {...@@ -500,6 +504,14 @@ pub fn getCAbiIntParamRegs(cc: std.builtin.CallingConvention) []const Register {
500 };504 };
501}505}
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
503pub fn getCAbiIntReturnRegs(cc: std.builtin.CallingConvention) []const Register {515pub fn getCAbiIntReturnRegs(cc: std.builtin.CallingConvention) []const Register {
504 return switch (cc) {516 return switch (cc) {
505 .SysV => &SysV.c_abi_int_return_regs,517 .SysV => &SysV.c_abi_int_return_regs,
...@@ -508,6 +520,14 @@ pub fn getCAbiIntReturnRegs(cc: std.builtin.CallingConvention) []const Register...@@ -508,6 +520,14 @@ pub fn getCAbiIntReturnRegs(cc: std.builtin.CallingConvention) []const Register
508 };520 };
509}521}
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
511const gp_regs = [_]Register{531const gp_regs = [_]Register{
512 .rax, .rcx, .rdx, .rbx, .rsi, .rdi, .r8, .r9, .r10, .r11, .r12, .r13, .r14, .r15,532 .rax, .rcx, .rdx, .rbx, .rsi, .rdi, .r8, .r9, .r10, .r11, .r12, .r13, .r14, .r15,
513};533};
test/behavior/bitcast.zig+1-1
...@@ -375,11 +375,11 @@ test "comptime @bitCast packed struct to int and back" {...@@ -375,11 +375,11 @@ test "comptime @bitCast packed struct to int and back" {
375375
376test "comptime bitcast with fields following f80" {376test "comptime bitcast with fields following f80" {
377 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;377 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
378 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
379 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;378 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
380 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;379 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
381 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO380 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
382 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;381 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
384 const FloatT = extern struct { f: f80, x: u128 align(16) };384 const FloatT = extern struct { f: f80, x: u128 align(16) };
385 const x: FloatT = .{ .f = 0.5, .x = 123 };385 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" {...@@ -1379,7 +1379,7 @@ test "comptime fixed-width float zero divided by zero produces NaN" {
1379 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1379 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1380 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1380 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1381 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;1381 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
1384 inline for (.{ f16, f32, f64, f80, f128 }) |F| {1384 inline for (.{ f16, f32, f64, f80, f128 }) |F| {
1385 try expect(math.isNan(@as(F, 0) / @as(F, 0)));1385 try expect(math.isNan(@as(F, 0) / @as(F, 0)));
test/behavior/math.zig+1-1
...@@ -1538,9 +1538,9 @@ test "NaN comparison" {...@@ -1538,9 +1538,9 @@ test "NaN comparison" {
1538test "NaN comparison f80" {1538test "NaN comparison f80" {
1539 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1539 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1540 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1540 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1541 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1542 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1541 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1543 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;1542 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
1545 try testNanEqNan(f80);1545 try testNanEqNan(f80);
1546 try comptime testNanEqNan(f80);1546 try comptime testNanEqNan(f80);