Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions doc/langref.html.in
Original file line number Diff line number Diff line change
Expand Up @@ -6689,7 +6689,7 @@ comptime {
{#header_close#}

{#header_open|@asyncCall#}
<pre>{#syntax#}@asyncCall(frame_buffer: []align(@alignOf(@Frame(anyAsyncFunction))) u8, result_ptr, function_ptr, args: ...) anyframe->T{#endsyntax#}</pre>
<pre>{#syntax#}@asyncCall(frame_buffer: []align(@alignOf(@Frame(anyAsyncFunction))) u8, result_ptr, function_ptr, args: var) anyframe->T{#endsyntax#}</pre>
<p>
{#syntax#}@asyncCall{#endsyntax#} performs an {#syntax#}async{#endsyntax#} call on a function pointer,
which may or may not be an {#link|async function|Async Functions#}.
Expand All @@ -6716,7 +6716,7 @@ test "async fn pointer in a struct field" {
};
var foo = Foo{ .bar = func };
var bytes: [64]u8 align(@alignOf(@Frame(func))) = undefined;
const f = @asyncCall(&bytes, {}, foo.bar, &data);
const f = @asyncCall(&bytes, {}, foo.bar, .{&data});
assert(data == 2);
resume f;
assert(data == 4);
Expand Down
2 changes: 1 addition & 1 deletion lib/std/dwarf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, endia
const F = @TypeOf(async parseFormValue(allocator, in_stream, child_form_id, endian, is_64));
var frame = try allocator.create(F);
defer allocator.destroy(frame);
return await @asyncCall(frame, {}, parseFormValue, allocator, in_stream, child_form_id, endian, is_64);
return await @asyncCall(frame, {}, parseFormValue, .{ allocator, in_stream, child_form_id, endian, is_64 });
},
else => error.InvalidDebugInfo,
};
Expand Down
2 changes: 1 addition & 1 deletion lib/std/special/test_runner.zig
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub fn main() anyerror!void {
async_frame_buffer = try std.heap.page_allocator.alignedAlloc(u8, std.Target.stack_align, size);
}
const casted_fn = @ptrCast(fn () callconv(.Async) anyerror!void, test_fn.func);
break :blk await @asyncCall(async_frame_buffer, {}, casted_fn);
break :blk await @asyncCall(async_frame_buffer, {}, casted_fn, .{});
},
.blocking => {
skip_count += 1;
Expand Down
2 changes: 1 addition & 1 deletion lib/std/start.zig
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ inline fn initEventLoopAndCallMain() u8 {

var result: u8 = undefined;
var frame: @Frame(callMainAsync) = undefined;
_ = @asyncCall(&frame, &result, callMainAsync, loop);
_ = @asyncCall(&frame, &result, callMainAsync, .{loop});
loop.run();
return result;
}
Expand Down
15 changes: 15 additions & 0 deletions src/all_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2641,6 +2641,7 @@ enum IrInstSrcId {
IrInstSrcIdCall,
IrInstSrcIdCallArgs,
IrInstSrcIdCallExtra,
IrInstSrcIdAsyncCallExtra,
IrInstSrcIdConst,
IrInstSrcIdReturn,
IrInstSrcIdContainerInitList,
Expand Down Expand Up @@ -3255,6 +3256,20 @@ struct IrInstSrcCallExtra {
ResultLoc *result_loc;
};

// This is a pass1 instruction, used by @asyncCall, when the args node
// is not a literal.
// `args` is expected to be either a struct or a tuple.
struct IrInstSrcAsyncCallExtra {
IrInstSrc base;

CallModifier modifier;
IrInstSrc *fn_ref;
IrInstSrc *ret_ptr;
IrInstSrc *new_stack;
IrInstSrc *args;
ResultLoc *result_loc;
};

struct IrInstGenCall {
IrInstGen base;

Expand Down
168 changes: 139 additions & 29 deletions src/ir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCall *>(inst));
case IrInstSrcIdCallExtra:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCallExtra *>(inst));
case IrInstSrcIdAsyncCallExtra:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAsyncCallExtra *>(inst));
case IrInstSrcIdUnOp:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnOp *>(inst));
case IrInstSrcIdCondBr:
Expand Down Expand Up @@ -1173,6 +1175,10 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcCallExtra *) {
return IrInstSrcIdCallExtra;
}

static constexpr IrInstSrcId ir_inst_id(IrInstSrcAsyncCallExtra *) {
return IrInstSrcIdAsyncCallExtra;
}

static constexpr IrInstSrcId ir_inst_id(IrInstSrcConst *) {
return IrInstSrcIdConst;
}
Expand Down Expand Up @@ -2442,6 +2448,25 @@ static IrInstSrc *ir_build_call_extra(IrBuilderSrc *irb, Scope *scope, AstNode *
return &call_instruction->base;
}

static IrInstSrc *ir_build_async_call_extra(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
CallModifier modifier, IrInstSrc *fn_ref, IrInstSrc *ret_ptr, IrInstSrc *new_stack, IrInstSrc *args, ResultLoc *result_loc)
{
IrInstSrcAsyncCallExtra *call_instruction = ir_build_instruction<IrInstSrcAsyncCallExtra>(irb, scope, source_node);
call_instruction->modifier = modifier;
call_instruction->fn_ref = fn_ref;
call_instruction->ret_ptr = ret_ptr;
call_instruction->new_stack = new_stack;
call_instruction->args = args;
call_instruction->result_loc = result_loc;

ir_ref_instruction(fn_ref, irb->current_basic_block);
if (ret_ptr != nullptr) ir_ref_instruction(ret_ptr, irb->current_basic_block);
ir_ref_instruction(new_stack, irb->current_basic_block);
ir_ref_instruction(args, irb->current_basic_block);

return &call_instruction->base;
}

static IrInstSrc *ir_build_call_args(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *options, IrInstSrc *fn_ref, IrInstSrc **args_ptr, size_t args_len,
ResultLoc *result_loc)
Expand Down Expand Up @@ -6183,11 +6208,10 @@ static IrInstSrc *ir_gen_this(IrBuilderSrc *irb, Scope *orig_scope, AstNode *nod
static IrInstSrc *ir_gen_async_call(IrBuilderSrc *irb, Scope *scope, AstNode *await_node, AstNode *call_node,
LVal lval, ResultLoc *result_loc)
{
size_t arg_offset = 3;
if (call_node->data.fn_call_expr.params.length < arg_offset) {
if (call_node->data.fn_call_expr.params.length != 4) {
add_node_error(irb->codegen, call_node,
buf_sprintf("expected at least %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize,
arg_offset, call_node->data.fn_call_expr.params.length));
buf_sprintf("expected 4 arguments, found %" ZIG_PRI_usize,
call_node->data.fn_call_expr.params.length));
return irb->codegen->invalid_inst_src;
}

Expand All @@ -6206,20 +6230,37 @@ static IrInstSrc *ir_gen_async_call(IrBuilderSrc *irb, Scope *scope, AstNode *aw
if (fn_ref == irb->codegen->invalid_inst_src)
return fn_ref;

size_t arg_count = call_node->data.fn_call_expr.params.length - arg_offset;
IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(arg_count);
for (size_t i = 0; i < arg_count; i += 1) {
AstNode *arg_node = call_node->data.fn_call_expr.params.at(i + arg_offset);
IrInstSrc *arg = ir_gen_node(irb, arg_node, scope);
if (arg == irb->codegen->invalid_inst_src)
return arg;
args[i] = arg;
}

CallModifier modifier = (await_node == nullptr) ? CallModifierAsync : CallModifierNone;
bool is_async_call_builtin = true;
IrInstSrc *call = ir_build_call_src(irb, scope, call_node, nullptr, fn_ref, arg_count, args,
ret_ptr, modifier, is_async_call_builtin, bytes, result_loc);
AstNode *args_node = call_node->data.fn_call_expr.params.at(3);
if (args_node->type == NodeTypeContainerInitExpr) {
if (args_node->data.container_init_expr.kind == ContainerInitKindArray ||
args_node->data.container_init_expr.entries.length == 0)
{
size_t arg_count = args_node->data.container_init_expr.entries.length;
IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(arg_count);
for (size_t i = 0; i < arg_count; i += 1) {
AstNode *arg_node = args_node->data.container_init_expr.entries.at(i);
IrInstSrc *arg = ir_gen_node(irb, arg_node, scope);
if (arg == irb->codegen->invalid_inst_src)
return arg;
args[i] = arg;
}

IrInstSrc *call = ir_build_call_src(irb, scope, call_node, nullptr, fn_ref, arg_count, args,
ret_ptr, modifier, is_async_call_builtin, bytes, result_loc);
return ir_lval_wrap(irb, scope, call, lval, result_loc);
} else {
exec_add_error_node(irb->codegen, irb->exec, args_node,
buf_sprintf("TODO: @asyncCall with anon struct literal"));
return irb->codegen->invalid_inst_src;
}
}
IrInstSrc *args = ir_gen_node(irb, args_node, scope);
if (args == irb->codegen->invalid_inst_src)
return args;

IrInstSrc *call = ir_build_async_call_extra(irb, scope, call_node, modifier, fn_ref, ret_ptr, bytes, args, result_loc);
return ir_lval_wrap(irb, scope, call, lval, result_loc);
}

Expand Down Expand Up @@ -20719,40 +20760,106 @@ static IrInstGen *ir_analyze_call_extra(IrAnalyze *ira, IrInst* source_instr,
modifier, stack, stack_src, false, args_ptr, args_len, nullptr, result_loc);
}

static IrInstGen *ir_analyze_instruction_call_extra(IrAnalyze *ira, IrInstSrcCallExtra *instruction) {
IrInstGen *args = instruction->args->child;
static IrInstGen *ir_analyze_async_call_extra(IrAnalyze *ira, IrInst* source_instr, CallModifier modifier,
IrInstSrc *pass1_fn_ref, IrInstSrc *ret_ptr, IrInstSrc *new_stack, IrInstGen **args_ptr, size_t args_len, ResultLoc *result_loc)
{
IrInstGen *fn_ref = pass1_fn_ref->child;
if (type_is_invalid(fn_ref->value->type))
return ira->codegen->invalid_inst_gen;

if (ir_should_inline(ira->old_irb.exec, source_instr->scope)) {
ir_add_error(ira, source_instr, buf_sprintf("TODO: comptime @asyncCall"));
return ira->codegen->invalid_inst_gen;
}

IrInstGen *first_arg_ptr = nullptr;
IrInst *first_arg_ptr_src = nullptr;
ZigFn *fn = nullptr;
if (instr_is_comptime(fn_ref)) {
if (fn_ref->value->type->id == ZigTypeIdBoundFn) {
assert(fn_ref->value->special == ConstValSpecialStatic);
fn = fn_ref->value->data.x_bound_fn.fn;
first_arg_ptr = fn_ref->value->data.x_bound_fn.first_arg;
first_arg_ptr_src = fn_ref->value->data.x_bound_fn.first_arg_src;
if (type_is_invalid(first_arg_ptr->value->type))
return ira->codegen->invalid_inst_gen;
} else {
fn = ir_resolve_fn(ira, fn_ref);
}
}

IrInstGen *ret_ptr_uncasted = nullptr;
if (ret_ptr != nullptr) {
ret_ptr_uncasted = ret_ptr->child;
if (type_is_invalid(ret_ptr_uncasted->value->type))
return ira->codegen->invalid_inst_gen;
}

ZigType *fn_type = (fn != nullptr) ? fn->type_entry : fn_ref->value->type;
IrInstGen *casted_new_stack = analyze_casted_new_stack(ira, source_instr, new_stack->child,
&new_stack->base, true, fn);
if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value->type))
return ira->codegen->invalid_inst_gen;

return ir_analyze_fn_call(ira, source_instr, fn, fn_type, fn_ref, first_arg_ptr, first_arg_ptr_src,
modifier, casted_new_stack, &new_stack->base, true, args_ptr, args_len, ret_ptr_uncasted, result_loc);
}

static bool ir_extract_tuple_call_args(IrAnalyze *ira, IrInst *source_instr, IrInstGen *args, IrInstGen ***args_ptr, size_t *args_len) {
ZigType *args_type = args->value->type;
if (type_is_invalid(args_type))
return ira->codegen->invalid_inst_gen;
return false;

if (args_type->id != ZigTypeIdStruct) {
ir_add_error(ira, &args->base,
buf_sprintf("expected tuple or struct, found '%s'", buf_ptr(&args_type->name)));
return ira->codegen->invalid_inst_gen;
return false;
}

IrInstGen **args_ptr = nullptr;
size_t args_len = 0;

if (is_tuple(args_type)) {
args_len = args_type->data.structure.src_field_count;
args_ptr = heap::c_allocator.allocate<IrInstGen *>(args_len);
for (size_t i = 0; i < args_len; i += 1) {
*args_len = args_type->data.structure.src_field_count;
*args_ptr = heap::c_allocator.allocate<IrInstGen *>(*args_len);
for (size_t i = 0; i < *args_len; i += 1) {
TypeStructField *arg_field = args_type->data.structure.fields[i];
args_ptr[i] = ir_analyze_struct_value_field_value(ira, &instruction->base.base, args, arg_field);
if (type_is_invalid(args_ptr[i]->value->type))
return ira->codegen->invalid_inst_gen;
(*args_ptr)[i] = ir_analyze_struct_value_field_value(ira, source_instr, args, arg_field);
if (type_is_invalid((*args_ptr)[i]->value->type))
return false;
}
} else {
ir_add_error(ira, &args->base, buf_sprintf("TODO: struct args"));
return false;
}
return true;
}

static IrInstGen *ir_analyze_instruction_call_extra(IrAnalyze *ira, IrInstSrcCallExtra *instruction) {
IrInstGen *args = instruction->args->child;
IrInstGen **args_ptr = nullptr;
size_t args_len = 0;
if (!ir_extract_tuple_call_args(ira, &instruction->base.base, args, &args_ptr, &args_len)) {
return ira->codegen->invalid_inst_gen;
}

IrInstGen *result = ir_analyze_call_extra(ira, &instruction->base.base, instruction->options,
instruction->fn_ref, args_ptr, args_len, instruction->result_loc);
heap::c_allocator.deallocate(args_ptr, args_len);
return result;
}

static IrInstGen *ir_analyze_instruction_async_call_extra(IrAnalyze *ira, IrInstSrcAsyncCallExtra *instruction) {
IrInstGen *args = instruction->args->child;
IrInstGen **args_ptr = nullptr;
size_t args_len = 0;
if (!ir_extract_tuple_call_args(ira, &instruction->base.base, args, &args_ptr, &args_len)) {
return ira->codegen->invalid_inst_gen;
}

IrInstGen *result = ir_analyze_async_call_extra(ira, &instruction->base.base, instruction->modifier,
instruction->fn_ref, instruction->ret_ptr, instruction->new_stack, args_ptr, args_len, instruction->result_loc);
heap::c_allocator.deallocate(args_ptr, args_len);
return result;
}

static IrInstGen *ir_analyze_instruction_call_args(IrAnalyze *ira, IrInstSrcCallArgs *instruction) {
IrInstGen **args_ptr = heap::c_allocator.allocate<IrInstGen *>(instruction->args_len);
for (size_t i = 0; i < instruction->args_len; i += 1) {
Expand Down Expand Up @@ -31101,6 +31208,8 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
return ir_analyze_instruction_call_args(ira, (IrInstSrcCallArgs *)instruction);
case IrInstSrcIdCallExtra:
return ir_analyze_instruction_call_extra(ira, (IrInstSrcCallExtra *)instruction);
case IrInstSrcIdAsyncCallExtra:
return ir_analyze_instruction_async_call_extra(ira, (IrInstSrcAsyncCallExtra *)instruction);
case IrInstSrcIdBr:
return ir_analyze_instruction_br(ira, (IrInstSrcBr *)instruction);
case IrInstSrcIdCondBr:
Expand Down Expand Up @@ -31610,6 +31719,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {
case IrInstSrcIdDeclVar:
case IrInstSrcIdStorePtr:
case IrInstSrcIdCallExtra:
case IrInstSrcIdAsyncCallExtra:
case IrInstSrcIdCall:
case IrInstSrcIdCallArgs:
case IrInstSrcIdReturn:
Expand Down
Loading