From 1f602fe8c5b3dea9f00f96e70dad73ebce405b49 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
{#syntax#}@call(options: std.builtin.CallOptions, function: var, args: var) var{#endsyntax#}
+ + Calls a function, in the same way that invoking an expression with parentheses does: +
+ {#code_begin|test|call#} +const assert = @import("std").debug.assert; + +test "noinline function call" { + assert(@call(.{}, add, .{3, 9}) == 12); +} + +fn add(a: i32, b: i32) i32 { + return a + b; +} + {#code_end#} ++ {#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The + {#syntax#}CallOptions{#endsyntax#} struct is reproduced here: +
+ {#code_begin|syntax#} +pub const CallOptions = struct { + modifier: Modifier = .auto, + stack: ?[]align(std.Target.stack_align) u8 = null, + + pub const Modifier = enum { + /// Equivalent to function call syntax. + auto, + + /// Asserts that the function call will not suspend. This allows a + /// non-async function to call an async function. + no_async, + + /// The function call will return an async function frame instead of + /// the function's result, which is expected to then be awaited. + /// This is equivalent to using the `async` keyword in front of function + /// call syntax. + async_call, + + /// Prevents tail call optimization. This guarantees that the return + /// address will point to the callsite, as opposed to the callsite's + /// callsite. If the call is otherwise required to be tail-called + /// or inlined, a compile error is emitted instead. + never_tail, + + /// Guarantees that the call will not be inlined. If the call is + /// otherwise required to be inlined, a compile error is emitted instead. + never_inline, + + /// Guarantees that the call will be generated with tail call optimization. + /// If this is not possible, a compile error is emitted instead. + always_tail, + + /// Guarantees that the call will inlined at the callsite. + /// If this is not possible, a compile error is emitted instead. + always_inline, + + /// Evaluates the call at compile-time. If the call cannot be completed at + /// compile-time, a compile error is emitted instead. + compile_time, + }; +}; + {#code_end#} + {#header_close#} + {#header_open|@cDefine#}{#syntax#}@cDefine(comptime name: []u8, value){#endsyntax#}
@@ -7445,7 +7510,7 @@ fn add(a: i32, b: i32) i32 { return a + b; } Unlike a normal function call, however, {#syntax#}@inlineCall{#endsyntax#} guarantees that the call will be inlined. If the call cannot be inlined, a compile error is emitted.
- {#see_also|@noInlineCall#} + {#see_also|@call#} {#header_close#} {#header_open|@intCast#} @@ -7647,29 +7712,6 @@ fn targetFunction(x: i32) usize { {#code_end#} {#header_close#} - {#header_open|@noInlineCall#} -{#syntax#}@noInlineCall(function: var, args: ...) var{#endsyntax#}
- - This calls a function, in the same way that invoking an expression with parentheses does: -
- {#code_begin|test#} -const assert = @import("std").debug.assert; - -test "noinline function call" { - assert(@noInlineCall(add, 3, 9) == 12); -} - -fn add(a: i32, b: i32) i32 { - return a + b; -} - {#code_end#} -- Unlike a normal function call, however, {#syntax#}@noInlineCall{#endsyntax#} guarantees that the call - will not be inlined. If the call must be inlined, a compile error is emitted. -
- {#see_also|@inlineCall#} - {#header_close#} - {#header_open|@OpaqueType#}{#syntax#}@OpaqueType() type{#endsyntax#}
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
index 791d46bc34..8ac58ad2f4 100644
--- a/lib/std/builtin.zig
+++ b/lib/std/builtin.zig
@@ -379,13 +379,39 @@ pub const CallOptions = struct {
stack: ?[]align(std.Target.stack_align) u8 = null,
pub const Modifier = enum {
+ /// Equivalent to function call syntax.
auto,
+
+ /// Asserts that the function call will not suspend. This allows a
+ /// non-async function to call an async function.
no_async,
+
+ /// The function call will return an async function frame instead of
+ /// the function's result, which is expected to then be awaited.
+ /// This is equivalent to using the `async` keyword in front of function
+ /// call syntax.
async_call,
+
+ /// Prevents tail call optimization. This guarantees that the return
+ /// address will point to the callsite, as opposed to the callsite's
+ /// callsite. If the call is otherwise required to be tail-called
+ /// or inlined, a compile error is emitted instead.
never_tail,
+
+ /// Guarantees that the call will not be inlined. If the call is
+ /// otherwise required to be inlined, a compile error is emitted instead.
never_inline,
+
+ /// Guarantees that the call will be generated with tail call optimization.
+ /// If this is not possible, a compile error is emitted instead.
always_tail,
+
+ /// Guarantees that the call will inlined at the callsite.
+ /// If this is not possible, a compile error is emitted instead.
always_inline,
+
+ /// Evaluates the call at compile-time. If the call cannot be completed at
+ /// compile-time, a compile error is emitted instead.
compile_time,
};
};
diff --git a/lib/std/special/start.zig b/lib/std/special/start.zig
index a93b01c290..8815e17a91 100644
--- a/lib/std/special/start.zig
+++ b/lib/std/special/start.zig
@@ -125,7 +125,7 @@ nakedcc fn _start() noreturn {
}
// If LLVM inlines stack variables into _start, they will overwrite
// the command line argument data.
- @noInlineCall(posixCallMainAndExit);
+ @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
}
stdcallcc fn WinMainCRTStartup() noreturn {
diff --git a/src/all_types.hpp b/src/all_types.hpp
index c3d5793d7a..c350b04c9e 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1701,7 +1701,6 @@ enum BuiltinFnId {
BuiltinFnIdByteOffsetOf,
BuiltinFnIdBitOffsetOf,
BuiltinFnIdInlineCall,
- BuiltinFnIdNoInlineCall,
BuiltinFnIdNewStackCall,
BuiltinFnIdAsyncCall,
BuiltinFnIdTypeId,
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 35ad039d68..c1d3dc3327 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -8133,7 +8133,6 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdRound, "round", 2);
create_builtin_fn(g, BuiltinFnIdMulAdd, "mulAdd", 4);
create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
- create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);
create_builtin_fn(g, BuiltinFnIdNewStackCall, "newStackCall", SIZE_MAX);
create_builtin_fn(g, BuiltinFnIdAsyncCall, "asyncCall", SIZE_MAX);
create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);
diff --git a/src/ir.cpp b/src/ir.cpp
index 23114f9b50..e40fb854d6 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -6014,7 +6014,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
return ir_lval_wrap(irb, scope, offset_of, lval, result_loc);
}
case BuiltinFnIdInlineCall:
- case BuiltinFnIdNoInlineCall:
{
if (node->data.fn_call_expr.params.length == 0) {
add_node_error(irb->codegen, node, buf_sprintf("expected at least 1 argument, found 0"));
@@ -6035,11 +6034,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
if (args[i] == irb->codegen->invalid_instruction)
return args[i];
}
- CallModifier modifier = (builtin_fn->id == BuiltinFnIdInlineCall) ?
- CallModifierAlwaysInline : CallModifierNeverInline;
IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args,
- nullptr, modifier, false, nullptr, result_loc);
+ nullptr, CallModifierAlwaysInline, false, nullptr, result_loc);
return ir_lval_wrap(irb, scope, call, lval, result_loc);
}
case BuiltinFnIdNewStackCall:
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 49c7094fb0..d1244188e6 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -13,11 +13,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\export fn entry3() void {
\\ comptime @call(.{ .modifier = .never_tail }, foo, .{});
\\}
+ \\export fn entry4() void {
+ \\ @call(.{ .modifier = .never_inline }, bar, .{});
+ \\}
+ \\export fn entry5(c: bool) void {
+ \\ var baz = if (c) baz1 else baz2;
+ \\ @call(.{ .modifier = .compile_time }, baz, .{});
+ \\}
\\fn foo() void {}
+ \\inline fn bar() void {}
+ \\fn baz1() void {}
+ \\fn baz2() void {}
,
"tmp.zig:2:21: error: expected tuple or struct, found 'void'",
"tmp.zig:5:58: error: unable to perform 'never_inline' call at compile-time",
"tmp.zig:8:56: error: unable to perform 'never_tail' call at compile-time",
+ "tmp.zig:11:5: error: no-inline call of inline function",
+ "tmp.zig:15:43: error: unable to evaluate constant expression",
);
cases.add(
@@ -1945,17 +1957,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
"tmp.zig:2:12: error: use of undeclared identifier 'SomeNonexistentType'",
);
- cases.add(
- "@noInlineCall on an inline function",
- \\inline fn foo() void {}
- \\
- \\export fn entry() void {
- \\ @noInlineCall(foo);
- \\}
- ,
- "tmp.zig:4:5: error: no-inline call of inline function",
- );
-
cases.add(
"comptime continue inside runtime catch",
\\export fn entry(c: bool) void {
--
cgit v1.2.3
From 71b7f4b47f69e9b3241e9d44554572258f5eb5b1 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
+ When the {#syntax#}stack{#endsyntax#} option is provided, instead of using the same stack as the caller, the function uses the provided stack.
+
{#syntax#}@newStackCall(new_stack: []align(target_stack_align) u8, function: var, args: ...) var{#endsyntax#}
- - This calls a function, in the same way that invoking an expression with parentheses does. However, - instead of using the same stack as the caller, the function uses the stack provided in the {#syntax#}new_stack{#endsyntax#} - parameter. -
-- The new stack must be aligned to {#syntax#}target_stack_align{#endsyntax#} bytes. This is a target-specific - number. A safe value that will work on all targets is {#syntax#}16{#endsyntax#}. This value can - also be obtained by using {#link|@sizeOf#} on the {#link|@Frame#} type of {#link|Async Functions#}. -
- {#code_begin|test#} -const std = @import("std"); -const assert = std.debug.assert; - -var new_stack_bytes: [1024]u8 align(16) = undefined; - -test "calling a function with a new stack" { - const arg = 1234; - - const a = @newStackCall(new_stack_bytes[0..512], targetFunction, arg); - const b = @newStackCall(new_stack_bytes[512..], targetFunction, arg); - _ = targetFunction(arg); - - assert(arg == 1234); - assert(a < b); -} - -fn targetFunction(x: i32) usize { - assert(x == 1234); - - var local_variable: i32 = 42; - const ptr = &local_variable; - ptr.* += 1; - - assert(local_variable == 43); - return @ptrToInt(ptr); -} - {#code_end#} - {#header_close#} - {#header_open|@OpaqueType#}{#syntax#}@OpaqueType() type{#endsyntax#}
diff --git a/lib/std/special/start.zig b/lib/std/special/start.zig
index b00b5213a7..d10431da20 100644
--- a/lib/std/special/start.zig
+++ b/lib/std/special/start.zig
@@ -184,7 +184,7 @@ fn posixCallMainAndExit() noreturn {
// 0,
//) catch @panic("out of memory");
//std.os.mprotect(new_stack[0..std.mem.page_size], std.os.PROT_NONE) catch {};
- //std.os.exit(@newStackCall(new_stack, callMainWithArgs, argc, argv, envp));
+ //std.os.exit(@call(.{.stack = new_stack}, callMainWithArgs, .{argc, argv, envp}));
}
std.os.exit(@call(.{ .modifier = .always_inline }, callMainWithArgs, .{ argc, argv, envp }));
diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig
index 8d1c32cefd..1d11d7969d 100644
--- a/src-self-hosted/ir.zig
+++ b/src-self-hosted/ir.zig
@@ -321,7 +321,7 @@ pub const Inst = struct {
}
const llvm_cc = llvm.CCallConv;
- const fn_inline = llvm.FnInline.Auto;
+ const call_attr = llvm.CallAttr.Auto;
return llvm.BuildCall(
ofile.builder,
@@ -329,7 +329,7 @@ pub const Inst = struct {
args.ptr,
@intCast(c_uint, args.len),
llvm_cc,
- fn_inline,
+ call_attr,
"",
) orelse error.OutOfMemory;
}
diff --git a/src/ir.cpp b/src/ir.cpp
index 6e0e321300..e6978eae77 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -13358,6 +13358,15 @@ static IrInstruction *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst
return ir_get_deref(ira, source_instr, field_ptr, nullptr);
}
+static IrInstruction *ir_analyze_optional_value_payload_value(IrAnalyze *ira, IrInstruction *source_instr,
+ IrInstruction *optional_operand, bool safety_check_on)
+{
+ IrInstruction *opt_ptr = ir_get_ref(ira, source_instr, optional_operand, true, false);
+ IrInstruction *payload_ptr = ir_analyze_unwrap_optional_payload(ira, source_instr, opt_ptr,
+ safety_check_on, false);
+ return ir_get_deref(ira, source_instr, payload_ptr, nullptr);
+}
+
static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
ZigType *wanted_type, IrInstruction *value)
{
@@ -17521,7 +17530,7 @@ static IrInstruction *analyze_casted_new_stack(IrAnalyze *ira, IrInstruction *so
arch_stack_pointer_register_name(ira->codegen->zig_target->arch) == nullptr)
{
ir_add_error(ira, source_instr,
- buf_sprintf("target arch '%s' does not support @newStackCall",
+ buf_sprintf("target arch '%s' does not support calling with a new stack",
target_arch_name(ira->codegen->zig_target->arch)));
}
@@ -18223,13 +18232,21 @@ static IrInstruction *ir_analyze_call_extra(IrAnalyze *ira, IrInstruction *sourc
TypeStructField *stack_field = find_struct_type_field(options->value->type, buf_create_from_str("stack"));
ir_assert(stack_field != nullptr, source_instr);
- IrInstruction *stack = ir_analyze_struct_value_field_value(ira, source_instr, options, stack_field);
- IrInstruction *stack_is_non_null_inst = ir_analyze_test_non_null(ira, source_instr, stack);
+ IrInstruction *opt_stack = ir_analyze_struct_value_field_value(ira, source_instr, options, stack_field);
+ if (type_is_invalid(opt_stack->value->type))
+ return ira->codegen->invalid_instruction;
+ IrInstruction *stack_is_non_null_inst = ir_analyze_test_non_null(ira, source_instr, opt_stack);
bool stack_is_non_null;
if (!ir_resolve_bool(ira, stack_is_non_null_inst, &stack_is_non_null))
return ira->codegen->invalid_instruction;
- if (!stack_is_non_null)
+ IrInstruction *stack;
+ if (stack_is_non_null) {
+ stack = ir_analyze_optional_value_payload_value(ira, source_instr, opt_stack, false);
+ if (type_is_invalid(stack->value->type))
+ return ira->codegen->invalid_instruction;
+ } else {
stack = nullptr;
+ }
return ir_analyze_fn_call(ira, source_instr, fn, fn_type, fn_ref, first_arg_ptr,
modifier, stack, false, args_ptr, args_len, nullptr, result_loc);
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index d1244188e6..9494879f71 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -26,8 +26,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\fn baz2() void {}
,
"tmp.zig:2:21: error: expected tuple or struct, found 'void'",
- "tmp.zig:5:58: error: unable to perform 'never_inline' call at compile-time",
- "tmp.zig:8:56: error: unable to perform 'never_tail' call at compile-time",
+ "tmp.zig:5:14: error: unable to perform 'never_inline' call at compile-time",
+ "tmp.zig:8:14: error: unable to perform 'never_tail' call at compile-time",
"tmp.zig:11:5: error: no-inline call of inline function",
"tmp.zig:15:43: error: unable to evaluate constant expression",
);
@@ -44,13 +44,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
);
cases.addCase(x: {
- var tc = cases.create("@newStackCall on unsupported target",
+ var tc = cases.create("call with new stack on unsupported target",
\\export fn entry() void {
\\ var buf: [10]u8 align(16) = undefined;
- \\ @newStackCall(&buf, foo);
+ \\ @call(.{.stack = &buf}, foo);
\\}
\\fn foo() void {}
- , "tmp.zig:3:5: error: target arch 'wasm32' does not support @newStackCall");
+ , "tmp.zig:3:5: error: target arch 'wasm32' does not support calling with a new stack");
tc.target = tests.Target{
.Cross = tests.CrossTarget{
.arch = .wasm32,
diff --git a/test/stage1/behavior/new_stack_call.zig b/test/stage1/behavior/new_stack_call.zig
index b057566d9c..69763a52c6 100644
--- a/test/stage1/behavior/new_stack_call.zig
+++ b/test/stage1/behavior/new_stack_call.zig
@@ -18,8 +18,8 @@ test "calling a function with a new stack" {
const arg = 1234;
- const a = @newStackCall(new_stack_bytes[0..512], targetFunction, arg);
- const b = @newStackCall(new_stack_bytes[512..], targetFunction, arg);
+ const a = @call(.{ .stack = new_stack_bytes[0..512] }, targetFunction, .{arg});
+ const b = @call(.{ .stack = new_stack_bytes[512..] }, targetFunction, .{arg});
_ = targetFunction(arg);
expect(arg == 1234);
--
cgit v1.2.3
From 656cc33f8d49cb5e79cd3f9f8f56963747d43ed6 Mon Sep 17 00:00:00 2001
From: Andrew Kelley