From 5a8c445779b3b87ba38fbcac0efcc7ebb3787161 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 20 Sep 2022 20:47:06 -0700 Subject: [PATCH] stage2: add call_async ZIR instruction This is to be used when the source code looks like this: var a = async b(); The instruction acts both as an alloc as well as a function call using that alloc as the result location. This avoids multiple ZIR instructions as well as complicated "inferred pointer" semantic analysis for a common case. AstGen is not yet updated to emit this new instruction. --- src/Air.zig | 11 +++ src/AstGen.zig | 3 +- src/InternPool.zig | 24 +++++ src/Liveness.zig | 113 +++++++++++++-------- src/Liveness/Verify.zig | 14 +++ src/Module.zig | 58 +++++++++++ src/Sema.zig | 187 ++++++++++++++++++++++++++--------- src/TypedValue.zig | 1 + src/Zir.zig | 23 ++++- src/arch/aarch64/CodeGen.zig | 2 + src/arch/arm/CodeGen.zig | 2 + src/arch/riscv64/CodeGen.zig | 2 + src/arch/sparc64/CodeGen.zig | 2 + src/arch/wasm/CodeGen.zig | 3 + src/arch/x86_64/CodeGen.zig | 2 + src/codegen.zig | 1 + src/codegen/c.zig | 2 + src/codegen/llvm.zig | 58 ++++++++++- src/codegen/spirv.zig | 1 + src/print_air.zig | 23 ++++- src/print_zir.zig | 36 ++++++- src/type.zig | 39 +++++++- src/value.zig | 1 + 23 files changed, 510 insertions(+), 98 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index ae0268f35d9499f9e11433777846690fa9e3d2a0..dc08e4de269356582eac8a536ed89a493c68e7fe 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -310,6 +310,9 @@ pub const Inst = struct { call_never_tail, /// Same as `call` except with the `never_inline` attribute. call_never_inline, + /// Async function call. + /// Uses `ty_pl` field with the `AsyncCall` payload. + call_async, /// Count leading zeroes of an integer according to its representation in twos complement. /// Result type will always be an unsigned integer big enough to fit the answer. /// Uses the `ty_op` field. @@ -1070,6 +1073,12 @@ pub const Call = struct { args_len: u32, }; +/// Trailing is a list of `Inst.Ref` for every `args_len`. +pub const AsyncCall = struct { + callee: Inst.Ref, + args_len: u32, +}; + /// This data is stored inside extra, with two sets of trailing `Inst.Ref`: /// * 0. the then body, according to `then_body_len`. /// * 1. the else body, according to `else_body_len`. @@ -1340,6 +1349,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) .ptr_add, .ptr_sub, .try_ptr, + .call_async, => return air.getRefType(datas[inst].ty_pl.ty), .interned => return ip.typeOf(datas[inst].interned).toType(), @@ -1583,6 +1593,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { .call_always_tail, .call_never_tail, .call_never_inline, + .call_async, .cond_br, .switch_br, .@"try", diff --git a/src/AstGen.zig b/src/AstGen.zig index 27e8433cb69832ee4b2dcff68262e680235c2e88..b17f3b3a970742c2d8353fc4e80545d7c56f5bde 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2788,6 +2788,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .validate_deref, .save_err_ret_index, .restore_err_ret_index, + .async_call, => break :b true, .@"defer" => unreachable, @@ -8701,7 +8702,7 @@ fn builtinCall( return rvalue(gz, ri, result, node); }, .async_call => { - const result = try gz.addExtendedPayload(.builtin_async_call, Zir.Inst.AsyncCall{ + const result = try gz.addExtendedPayload(.builtin_async_call, Zir.Inst.BuiltinAsyncCall{ .node = gz.nodeIndexToRelative(node), .frame_buffer = try expr(gz, scope, .{ .rl = .none }, params[0]), .result_ptr = try expr(gz, scope, .{ .rl = .none }, params[1]), diff --git a/src/InternPool.zig b/src/InternPool.zig index 1a89c239ef0967406fac4d67a62f80dcf0ee18c7..6527f65d6e04dab5e70b5925c1d38374bd1227ef 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -209,6 +209,8 @@ pub const Key = union(enum) { /// `anyframe->T`. The payload is the child type, which may be `none` to indicate /// `anyframe`. anyframe_type: Index, + /// The payload is the function whose frame it refers to. + async_frame_type: Module.Fn.Index, error_union_type: ErrorUnionType, simple_type: SimpleType, /// This represents a struct that has been explicitly declared in source code, @@ -711,6 +713,7 @@ pub const Key = union(enum) { .enum_tag, .empty_enum_value, .inferred_error_set_type, + .async_frame_type, .un, => |x| Hash.hash(seed, asBytes(&x)), @@ -930,6 +933,10 @@ pub const Key = union(enum) { const b_info = b.error_union_type; return std.meta.eql(a_info, b_info); }, + .async_frame_type => |a_info| { + const b_info = b.async_frame_type; + return a_info == b_info; + }, .simple_type => |a_info| { const b_info = b.simple_type; return a_info == b_info; @@ -1192,6 +1199,7 @@ pub const Key = union(enum) { .enum_type, .anon_struct_type, .func_type, + .async_frame_type, => .type_type, inline .runtime_value, @@ -1432,6 +1440,7 @@ pub const Index = enum(u32) { trailing: struct { names: []NullTerminatedString }, }, type_inferred_error_set: struct { data: Module.Fn.InferredErrorSet.Index }, + type_async_frame: Module.Fn.Index, type_enum_auto: struct { const @"data.fields_len" = opaque {}; data: *EnumAuto, @@ -1869,6 +1878,9 @@ pub const Tag = enum(u8) { /// An untagged union type which has a safety tag. /// `data` is `Module.Union.Index`. type_union_safety, + /// The async frame type of a function. + /// data is `Module.Fn.Index`. + type_async_frame, /// A function body type. /// `data` is extra index to `TypeFunction`. type_function, @@ -2059,6 +2071,7 @@ pub const Tag = enum(u8) { .type_error_union => ErrorUnionType, .type_error_set => ErrorSet, .type_inferred_error_set => unreachable, + .type_async_frame => unreachable, .type_enum_auto => EnumAuto, .type_enum_explicit => EnumExplicit, .type_enum_nonexhaustive => EnumExplicit, @@ -2636,6 +2649,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { .type_inferred_error_set => .{ .inferred_error_set_type = @as(Module.Fn.InferredErrorSet.Index, @enumFromInt(data)), }, + .type_async_frame => .{ .async_frame_type = @enumFromInt(data) }, .type_opaque => .{ .opaque_type = ip.extraData(Key.OpaqueType, data) }, .type_struct => { @@ -3240,6 +3254,12 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { .data = @intFromEnum(ies_index), }); }, + .async_frame_type => |fn_index| { + ip.items.appendAssumeCapacity(.{ + .tag = .type_async_frame, + .data = @intFromEnum(fn_index), + }); + }, .simple_type => |simple_type| { ip.items.appendAssumeCapacity(.{ .tag = .simple_type, @@ -5053,6 +5073,7 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void { break :b @sizeOf(ErrorSet) + (@sizeOf(u32) * info.names_len); }, .type_inferred_error_set => @sizeOf(Module.Fn.InferredErrorSet), + .type_async_frame => 0, .type_enum_explicit, .type_enum_nonexhaustive => @sizeOf(EnumExplicit), .type_enum_auto => @sizeOf(EnumAuto), .type_opaque => @sizeOf(Key.OpaqueType), @@ -5195,6 +5216,7 @@ fn dumpAllFallible(ip: *const InternPool) anyerror!void { .type_error_union, .type_error_set, .type_inferred_error_set, + .type_async_frame, .type_enum_explicit, .type_enum_nonexhaustive, .type_enum_auto, @@ -5578,6 +5600,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index { .type_error_union, .type_error_set, .type_inferred_error_set, + .type_async_frame, .type_enum_auto, .type_enum_explicit, .type_enum_nonexhaustive, @@ -5926,6 +5949,7 @@ pub fn zigTypeTagOrPoison(ip: *const InternPool, index: Index) error{GenericPois => .Union, .type_function => .Fn, + .type_async_frame => .Frame, // values, not types .undef, diff --git a/src/Liveness.zig b/src/Liveness.zig index e2449371397a975e4c8b4126f1962f70666438e8..dbe92e5a5ebf4269a1c7688c6b8aecea02d8f95d 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -484,28 +484,15 @@ pub fn categorizeOperand( const inst_data = air_datas[inst].pl_op; const callee = inst_data.operand; const extra = air.extraData(Air.Call, inst_data.payload); - const args = @as([]const Air.Inst.Ref, @ptrCast(air.extra[extra.end..][0..extra.data.args_len])); - if (args.len + 1 <= bpi - 1) { - if (callee == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); - for (args, 0..) |arg, i| { - if (arg == operand_ref) return matchOperandSmallIndex(l, inst, @as(OperandInt, @intCast(i + 1)), .write); - } - return .write; - } - var bt = l.iterateBigTomb(inst); - if (bt.feed()) { - if (callee == operand_ref) return .tomb; - } else { - if (callee == operand_ref) return .write; - } - for (args) |arg| { - if (bt.feed()) { - if (arg == operand_ref) return .tomb; - } else { - if (arg == operand_ref) return .write; - } - } - return .write; + const args: []const Air.Inst.Ref = @ptrCast(air.extra[extra.end..][0..extra.data.args_len]); + return categorizeOperandCall(l, inst, operand_ref, callee, args); + }, + .call_async => { + const inst_data = air_datas[inst].ty_pl; + const extra = air.extraData(Air.AsyncCall, inst_data.payload); + const callee = extra.data.callee; + const args: []const Air.Inst.Ref = @ptrCast(air.extra[extra.end..][0..extra.data.args_len]); + return categorizeOperandCall(l, inst, operand_ref, callee, args); }, .select => { const pl_op = air_datas[inst].pl_op; @@ -674,6 +661,36 @@ pub fn categorizeOperand( } } +fn categorizeOperandCall( + l: Liveness, + inst: Air.Inst.Index, + operand_ref: Air.Inst.Ref, + callee: Air.Inst.Ref, + args: []const Air.Inst.Ref, +) OperandCategory { + if (args.len + 1 <= bpi - 1) { + if (callee == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); + for (args, 0..) |arg, i| { + if (arg == operand_ref) return matchOperandSmallIndex(l, inst, @intCast(i + 1), .write); + } + return .write; + } + var bt = l.iterateBigTomb(inst); + if (bt.feed()) { + if (callee == operand_ref) return .tomb; + } else { + if (callee == operand_ref) return .write; + } + for (args) |arg| { + if (bt.feed()) { + if (arg == operand_ref) return .tomb; + } else { + if (arg == operand_ref) return .write; + } + } + return .write; +} + fn matchOperandSmallIndex( l: Liveness, inst: Air.Inst.Index, @@ -1108,23 +1125,15 @@ fn analyzeInst( const inst_data = inst_datas[inst].pl_op; const callee = inst_data.operand; const extra = a.air.extraData(Air.Call, inst_data.payload); - const args = @as([]const Air.Inst.Ref, @ptrCast(a.air.extra[extra.end..][0..extra.data.args_len])); - if (args.len + 1 <= bpi - 1) { - var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1); - buf[0] = callee; - @memcpy(buf[1..][0..args.len], args); - return analyzeOperands(a, pass, data, inst, buf); - } - - var big = try AnalyzeBigOperands(pass).init(a, data, inst, args.len + 1); - defer big.deinit(); - var i: usize = args.len; - while (i > 0) { - i -= 1; - try big.feed(args[i]); - } - try big.feed(callee); - return big.finish(); + const args: []const Air.Inst.Ref = @ptrCast(a.air.extra[extra.end..][0..extra.data.args_len]); + return analyzeInstCall(a, pass, data, inst, callee, args); + }, + .call_async => { + const inst_data = inst_datas[inst].ty_pl; + const extra = a.air.extraData(Air.AsyncCall, inst_data.payload); + const callee = extra.data.callee; + const args: []const Air.Inst.Ref = @ptrCast(a.air.extra[extra.end..][0..extra.data.args_len]); + return analyzeInstCall(a, pass, data, inst, callee, args); }, .select => { const pl_op = inst_datas[inst].pl_op; @@ -1253,6 +1262,32 @@ fn analyzeInst( } } +fn analyzeInstCall( + a: *Analysis, + comptime pass: LivenessPass, + data: *LivenessPassData(pass), + inst: Air.Inst.Index, + callee: Air.Inst.Ref, + args: []const Air.Inst.Ref, +) Allocator.Error!void { + if (args.len + 1 <= bpi - 1) { + var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1); + buf[0] = callee; + @memcpy(buf[1..][0..args.len], args); + return analyzeOperands(a, pass, data, inst, buf); + } + + var big = try AnalyzeBigOperands(pass).init(a, data, inst, args.len + 1); + defer big.deinit(); + var i: usize = args.len; + while (i > 0) { + i -= 1; + try big.feed(args[i]); + } + try big.feed(callee); + return big.finish(); +} + /// Every instruction should hit this (after handling any nested bodies), in every pass. In the /// initial pass, it is responsible for marking deaths of the (first three) operands and noticing /// immediate deaths. diff --git a/src/Liveness/Verify.zig b/src/Liveness/Verify.zig index 768d06a103412099cb8b77b9797fa04538f3ead7..5082c070e2424456d7a93108bb9644bfef3aa92a 100644 --- a/src/Liveness/Verify.zig +++ b/src/Liveness/Verify.zig @@ -349,6 +349,20 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { } try self.verifyInst(inst); }, + .call_async => { + const ty_pl = data[inst].ty_pl; + const extra = self.air.extraData(Air.AsyncCall, ty_pl.payload); + const args: []const Air.Inst.Ref = @ptrCast( + self.air.extra[extra.end..][0..extra.data.args_len], + ); + + var bt = self.liveness.iterateBigTomb(inst); + try self.verifyOperand(inst, extra.data.callee, bt.feed()); + for (args) |arg| { + try self.verifyOperand(inst, arg, bt.feed()); + } + try self.verifyInst(inst); + }, .assembly => { const ty_pl = data[inst].ty_pl; const extra = self.air.extraData(Air.Asm, ty_pl.payload); diff --git a/src/Module.zig b/src/Module.zig index 3bcc920fe2703a0e09cdf07d2462d28075ee6c0a..6b3274bf1b20b76313959619739bdefc7d78e8b5 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1437,6 +1437,7 @@ pub const Fn = struct { generic_owner_decl: Decl.OptionalIndex, state: Analysis, + async_status: AsyncStatus, is_cold: bool = false, is_noinline: bool, calls_or_awaits_errorable_fn: bool = false, @@ -1481,6 +1482,12 @@ pub const Fn = struct { success, }; + pub const AsyncStatus = enum { + unknown, + yes_async, + not_async, + }; + /// This struct is used to keep track of any dependencies related to functions instances /// that return inferred error sets. Note that a function may be associated to /// multiple different error sets, for example an inferred error set which @@ -1608,6 +1615,14 @@ pub const Fn = struct { else => unreachable, } } + + pub fn isAsync(func: Fn) bool { + return switch (func.async_status) { + .unknown => unreachable, + .yes_async => true, + .not_async => false, + }; + } }; pub const DeclAdapter = struct { @@ -2340,6 +2355,36 @@ pub const SrcLoc = struct { const full = tree.fullCall(&buf, node).?; return nodeToSpan(tree, full.ast.fn_expr); }, + .node_offset_async_call_func => |node_off| { + const tree = try src_loc.file_scope.getTree(gpa); + const node_tags = tree.nodes.items(.tag); + const node = src_loc.declRelativeToNodeIndex(node_off); + const var_decl: Ast.full.VarDecl = switch (node_tags[node]) { + .global_var_decl => tree.globalVarDecl(node), + .local_var_decl => tree.localVarDecl(node), + .simple_var_decl => tree.simpleVarDecl(node), + .aligned_var_decl => tree.alignedVarDecl(node), + else => unreachable, + }; + const init_node = var_decl.ast.init_node; + var params: [1]Ast.Node.Index = undefined; + const full = switch (node_tags[init_node]) { + .call_one, + .call_one_comma, + .async_call_one, + .async_call_one_comma, + => tree.callOne(¶ms, init_node), + + .call, + .call_comma, + .async_call, + .async_call_comma, + => tree.callFull(init_node), + + else => unreachable, + }; + return nodeToSpan(tree, full.ast.fn_expr); + }, .node_offset_field_name => |node_off| { const tree = try src_loc.file_scope.getTree(gpa); const node_datas = tree.nodes.items(.data); @@ -2963,6 +3008,14 @@ pub const LazySrcLoc = union(enum) { /// to the callee expression. /// The Decl is determined contextually. node_offset_call_func: i32, + /// Example: + /// var a = async b(); + /// ~ + /// The source location points to the callee expression of a function call + /// expression of a variable declaration, found by taking this AST node + /// index offset from the containing Decl AST node, which points to the + /// variable declaration node. The Decl is determined contextually. + node_offset_async_call_func: i32, /// The payload is offset from the containing Decl AST node. /// The source location points to the field name of: /// * a field access expression (`a.b`), or @@ -3192,6 +3245,7 @@ pub const LazySrcLoc = union(enum) { .node_offset_slice_end, .node_offset_slice_sentinel, .node_offset_call_func, + .node_offset_async_call_func, .node_offset_field_name, .node_offset_deref_ptr, .node_offset_asm_source, @@ -6869,6 +6923,10 @@ pub fn singleErrorSetType(mod: *Module, name: InternPool.NullTerminatedString) A return (try mod.intern_pool.get(mod.gpa, .{ .error_set_type = .{ .names = names } })).toType(); } +pub fn asyncFrameType(mod: *Module, func_index: Fn.Index) Allocator.Error!Type { + return (try mod.intern_pool.get(mod.gpa, .{ .async_frame_type = func_index })).toType(); +} + /// Sorts `names` in place. pub fn errorSetFromUnsortedNames( mod: *Module, diff --git a/src/Sema.zig b/src/Sema.zig index d2c930176f9b492e554437997e0e5a7bd06abe72..cbb0fbdc43db4dabee2f9a0ccdeddf7c2661f5f3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -941,8 +941,9 @@ fn analyzeBodyInner( .bool_br_and => try sema.zirBoolBr(block, inst, false), .bool_br_or => try sema.zirBoolBr(block, inst, true), .c_import => try sema.zirCImport(block, inst), - .call => try sema.zirCall(block, inst, .direct), - .field_call => try sema.zirCall(block, inst, .field), + .call => try sema.zirCall(block, inst, Zir.Inst.Call), + .field_call => try sema.zirCall(block, inst, Zir.Inst.FieldCall), + .async_call => try sema.zirAsyncCall(block, inst), .closure_get => try sema.zirClosureGet(block, inst), .cmp_lt => try sema.zirCmp(block, inst, .lt), .cmp_lte => try sema.zirCmp(block, inst, .lte), @@ -6435,7 +6436,7 @@ fn zirCall( sema: *Sema, block: *Block, inst: Zir.Inst.Index, - comptime kind: enum { direct, field }, + comptime ExtraType: type, ) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -6444,10 +6445,6 @@ fn zirCall( const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const callee_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node }; const call_src = inst_data.src(); - const ExtraType = switch (kind) { - .direct => Zir.Inst.Call, - .field => Zir.Inst.FieldCall, - }; const extra = sema.code.extraData(ExtraType, inst_data.payload_index); const args_len = extra.data.flags.args_len; @@ -6455,38 +6452,90 @@ fn zirCall( const ensure_result_used = extra.data.flags.ensure_result_used; const pop_error_return_trace = extra.data.flags.pop_error_return_trace; - const callee: ResolvedFieldCallee = switch (kind) { - .direct => .{ .direct = try sema.resolveInst(extra.data.callee) }, - .field => blk: { + const callee: ResolvedFieldCallee = switch (ExtraType) { + Zir.Inst.Call => .{ .direct = try sema.resolveInst(extra.data.callee) }, + Zir.Inst.FieldCall => blk: { const object_ptr = try sema.resolveInst(extra.data.obj_ptr); const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.data.field_name_start)); const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; break :blk try sema.fieldCallBind(block, callee_src, object_ptr, field_name, field_name_src); }, + else => @compileError("unreachable"), }; + return callCommon( + sema, + block, + inst, + callee_src, + call_src, + callee, + args_len, + modifier, + extra.end, + ensure_result_used, + pop_error_return_trace, + ); +} + +fn zirAsyncCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const func_src: LazySrcLoc = .{ .node_offset_async_call_func = inst_data.src_node }; + const call_src: LazySrcLoc = .{ .node_offset_var_decl_init = inst_data.src_node }; + const extra = sema.code.extraData(Zir.Inst.AsyncCall, inst_data.payload_index); + const args_len = extra.data.args_len; + const callee: ResolvedFieldCallee = .{ .direct = try sema.resolveInst(extra.data.callee) }; + return callCommon( + sema, + block, + inst, + func_src, + call_src, + callee, + args_len, + .async_kw, + extra.end, + false, + false, + ); +} + +fn callCommon( + sema: *Sema, + block: *Block, + inst: Zir.Inst.Index, + callee_src: LazySrcLoc, + call_src: LazySrcLoc, + callee: ResolvedFieldCallee, + args_len: u32, + modifier: std.builtin.CallModifier, + extra_end: usize, + ensure_result_used: bool, + pop_error_return_trace: bool, +) CompileError!Air.Inst.Ref { + const mod = sema.mod; + var resolved_args: []Air.Inst.Ref = undefined; var bound_arg_src: ?LazySrcLoc = null; - var func: Air.Inst.Ref = undefined; var arg_index: u32 = 0; - switch (callee) { - .direct => |func_inst| { + const func: Air.Inst.Ref = switch (callee) { + .direct => |func_inst| f: { resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len); - func = func_inst; + break :f func_inst; }, - .method => |method| { + .method => |method| f: { resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_len + 1); - func = method.func_inst; resolved_args[0] = method.arg0_inst; arg_index += 1; bound_arg_src = callee_src; + break :f method.func_inst; }, - } + }; const callee_ty = sema.typeOf(func); const total_args = args_len + @intFromBool(bound_arg_src != null); const func_ty = try sema.checkCallArgumentCount(block, func, callee_src, callee_ty, total_args, bound_arg_src != null); - const args_body = sema.code.extra[extra.end..]; + const args_body = sema.code.extra[extra_end..]; var input_is_error = false; const block_index = @as(Air.Inst.Index, @intCast(block.instructions.items.len)); @@ -6501,7 +6550,7 @@ fn zirCall( arg_index += 1; }) { const func_ty_info = mod.typeToFunc(func_ty).?; - const arg_end = sema.code.extra[extra.end + extra_index]; + const arg_end = sema.code.extra[extra_end + extra_index]; defer arg_start = arg_end; // Generate args to comptime params in comptime block. @@ -6730,8 +6779,7 @@ fn analyzeCall( .never_tail => Air.Inst.Tag.call_never_tail, .never_inline => Air.Inst.Tag.call_never_inline, .always_tail => Air.Inst.Tag.call_always_tail, - - .async_kw => return sema.failWithUseOfAsync(block, call_src), + .async_kw => Air.Inst.Tag.call_async, }; if (modifier == .never_inline and func_ty_info.cc == .Inline) { @@ -7158,18 +7206,20 @@ fn analyzeCall( } } - try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len + - args.len); - const func_inst = try block.addInst(.{ - .tag = call_tag, - .data = .{ .pl_op = .{ - .operand = func, - .payload = sema.addExtraAssumeCapacity(Air.Call{ - .args_len = @as(u32, @intCast(args.len)), - }), - } }, - }); - sema.appendRefsAssumeCapacity(args); + if (call_tag == .call_async) { + const func_val = sema.resolveConstValue(block, func_src, func, "function is not comptime-known; @asyncCall required") catch |err| { + if (err == error.AnalysisFail and comptime_reason != null) try comptime_reason.?.explain(sema, sema.err); + return err; + }; + const module_fn_index = switch (mod.intern_pool.indexToKey(func_val.toIntern())) { + .func => |function| function.index, + .ptr => |ptr| mod.declPtr(ptr.addr.decl).val.getFunctionIndex(mod).unwrap().?, + else => unreachable, + }; + break :res try addAsyncCallInst(sema, block, func, module_fn_index, args); + } + + const func_inst = try addCallInst(sema, block, func, args, call_tag); if (call_tag == .call_always_tail) { if (ensure_result_used) { @@ -7206,6 +7256,56 @@ fn analyzeCall( return result; } +fn addCallInst( + sema: *Sema, + block: *Block, + callee: Air.Inst.Ref, + args: []const Air.Inst.Ref, + call_tag: Air.Inst.Tag, +) Allocator.Error!Air.Inst.Ref { + try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len + + args.len); + const call_inst = try block.addInst(.{ + .tag = call_tag, + .data = .{ .pl_op = .{ + .operand = callee, + .payload = sema.addExtraAssumeCapacity(Air.Call{ + .args_len = @intCast(args.len), + }), + } }, + }); + sema.appendRefsAssumeCapacity(args); + return call_inst; +} + +fn addAsyncCallInst( + sema: *Sema, + block: *Block, + callee: Air.Inst.Ref, + callee_fn: Module.Fn.Index, + args: []const Air.Inst.Ref, +) Allocator.Error!Air.Inst.Ref { + const mod = sema.mod; + const frame_ty = try mod.asyncFrameType(callee_fn); + const frame_ty_ref = try sema.addType(frame_ty); + try sema.air_extra.ensureUnusedCapacity( + sema.gpa, + @typeInfo(Air.AsyncCall).Struct.fields.len + args.len, + ); + const call_inst = try block.addInst(.{ + .tag = .call_async, + .data = .{ .ty_pl = .{ + .ty = frame_ty_ref, + .payload = sema.addExtraAssumeCapacity(Air.AsyncCall{ + .callee = callee, + .args_len = @intCast(args.len), + }), + } }, + }); + sema.appendRefsAssumeCapacity(args); + return call_inst; +} + fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref { const mod = sema.mod; const target = mod.getTarget(); @@ -7664,19 +7764,10 @@ fn instantiateGenericCall( } try mod.ensureFuncBodyAnalysisQueued(callee_index); - - try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len + - runtime_args_len); - const result = try block.addInst(.{ - .tag = call_tag, - .data = .{ .pl_op = .{ - .operand = callee_inst, - .payload = sema.addExtraAssumeCapacity(Air.Call{ - .args_len = runtime_args_len, - }), - } }, - }); - sema.appendRefsAssumeCapacity(runtime_args); + const result = switch (call_tag) { + .call_async => try addAsyncCallInst(sema, block, callee_inst, callee_index, runtime_args), + else => try addCallInst(sema, block, callee_inst, runtime_args, call_tag), + }; if (ensure_result_used) { try sema.ensureResultUsed(block, sema.typeOf(result), call_src); @@ -9221,6 +9312,7 @@ fn funcCommon( const generic_owner_decl = if (comptime_args == null) .none else new_func.generic_owner_decl; new_func.* = .{ .state = anal_state, + .async_status = .unknown, .zir_body_inst = func_inst, .owner_decl = sema.owner_decl_index, .generic_owner_decl = generic_owner_decl, @@ -33730,6 +33822,7 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { .error_set_type, .inferred_error_set_type => false, .func_type => true, + .async_frame_type => false, .simple_type => |t| switch (t) { .f16, @@ -35271,6 +35364,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { .type_inferred_error_set, .type_opaque, .type_function, + .type_async_frame, => null, .simple_type, // handled above // values, not types @@ -35934,6 +36028,7 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { }, .opaque_type => false, + .async_frame_type => false, .enum_type => |enum_type| try sema.typeRequiresComptime(enum_type.tag_ty.toType()), // values, not types diff --git a/src/TypedValue.zig b/src/TypedValue.zig index 5abcd7b2807b65885cf836c95c25b80254d4df13..106fd205a816880441432d378dafe8ad88ff55c8 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -192,6 +192,7 @@ pub fn print( .func_type, .error_set_type, .inferred_error_set_type, + .async_frame_type, => return Type.print(val.toType(), writer, mod), .undef => return writer.writeAll("undefined"), .runtime_value => return writer.writeAll("(runtime value)"), diff --git a/src/Zir.zig b/src/Zir.zig index d3dc549dcfa04bc939dd0ed7be320d9a8460e7cf..3d685554bf52c54894e27d0b1015ebcf10da3342 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -313,6 +313,11 @@ pub const Inst = struct { /// Uses the `pl_node` union field with payload `BuiltinCall`. /// AST node is the builtin call. builtin_call, + /// An async function call that also acts as an alloc. Corresponds with + /// the syntax `var foo = async bar();`. + /// Uses the `pl_node` union field with payload `AsyncCall` + /// AST node is the entire variable declaration, with the init node being a call. + async_call, /// `<` /// Uses the `pl_node` union field. Payload is `Bin`. cmp_lt, @@ -1026,6 +1031,7 @@ pub const Inst = struct { .bool_not, .call, .field_call, + .async_call, .cmp_lt, .cmp_lte, .cmp_eq, @@ -1330,6 +1336,7 @@ pub const Inst = struct { .bool_not, .call, .field_call, + .async_call, .cmp_lt, .cmp_lte, .cmp_eq, @@ -1564,6 +1571,7 @@ pub const Inst = struct { .for_len = .pl_node, .call = .pl_node, .field_call = .pl_node, + .async_call = .pl_node, .cmp_lt = .pl_node, .cmp_lte = .pl_node, .cmp_eq = .pl_node, @@ -1944,7 +1952,7 @@ pub const Inst = struct { /// `small` contains `NameStrategy`. reify, /// Implements the `@asyncCall` builtin. - /// `operand` is payload index to `AsyncCall`. + /// `operand` is payload index to `BuiltinAsyncCall`. builtin_async_call, /// Implements the `@cmpxchgStrong` and `@cmpxchgWeak` builtins. /// `small` 0=>weak 1=>strong @@ -2531,6 +2539,16 @@ pub const Inst = struct { field_name_start: u32, }; + /// Not to be confused with BuiltinAsyncCall. + /// Stored inside extra, with trailing arguments according to `args_len`. + /// Implicit 0. arg_0_start: u32, // always same as `args_len` + /// 1. arg_end: u32, // for each `args_len` + /// arg_N_start is the same as arg_N-1_end + pub const AsyncCall = struct { + callee: Ref, + args_len: u32, + }; + pub const TypeOfPeer = struct { src_node: i32, body_len: u32, @@ -3101,7 +3119,8 @@ pub const Inst = struct { b: Ref, }; - pub const AsyncCall = struct { + /// Not to be confused with AsyncCall. + pub const BuiltinAsyncCall = struct { node: i32, frame_buffer: Ref, result_ptr: Ref, diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 4d212148b27d63759735132c0309a2694c1a7b31..f2199c1a57033e8696f9193fe208decee8ea5431 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -819,6 +819,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .call_always_tail => try self.airCall(inst, .always_tail), .call_never_tail => try self.airCall(inst, .never_tail), .call_never_inline => try self.airCall(inst, .never_inline), + .call_async => try self.airCall(inst, .async_kw), .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), @@ -4242,6 +4243,7 @@ fn airFence(self: *Self) !void { fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { if (modifier == .always_tail) return self.fail("TODO implement tail calls for aarch64", .{}); + if (modifier == .async_kw) return self.fail("TODO implement async calls for aarch64", .{}); const pl_op = self.air.instructions.items(.data)[inst].pl_op; const callee = pl_op.operand; const extra = self.air.extraData(Air.Call, pl_op.payload); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index d3f1efb192063bcbcce0830c803626a70ed03541..e58725648beb2c8cd13e537c5321685dff9a9730 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -803,6 +803,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .call_always_tail => try self.airCall(inst, .always_tail), .call_never_tail => try self.airCall(inst, .never_tail), .call_never_inline => try self.airCall(inst, .never_inline), + .call_async => try self.airCall(inst, .async_kw), .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), @@ -4215,6 +4216,7 @@ fn airFence(self: *Self) !void { fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { if (modifier == .always_tail) return self.fail("TODO implement tail calls for arm", .{}); + if (modifier == .async_kw) return self.fail("TODO implement async calls for arm", .{}); const pl_op = self.air.instructions.items(.data)[inst].pl_op; const callee = pl_op.operand; const extra = self.air.extraData(Air.Call, pl_op.payload); diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index b15ac531e0e6c675da3809f07ed95792623ee96d..810279b956287aecb8b2eb5978e3d7487c060bf8 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -638,6 +638,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .call_always_tail => try self.airCall(inst, .always_tail), .call_never_tail => try self.airCall(inst, .never_tail), .call_never_inline => try self.airCall(inst, .never_inline), + .call_async => try self.airCall(inst, .async_kw), .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), @@ -1707,6 +1708,7 @@ fn airFence(self: *Self) !void { fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { const mod = self.bin_file.options.module.?; if (modifier == .always_tail) return self.fail("TODO implement tail calls for riscv64", .{}); + if (modifier == .async_kw) return self.fail("TODO implement async calls for riscv64", .{}); const pl_op = self.air.instructions.items(.data)[inst].pl_op; const fn_ty = self.typeOf(pl_op.operand); const callee = pl_op.operand; diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 648e1bee4570e32e5c152131778557fbde523b77..ca256ea8243280965612b98c1aa88ed610aa71d0 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -651,6 +651,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .call_always_tail => try self.airCall(inst, .always_tail), .call_never_tail => try self.airCall(inst, .never_tail), .call_never_inline => try self.airCall(inst, .never_inline), + .call_async => try self.airCall(inst, .async_kw), .atomic_store_unordered => @panic("TODO try self.airAtomicStore(inst, .Unordered)"), .atomic_store_monotonic => @panic("TODO try self.airAtomicStore(inst, .Monotonic)"), @@ -1293,6 +1294,7 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { if (modifier == .always_tail) return self.fail("TODO implement tail calls for {}", .{self.target.cpu.arch}); + if (modifier == .async_kw) return self.fail("TODO implement async calls for {}", .{self.target.cpu.arch}); const pl_op = self.air.instructions.items(.data)[inst].pl_op; const callee = pl_op.operand; diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 33d4a46741f990a4a933c32a737a1ee5c2b3cc4f..e691e51bbbbb6d63a108659d63455e2d15167d95 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1930,6 +1930,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { .call_always_tail => func.airCall(inst, .always_tail), .call_never_tail => func.airCall(inst, .never_tail), .call_never_inline => func.airCall(inst, .never_inline), + .call_async => func.airCall(inst, .async_kw), .is_err => func.airIsErr(inst, .i32_ne), .is_non_err => func.airIsErr(inst, .i32_eq), @@ -2180,6 +2181,7 @@ fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void { if (modifier == .always_tail) return func.fail("TODO implement tail calls for wasm", .{}); + if (modifier == .async_kw) return func.fail("TODO implement async calls for wasm", .{}); const pl_op = func.air.instructions.items(.data)[inst].pl_op; const extra = func.air.extraData(Air.Call, pl_op.payload); const args = @as([]const Air.Inst.Ref, @ptrCast(func.air.extra[extra.end..][0..extra.data.args_len])); @@ -3125,6 +3127,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { .func_type, .error_set_type, .inferred_error_set_type, + .async_frame_type, => unreachable, // types, not values .undef, .runtime_value => unreachable, // handled above diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 9d4804c8c882aaec5913fd7304e2ebbcd19afa50..be9f465cc859410ecab4f4fa6f30e7e788d68330 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1901,6 +1901,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .call_always_tail => try self.airCall(inst, .always_tail), .call_never_tail => try self.airCall(inst, .never_tail), .call_never_inline => try self.airCall(inst, .never_inline), + .call_async => try self.airCall(inst, .async_kw), .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), @@ -8059,6 +8060,7 @@ fn airFence(self: *Self, inst: Air.Inst.Index) !void { fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { const mod = self.bin_file.options.module.?; if (modifier == .always_tail) return self.fail("TODO implement tail calls for x86_64", .{}); + if (modifier == .async_kw) return self.fail("TODO implement async calls for x86_64", .{}); const pl_op = self.air.instructions.items(.data)[inst].pl_op; const callee = pl_op.operand; const extra = self.air.extraData(Air.Call, pl_op.payload); diff --git a/src/codegen.zig b/src/codegen.zig index 69499fb1ad80f0d7a1dc48e88c2a7f5aa736fe7d..3547be840086182c4728ecabbb605fadc361e37c 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -222,6 +222,7 @@ pub fn generateSymbol( .func_type, .error_set_type, .inferred_error_set_type, + .async_frame_type, => unreachable, // types, not values .undef, .runtime_value => unreachable, // handled above diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 38723a05f144b8dc8896c398eaf17db387e27395..19df11efdaea2a2aab257b7ac2ebeb67a336ab57 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -927,6 +927,7 @@ pub const DeclGen = struct { .func_type, .error_set_type, .inferred_error_set_type, + .async_frame_type, // memoization, not values .memoized_call, => unreachable, @@ -2999,6 +3000,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .call_always_tail => .none, .call_never_tail => try airCall(f, inst, .never_tail), .call_never_inline => try airCall(f, inst, .never_inline), + .call_async => try airCall(f, inst, .async_kw), .float_from_int, .int_from_float, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index e9f29b725329ebbe1de7ffa669a897afd13252df..2cb15bc374a65e6a35ec09d67b63b69810bb65b9 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3017,11 +3017,58 @@ pub const Object = struct { .Null => unreachable, .EnumLiteral => unreachable, - .Frame => @panic("TODO implement llvmType for Frame types"), - .AnyFrame => @panic("TODO implement llvmType for AnyFrame types"), + .Frame => { + const gop = try o.type_map.getOrPut(gpa, t.toIntern()); + if (gop.found_existing) return gop.value_ptr.*; + + const func_index = mod.intern_pool.indexToKey(t.toIntern()).async_frame_type; + const func = mod.funcPtr(func_index); + const owner_decl = mod.declPtr(func.owner_decl); + + var name_buf = std.ArrayList(u8).init(gpa); + defer name_buf.deinit(); + try name_buf.appendSlice("@Frame("); + try owner_decl.renderFullyQualifiedName(mod, name_buf.writer()); + try name_buf.appendSlice(")\x00"); + const name = name_buf.items[0 .. name_buf.items.len - 1 :0]; + + const llvm_struct_ty = o.context.structCreateNamed(name); + gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls + + return lowerAsyncFrameType(o, func, llvm_struct_ty); + //if (func.isAsync()) { + // return lowerAsyncFrameType(o, func, llvm_struct_ty); + //} else { + // @panic("lower llvm @Frame() type of non-async function"); + //} + }, + .AnyFrame => return o.context.pointerType(0), } } + fn lowerAsyncFrameType( + o: *Object, + func: *Module.Fn, + llvm_struct_ty: *llvm.Type, + ) Allocator.Error!*llvm.Type { + const gpa = o.gpa; + var llvm_field_types: std.ArrayListUnmanaged(*llvm.Type) = .{}; + defer llvm_field_types.deinit(gpa); + + try llvm_field_types.ensureUnusedCapacity(gpa, 1); + _ = func; + llvm_field_types.appendAssumeCapacity(o.context.intType(32)); + + const any_underaligned_fields = false; + llvm_struct_ty.structSetBody( + llvm_field_types.items.ptr, + @intCast(llvm_field_types.items.len), + llvm.Bool.fromBool(any_underaligned_fields), + ); + + return llvm_struct_ty; + } + fn lowerTypeFn(o: *Object, fn_ty: Type) Allocator.Error!*llvm.Type { const mod = o.module; const fn_info = mod.typeToFunc(fn_ty).?; @@ -3148,6 +3195,7 @@ pub const Object = struct { .func_type, .error_set_type, .inferred_error_set_type, + .async_frame_type, => unreachable, // types, not values .undef, .runtime_value => unreachable, // handled above @@ -4474,6 +4522,7 @@ pub const FuncGen = struct { .call_always_tail => try self.airCall(inst, .AlwaysTail), .call_never_tail => try self.airCall(inst, .NeverTail), .call_never_inline => try self.airCall(inst, .NeverInline), + .call_async => try self.airCallAsync(inst), .ptr_slice_ptr_ptr => try self.airPtrSliceFieldPtr(inst, 0), .ptr_slice_len_ptr => try self.airPtrSliceFieldPtr(inst, 1), @@ -4939,6 +4988,11 @@ pub const FuncGen = struct { _ = fg.builder.buildUnreachable(); } + fn airCallAsync(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { + _ = inst; + return self.todo("lower async call", .{}); + } + fn airRet(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { const o = self.dg.object; const mod = o.module; diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index e8cac568c68d0cb1cb20751c4e329fd1161f4720..5f52a99391ad5f2fd4d7eb9cc000d8d8d2cc47b5 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -642,6 +642,7 @@ pub const DeclGen = struct { .func_type, .error_set_type, .inferred_error_set_type, + .async_frame_type, => unreachable, // types, not values .undef, .runtime_value => unreachable, // handled above diff --git a/src/print_air.zig b/src/print_air.zig index 92b48f762210d56a79a3fded795edcc4382f9746..e1ac169340c302a293bb9067e276f37f5c021ffc 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -329,6 +329,7 @@ const Writer = struct { .reduce, .reduce_optimized => try w.writeReduce(s, inst), .cmp_vector, .cmp_vector_optimized => try w.writeCmpVector(s, inst), .vector_store_elem => try w.writeVectorStoreElem(s, inst), + .call_async => try w.writeCallAsync(s, inst), .dbg_block_begin, .dbg_block_end => {}, @@ -699,8 +700,26 @@ const Writer = struct { fn writeCall(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const pl_op = w.air.instructions.items(.data)[inst].pl_op; const extra = w.air.extraData(Air.Call, pl_op.payload); - const args = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra[extra.end..][0..extra.data.args_len])); - try w.writeOperand(s, inst, 0, pl_op.operand); + const args: []const Air.Inst.Ref = @ptrCast(w.air.extra[extra.end..][0..extra.data.args_len]); + return finishWriteCall(w, s, inst, pl_op.operand, args); + } + + fn writeCallAsync(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; + const extra = w.air.extraData(Air.AsyncCall, ty_pl.payload); + const callee = extra.data.callee; + const args: []const Air.Inst.Ref = @ptrCast(w.air.extra[extra.end..][0..extra.data.args_len]); + return finishWriteCall(w, s, inst, callee, args); + } + + fn finishWriteCall( + w: *Writer, + s: anytype, + inst: Air.Inst.Index, + callee: Air.Inst.Ref, + args: []const Air.Inst.Ref, + ) @TypeOf(s).Error!void { + try w.writeOperand(s, inst, 0, callee); try s.writeAll(", ["); for (args, 0..) |arg, i| { if (i != 0) try s.writeAll(", "); diff --git a/src/print_zir.zig b/src/print_zir.zig index 42a9abf401c5556ea78488ff1f9fc94e802fb348..8633c174232df353c0c5b6236858e26e5a7f48bd 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -362,6 +362,7 @@ const Writer = struct { .call => try self.writeCall(stream, inst, .direct), .field_call => try self.writeCall(stream, inst, .field), + .async_call => try self.writeAsyncCall(stream, inst), .block, .block_comptime, @@ -837,7 +838,7 @@ const Writer = struct { } fn writeBuiltinAsyncCall(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void { - const extra = self.code.extraData(Zir.Inst.AsyncCall, extended.operand).data; + const extra = self.code.extraData(Zir.Inst.BuiltinAsyncCall, extended.operand).data; try self.writeInstRef(stream, extra.frame_buffer); try stream.writeAll(", "); try self.writeInstRef(stream, extra.result_ptr); @@ -1187,11 +1188,13 @@ const Writer = struct { try self.writeSrc(stream, src); } + const CallKind = enum { direct, field }; + fn writeCall( self: *Writer, stream: anytype, inst: Zir.Inst.Index, - comptime kind: enum { direct, field }, + comptime kind: CallKind, ) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const ExtraType = switch (kind) { @@ -1201,11 +1204,12 @@ const Writer = struct { const extra = self.code.extraData(ExtraType, inst_data.payload_index); const args_len = extra.data.flags.args_len; const body = self.code.extra[extra.end..]; + const modifier: std.builtin.CallModifier = @enumFromInt(extra.data.flags.packed_modifier); if (extra.data.flags.ensure_result_used) { try stream.writeAll("nodiscard "); } - try stream.print(".{s}, ", .{@tagName(@as(std.builtin.CallModifier, @enumFromInt(extra.data.flags.packed_modifier)))}); + try stream.print(".{s}, ", .{@tagName(modifier)}); switch (kind) { .direct => try self.writeInstRef(stream, extra.data.callee), .field => { @@ -1214,6 +1218,28 @@ const Writer = struct { try stream.print(", \"{}\"", .{std.zig.fmtEscapes(field_name)}); }, } + return finishWriteCall(self, stream, body, args_len, extra.end, inst_data.src()); + } + + fn writeAsyncCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Zir.Inst.AsyncCall, inst_data.payload_index); + const args_len = extra.data.args_len; + const body = self.code.extra[extra.end..]; + const callee = extra.data.callee; + try stream.print(".{s}, ", .{@tagName(std.builtin.CallModifier.async_kw)}); + try self.writeInstRef(stream, callee); + return finishWriteCall(self, stream, body, args_len, extra.end, inst_data.src()); + } + + fn finishWriteCall( + self: *Writer, + stream: anytype, + body: []const Zir.Inst.Index, + args_len: u32, + extra_end: usize, + src: Module.LazySrcLoc, + ) !void { try stream.writeAll(", ["); self.indent += 2; @@ -1224,7 +1250,7 @@ const Writer = struct { var arg_start: u32 = args_len; while (i < args_len) : (i += 1) { try stream.writeByteNTimes(' ', self.indent); - const arg_end = self.code.extra[extra.end + i]; + const arg_end = self.code.extra[extra_end + i]; defer arg_start = arg_end; const arg_body = body[arg_start..arg_end]; try self.writeBracedBody(stream, arg_body); @@ -1237,7 +1263,7 @@ const Writer = struct { } try stream.writeAll("]) "); - try self.writeSrc(stream, inst_data.src()); + try self.writeSrc(stream, src); } fn writeBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { diff --git a/src/type.zig b/src/type.zig index e4ae2d2c353955787d8bf304a07e8f85ef5d4049..1a6bffbc1fbdb7eef891b20f9550b2280a04806c 100644 --- a/src/type.zig +++ b/src/type.zig @@ -409,6 +409,13 @@ pub const Type = struct { try writer.writeAll("anyframe->"); return print(child.toType(), writer, mod); }, + .async_frame_type => |func_index| { + const func = mod.funcPtr(func_index); + const owner_decl = mod.declPtr(func.owner_decl); + try writer.writeAll("@Frame("); + try owner_decl.renderFullyQualifiedName(mod, writer); + try writer.writeAll(")"); + }, // values, not types .undef, @@ -506,6 +513,7 @@ pub const Type = struct { .error_union_type, .error_set_type, .inferred_error_set_type, + .async_frame_type, => true, // These are function *bodies*, not pointers. @@ -666,6 +674,7 @@ pub const Type = struct { .anon_struct_type, .opaque_type, .anyframe_type, + .async_frame_type, // These are function bodies, not function pointers. .func_type, => false, @@ -1068,6 +1077,9 @@ pub const Type = struct { .opaque_type => return AbiAlignmentAdvanced{ .scalar = 1 }, .enum_type => |enum_type| return AbiAlignmentAdvanced{ .scalar = enum_type.tag_ty.toType().abiAlignment(mod) }, + // TODO: revisit this + .async_frame_type => return AbiAlignmentAdvanced{ .scalar = 16 }, + // values, not types .undef, .runtime_value, @@ -1484,6 +1496,25 @@ pub const Type = struct { .opaque_type => unreachable, // no size available .enum_type => |enum_type| return AbiSizeAdvanced{ .scalar = enum_type.tag_ty.toType().abiSize(mod) }, + .async_frame_type => { + switch (strat) { + .sema => |sema| { + _ = sema; + @panic("they asked for the size of an async frame from sema"); + }, + .lazy => { + // TODO: sometimes this might already be resolved + return .{ .val = (try mod.intern(.{ .int = .{ + .ty = .comptime_int_type, + .storage = .{ .lazy_size = ty.toIntern() }, + } })).toValue() }; + }, + .eager => { + @panic("they eagerly asked for the size of an async frame"); + }, + } + }, + // values, not types .undef, .runtime_value, @@ -1509,7 +1540,7 @@ pub const Type = struct { } } - pub fn abiSizeAdvancedUnion( + fn abiSizeAdvancedUnion( ty: Type, mod: *Module, strat: AbiAlignmentAdvancedStrat, @@ -1717,6 +1748,9 @@ pub const Type = struct { }, .opaque_type => unreachable, .enum_type => |enum_type| return bitSizeAdvanced(enum_type.tag_ty.toType(), mod, opt_sema), + .async_frame_type => { + @panic("TODO bitSize async_frame_type"); + }, // values, not types .undef, @@ -2263,6 +2297,7 @@ pub const Type = struct { .ptr_type => unreachable, .anyframe_type => unreachable, + .async_frame_type => unreachable, .array_type => unreachable, .opt_type => unreachable, @@ -2445,6 +2480,7 @@ pub const Type = struct { .anyframe_type, .error_set_type, .inferred_error_set_type, + .async_frame_type, => return null, inline .array_type, .vector_type => |seq_type, seq_tag| { @@ -2760,6 +2796,7 @@ pub const Type = struct { }, .opaque_type => false, + .async_frame_type => false, .enum_type => |enum_type| enum_type.tag_ty.toType().comptimeOnly(mod), diff --git a/src/value.zig b/src/value.zig index 6b85ebd552db01f882d3c0a0aab45db6583ae2ba..f551120b6de06840103bd10129fc17ebfbc9c004 100644 --- a/src/value.zig +++ b/src/value.zig @@ -349,6 +349,7 @@ pub const Value = struct { .func_type, .error_set_type, .inferred_error_set_type, + .async_frame_type, .undef, .runtime_value, -- 2.54.0