authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-01-21 23:54:14+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-01-22 01:04:20+02:00
logaa626deadddcf26a5789d29d5ba88c978ee53b89
treefc1823f08bc9d99b124993e258e5c0e56a753943
parenta28fbf3132b0b12841ecd70518804123149f3ac0

llvm: implement explicit Win64 and SysV calling conventions


3 files changed, 211 insertions(+), 164 deletions(-)

src/codegen/llvm.zig+183-164
...@@ -10396,12 +10396,7 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool...@@ -10396,12 +10396,7 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool
10396 .mips, .mipsel => return false,10396 .mips, .mipsel => return false,
10397 .x86_64 => switch (target.os.tag) {10397 .x86_64 => switch (target.os.tag) {
10398 .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory,10398 .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory,
10399 else => {10399 else => return firstParamSRetSystemV(fn_info.return_type, target),
10400 const class = x86_64_abi.classifySystemV(fn_info.return_type, target, .ret);
10401 if (class[0] == .memory) return true;
10402 if (class[0] == .x87 and class[2] != .none) return true;
10403 return false;
10404 },
10405 },10400 },
10406 .wasm32 => return wasm_c_abi.classifyType(fn_info.return_type, target)[0] == .indirect,10401 .wasm32 => return wasm_c_abi.classifyType(fn_info.return_type, target)[0] == .indirect,
10407 .aarch64, .aarch64_be => return aarch64_c_abi.classifyType(fn_info.return_type, target) == .memory,10402 .aarch64, .aarch64_be => return aarch64_c_abi.classifyType(fn_info.return_type, target) == .memory,
...@@ -10413,11 +10408,20 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool...@@ -10413,11 +10408,20 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool
10413 .riscv32, .riscv64 => return riscv_c_abi.classifyType(fn_info.return_type, target) == .memory,10408 .riscv32, .riscv64 => return riscv_c_abi.classifyType(fn_info.return_type, target) == .memory,
10414 else => return false, // TODO investigate C ABI for other architectures10409 else => return false, // TODO investigate C ABI for other architectures
10415 },10410 },
10411 .SysV => return firstParamSRetSystemV(fn_info.return_type, target),
10412 .Win64 => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory,
10416 .Stdcall => return !isScalar(fn_info.return_type),10413 .Stdcall => return !isScalar(fn_info.return_type),
10417 else => return false,10414 else => return false,
10418 }10415 }
10419}10416}
1042010417
10418fn firstParamSRetSystemV(ty: Type, target: std.Target) bool {
10419 const class = x86_64_abi.classifySystemV(ty, target, .ret);
10420 if (class[0] == .memory) return true;
10421 if (class[0] == .x87 and class[2] != .none) return true;
10422 return false;
10423}
10424
10421/// In order to support the C calling convention, some return types need to be lowered10425/// In order to support the C calling convention, some return types need to be lowered
10422/// completely differently in the function prototype to honor the C ABI, and then10426/// completely differently in the function prototype to honor the C ABI, and then
10423/// be effectively bitcasted to the actual return type.10427/// be effectively bitcasted to the actual return type.
...@@ -10442,77 +10446,14 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*llvm.Type {...@@ -10442,77 +10446,14 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*llvm.Type {
10442 }10446 }
10443 },10447 },
10444 .C => {10448 .C => {
10445 const is_scalar = isScalar(fn_info.return_type);
10446 switch (target.cpu.arch) {10449 switch (target.cpu.arch) {
10447 .mips, .mipsel => return dg.lowerType(fn_info.return_type),10450 .mips, .mipsel => return dg.lowerType(fn_info.return_type),
10448 .x86_64 => switch (target.os.tag) {10451 .x86_64 => switch (target.os.tag) {
10449 .windows => switch (x86_64_abi.classifyWindows(fn_info.return_type, target)) {10452 .windows => return lowerWin64FnRetTy(dg, fn_info),
10450 .integer => {10453 else => return lowerSystemVFnRetTy(dg, fn_info),
10451 if (is_scalar) {
10452 return dg.lowerType(fn_info.return_type);
10453 } else {
10454 const abi_size = fn_info.return_type.abiSize(target);
10455 return dg.context.intType(@intCast(c_uint, abi_size * 8));
10456 }
10457 },
10458 .win_i128 => return dg.context.intType(64).vectorType(2),
10459 .memory => return dg.context.voidType(),
10460 .sse => return dg.lowerType(fn_info.return_type),
10461 else => unreachable,
10462 },
10463 else => {
10464 if (is_scalar) {
10465 return dg.lowerType(fn_info.return_type);
10466 }
10467 const classes = x86_64_abi.classifySystemV(fn_info.return_type, target, .ret);
10468 if (classes[0] == .memory) {
10469 return dg.context.voidType();
10470 }
10471 var llvm_types_buffer: [8]*llvm.Type = undefined;
10472 var llvm_types_index: u32 = 0;
10473 for (classes) |class| {
10474 switch (class) {
10475 .integer => {
10476 llvm_types_buffer[llvm_types_index] = dg.context.intType(64);
10477 llvm_types_index += 1;
10478 },
10479 .sse, .sseup => {
10480 llvm_types_buffer[llvm_types_index] = dg.context.doubleType();
10481 llvm_types_index += 1;
10482 },
10483 .float => {
10484 llvm_types_buffer[llvm_types_index] = dg.context.floatType();
10485 llvm_types_index += 1;
10486 },
10487 .float_combine => {
10488 llvm_types_buffer[llvm_types_index] = dg.context.floatType().vectorType(2);
10489 llvm_types_index += 1;
10490 },
10491 .x87 => {
10492 if (llvm_types_index != 0 or classes[2] != .none) {
10493 return dg.context.voidType();
10494 }
10495 llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type();
10496 llvm_types_index += 1;
10497 },
10498 .x87up => continue,
10499 .complex_x87 => {
10500 @panic("TODO");
10501 },
10502 .memory => unreachable, // handled above
10503 .win_i128 => unreachable, // windows only
10504 .none => break,
10505 }
10506 }
10507 if (classes[0] == .integer and classes[1] == .none) {
10508 const abi_size = fn_info.return_type.abiSize(target);
10509 return dg.context.intType(@intCast(c_uint, abi_size * 8));
10510 }
10511 return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False);
10512 },
10513 },10454 },
10514 .wasm32 => {10455 .wasm32 => {
10515 if (is_scalar) {10456 if (isScalar(fn_info.return_type)) {
10516 return dg.lowerType(fn_info.return_type);10457 return dg.lowerType(fn_info.return_type);
10517 }10458 }
10518 const classes = wasm_c_abi.classifyType(fn_info.return_type, target);10459 const classes = wasm_c_abi.classifyType(fn_info.return_type, target);
...@@ -10569,6 +10510,8 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*llvm.Type {...@@ -10569,6 +10510,8 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*llvm.Type {
10569 else => return dg.lowerType(fn_info.return_type),10510 else => return dg.lowerType(fn_info.return_type),
10570 }10511 }
10571 },10512 },
10513 .Win64 => return lowerWin64FnRetTy(dg, fn_info),
10514 .SysV => return lowerSystemVFnRetTy(dg, fn_info),
10572 .Stdcall => {10515 .Stdcall => {
10573 if (isScalar(fn_info.return_type)) {10516 if (isScalar(fn_info.return_type)) {
10574 return dg.lowerType(fn_info.return_type);10517 return dg.lowerType(fn_info.return_type);
...@@ -10580,6 +10523,76 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*llvm.Type {...@@ -10580,6 +10523,76 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*llvm.Type {
10580 }10523 }
10581}10524}
1058210525
10526fn lowerWin64FnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*llvm.Type {
10527 const target = dg.module.getTarget();
10528 switch (x86_64_abi.classifyWindows(fn_info.return_type, target)) {
10529 .integer => {
10530 if (isScalar(fn_info.return_type)) {
10531 return dg.lowerType(fn_info.return_type);
10532 } else {
10533 const abi_size = fn_info.return_type.abiSize(target);
10534 return dg.context.intType(@intCast(c_uint, abi_size * 8));
10535 }
10536 },
10537 .win_i128 => return dg.context.intType(64).vectorType(2),
10538 .memory => return dg.context.voidType(),
10539 .sse => return dg.lowerType(fn_info.return_type),
10540 else => unreachable,
10541 }
10542}
10543
10544fn lowerSystemVFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*llvm.Type {
10545 if (isScalar(fn_info.return_type)) {
10546 return dg.lowerType(fn_info.return_type);
10547 }
10548 const target = dg.module.getTarget();
10549 const classes = x86_64_abi.classifySystemV(fn_info.return_type, target, .ret);
10550 if (classes[0] == .memory) {
10551 return dg.context.voidType();
10552 }
10553 var llvm_types_buffer: [8]*llvm.Type = undefined;
10554 var llvm_types_index: u32 = 0;
10555 for (classes) |class| {
10556 switch (class) {
10557 .integer => {
10558 llvm_types_buffer[llvm_types_index] = dg.context.intType(64);
10559 llvm_types_index += 1;
10560 },
10561 .sse, .sseup => {
10562 llvm_types_buffer[llvm_types_index] = dg.context.doubleType();
10563 llvm_types_index += 1;
10564 },
10565 .float => {
10566 llvm_types_buffer[llvm_types_index] = dg.context.floatType();
10567 llvm_types_index += 1;
10568 },
10569 .float_combine => {
10570 llvm_types_buffer[llvm_types_index] = dg.context.floatType().vectorType(2);
10571 llvm_types_index += 1;
10572 },
10573 .x87 => {
10574 if (llvm_types_index != 0 or classes[2] != .none) {
10575 return dg.context.voidType();
10576 }
10577 llvm_types_buffer[llvm_types_index] = dg.context.x86FP80Type();
10578 llvm_types_index += 1;
10579 },
10580 .x87up => continue,
10581 .complex_x87 => {
10582 @panic("TODO");
10583 },
10584 .memory => unreachable, // handled above
10585 .win_i128 => unreachable, // windows only
10586 .none => break,
10587 }
10588 }
10589 if (classes[0] == .integer and classes[1] == .none) {
10590 const abi_size = fn_info.return_type.abiSize(target);
10591 return dg.context.intType(@intCast(c_uint, abi_size * 8));
10592 }
10593 return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False);
10594}
10595
10583const ParamTypeIterator = struct {10596const ParamTypeIterator = struct {
10584 dg: *DeclGen,10597 dg: *DeclGen,
10585 fn_info: Type.Payload.Function.Data,10598 fn_info: Type.Payload.Function.Data,
...@@ -10629,7 +10642,6 @@ const ParamTypeIterator = struct {...@@ -10629,7 +10642,6 @@ const ParamTypeIterator = struct {
10629 it.zig_index += 1;10642 it.zig_index += 1;
10630 return .no_bits;10643 return .no_bits;
10631 }10644 }
10632 const dg = it.dg;
10633 switch (it.fn_info.cc) {10645 switch (it.fn_info.cc) {
10634 .Unspecified, .Inline => {10646 .Unspecified, .Inline => {
10635 it.zig_index += 1;10647 it.zig_index += 1;
...@@ -10648,7 +10660,6 @@ const ParamTypeIterator = struct {...@@ -10648,7 +10660,6 @@ const ParamTypeIterator = struct {
10648 @panic("TODO implement async function lowering in the LLVM backend");10660 @panic("TODO implement async function lowering in the LLVM backend");
10649 },10661 },
10650 .C => {10662 .C => {
10651 const is_scalar = isScalar(ty);
10652 switch (it.target.cpu.arch) {10663 switch (it.target.cpu.arch) {
10653 .mips, .mipsel => {10664 .mips, .mipsel => {
10654 it.zig_index += 1;10665 it.zig_index += 1;
...@@ -10656,99 +10667,13 @@ const ParamTypeIterator = struct {...@@ -10656,99 +10667,13 @@ const ParamTypeIterator = struct {
10656 return .byval;10667 return .byval;
10657 },10668 },
10658 .x86_64 => switch (it.target.os.tag) {10669 .x86_64 => switch (it.target.os.tag) {
10659 .windows => switch (x86_64_abi.classifyWindows(ty, it.target)) {10670 .windows => return it.nextWin64(ty),
10660 .integer => {10671 else => return it.nextSystemV(ty),
10661 if (is_scalar) {
10662 it.zig_index += 1;
10663 it.llvm_index += 1;
10664 return .byval;
10665 } else {
10666 it.zig_index += 1;
10667 it.llvm_index += 1;
10668 return .abi_sized_int;
10669 }
10670 },
10671 .win_i128 => {
10672 it.zig_index += 1;
10673 it.llvm_index += 1;
10674 return .byref;
10675 },
10676 .memory => {
10677 it.zig_index += 1;
10678 it.llvm_index += 1;
10679 return .byref_mut;
10680 },
10681 .sse => {
10682 it.zig_index += 1;
10683 it.llvm_index += 1;
10684 return .byval;
10685 },
10686 else => unreachable,
10687 },
10688 else => {
10689 const classes = x86_64_abi.classifySystemV(ty, it.target, .arg);
10690 if (classes[0] == .memory) {
10691 it.zig_index += 1;
10692 it.llvm_index += 1;
10693 it.byval_attr = true;
10694 return .byref;
10695 }
10696 if (is_scalar) {
10697 it.zig_index += 1;
10698 it.llvm_index += 1;
10699 return .byval;
10700 }
10701 var llvm_types_buffer: [8]*llvm.Type = undefined;
10702 var llvm_types_index: u32 = 0;
10703 for (classes) |class| {
10704 switch (class) {
10705 .integer => {
10706 llvm_types_buffer[llvm_types_index] = dg.context.intType(64);
10707 llvm_types_index += 1;
10708 },
10709 .sse, .sseup => {
10710 llvm_types_buffer[llvm_types_index] = dg.context.doubleType();
10711 llvm_types_index += 1;
10712 },
10713 .float => {
10714 llvm_types_buffer[llvm_types_index] = dg.context.floatType();
10715 llvm_types_index += 1;
10716 },
10717 .float_combine => {
10718 llvm_types_buffer[llvm_types_index] = dg.context.floatType().vectorType(2);
10719 llvm_types_index += 1;
10720 },
10721 .x87 => {
10722 it.zig_index += 1;
10723 it.llvm_index += 1;
10724 it.byval_attr = true;
10725 return .byref;
10726 },
10727 .x87up => unreachable,
10728 .complex_x87 => {
10729 @panic("TODO");
10730 },
10731 .memory => unreachable, // handled above
10732 .win_i128 => unreachable, // windows only
10733 .none => break,
10734 }
10735 }
10736 if (classes[0] == .integer and classes[1] == .none) {
10737 it.zig_index += 1;
10738 it.llvm_index += 1;
10739 return .abi_sized_int;
10740 }
10741 it.llvm_types_buffer = llvm_types_buffer;
10742 it.llvm_types_len = llvm_types_index;
10743 it.llvm_index += llvm_types_index;
10744 it.zig_index += 1;
10745 return .multiple_llvm_types;
10746 },
10747 },10672 },
10748 .wasm32 => {10673 .wasm32 => {
10749 it.zig_index += 1;10674 it.zig_index += 1;
10750 it.llvm_index += 1;10675 it.llvm_index += 1;
10751 if (is_scalar) {10676 if (isScalar(ty)) {
10752 return .byval;10677 return .byval;
10753 }10678 }
10754 const classes = wasm_c_abi.classifyType(ty, it.target);10679 const classes = wasm_c_abi.classifyType(ty, it.target);
...@@ -10766,7 +10691,7 @@ const ParamTypeIterator = struct {...@@ -10766,7 +10691,7 @@ const ParamTypeIterator = struct {
10766 .byval => return .byval,10691 .byval => return .byval,
10767 .integer => {10692 .integer => {
10768 it.llvm_types_len = 1;10693 it.llvm_types_len = 1;
10769 it.llvm_types_buffer[0] = dg.context.intType(64);10694 it.llvm_types_buffer[0] = it.dg.context.intType(64);
10770 return .multiple_llvm_types;10695 return .multiple_llvm_types;
10771 },10696 },
10772 .double_integer => return Lowering{ .i64_array = 2 },10697 .double_integer => return Lowering{ .i64_array = 2 },
...@@ -10806,6 +10731,8 @@ const ParamTypeIterator = struct {...@@ -10806,6 +10731,8 @@ const ParamTypeIterator = struct {
10806 },10731 },
10807 }10732 }
10808 },10733 },
10734 .Win64 => return it.nextWin64(ty),
10735 .SysV => return it.nextSystemV(ty),
10809 .Stdcall => {10736 .Stdcall => {
10810 it.zig_index += 1;10737 it.zig_index += 1;
10811 it.llvm_index += 1;10738 it.llvm_index += 1;
...@@ -10824,6 +10751,98 @@ const ParamTypeIterator = struct {...@@ -10824,6 +10751,98 @@ const ParamTypeIterator = struct {
10824 },10751 },
10825 }10752 }
10826 }10753 }
10754
10755 fn nextWin64(it: *ParamTypeIterator, ty: Type) ?Lowering {
10756 switch (x86_64_abi.classifyWindows(ty, it.target)) {
10757 .integer => {
10758 if (isScalar(ty)) {
10759 it.zig_index += 1;
10760 it.llvm_index += 1;
10761 return .byval;
10762 } else {
10763 it.zig_index += 1;
10764 it.llvm_index += 1;
10765 return .abi_sized_int;
10766 }
10767 },
10768 .win_i128 => {
10769 it.zig_index += 1;
10770 it.llvm_index += 1;
10771 return .byref;
10772 },
10773 .memory => {
10774 it.zig_index += 1;
10775 it.llvm_index += 1;
10776 return .byref_mut;
10777 },
10778 .sse => {
10779 it.zig_index += 1;
10780 it.llvm_index += 1;
10781 return .byval;
10782 },
10783 else => unreachable,
10784 }
10785 }
10786
10787 fn nextSystemV(it: *ParamTypeIterator, ty: Type) ?Lowering {
10788 const classes = x86_64_abi.classifySystemV(ty, it.target, .arg);
10789 if (classes[0] == .memory) {
10790 it.zig_index += 1;
10791 it.llvm_index += 1;
10792 it.byval_attr = true;
10793 return .byref;
10794 }
10795 if (isScalar(ty)) {
10796 it.zig_index += 1;
10797 it.llvm_index += 1;
10798 return .byval;
10799 }
10800 var llvm_types_buffer: [8]*llvm.Type = undefined;
10801 var llvm_types_index: u32 = 0;
10802 for (classes) |class| {
10803 switch (class) {
10804 .integer => {
10805 llvm_types_buffer[llvm_types_index] = it.dg.context.intType(64);
10806 llvm_types_index += 1;
10807 },
10808 .sse, .sseup => {
10809 llvm_types_buffer[llvm_types_index] = it.dg.context.doubleType();
10810 llvm_types_index += 1;
10811 },
10812 .float => {
10813 llvm_types_buffer[llvm_types_index] = it.dg.context.floatType();
10814 llvm_types_index += 1;
10815 },
10816 .float_combine => {
10817 llvm_types_buffer[llvm_types_index] = it.dg.context.floatType().vectorType(2);
10818 llvm_types_index += 1;
10819 },
10820 .x87 => {
10821 it.zig_index += 1;
10822 it.llvm_index += 1;
10823 it.byval_attr = true;
10824 return .byref;
10825 },
10826 .x87up => unreachable,
10827 .complex_x87 => {
10828 @panic("TODO");
10829 },
10830 .memory => unreachable, // handled above
10831 .win_i128 => unreachable, // windows only
10832 .none => break,
10833 }
10834 }
10835 if (classes[0] == .integer and classes[1] == .none) {
10836 it.zig_index += 1;
10837 it.llvm_index += 1;
10838 return .abi_sized_int;
10839 }
10840 it.llvm_types_buffer = llvm_types_buffer;
10841 it.llvm_types_len = llvm_types_index;
10842 it.llvm_index += llvm_types_index;
10843 it.zig_index += 1;
10844 return .multiple_llvm_types;
10845 }
10827};10846};
1082810847
10829fn iterateParamTypes(dg: *DeclGen, fn_info: Type.Payload.Function.Data) ParamTypeIterator {10848fn iterateParamTypes(dg: *DeclGen, fn_info: Type.Payload.Function.Data) ParamTypeIterator {
test/c_abi/cfuncs.c+12
...@@ -1015,3 +1015,15 @@ void __attribute__((stdcall)) stdcall_big_union(union BigUnion x) {...@@ -1015,3 +1015,15 @@ void __attribute__((stdcall)) stdcall_big_union(union BigUnion x) {
1015 assert_or_panic(x.a.c == 3);1015 assert_or_panic(x.a.c == 3);
1016 assert_or_panic(x.a.d == 4);1016 assert_or_panic(x.a.d == 4);
1017}1017}
1018
1019#ifdef __x86_64__
1020struct ByRef __attribute__((ms_abi)) c_explict_win64(struct ByRef in) {
1021 in.val = 42;
1022 return in;
1023}
1024
1025struct ByRef __attribute__((sysv_abi)) c_explict_sys_v(struct ByRef in) {
1026 in.val = 42;
1027 return in;
1028}
1029#endif
test/c_abi/main.zig+16
...@@ -1190,3 +1190,19 @@ test "Stdcall ABI big union" {...@@ -1190,3 +1190,19 @@ test "Stdcall ABI big union" {
1190 };1190 };
1191 stdcall_big_union(x);1191 stdcall_big_union(x);
1192}1192}
1193
1194extern fn c_explict_win64(ByRef) callconv(.Win64) ByRef;
1195test "explicit SysV calling convention" {
1196 if (builtin.cpu.arch != .x86_64) return error.SkipZigTest;
1197
1198 const res = c_explict_win64(.{ .val = 1, .arr = undefined });
1199 try expect(res.val == 42);
1200}
1201
1202extern fn c_explict_sys_v(ByRef) callconv(.SysV) ByRef;
1203test "explicit Win64 calling convention" {
1204 if (builtin.cpu.arch != .x86_64) return error.SkipZigTest;
1205
1206 const res = c_explict_sys_v(.{ .val = 1, .arr = undefined });
1207 try expect(res.val == 42);
1208}