| 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,6 +6904,41 @@ pub const CallOptions = struct { |
| 6904 | }; | 6904 | }; |
| 6905 | }; | 6905 | }; |
| 6906 | {#code_end#} | 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 | {#header_close#} | 6942 | {#header_close#} |
| 6908 | 6943 | ||
| 6909 | {#header_open|@cDefine#} | 6944 | {#header_open|@cDefine#} |
| ... | @@ -7649,48 +7684,6 @@ mem.set(u8, dest, c);{#endsyntax#}</pre> | ... | @@ -7649,48 +7684,6 @@ mem.set(u8, dest, c);{#endsyntax#}</pre> |
| 7649 | </p> | 7684 | </p> |
| 7650 | {#header_close#} | 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 | {#header_open|@OpaqueType#} | 7687 | {#header_open|@OpaqueType#} |
| 7695 | <pre>{#syntax#}@OpaqueType() type{#endsyntax#}</pre> | 7688 | <pre>{#syntax#}@OpaqueType() type{#endsyntax#}</pre> |
| 7696 | <p> | 7689 | <p> |
lib/std/special/start.zig+1-1| ... | @@ -184,7 +184,7 @@ fn posixCallMainAndExit() noreturn { | ... | @@ -184,7 +184,7 @@ fn posixCallMainAndExit() noreturn { |
| 184 | // 0, | 184 | // 0, |
| 185 | //) catch @panic("out of memory"); | 185 | //) catch @panic("out of memory"); |
| 186 | //std.os.mprotect(new_stack[0..std.mem.page_size], std.os.PROT_NONE) catch {}; | 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 | std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp })); | 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,7 +321,7 @@ pub const Inst = struct { |
| 321 | } | 321 | } |
| 322 | 322 | ||
| 323 | const llvm_cc = llvm.CCallConv; | 323 | const llvm_cc = llvm.CCallConv; |
| 324 | const fn_inline = llvm.FnInline.Auto; | 324 | const call_attr = llvm.CallAttr.Auto; |
| 325 | 325 | ||
| 326 | return llvm.BuildCall( | 326 | return llvm.BuildCall( |
| 327 | ofile.builder, | 327 | ofile.builder, |
| ... | @@ -329,7 +329,7 @@ pub const Inst = struct { | ... | @@ -329,7 +329,7 @@ pub const Inst = struct { |
| 329 | args.ptr, | 329 | args.ptr, |
| 330 | @intCast(c_uint, args.len), | 330 | @intCast(c_uint, args.len), |
| 331 | llvm_cc, | 331 | llvm_cc, |
| 332 | fn_inline, | 332 | call_attr, |
| 333 | "", | 333 | "", |
| 334 | ) orelse error.OutOfMemory; | 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,6 +13358,15 @@ static IrInstruction *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst |
| 13358 | return ir_get_deref(ira, source_instr, field_ptr, nullptr); | 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 | static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, | 13370 | static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, |
| 13362 | ZigType *wanted_type, IrInstruction *value) | 13371 | ZigType *wanted_type, IrInstruction *value) |
| 13363 | { | 13372 | { |
| ... | @@ -17521,7 +17530,7 @@ static IrInstruction *analyze_casted_new_stack(IrAnalyze *ira, IrInstruction *so | ... | @@ -17521,7 +17530,7 @@ static IrInstruction *analyze_casted_new_stack(IrAnalyze *ira, IrInstruction *so |
| 17521 | arch_stack_pointer_register_name(ira->codegen->zig_target->arch) == nullptr) | 17530 | arch_stack_pointer_register_name(ira->codegen->zig_target->arch) == nullptr) |
| 17522 | { | 17531 | { |
| 17523 | ir_add_error(ira, source_instr, | 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 | target_arch_name(ira->codegen->zig_target->arch))); | 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,13 +18232,21 @@ static IrInstruction *ir_analyze_call_extra(IrAnalyze *ira, IrInstruction *sourc |
| 18223 | 18232 | ||
| 18224 | TypeStructField *stack_field = find_struct_type_field(options->value->type, buf_create_from_str("stack")); | 18233 | TypeStructField *stack_field = find_struct_type_field(options->value->type, buf_create_from_str("stack")); |
| 18225 | ir_assert(stack_field != nullptr, source_instr); | 18234 | ir_assert(stack_field != nullptr, source_instr); |
| 18226 | IrInstruction *stack = ir_analyze_struct_value_field_value(ira, source_instr, options, stack_field); | 18235 | IrInstruction *opt_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); | 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 | bool stack_is_non_null; | 18239 | bool stack_is_non_null; |
| 18229 | if (!ir_resolve_bool(ira, stack_is_non_null_inst, &stack_is_non_null)) | 18240 | if (!ir_resolve_bool(ira, stack_is_non_null_inst, &stack_is_non_null)) |
| 18230 | return ira->codegen->invalid_instruction; | 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 | stack = nullptr; | 18248 | stack = nullptr; |
| 18249 | } | ||
| 18233 | 18250 | ||
| 18234 | return ir_analyze_fn_call(ira, source_instr, fn, fn_type, fn_ref, first_arg_ptr, | 18251 | return ir_analyze_fn_call(ira, source_instr, fn, fn_type, fn_ref, first_arg_ptr, |
| 18235 | modifier, stack, false, args_ptr, args_len, nullptr, result_loc); | 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,8 +26,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 26 | \\fn baz2() void {} | 26 | \\fn baz2() void {} |
| 27 | , | 27 | , |
| 28 | "tmp.zig:2:21: error: expected tuple or struct, found 'void'", | 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", | 29 | "tmp.zig:5:14: 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", | 30 | "tmp.zig:8:14: error: unable to perform 'never_tail' call at compile-time", |
| 31 | "tmp.zig:11:5: error: no-inline call of inline function", | 31 | "tmp.zig:11:5: error: no-inline call of inline function", |
| 32 | "tmp.zig:15:43: error: unable to evaluate constant expression", | 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,13 +44,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 44 | ); | 44 | ); |
| 45 | 45 | ||
| 46 | cases.addCase(x: { | 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 | \\export fn entry() void { | 48 | \\export fn entry() void { |
| 49 | \\ var buf: [10]u8 align(16) = undefined; | 49 | \\ var buf: [10]u8 align(16) = undefined; |
| 50 | \\ @newStackCall(&buf, foo); | 50 | \\ @call(.{.stack = &buf}, foo); |
| 51 | \\} | 51 | \\} |
| 52 | \\fn foo() void {} | 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 | tc.target = tests.Target{ | 54 | tc.target = tests.Target{ |
| 55 | .Cross = tests.CrossTarget{ | 55 | .Cross = tests.CrossTarget{ |
| 56 | .arch = .wasm32, | 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,8 +18,8 @@ test "calling a function with a new stack" { |
| 18 | 18 | ||
| 19 | const arg = 1234; | 19 | const arg = 1234; |
| 20 | 20 | ||
| 21 | const a = @newStackCall(new_stack_bytes[0..512], targetFunction, arg); | 21 | const a = @call(.{ .stack = new_stack_bytes[0..512] }, targetFunction, .{arg}); |
| 22 | const b = @newStackCall(new_stack_bytes[512..], targetFunction, arg); | 22 | const b = @call(.{ .stack = new_stack_bytes[512..] }, targetFunction, .{arg}); |
| 23 | _ = targetFunction(arg); | 23 | _ = targetFunction(arg); |
| 24 | 24 | ||
| 25 | expect(arg == 1234); | 25 | expect(arg == 1234); |