| author | |
| committer | |
| log | 71b7f4b47f69e9b3241e9d44554572258f5eb5b1 |
| tree | 9e60d95d273bb488d7ffcef8eaa5217c72f32079 |
| parent | 343987cd057c5f2f0aad197518d7d573579d0d08 |
| signature | Commit is signed but in an unrecognized format. |
6 files changed, 66 insertions(+), 56 deletions(-)
doc/langref.html.in+35-42| ... | ... | @@ -6904,6 +6904,41 @@ pub const CallOptions = struct { |
| 6904 | 6904 | }; |
| 6905 | 6905 | }; |
| 6906 | 6906 | {#code_end#} |
| 6907 | ||
| 6908 | {#header_open|Calling with a New Stack#} | |
| 6909 | <p> | |
| 6910 | When the {#syntax#}stack{#endsyntax#} option is provided, instead of using the same stack as the caller, the function uses the provided stack. | |
| 6911 | </p> | |
| 6912 | {#code_begin|test|new_stack_call#} | |
| 6913 | const std = @import("std"); | |
| 6914 | const assert = std.debug.assert; | |
| 6915 | ||
| 6916 | var new_stack_bytes: [1024]u8 align(16) = undefined; | |
| 6917 | ||
| 6918 | test "calling a function with a new stack" { | |
| 6919 | const arg = 1234; | |
| 6920 | ||
| 6921 | const a = @call(.{.stack = new_stack_bytes[0..512]}, targetFunction, .{arg}); | |
| 6922 | const b = @call(.{.stack = new_stack_bytes[512..]}, targetFunction, .{arg}); | |
| 6923 | _ = targetFunction(arg); | |
| 6924 | ||
| 6925 | assert(arg == 1234); | |
| 6926 | assert(a < b); | |
| 6927 | } | |
| 6928 | ||
| 6929 | fn targetFunction(x: i32) usize { | |
| 6930 | assert(x == 1234); | |
| 6931 | ||
| 6932 | var local_variable: i32 = 42; | |
| 6933 | const ptr = &local_variable; | |
| 6934 | ptr.* += 1; | |
| 6935 | ||
| 6936 | assert(local_variable == 43); | |
| 6937 | return @ptrToInt(ptr); | |
| 6938 | } | |
| 6939 | {#code_end#} | |
| 6940 | {#header_close#} | |
| 6941 | ||
| 6907 | 6942 | {#header_close#} |
| 6908 | 6943 | |
| 6909 | 6944 | {#header_open|@cDefine#} |
| ... | ... | @@ -7649,48 +7684,6 @@ mem.set(u8, dest, c);{#endsyntax#}</pre> |
| 7649 | 7684 | </p> |
| 7650 | 7685 | {#header_close#} |
| 7651 | 7686 | |
| 7652 | {#header_open|@newStackCall#} | |
| 7653 | <pre>{#syntax#}@newStackCall(new_stack: []align(target_stack_align) u8, function: var, args: ...) var{#endsyntax#}</pre> | |
| 7654 | <p> | |
| 7655 | This calls a function, in the same way that invoking an expression with parentheses does. However, | |
| 7656 | instead of using the same stack as the caller, the function uses the stack provided in the {#syntax#}new_stack{#endsyntax#} | |
| 7657 | parameter. | |
| 7658 | </p> | |
| 7659 | <p> | |
| 7660 | The new stack must be aligned to {#syntax#}target_stack_align{#endsyntax#} bytes. This is a target-specific | |
| 7661 | number. A safe value that will work on all targets is {#syntax#}16{#endsyntax#}. This value can | |
| 7662 | also be obtained by using {#link|@sizeOf#} on the {#link|@Frame#} type of {#link|Async Functions#}. | |
| 7663 | </p> | |
| 7664 | {#code_begin|test#} | |
| 7665 | const std = @import("std"); | |
| 7666 | const assert = std.debug.assert; | |
| 7667 | ||
| 7668 | var new_stack_bytes: [1024]u8 align(16) = undefined; | |
| 7669 | ||
| 7670 | test "calling a function with a new stack" { | |
| 7671 | const arg = 1234; | |
| 7672 | ||
| 7673 | const a = @newStackCall(new_stack_bytes[0..512], targetFunction, arg); | |
| 7674 | const b = @newStackCall(new_stack_bytes[512..], targetFunction, arg); | |
| 7675 | _ = targetFunction(arg); | |
| 7676 | ||
| 7677 | assert(arg == 1234); | |
| 7678 | assert(a < b); | |
| 7679 | } | |
| 7680 | ||
| 7681 | fn targetFunction(x: i32) usize { | |
| 7682 | assert(x == 1234); | |
| 7683 | ||
| 7684 | var local_variable: i32 = 42; | |
| 7685 | const ptr = &local_variable; | |
| 7686 | ptr.* += 1; | |
| 7687 | ||
| 7688 | assert(local_variable == 43); | |
| 7689 | return @ptrToInt(ptr); | |
| 7690 | } | |
| 7691 | {#code_end#} | |
| 7692 | {#header_close#} | |
| 7693 | ||
| 7694 | 7687 | {#header_open|@OpaqueType#} |
| 7695 | 7688 | <pre>{#syntax#}@OpaqueType() type{#endsyntax#}</pre> |
| 7696 | 7689 | <p> |
lib/std/special/start.zig+1-1| ... | ... | @@ -184,7 +184,7 @@ fn posixCallMainAndExit() noreturn { |
| 184 | 184 | // 0, |
| 185 | 185 | //) catch @panic("out of memory"); |
| 186 | 186 | //std.os.mprotect(new_stack[0..std.mem.page_size], std.os.PROT_NONE) catch {}; |
| 187 | //std.os.exit(@newStackCall(new_stack, callMainWithArgs, argc, argv, envp)); | |
| 187 | //std.os.exit(@call(.{.stack = new_stack}, callMainWithArgs, .{argc, argv, envp})); | |
| 188 | 188 | } |
| 189 | 189 | |
| 190 | 190 | std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp })); |
src-self-hosted/ir.zig+2-2| ... | ... | @@ -321,7 +321,7 @@ pub const Inst = struct { |
| 321 | 321 | } |
| 322 | 322 | |
| 323 | 323 | const llvm_cc = llvm.CCallConv; |
| 324 | const fn_inline = llvm.FnInline.Auto; | |
| 324 | const call_attr = llvm.CallAttr.Auto; | |
| 325 | 325 | |
| 326 | 326 | return llvm.BuildCall( |
| 327 | 327 | ofile.builder, |
| ... | ... | @@ -329,7 +329,7 @@ pub const Inst = struct { |
| 329 | 329 | args.ptr, |
| 330 | 330 | @intCast(c_uint, args.len), |
| 331 | 331 | llvm_cc, |
| 332 | fn_inline, | |
| 332 | call_attr, | |
| 333 | 333 | "", |
| 334 | 334 | ) orelse error.OutOfMemory; |
| 335 | 335 | } |
src/ir.cpp+21-4| ... | ... | @@ -13358,6 +13358,15 @@ static IrInstruction *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst |
| 13358 | 13358 | return ir_get_deref(ira, source_instr, field_ptr, nullptr); |
| 13359 | 13359 | } |
| 13360 | 13360 | |
| 13361 | static IrInstruction *ir_analyze_optional_value_payload_value(IrAnalyze *ira, IrInstruction *source_instr, | |
| 13362 | IrInstruction *optional_operand, bool safety_check_on) | |
| 13363 | { | |
| 13364 | IrInstruction *opt_ptr = ir_get_ref(ira, source_instr, optional_operand, true, false); | |
| 13365 | IrInstruction *payload_ptr = ir_analyze_unwrap_optional_payload(ira, source_instr, opt_ptr, | |
| 13366 | safety_check_on, false); | |
| 13367 | return ir_get_deref(ira, source_instr, payload_ptr, nullptr); | |
| 13368 | } | |
| 13369 | ||
| 13361 | 13370 | static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, |
| 13362 | 13371 | ZigType *wanted_type, IrInstruction *value) |
| 13363 | 13372 | { |
| ... | ... | @@ -17521,7 +17530,7 @@ static IrInstruction *analyze_casted_new_stack(IrAnalyze *ira, IrInstruction *so |
| 17521 | 17530 | arch_stack_pointer_register_name(ira->codegen->zig_target->arch) == nullptr) |
| 17522 | 17531 | { |
| 17523 | 17532 | ir_add_error(ira, source_instr, |
| 17524 | buf_sprintf("target arch '%s' does not support @newStackCall", | |
| 17533 | buf_sprintf("target arch '%s' does not support calling with a new stack", | |
| 17525 | 17534 | target_arch_name(ira->codegen->zig_target->arch))); |
| 17526 | 17535 | } |
| 17527 | 17536 | |
| ... | ... | @@ -18223,13 +18232,21 @@ static IrInstruction *ir_analyze_call_extra(IrAnalyze *ira, IrInstruction *sourc |
| 18223 | 18232 | |
| 18224 | 18233 | TypeStructField *stack_field = find_struct_type_field(options->value->type, buf_create_from_str("stack")); |
| 18225 | 18234 | ir_assert(stack_field != nullptr, source_instr); |
| 18226 | IrInstruction *stack = ir_analyze_struct_value_field_value(ira, source_instr, options, stack_field); | |
| 18227 | IrInstruction *stack_is_non_null_inst = ir_analyze_test_non_null(ira, source_instr, stack); | |
| 18235 | IrInstruction *opt_stack = ir_analyze_struct_value_field_value(ira, source_instr, options, stack_field); | |
| 18236 | if (type_is_invalid(opt_stack->value->type)) | |
| 18237 | return ira->codegen->invalid_instruction; | |
| 18238 | IrInstruction *stack_is_non_null_inst = ir_analyze_test_non_null(ira, source_instr, opt_stack); | |
| 18228 | 18239 | bool stack_is_non_null; |
| 18229 | 18240 | if (!ir_resolve_bool(ira, stack_is_non_null_inst, &stack_is_non_null)) |
| 18230 | 18241 | return ira->codegen->invalid_instruction; |
| 18231 | if (!stack_is_non_null) | |
| 18242 | IrInstruction *stack; | |
| 18243 | if (stack_is_non_null) { | |
| 18244 | stack = ir_analyze_optional_value_payload_value(ira, source_instr, opt_stack, false); | |
| 18245 | if (type_is_invalid(stack->value->type)) | |
| 18246 | return ira->codegen->invalid_instruction; | |
| 18247 | } else { | |
| 18232 | 18248 | stack = nullptr; |
| 18249 | } | |
| 18233 | 18250 | |
| 18234 | 18251 | return ir_analyze_fn_call(ira, source_instr, fn, fn_type, fn_ref, first_arg_ptr, |
| 18235 | 18252 | modifier, stack, false, args_ptr, args_len, nullptr, result_loc); |
test/compile_errors.zig+5-5| ... | ... | @@ -26,8 +26,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 26 | 26 | \\fn baz2() void {} |
| 27 | 27 | , |
| 28 | 28 | "tmp.zig:2:21: error: expected tuple or struct, found 'void'", |
| 29 | "tmp.zig:5:58: error: unable to perform 'never_inline' call at compile-time", | |
| 30 | "tmp.zig:8:56: error: unable to perform 'never_tail' call at compile-time", | |
| 29 | "tmp.zig:5:14: error: unable to perform 'never_inline' call at compile-time", | |
| 30 | "tmp.zig:8:14: error: unable to perform 'never_tail' call at compile-time", | |
| 31 | 31 | "tmp.zig:11:5: error: no-inline call of inline function", |
| 32 | 32 | "tmp.zig:15:43: error: unable to evaluate constant expression", |
| 33 | 33 | ); |
| ... | ... | @@ -44,13 +44,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 44 | 44 | ); |
| 45 | 45 | |
| 46 | 46 | cases.addCase(x: { |
| 47 | var tc = cases.create("@newStackCall on unsupported target", | |
| 47 | var tc = cases.create("call with new stack on unsupported target", | |
| 48 | 48 | \\export fn entry() void { |
| 49 | 49 | \\ var buf: [10]u8 align(16) = undefined; |
| 50 | \\ @newStackCall(&buf, foo); | |
| 50 | \\ @call(.{.stack = &buf}, foo); | |
| 51 | 51 | \\} |
| 52 | 52 | \\fn foo() void {} |
| 53 | , "tmp.zig:3:5: error: target arch 'wasm32' does not support @newStackCall"); | |
| 53 | , "tmp.zig:3:5: error: target arch 'wasm32' does not support calling with a new stack"); | |
| 54 | 54 | tc.target = tests.Target{ |
| 55 | 55 | .Cross = tests.CrossTarget{ |
| 56 | 56 | .arch = .wasm32, |
test/stage1/behavior/new_stack_call.zig+2-2| ... | ... | @@ -18,8 +18,8 @@ test "calling a function with a new stack" { |
| 18 | 18 | |
| 19 | 19 | const arg = 1234; |
| 20 | 20 | |
| 21 | const a = @newStackCall(new_stack_bytes[0..512], targetFunction, arg); | |
| 22 | const b = @newStackCall(new_stack_bytes[512..], targetFunction, arg); | |
| 21 | const a = @call(.{ .stack = new_stack_bytes[0..512] }, targetFunction, .{arg}); | |
| 22 | const b = @call(.{ .stack = new_stack_bytes[512..] }, targetFunction, .{arg}); | |
| 23 | 23 | _ = targetFunction(arg); |
| 24 | 24 | |
| 25 | 25 | expect(arg == 1234); |