| author | |
| committer | |
| log | aca9c74e80e106309b9783ff251ab0cdd3fb9626 |
| tree | 16c65995ac6d3b434af61c4cd61a191b81dd93a0 |
| parent | d93edadead45e447e6bc16c0934a3031a06d0fd8 |
| parent | 9bb1104e373dec192fb2a22d48b023330ddbaeae |
| signature |
implement defining C variadic functions26 files changed, 661 insertions(+), 26 deletions(-)
doc/langref.html.in+53-6| ... | ... | @@ -8088,6 +8088,35 @@ test "main" { |
| 8088 | 8088 | {#see_also|Import from C Header File|@cImport|@cDefine|@cInclude#} |
| 8089 | 8089 | {#header_close#} |
| 8090 | 8090 | |
| 8091 | {#header_open|@cVaArg#} | |
| 8092 | <pre>{#syntax#}@cVaArg(operand: *std.builtin.VaList, comptime T: type) T{#endsyntax#}</pre> | |
| 8093 | <p> | |
| 8094 | Implements the C macro {#syntax#}va_arg{#endsyntax#}. | |
| 8095 | </p> | |
| 8096 | {#see_also|@cVaCopy|@cVaEnd|@cVaStart#} | |
| 8097 | {#header_close#} | |
| 8098 | {#header_open|@cVaCopy#} | |
| 8099 | <pre>{#syntax#}@cVaCopy(src: *std.builtin.VaList) std.builtin.VaList{#endsyntax#}</pre> | |
| 8100 | <p> | |
| 8101 | Implements the C macro {#syntax#}va_copy{#endsyntax#}. | |
| 8102 | </p> | |
| 8103 | {#see_also|@cVaArg|@cVaEnd|@cVaStart#} | |
| 8104 | {#header_close#} | |
| 8105 | {#header_open|@cVaEnd#} | |
| 8106 | <pre>{#syntax#}@cVaEnd(src: *std.builtin.VaList) void{#endsyntax#}</pre> | |
| 8107 | <p> | |
| 8108 | Implements the C macro {#syntax#}va_end{#endsyntax#}. | |
| 8109 | </p> | |
| 8110 | {#see_also|@cVaArg|@cVaCopy|@cVaStart#} | |
| 8111 | {#header_close#} | |
| 8112 | {#header_open|@cVaStart#} | |
| 8113 | <pre>{#syntax#}@cVaStart() std.builtin.VaList{#endsyntax#}</pre> | |
| 8114 | <p> | |
| 8115 | Implements the C macro {#syntax#}va_start{#endsyntax#}. Only valid inside a variadic function. | |
| 8116 | </p> | |
| 8117 | {#see_also|@cVaArg|@cVaCopy|@cVaEnd#} | |
| 8118 | {#header_close#} | |
| 8119 | ||
| 8091 | 8120 | {#header_open|@divExact#} |
| 8092 | 8121 | <pre>{#syntax#}@divExact(numerator: T, denominator: T) T{#endsyntax#}</pre> |
| 8093 | 8122 | <p> |
| ... | ... | @@ -10802,14 +10831,32 @@ test "variadic function" { |
| 10802 | 10831 | } |
| 10803 | 10832 | {#code_end#} |
| 10804 | 10833 | <p> |
| 10805 | Non extern variadic functions are currently not implemented, but there | |
| 10806 | is an accepted proposal. See <a href="https://github.com/ziglang/zig/issues/515">#515</a>. | |
| 10834 | Variadic functions can be implemented using {#link|@cVaStart#}, {#link|@cVaEnd#}, {#link|@cVaArg#} and {#link|@cVaCopy#} | |
| 10807 | 10835 | </p> |
| 10808 | {#code_begin|obj_err|non-extern function is variadic#} | |
| 10809 | export fn printf(format: [*:0]const u8, ...) c_int { | |
| 10810 | _ = format; | |
| 10836 | {#code_begin|test|defining_variadic_function#} | |
| 10837 | const std = @import("std"); | |
| 10838 | const testing = std.testing; | |
| 10839 | const builtin = @import("builtin"); | |
| 10811 | 10840 | |
| 10812 | return 0; | |
| 10841 | fn add(count: c_int, ...) callconv(.C) c_int { | |
| 10842 | var ap = @cVaStart(); | |
| 10843 | defer @cVaEnd(&ap); | |
| 10844 | var i: usize = 0; | |
| 10845 | var sum: c_int = 0; | |
| 10846 | while (i < count) : (i += 1) { | |
| 10847 | sum += @cVaArg(&ap, c_int); | |
| 10848 | } | |
| 10849 | return sum; | |
| 10850 | } | |
| 10851 | ||
| 10852 | test "defining a variadic function" { | |
| 10853 | // Variadic functions are currently disabled on some targets due to miscompilations. | |
| 10854 | if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .windows and builtin.os.tag != .macos) return error.SkipZigTest; | |
| 10855 | if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; | |
| 10856 | ||
| 10857 | try std.testing.expectEqual(@as(c_int, 0), add(0)); | |
| 10858 | try std.testing.expectEqual(@as(c_int, 1), add(1, @as(c_int, 1))); | |
| 10859 | try std.testing.expectEqual(@as(c_int, 3), add(2, @as(c_int, 1), @as(c_int, 2))); | |
| 10813 | 10860 | } |
| 10814 | 10861 | {#code_end#} |
| 10815 | 10862 | {#header_close#} |
lib/std/builtin.zig+81| ... | ... | @@ -620,6 +620,87 @@ pub const CallModifier = enum { |
| 620 | 620 | compile_time, |
| 621 | 621 | }; |
| 622 | 622 | |
| 623 | /// This data structure is used by the Zig language code generation and | |
| 624 | /// therefore must be kept in sync with the compiler implementation. | |
| 625 | pub const VaListAarch64 = extern struct { | |
| 626 | __stack: *anyopaque, | |
| 627 | __gr_top: *anyopaque, | |
| 628 | __vr_top: *anyopaque, | |
| 629 | __gr_offs: c_int, | |
| 630 | __vr_offs: c_int, | |
| 631 | }; | |
| 632 | ||
| 633 | /// This data structure is used by the Zig language code generation and | |
| 634 | /// therefore must be kept in sync with the compiler implementation. | |
| 635 | pub const VaListHexagon = extern struct { | |
| 636 | __gpr: c_long, | |
| 637 | __fpr: c_long, | |
| 638 | __overflow_arg_area: *anyopaque, | |
| 639 | __reg_save_area: *anyopaque, | |
| 640 | }; | |
| 641 | ||
| 642 | /// This data structure is used by the Zig language code generation and | |
| 643 | /// therefore must be kept in sync with the compiler implementation. | |
| 644 | pub const VaListPowerPc = extern struct { | |
| 645 | gpr: u8, | |
| 646 | fpr: u8, | |
| 647 | reserved: c_ushort, | |
| 648 | overflow_arg_area: *anyopaque, | |
| 649 | reg_save_area: *anyopaque, | |
| 650 | }; | |
| 651 | ||
| 652 | /// This data structure is used by the Zig language code generation and | |
| 653 | /// therefore must be kept in sync with the compiler implementation. | |
| 654 | pub const VaListS390x = extern struct { | |
| 655 | __current_saved_reg_area_pointer: *anyopaque, | |
| 656 | __saved_reg_area_end_pointer: *anyopaque, | |
| 657 | __overflow_area_pointer: *anyopaque, | |
| 658 | }; | |
| 659 | ||
| 660 | /// This data structure is used by the Zig language code generation and | |
| 661 | /// therefore must be kept in sync with the compiler implementation. | |
| 662 | pub const VaListX86_64 = extern struct { | |
| 663 | gp_offset: c_uint, | |
| 664 | fp_offset: c_uint, | |
| 665 | overflow_arg_area: *anyopaque, | |
| 666 | reg_save_area: *anyopaque, | |
| 667 | }; | |
| 668 | ||
| 669 | /// This data structure is used by the Zig language code generation and | |
| 670 | /// therefore must be kept in sync with the compiler implementation. | |
| 671 | pub const VaList = switch (builtin.cpu.arch) { | |
| 672 | .aarch64 => switch (builtin.os.tag) { | |
| 673 | .windows => *u8, | |
| 674 | .ios, .macos, .tvos, .watchos => *u8, | |
| 675 | else => @compileError("disabled due to miscompilations"), // VaListAarch64, | |
| 676 | }, | |
| 677 | .arm => switch (builtin.os.tag) { | |
| 678 | .ios, .macos, .tvos, .watchos => *u8, | |
| 679 | else => *anyopaque, | |
| 680 | }, | |
| 681 | .amdgcn => *u8, | |
| 682 | .avr => *anyopaque, | |
| 683 | .bpfel, .bpfeb => *anyopaque, | |
| 684 | .hexagon => if (builtin.target.isMusl()) VaListHexagon else *u8, | |
| 685 | .mips, .mipsel, .mips64, .mips64el => *anyopaque, | |
| 686 | .riscv32, .riscv64 => *anyopaque, | |
| 687 | .powerpc, .powerpcle => switch (builtin.os.tag) { | |
| 688 | .ios, .macos, .tvos, .watchos, .aix => *u8, | |
| 689 | else => VaListPowerPc, | |
| 690 | }, | |
| 691 | .powerpc64, .powerpc64le => *u8, | |
| 692 | .sparc, .sparcel, .sparc64 => *anyopaque, | |
| 693 | .spirv32, .spirv64 => *anyopaque, | |
| 694 | .s390x => VaListS390x, | |
| 695 | .wasm32, .wasm64 => *anyopaque, | |
| 696 | .x86 => *u8, | |
| 697 | .x86_64 => switch (builtin.os.tag) { | |
| 698 | .windows => @compileError("disabled due to miscompilations"), // *u8, | |
| 699 | else => VaListX86_64, | |
| 700 | }, | |
| 701 | else => @compileError("VaList not supported for this target yet"), | |
| 702 | }; | |
| 703 | ||
| 623 | 704 | /// This data structure is used by the Zig language code generation and |
| 624 | 705 | /// therefore must be kept in sync with the compiler implementation. |
| 625 | 706 | pub const PrefetchOptions = struct { |
src/Air.zig+17| ... | ... | @@ -741,6 +741,19 @@ pub const Inst = struct { |
| 741 | 741 | /// Uses the `vector_store_elem` field. |
| 742 | 742 | vector_store_elem, |
| 743 | 743 | |
| 744 | /// Implements @cVaArg builtin. | |
| 745 | /// Uses the `ty_op` field. | |
| 746 | c_va_arg, | |
| 747 | /// Implements @cVaCopy builtin. | |
| 748 | /// Uses the `ty_op` field. | |
| 749 | c_va_copy, | |
| 750 | /// Implements @cVaEnd builtin. | |
| 751 | /// Uses the `un_op` field. | |
| 752 | c_va_end, | |
| 753 | /// Implements @cVaStart builtin. | |
| 754 | /// Uses the `ty` field. | |
| 755 | c_va_start, | |
| 756 | ||
| 744 | 757 | pub fn fromCmpOp(op: std.math.CompareOperator, optimized: bool) Tag { |
| 745 | 758 | switch (op) { |
| 746 | 759 | .lt => return if (optimized) .cmp_lt_optimized else .cmp_lt, |
| ... | ... | @@ -1092,6 +1105,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 1092 | 1105 | .ret_ptr, |
| 1093 | 1106 | .arg, |
| 1094 | 1107 | .err_return_trace, |
| 1108 | .c_va_start, | |
| 1095 | 1109 | => return datas[inst].ty, |
| 1096 | 1110 | |
| 1097 | 1111 | .assembly, |
| ... | ... | @@ -1156,6 +1170,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 1156 | 1170 | .byte_swap, |
| 1157 | 1171 | .bit_reverse, |
| 1158 | 1172 | .addrspace_cast, |
| 1173 | .c_va_arg, | |
| 1174 | .c_va_copy, | |
| 1159 | 1175 | => return air.getRefType(datas[inst].ty_op.ty), |
| 1160 | 1176 | |
| 1161 | 1177 | .loop, |
| ... | ... | @@ -1187,6 +1203,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 1187 | 1203 | .prefetch, |
| 1188 | 1204 | .set_err_return_trace, |
| 1189 | 1205 | .vector_store_elem, |
| 1206 | .c_va_end, | |
| 1190 | 1207 | => return Type.void, |
| 1191 | 1208 | |
| 1192 | 1209 | .ptrtoint, |
src/AstGen.zig+47-6| ... | ... | @@ -42,6 +42,7 @@ string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.d |
| 42 | 42 | compile_errors: ArrayListUnmanaged(Zir.Inst.CompileErrors.Item) = .{}, |
| 43 | 43 | /// The topmost block of the current function. |
| 44 | 44 | fn_block: ?*GenZir = null, |
| 45 | fn_var_args: bool = false, | |
| 45 | 46 | /// Maps string table indexes to the first `@import` ZIR instruction |
| 46 | 47 | /// that uses this string as the operand. |
| 47 | 48 | imports: std.AutoArrayHashMapUnmanaged(u32, Ast.TokenIndex) = .{}, |
| ... | ... | @@ -3892,10 +3893,6 @@ fn fnDecl( |
| 3892 | 3893 | .noalias_bits = noalias_bits, |
| 3893 | 3894 | }); |
| 3894 | 3895 | } else func: { |
| 3895 | if (is_var_args) { | |
| 3896 | return astgen.failTok(fn_proto.ast.fn_token, "non-extern function is variadic", .{}); | |
| 3897 | } | |
| 3898 | ||
| 3899 | 3896 | // as a scope, fn_gz encloses ret_gz, but for instruction list, fn_gz stacks on ret_gz |
| 3900 | 3897 | fn_gz.instructions_top = ret_gz.instructions.items.len; |
| 3901 | 3898 | |
| ... | ... | @@ -3903,6 +3900,10 @@ fn fnDecl( |
| 3903 | 3900 | astgen.fn_block = &fn_gz; |
| 3904 | 3901 | defer astgen.fn_block = prev_fn_block; |
| 3905 | 3902 | |
| 3903 | const prev_var_args = astgen.fn_var_args; | |
| 3904 | astgen.fn_var_args = is_var_args; | |
| 3905 | defer astgen.fn_var_args = prev_var_args; | |
| 3906 | ||
| 3906 | 3907 | astgen.advanceSourceCursorToNode(body_node); |
| 3907 | 3908 | const lbrace_line = astgen.source_line - decl_gz.decl_line; |
| 3908 | 3909 | const lbrace_column = astgen.source_column; |
| ... | ... | @@ -6071,7 +6072,7 @@ fn whileExpr( |
| 6071 | 6072 | const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err; |
| 6072 | 6073 | break :c .{ |
| 6073 | 6074 | .inst = err_union, |
| 6074 | .bool_bit = try cond_scope.addUnNode(tag, err_union, while_full.ast.then_expr), | |
| 6075 | .bool_bit = try cond_scope.addUnNode(tag, err_union, while_full.ast.cond_expr), | |
| 6075 | 6076 | }; |
| 6076 | 6077 | } else if (while_full.payload_token) |_| { |
| 6077 | 6078 | const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none }; |
| ... | ... | @@ -6079,7 +6080,7 @@ fn whileExpr( |
| 6079 | 6080 | const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null; |
| 6080 | 6081 | break :c .{ |
| 6081 | 6082 | .inst = optional, |
| 6082 | .bool_bit = try cond_scope.addUnNode(tag, optional, while_full.ast.then_expr), | |
| 6083 | .bool_bit = try cond_scope.addUnNode(tag, optional, while_full.ast.cond_expr), | |
| 6083 | 6084 | }; |
| 6084 | 6085 | } else { |
| 6085 | 6086 | const cond = try expr(&cond_scope, &cond_scope.base, bool_ri, while_full.ast.cond_expr); |
| ... | ... | @@ -8384,6 +8385,46 @@ fn builtinCall( |
| 8384 | 8385 | }); |
| 8385 | 8386 | return rvalue(gz, ri, result, node); |
| 8386 | 8387 | }, |
| 8388 | .c_va_arg => { | |
| 8389 | if (astgen.fn_block == null) { | |
| 8390 | return astgen.failNode(node, "'@cVaArg' outside function scope", .{}); | |
| 8391 | } | |
| 8392 | const result = try gz.addExtendedPayload(.c_va_arg, Zir.Inst.BinNode{ | |
| 8393 | .node = gz.nodeIndexToRelative(node), | |
| 8394 | .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]), | |
| 8395 | .rhs = try typeExpr(gz, scope, params[1]), | |
| 8396 | }); | |
| 8397 | return rvalue(gz, ri, result, node); | |
| 8398 | }, | |
| 8399 | .c_va_copy => { | |
| 8400 | if (astgen.fn_block == null) { | |
| 8401 | return astgen.failNode(node, "'@cVaCopy' outside function scope", .{}); | |
| 8402 | } | |
| 8403 | const result = try gz.addExtendedPayload(.c_va_copy, Zir.Inst.UnNode{ | |
| 8404 | .node = gz.nodeIndexToRelative(node), | |
| 8405 | .operand = try expr(gz, scope, .{ .rl = .none }, params[0]), | |
| 8406 | }); | |
| 8407 | return rvalue(gz, ri, result, node); | |
| 8408 | }, | |
| 8409 | .c_va_end => { | |
| 8410 | if (astgen.fn_block == null) { | |
| 8411 | return astgen.failNode(node, "'@cVaEnd' outside function scope", .{}); | |
| 8412 | } | |
| 8413 | const result = try gz.addExtendedPayload(.c_va_end, Zir.Inst.UnNode{ | |
| 8414 | .node = gz.nodeIndexToRelative(node), | |
| 8415 | .operand = try expr(gz, scope, .{ .rl = .none }, params[0]), | |
| 8416 | }); | |
| 8417 | return rvalue(gz, ri, result, node); | |
| 8418 | }, | |
| 8419 | .c_va_start => { | |
| 8420 | if (astgen.fn_block == null) { | |
| 8421 | return astgen.failNode(node, "'@cVaStart' outside function scope", .{}); | |
| 8422 | } | |
| 8423 | if (!astgen.fn_var_args) { | |
| 8424 | return astgen.failNode(node, "'@cVaStart' in a non-variadic function", .{}); | |
| 8425 | } | |
| 8426 | return rvalue(gz, ri, try gz.addNodeExtended(.c_va_start, node), node); | |
| 8427 | }, | |
| 8387 | 8428 | } |
| 8388 | 8429 | } |
| 8389 | 8430 |
src/BuiltinFn.zig+28| ... | ... | @@ -30,6 +30,10 @@ pub const Tag = enum { |
| 30 | 30 | compile_log, |
| 31 | 31 | ctz, |
| 32 | 32 | c_undef, |
| 33 | c_va_arg, | |
| 34 | c_va_copy, | |
| 35 | c_va_end, | |
| 36 | c_va_start, | |
| 33 | 37 | div_exact, |
| 34 | 38 | div_floor, |
| 35 | 39 | div_trunc, |
| ... | ... | @@ -354,6 +358,30 @@ pub const list = list: { |
| 354 | 358 | .param_count = 1, |
| 355 | 359 | }, |
| 356 | 360 | }, |
| 361 | .{ | |
| 362 | "@cVaArg", .{ | |
| 363 | .tag = .c_va_arg, | |
| 364 | .param_count = 2, | |
| 365 | }, | |
| 366 | }, | |
| 367 | .{ | |
| 368 | "@cVaCopy", .{ | |
| 369 | .tag = .c_va_copy, | |
| 370 | .param_count = 1, | |
| 371 | }, | |
| 372 | }, | |
| 373 | .{ | |
| 374 | "@cVaEnd", .{ | |
| 375 | .tag = .c_va_end, | |
| 376 | .param_count = 1, | |
| 377 | }, | |
| 378 | }, | |
| 379 | .{ | |
| 380 | "@cVaStart", .{ | |
| 381 | .tag = .c_va_start, | |
| 382 | .param_count = 0, | |
| 383 | }, | |
| 384 | }, | |
| 357 | 385 | .{ |
| 358 | 386 | "@divExact", |
| 359 | 387 | .{ |
src/Liveness.zig+8| ... | ... | @@ -238,6 +238,7 @@ pub fn categorizeOperand( |
| 238 | 238 | .wasm_memory_size, |
| 239 | 239 | .err_return_trace, |
| 240 | 240 | .save_err_return_trace_index, |
| 241 | .c_va_start, | |
| 241 | 242 | => return .none, |
| 242 | 243 | |
| 243 | 244 | .fence => return .write, |
| ... | ... | @@ -279,6 +280,8 @@ pub fn categorizeOperand( |
| 279 | 280 | .splat, |
| 280 | 281 | .error_set_has_value, |
| 281 | 282 | .addrspace_cast, |
| 283 | .c_va_arg, | |
| 284 | .c_va_copy, | |
| 282 | 285 | => { |
| 283 | 286 | const o = air_datas[inst].ty_op; |
| 284 | 287 | if (o.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); |
| ... | ... | @@ -322,6 +325,7 @@ pub fn categorizeOperand( |
| 322 | 325 | .trunc_float, |
| 323 | 326 | .neg, |
| 324 | 327 | .cmp_lt_errors_len, |
| 328 | .c_va_end, | |
| 325 | 329 | => { |
| 326 | 330 | const o = air_datas[inst].un_op; |
| 327 | 331 | if (o == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none); |
| ... | ... | @@ -857,6 +861,7 @@ fn analyzeInst( |
| 857 | 861 | .wasm_memory_size, |
| 858 | 862 | .err_return_trace, |
| 859 | 863 | .save_err_return_trace_index, |
| 864 | .c_va_start, | |
| 860 | 865 | => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }), |
| 861 | 866 | |
| 862 | 867 | .not, |
| ... | ... | @@ -898,6 +903,8 @@ fn analyzeInst( |
| 898 | 903 | .splat, |
| 899 | 904 | .error_set_has_value, |
| 900 | 905 | .addrspace_cast, |
| 906 | .c_va_arg, | |
| 907 | .c_va_copy, | |
| 901 | 908 | => { |
| 902 | 909 | const o = inst_datas[inst].ty_op; |
| 903 | 910 | return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none }); |
| ... | ... | @@ -936,6 +943,7 @@ fn analyzeInst( |
| 936 | 943 | .neg_optimized, |
| 937 | 944 | .cmp_lt_errors_len, |
| 938 | 945 | .set_err_return_trace, |
| 946 | .c_va_end, | |
| 939 | 947 | => { |
| 940 | 948 | const operand = inst_datas[inst].un_op; |
| 941 | 949 | return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none }); |
src/Sema.zig+119-1| ... | ... | @@ -1148,6 +1148,10 @@ fn analyzeBodyInner( |
| 1148 | 1148 | .builtin_async_call => try sema.zirBuiltinAsyncCall( block, extended), |
| 1149 | 1149 | .cmpxchg => try sema.zirCmpxchg( block, extended), |
| 1150 | 1150 | .addrspace_cast => try sema.zirAddrSpaceCast( block, extended), |
| 1151 | .c_va_arg => try sema.zirCVaArg( block, extended), | |
| 1152 | .c_va_copy => try sema.zirCVaCopy( block, extended), | |
| 1153 | .c_va_end => try sema.zirCVaEnd( block, extended), | |
| 1154 | .c_va_start => try sema.zirCVaStart( block, extended), | |
| 1151 | 1155 | // zig fmt: on |
| 1152 | 1156 | |
| 1153 | 1157 | .fence => { |
| ... | ... | @@ -6426,6 +6430,11 @@ fn analyzeCall( |
| 6426 | 6430 | else => unreachable, |
| 6427 | 6431 | }; |
| 6428 | 6432 | if (!is_comptime_call and module_fn.state == .sema_failure) return error.AnalysisFail; |
| 6433 | if (func_ty_info.is_var_args) { | |
| 6434 | return sema.fail(block, call_src, "{s} call of variadic function", .{ | |
| 6435 | @as([]const u8, if (is_comptime_call) "comptime" else "inline"), | |
| 6436 | }); | |
| 6437 | } | |
| 6429 | 6438 | |
| 6430 | 6439 | // Analyze the ZIR. The same ZIR gets analyzed into a runtime function |
| 6431 | 6440 | // or an inlined call depending on what union tag the `label` field is |
| ... | ... | @@ -8407,6 +8416,7 @@ fn funcCommon( |
| 8407 | 8416 | ) CompileError!Air.Inst.Ref { |
| 8408 | 8417 | const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; |
| 8409 | 8418 | const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = src_node_offset }; |
| 8419 | const func_src = LazySrcLoc.nodeOffset(src_node_offset); | |
| 8410 | 8420 | |
| 8411 | 8421 | var is_generic = bare_return_type.tag() == .generic_poison or |
| 8412 | 8422 | alignment == null or |
| ... | ... | @@ -8414,6 +8424,15 @@ fn funcCommon( |
| 8414 | 8424 | section == .generic or |
| 8415 | 8425 | cc == null; |
| 8416 | 8426 | |
| 8427 | if (var_args) { | |
| 8428 | if (is_generic) { | |
| 8429 | return sema.fail(block, func_src, "generic function cannot be variadic", .{}); | |
| 8430 | } | |
| 8431 | if (cc.? != .C) { | |
| 8432 | return sema.fail(block, cc_src, "variadic function must have 'C' calling convention", .{}); | |
| 8433 | } | |
| 8434 | } | |
| 8435 | ||
| 8417 | 8436 | var destroy_fn_on_error = false; |
| 8418 | 8437 | const new_func: *Module.Fn = new_func: { |
| 8419 | 8438 | if (!has_body) break :new_func undefined; |
| ... | ... | @@ -16353,6 +16372,15 @@ fn finishCondBr( |
| 16353 | 16372 | return Air.indexToRef(block_inst); |
| 16354 | 16373 | } |
| 16355 | 16374 | |
| 16375 | fn checkNullableType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { | |
| 16376 | switch (ty.zigTypeTag()) { | |
| 16377 | .Optional, .Null, .Undefined => return, | |
| 16378 | .Pointer => if (ty.isPtrLikeOptional()) return, | |
| 16379 | else => {}, | |
| 16380 | } | |
| 16381 | return sema.failWithExpectedOptionalType(block, src, ty); | |
| 16382 | } | |
| 16383 | ||
| 16356 | 16384 | fn zirIsNonNull( |
| 16357 | 16385 | sema: *Sema, |
| 16358 | 16386 | block: *Block, |
| ... | ... | @@ -16364,6 +16392,7 @@ fn zirIsNonNull( |
| 16364 | 16392 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 16365 | 16393 | const src = inst_data.src(); |
| 16366 | 16394 | const operand = try sema.resolveInst(inst_data.operand); |
| 16395 | try sema.checkNullableType(block, src, sema.typeOf(operand)); | |
| 16367 | 16396 | return sema.analyzeIsNull(block, src, operand, true); |
| 16368 | 16397 | } |
| 16369 | 16398 | |
| ... | ... | @@ -16378,6 +16407,7 @@ fn zirIsNonNullPtr( |
| 16378 | 16407 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 16379 | 16408 | const src = inst_data.src(); |
| 16380 | 16409 | const ptr = try sema.resolveInst(inst_data.operand); |
| 16410 | try sema.checkNullableType(block, src, sema.typeOf(ptr).elemType2()); | |
| 16381 | 16411 | if ((try sema.resolveMaybeUndefVal(ptr)) == null) { |
| 16382 | 16412 | return block.addUnOp(.is_non_null_ptr, ptr); |
| 16383 | 16413 | } |
| ... | ... | @@ -16385,12 +16415,23 @@ fn zirIsNonNullPtr( |
| 16385 | 16415 | return sema.analyzeIsNull(block, src, loaded, true); |
| 16386 | 16416 | } |
| 16387 | 16417 | |
| 16418 | fn checkErrorType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { | |
| 16419 | switch (ty.zigTypeTag()) { | |
| 16420 | .ErrorSet, .ErrorUnion, .Undefined => return, | |
| 16421 | else => return sema.fail(block, src, "expected error union type, found '{}'", .{ | |
| 16422 | ty.fmt(sema.mod), | |
| 16423 | }), | |
| 16424 | } | |
| 16425 | } | |
| 16426 | ||
| 16388 | 16427 | fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 16389 | 16428 | const tracy = trace(@src()); |
| 16390 | 16429 | defer tracy.end(); |
| 16391 | 16430 | |
| 16392 | 16431 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 16432 | const src = inst_data.src(); | |
| 16393 | 16433 | const operand = try sema.resolveInst(inst_data.operand); |
| 16434 | try sema.checkErrorType(block, src, sema.typeOf(operand)); | |
| 16394 | 16435 | return sema.analyzeIsNonErr(block, inst_data.src(), operand); |
| 16395 | 16436 | } |
| 16396 | 16437 | |
| ... | ... | @@ -16401,6 +16442,7 @@ fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 16401 | 16442 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 16402 | 16443 | const src = inst_data.src(); |
| 16403 | 16444 | const ptr = try sema.resolveInst(inst_data.operand); |
| 16445 | try sema.checkErrorType(block, src, sema.typeOf(ptr).elemType2()); | |
| 16404 | 16446 | const loaded = try sema.analyzeLoad(block, src, ptr, src); |
| 16405 | 16447 | return sema.analyzeIsNonErr(block, src, loaded); |
| 16406 | 16448 | } |
| ... | ... | @@ -19020,6 +19062,79 @@ fn zirAddrSpaceCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst |
| 19020 | 19062 | }); |
| 19021 | 19063 | } |
| 19022 | 19064 | |
| 19065 | fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref { | |
| 19066 | const va_list_ty = try sema.getBuiltinType("VaList"); | |
| 19067 | const va_list_ptr = try Type.ptr(sema.arena, sema.mod, .{ | |
| 19068 | .pointee_type = va_list_ty, | |
| 19069 | .mutable = true, | |
| 19070 | .@"addrspace" = .generic, | |
| 19071 | }); | |
| 19072 | ||
| 19073 | const inst = try sema.resolveInst(zir_ref); | |
| 19074 | return sema.coerce(block, va_list_ptr, inst, src); | |
| 19075 | } | |
| 19076 | ||
| 19077 | fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { | |
| 19078 | const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; | |
| 19079 | const src = LazySrcLoc.nodeOffset(extra.node); | |
| 19080 | const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; | |
| 19081 | const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; | |
| 19082 | ||
| 19083 | const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.lhs); | |
| 19084 | const arg_ty = try sema.resolveType(block, ty_src, extra.rhs); | |
| 19085 | ||
| 19086 | if (!try sema.validateExternType(arg_ty, .param_ty)) { | |
| 19087 | const msg = msg: { | |
| 19088 | const msg = try sema.errMsg(block, ty_src, "cannot get '{}' from variadic argument", .{arg_ty.fmt(sema.mod)}); | |
| 19089 | errdefer msg.destroy(sema.gpa); | |
| 19090 | ||
| 19091 | const src_decl = sema.mod.declPtr(block.src_decl); | |
| 19092 | try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl), arg_ty, .param_ty); | |
| 19093 | ||
| 19094 | try sema.addDeclaredHereNote(msg, arg_ty); | |
| 19095 | break :msg msg; | |
| 19096 | }; | |
| 19097 | return sema.failWithOwnedErrorMsg(msg); | |
| 19098 | } | |
| 19099 | ||
| 19100 | try sema.requireRuntimeBlock(block, src, null); | |
| 19101 | return block.addTyOp(.c_va_arg, arg_ty, va_list_ref); | |
| 19102 | } | |
| 19103 | ||
| 19104 | fn zirCVaCopy(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { | |
| 19105 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; | |
| 19106 | const src = LazySrcLoc.nodeOffset(extra.node); | |
| 19107 | const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; | |
| 19108 | ||
| 19109 | const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand); | |
| 19110 | const va_list_ty = try sema.getBuiltinType("VaList"); | |
| 19111 | ||
| 19112 | try sema.requireRuntimeBlock(block, src, null); | |
| 19113 | return block.addTyOp(.c_va_copy, va_list_ty, va_list_ref); | |
| 19114 | } | |
| 19115 | ||
| 19116 | fn zirCVaEnd(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { | |
| 19117 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; | |
| 19118 | const src = LazySrcLoc.nodeOffset(extra.node); | |
| 19119 | const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; | |
| 19120 | ||
| 19121 | const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand); | |
| 19122 | ||
| 19123 | try sema.requireRuntimeBlock(block, src, null); | |
| 19124 | return block.addUnOp(.c_va_end, va_list_ref); | |
| 19125 | } | |
| 19126 | ||
| 19127 | fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { | |
| 19128 | const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand)); | |
| 19129 | ||
| 19130 | const va_list_ty = try sema.getBuiltinType("VaList"); | |
| 19131 | try sema.requireRuntimeBlock(block, src, null); | |
| 19132 | return block.addInst(.{ | |
| 19133 | .tag = .c_va_start, | |
| 19134 | .data = .{ .ty = va_list_ty }, | |
| 19135 | }); | |
| 19136 | } | |
| 19137 | ||
| 19023 | 19138 | fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 19024 | 19139 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 19025 | 19140 | const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| ... | ... | @@ -21524,7 +21639,10 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 21524 | 21639 | else => |e| return e, |
| 21525 | 21640 | }; |
| 21526 | 21641 | break :blk cc_tv.val.toEnum(std.builtin.CallingConvention); |
| 21527 | } else std.builtin.CallingConvention.Unspecified; | |
| 21642 | } else if (sema.owner_decl.is_exported and has_body) | |
| 21643 | .C | |
| 21644 | else | |
| 21645 | .Unspecified; | |
| 21528 | 21646 | |
| 21529 | 21647 | const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: { |
| 21530 | 21648 | const body_len = sema.code.extra[extra_index]; |
src/Zir.zig+12| ... | ... | @@ -1993,6 +1993,18 @@ pub const Inst = struct { |
| 1993 | 1993 | /// Implement the builtin `@addrSpaceCast` |
| 1994 | 1994 | /// `Operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand. |
| 1995 | 1995 | addrspace_cast, |
| 1996 | /// Implement builtin `@cVaArg`. | |
| 1997 | /// `operand` is payload index to `BinNode`. | |
| 1998 | c_va_arg, | |
| 1999 | /// Implement builtin `@cVaStart`. | |
| 2000 | /// `operand` is payload index to `UnNode`. | |
| 2001 | c_va_copy, | |
| 2002 | /// Implement builtin `@cVaStart`. | |
| 2003 | /// `operand` is payload index to `UnNode`. | |
| 2004 | c_va_end, | |
| 2005 | /// Implement builtin `@cVaStart`. | |
| 2006 | /// `operand` is `src_node: i32`. | |
| 2007 | c_va_start, | |
| 1996 | 2008 | |
| 1997 | 2009 | pub const InstData = struct { |
| 1998 | 2010 | opcode: Extended, |
src/arch/aarch64/CodeGen.zig+5| ... | ... | @@ -875,6 +875,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 875 | 875 | .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), |
| 876 | 876 | .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}), |
| 877 | 877 | |
| 878 | .c_va_arg => return self.fail("TODO implement c_va_arg", .{}), | |
| 879 | .c_va_copy => return self.fail("TODO implement c_va_copy", .{}), | |
| 880 | .c_va_end => return self.fail("TODO implement c_va_end", .{}), | |
| 881 | .c_va_start => return self.fail("TODO implement c_va_start", .{}), | |
| 882 | ||
| 878 | 883 | .wasm_memory_size => unreachable, |
| 879 | 884 | .wasm_memory_grow => unreachable, |
| 880 | 885 | // zig fmt: on |
src/arch/arm/CodeGen.zig+5| ... | ... | @@ -785,6 +785,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 785 | 785 | .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), |
| 786 | 786 | .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}), |
| 787 | 787 | |
| 788 | .c_va_arg => return self.fail("TODO implement c_va_arg", .{}), | |
| 789 | .c_va_copy => return self.fail("TODO implement c_va_copy", .{}), | |
| 790 | .c_va_end => return self.fail("TODO implement c_va_end", .{}), | |
| 791 | .c_va_start => return self.fail("TODO implement c_va_start", .{}), | |
| 792 | ||
| 788 | 793 | .wasm_memory_size => unreachable, |
| 789 | 794 | .wasm_memory_grow => unreachable, |
| 790 | 795 | // zig fmt: on |
src/arch/riscv64/CodeGen.zig+5| ... | ... | @@ -699,6 +699,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 699 | 699 | .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), |
| 700 | 700 | .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}), |
| 701 | 701 | |
| 702 | .c_va_arg => return self.fail("TODO implement c_va_arg", .{}), | |
| 703 | .c_va_copy => return self.fail("TODO implement c_va_copy", .{}), | |
| 704 | .c_va_end => return self.fail("TODO implement c_va_end", .{}), | |
| 705 | .c_va_start => return self.fail("TODO implement c_va_start", .{}), | |
| 706 | ||
| 702 | 707 | .wasm_memory_size => unreachable, |
| 703 | 708 | .wasm_memory_grow => unreachable, |
| 704 | 709 | // zig fmt: on |
src/arch/sparc64/CodeGen.zig+5| ... | ... | @@ -716,6 +716,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 716 | 716 | .error_set_has_value => @panic("TODO implement error_set_has_value"), |
| 717 | 717 | .vector_store_elem => @panic("TODO implement vector_store_elem"), |
| 718 | 718 | |
| 719 | .c_va_arg => @panic("TODO implement c_va_arg"), | |
| 720 | .c_va_copy => @panic("TODO implement c_va_copy"), | |
| 721 | .c_va_end => @panic("TODO implement c_va_end"), | |
| 722 | .c_va_start => @panic("TODO implement c_va_start"), | |
| 723 | ||
| 719 | 724 | .wasm_memory_size => unreachable, |
| 720 | 725 | .wasm_memory_grow => unreachable, |
| 721 | 726 | // zig fmt: on |
src/arch/wasm/CodeGen.zig+4| ... | ... | @@ -1972,6 +1972,10 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1972 | 1972 | .error_set_has_value, |
| 1973 | 1973 | .addrspace_cast, |
| 1974 | 1974 | .vector_store_elem, |
| 1975 | .c_va_arg, | |
| 1976 | .c_va_copy, | |
| 1977 | .c_va_end, | |
| 1978 | .c_va_start, | |
| 1975 | 1979 | => |tag| return func.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), |
| 1976 | 1980 | |
| 1977 | 1981 | .add_optimized, |
src/arch/x86_64/CodeGen.zig+5| ... | ... | @@ -787,6 +787,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 787 | 787 | .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), |
| 788 | 788 | .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}), |
| 789 | 789 | |
| 790 | .c_va_arg => return self.fail("TODO implement c_va_arg", .{}), | |
| 791 | .c_va_copy => return self.fail("TODO implement c_va_copy", .{}), | |
| 792 | .c_va_end => return self.fail("TODO implement c_va_end", .{}), | |
| 793 | .c_va_start => return self.fail("TODO implement c_va_start", .{}), | |
| 794 | ||
| 790 | 795 | .wasm_memory_size => unreachable, |
| 791 | 796 | .wasm_memory_grow => unreachable, |
| 792 | 797 | // zig fmt: on |
src/codegen/c.zig+5| ... | ... | @@ -2909,6 +2909,11 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, |
| 2909 | 2909 | .is_named_enum_value => return f.fail("TODO: C backend: implement is_named_enum_value", .{}), |
| 2910 | 2910 | .error_set_has_value => return f.fail("TODO: C backend: implement error_set_has_value", .{}), |
| 2911 | 2911 | .vector_store_elem => return f.fail("TODO: C backend: implement vector_store_elem", .{}), |
| 2912 | ||
| 2913 | .c_va_arg => return f.fail("TODO implement c_va_arg", .{}), | |
| 2914 | .c_va_copy => return f.fail("TODO implement c_va_copy", .{}), | |
| 2915 | .c_va_end => return f.fail("TODO implement c_va_end", .{}), | |
| 2916 | .c_va_start => return f.fail("TODO implement c_va_start", .{}), | |
| 2912 | 2917 | // zig fmt: on |
| 2913 | 2918 | }; |
| 2914 | 2919 | if (result_value == .local) { |
src/codegen/llvm.zig+93| ... | ... | @@ -4699,6 +4699,11 @@ pub const FuncGen = struct { |
| 4699 | 4699 | .dbg_block_end => try self.airDbgBlockEnd(), |
| 4700 | 4700 | .dbg_var_ptr => try self.airDbgVarPtr(inst), |
| 4701 | 4701 | .dbg_var_val => try self.airDbgVarVal(inst), |
| 4702 | ||
| 4703 | .c_va_arg => try self.airCVaArg(inst), | |
| 4704 | .c_va_copy => try self.airCVaCopy(inst), | |
| 4705 | .c_va_end => try self.airCVaEnd(inst), | |
| 4706 | .c_va_start => try self.airCVaStart(inst), | |
| 4702 | 4707 | // zig fmt: on |
| 4703 | 4708 | }; |
| 4704 | 4709 | if (opt_value) |val| { |
| ... | ... | @@ -5136,6 +5141,94 @@ pub const FuncGen = struct { |
| 5136 | 5141 | return null; |
| 5137 | 5142 | } |
| 5138 | 5143 | |
| 5144 | fn airCVaArg(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { | |
| 5145 | if (self.liveness.isUnused(inst)) return null; | |
| 5146 | ||
| 5147 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 5148 | const list = try self.resolveInst(ty_op.operand); | |
| 5149 | const arg_ty = self.air.getRefType(ty_op.ty); | |
| 5150 | const llvm_arg_ty = try self.dg.lowerType(arg_ty); | |
| 5151 | ||
| 5152 | return self.builder.buildVAArg(list, llvm_arg_ty, ""); | |
| 5153 | } | |
| 5154 | ||
| 5155 | fn airCVaCopy(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { | |
| 5156 | if (self.liveness.isUnused(inst)) return null; | |
| 5157 | ||
| 5158 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | |
| 5159 | const src_list = try self.resolveInst(ty_op.operand); | |
| 5160 | const va_list_ty = self.air.getRefType(ty_op.ty); | |
| 5161 | const llvm_va_list_ty = try self.dg.lowerType(va_list_ty); | |
| 5162 | ||
| 5163 | const target = self.dg.module.getTarget(); | |
| 5164 | const result_alignment = va_list_ty.abiAlignment(target); | |
| 5165 | const dest_list = self.buildAlloca(llvm_va_list_ty, result_alignment); | |
| 5166 | ||
| 5167 | const llvm_fn_name = "llvm.va_copy"; | |
| 5168 | const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: { | |
| 5169 | const param_types = [_]*llvm.Type{ | |
| 5170 | self.dg.context.intType(8).pointerType(0), | |
| 5171 | self.dg.context.intType(8).pointerType(0), | |
| 5172 | }; | |
| 5173 | const fn_type = llvm.functionType(self.context.voidType(), &param_types, param_types.len, .False); | |
| 5174 | break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type); | |
| 5175 | }; | |
| 5176 | ||
| 5177 | const args: [2]*llvm.Value = .{ dest_list, src_list }; | |
| 5178 | _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, args.len, .Fast, .Auto, ""); | |
| 5179 | ||
| 5180 | if (isByRef(va_list_ty)) { | |
| 5181 | return dest_list; | |
| 5182 | } else { | |
| 5183 | const loaded = self.builder.buildLoad(llvm_va_list_ty, dest_list, ""); | |
| 5184 | loaded.setAlignment(result_alignment); | |
| 5185 | return loaded; | |
| 5186 | } | |
| 5187 | } | |
| 5188 | ||
| 5189 | fn airCVaEnd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { | |
| 5190 | const un_op = self.air.instructions.items(.data)[inst].un_op; | |
| 5191 | const list = try self.resolveInst(un_op); | |
| 5192 | ||
| 5193 | const llvm_fn_name = "llvm.va_end"; | |
| 5194 | const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: { | |
| 5195 | const param_types = [_]*llvm.Type{self.dg.context.intType(8).pointerType(0)}; | |
| 5196 | const fn_type = llvm.functionType(self.context.voidType(), &param_types, param_types.len, .False); | |
| 5197 | break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type); | |
| 5198 | }; | |
| 5199 | const args: [1]*llvm.Value = .{list}; | |
| 5200 | _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, args.len, .Fast, .Auto, ""); | |
| 5201 | return null; | |
| 5202 | } | |
| 5203 | ||
| 5204 | fn airCVaStart(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { | |
| 5205 | if (self.liveness.isUnused(inst)) return null; | |
| 5206 | ||
| 5207 | const va_list_ty = self.air.typeOfIndex(inst); | |
| 5208 | const llvm_va_list_ty = try self.dg.lowerType(va_list_ty); | |
| 5209 | ||
| 5210 | const target = self.dg.module.getTarget(); | |
| 5211 | const result_alignment = va_list_ty.abiAlignment(target); | |
| 5212 | const list = self.buildAlloca(llvm_va_list_ty, result_alignment); | |
| 5213 | ||
| 5214 | const llvm_fn_name = "llvm.va_start"; | |
| 5215 | const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: { | |
| 5216 | const param_types = [_]*llvm.Type{self.dg.context.intType(8).pointerType(0)}; | |
| 5217 | const fn_type = llvm.functionType(self.context.voidType(), &param_types, param_types.len, .False); | |
| 5218 | break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type); | |
| 5219 | }; | |
| 5220 | const args: [1]*llvm.Value = .{list}; | |
| 5221 | _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, args.len, .Fast, .Auto, ""); | |
| 5222 | ||
| 5223 | if (isByRef(va_list_ty)) { | |
| 5224 | return list; | |
| 5225 | } else { | |
| 5226 | const loaded = self.builder.buildLoad(llvm_va_list_ty, list, ""); | |
| 5227 | loaded.setAlignment(result_alignment); | |
| 5228 | return loaded; | |
| 5229 | } | |
| 5230 | } | |
| 5231 | ||
| 5139 | 5232 | fn airCmp(self: *FuncGen, inst: Air.Inst.Index, op: math.CompareOperator, want_fast_math: bool) !?*llvm.Value { |
| 5140 | 5233 | if (self.liveness.isUnused(inst)) return null; |
| 5141 | 5234 | self.builder.setFastMath(want_fast_math); |
src/codegen/llvm/bindings.zig+3| ... | ... | @@ -965,6 +965,9 @@ pub const Builder = opaque { |
| 965 | 965 | |
| 966 | 966 | pub const buildAllocaInAddressSpace = ZigLLVMBuildAllocaInAddressSpace; |
| 967 | 967 | extern fn ZigLLVMBuildAllocaInAddressSpace(B: *Builder, Ty: *Type, AddressSpace: c_uint, Name: [*:0]const u8) *Value; |
| 968 | ||
| 969 | pub const buildVAArg = LLVMBuildVAArg; | |
| 970 | extern fn LLVMBuildVAArg(*Builder, List: *Value, Ty: *Type, Name: [*:0]const u8) *Value; | |
| 968 | 971 | }; |
| 969 | 972 | |
| 970 | 973 | pub const MDString = opaque { |
src/link/MachO/load_commands.zig+1-1| ... | ... | @@ -36,7 +36,7 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx |
| 36 | 36 | // LC_DYLD_INFO_ONLY |
| 37 | 37 | sizeofcmds += @sizeOf(macho.dyld_info_command); |
| 38 | 38 | // LC_FUNCTION_STARTS |
| 39 | if (has_text_segment and ctx.wants_function_starts) |_| { | |
| 39 | if (has_text_segment and ctx.wants_function_starts) { | |
| 40 | 40 | sizeofcmds += @sizeOf(macho.linkedit_data_command); |
| 41 | 41 | } |
| 42 | 42 | // LC_DATA_IN_CODE |
src/print_air.zig+4| ... | ... | @@ -191,6 +191,7 @@ const Writer = struct { |
| 191 | 191 | .neg_optimized, |
| 192 | 192 | .cmp_lt_errors_len, |
| 193 | 193 | .set_err_return_trace, |
| 194 | .c_va_end, | |
| 194 | 195 | => try w.writeUnOp(s, inst), |
| 195 | 196 | |
| 196 | 197 | .breakpoint, |
| ... | ... | @@ -205,6 +206,7 @@ const Writer = struct { |
| 205 | 206 | .ret_ptr, |
| 206 | 207 | .arg, |
| 207 | 208 | .err_return_trace, |
| 209 | .c_va_start, | |
| 208 | 210 | => try w.writeTy(s, inst), |
| 209 | 211 | |
| 210 | 212 | .not, |
| ... | ... | @@ -246,6 +248,8 @@ const Writer = struct { |
| 246 | 248 | .bit_reverse, |
| 247 | 249 | .error_set_has_value, |
| 248 | 250 | .addrspace_cast, |
| 251 | .c_va_arg, | |
| 252 | .c_va_copy, | |
| 249 | 253 | => try w.writeTyOp(s, inst), |
| 250 | 254 | |
| 251 | 255 | .block, |
src/print_zir.zig+4| ... | ... | @@ -465,6 +465,7 @@ const Writer = struct { |
| 465 | 465 | .frame, |
| 466 | 466 | .frame_address, |
| 467 | 467 | .breakpoint, |
| 468 | .c_va_start, | |
| 468 | 469 | => try self.writeExtNode(stream, extended), |
| 469 | 470 | |
| 470 | 471 | .builtin_src => { |
| ... | ... | @@ -504,6 +505,8 @@ const Writer = struct { |
| 504 | 505 | .error_to_int, |
| 505 | 506 | .int_to_error, |
| 506 | 507 | .reify, |
| 508 | .c_va_copy, | |
| 509 | .c_va_end, | |
| 507 | 510 | => { |
| 508 | 511 | const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 509 | 512 | const src = LazySrcLoc.nodeOffset(inst_data.node); |
| ... | ... | @@ -518,6 +521,7 @@ const Writer = struct { |
| 518 | 521 | .wasm_memory_grow, |
| 519 | 522 | .prefetch, |
| 520 | 523 | .addrspace_cast, |
| 524 | .c_va_arg, | |
| 521 | 525 | => { |
| 522 | 526 | const inst_data = self.code.extraData(Zir.Inst.BinNode, extended.operand).data; |
| 523 | 527 | const src = LazySrcLoc.nodeOffset(inst_data.node); |
src/type.zig+8-2| ... | ... | @@ -3489,7 +3489,10 @@ pub const Type = extern union { |
| 3489 | 3489 | return AbiSizeAdvanced{ .scalar = 0 }; |
| 3490 | 3490 | } |
| 3491 | 3491 | |
| 3492 | if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 }; | |
| 3492 | if (!(child_type.hasRuntimeBitsAdvanced(false, strat) catch |err| switch (err) { | |
| 3493 | error.NeedLazy => return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(strat.lazy, ty) }, | |
| 3494 | else => |e| return e, | |
| 3495 | })) return AbiSizeAdvanced{ .scalar = 1 }; | |
| 3493 | 3496 | |
| 3494 | 3497 | if (ty.optionalReprIsPayload()) { |
| 3495 | 3498 | return abiSizeAdvanced(child_type, target, strat); |
| ... | ... | @@ -3518,7 +3521,10 @@ pub const Type = extern union { |
| 3518 | 3521 | // in abiAlignmentAdvanced. |
| 3519 | 3522 | const data = ty.castTag(.error_union).?.data; |
| 3520 | 3523 | const code_size = abiSize(Type.anyerror, target); |
| 3521 | if (!data.payload.hasRuntimeBits()) { | |
| 3524 | if (!(data.payload.hasRuntimeBitsAdvanced(false, strat) catch |err| switch (err) { | |
| 3525 | error.NeedLazy => return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(strat.lazy, ty) }, | |
| 3526 | else => |e| return e, | |
| 3527 | })) { | |
| 3522 | 3528 | // Same as anyerror. |
| 3523 | 3529 | return AbiSizeAdvanced{ .scalar = code_size }; |
| 3524 | 3530 | } |
test/behavior/sizeof_and_typeof.zig+5| ... | ... | @@ -288,3 +288,8 @@ test "runtime instructions inside typeof in comptime only scope" { |
| 288 | 288 | try expect(@TypeOf((T{}).b) == i8); |
| 289 | 289 | } |
| 290 | 290 | } |
| 291 | ||
| 292 | test "@sizeOf optional of previously unresolved union" { | |
| 293 | const Node = union { a: usize }; | |
| 294 | try expect(@sizeOf(?Node) == @sizeOf(Node) + @alignOf(Node)); | |
| 295 | } |
test/behavior/var_args.zig+108-1| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | const builtin = @import("builtin"); |
| 2 | const expect = @import("std").testing.expect; | |
| 2 | const std = @import("std"); | |
| 3 | const expect = std.testing.expect; | |
| 3 | 4 | |
| 4 | 5 | fn add(args: anytype) i32 { |
| 5 | 6 | var sum = @as(i32, 0); |
| ... | ... | @@ -91,3 +92,109 @@ test "pass zero length array to var args param" { |
| 91 | 92 | fn doNothingWithFirstArg(args: anytype) void { |
| 92 | 93 | _ = args[0]; |
| 93 | 94 | } |
| 95 | ||
| 96 | test "simple variadic function" { | |
| 97 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 98 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 99 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 100 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 101 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 102 | if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .windows and builtin.os.tag != .macos) return error.SkipZigTest; // TODO | |
| 103 | if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO | |
| 104 | ||
| 105 | const S = struct { | |
| 106 | fn simple(...) callconv(.C) c_int { | |
| 107 | var ap = @cVaStart(); | |
| 108 | defer @cVaEnd(&ap); | |
| 109 | return @cVaArg(&ap, c_int); | |
| 110 | } | |
| 111 | ||
| 112 | fn add(count: c_int, ...) callconv(.C) c_int { | |
| 113 | var ap = @cVaStart(); | |
| 114 | defer @cVaEnd(&ap); | |
| 115 | var i: usize = 0; | |
| 116 | var sum: c_int = 0; | |
| 117 | while (i < count) : (i += 1) { | |
| 118 | sum += @cVaArg(&ap, c_int); | |
| 119 | } | |
| 120 | return sum; | |
| 121 | } | |
| 122 | }; | |
| 123 | ||
| 124 | try std.testing.expectEqual(@as(c_int, 0), S.simple(@as(c_int, 0))); | |
| 125 | try std.testing.expectEqual(@as(c_int, 1024), S.simple(@as(c_int, 1024))); | |
| 126 | try std.testing.expectEqual(@as(c_int, 0), S.add(0)); | |
| 127 | try std.testing.expectEqual(@as(c_int, 1), S.add(1, @as(c_int, 1))); | |
| 128 | try std.testing.expectEqual(@as(c_int, 3), S.add(2, @as(c_int, 1), @as(c_int, 2))); | |
| 129 | } | |
| 130 | ||
| 131 | test "variadic functions" { | |
| 132 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 133 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 134 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 135 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 136 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 137 | if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .windows and builtin.os.tag != .macos) return error.SkipZigTest; // TODO | |
| 138 | if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO | |
| 139 | ||
| 140 | const S = struct { | |
| 141 | fn printf(list_ptr: *std.ArrayList(u8), format: [*:0]const u8, ...) callconv(.C) void { | |
| 142 | var ap = @cVaStart(); | |
| 143 | defer @cVaEnd(&ap); | |
| 144 | vprintf(list_ptr, format, &ap); | |
| 145 | } | |
| 146 | ||
| 147 | fn vprintf( | |
| 148 | list: *std.ArrayList(u8), | |
| 149 | format: [*:0]const u8, | |
| 150 | ap: *std.builtin.VaList, | |
| 151 | ) callconv(.C) void { | |
| 152 | for (std.mem.span(format)) |c| switch (c) { | |
| 153 | 's' => { | |
| 154 | const arg = @cVaArg(ap, [*:0]const u8); | |
| 155 | list.writer().print("{s}", .{arg}) catch return; | |
| 156 | }, | |
| 157 | 'd' => { | |
| 158 | const arg = @cVaArg(ap, c_int); | |
| 159 | list.writer().print("{d}", .{arg}) catch return; | |
| 160 | }, | |
| 161 | else => unreachable, | |
| 162 | }; | |
| 163 | } | |
| 164 | }; | |
| 165 | ||
| 166 | var list = std.ArrayList(u8).init(std.testing.allocator); | |
| 167 | defer list.deinit(); | |
| 168 | S.printf(&list, "dsd", @as(c_int, 1), @as([*:0]const u8, "hello"), @as(c_int, 5)); | |
| 169 | try std.testing.expectEqualStrings("1hello5", list.items); | |
| 170 | } | |
| 171 | ||
| 172 | test "copy VaList" { | |
| 173 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 174 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 175 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 176 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 177 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 178 | if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .windows and builtin.os.tag != .macos) return error.SkipZigTest; // TODO | |
| 179 | if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO | |
| 180 | ||
| 181 | const S = struct { | |
| 182 | fn add(count: c_int, ...) callconv(.C) c_int { | |
| 183 | var ap = @cVaStart(); | |
| 184 | defer @cVaEnd(&ap); | |
| 185 | var copy = @cVaCopy(&ap); | |
| 186 | defer @cVaEnd(&copy); | |
| 187 | var i: usize = 0; | |
| 188 | var sum: c_int = 0; | |
| 189 | while (i < count) : (i += 1) { | |
| 190 | sum += @cVaArg(&ap, c_int); | |
| 191 | sum += @cVaArg(&copy, c_int) * 2; | |
| 192 | } | |
| 193 | return sum; | |
| 194 | } | |
| 195 | }; | |
| 196 | ||
| 197 | try std.testing.expectEqual(@as(c_int, 0), S.add(0)); | |
| 198 | try std.testing.expectEqual(@as(c_int, 3), S.add(1, @as(c_int, 1))); | |
| 199 | try std.testing.expectEqual(@as(c_int, 9), S.add(2, @as(c_int, 1), @as(c_int, 2))); | |
| 200 | } |
test/cases/compile_errors/invalid_capture_type.zig created+24| ... | ... | @@ -0,0 +1,24 @@ |
| 1 | export fn f1() void { | |
| 2 | if (true) |x| { _ = x; } | |
| 3 | } | |
| 4 | export fn f2() void { | |
| 5 | if (@as(usize, 5)) |_| {} | |
| 6 | } | |
| 7 | export fn f3() void { | |
| 8 | if (@as(usize, 5)) |_| {} else |_| {} | |
| 9 | } | |
| 10 | export fn f4() void { | |
| 11 | if (null) |_| {} | |
| 12 | } | |
| 13 | export fn f5() void { | |
| 14 | if (error.Foo) |_| {} else |_| {} | |
| 15 | } | |
| 16 | ||
| 17 | // error | |
| 18 | // backend=stage2 | |
| 19 | // target=native | |
| 20 | // | |
| 21 | // :2:9: error: expected optional type, found 'bool' | |
| 22 | // :5:9: error: expected optional type, found 'usize' | |
| 23 | // :8:9: error: expected error union type, found 'usize' | |
| 24 | // :14:9: error: expected error union type, found 'error{Foo}' |
test/cases/compile_errors/invalid_variadic_function.zig created+12| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | fn foo(...) void {} | |
| 2 | fn bar(a: anytype, ...) callconv(a) void {} | |
| 3 | ||
| 4 | comptime { _ = foo; } | |
| 5 | comptime { _ = bar; } | |
| 6 | ||
| 7 | // error | |
| 8 | // backend=stage2 | |
| 9 | // target=native | |
| 10 | // | |
| 11 | // :1:1: error: variadic function must have 'C' calling convention | |
| 12 | // :2:1: error: generic function cannot be variadic |
test/cases/compile_errors/stage1/obj/invalid_maybe_type.zig deleted-9| ... | ... | @@ -1,9 +0,0 @@ |
| 1 | export fn f() void { | |
| 2 | if (true) |x| { _ = x; } | |
| 3 | } | |
| 4 | ||
| 5 | // error | |
| 6 | // backend=stage1 | |
| 7 | // target=native | |
| 8 | // | |
| 9 | // tmp.zig:2:9: error: expected optional type, found 'bool' |