From a6eb83bd1b7739156ab01e3fc55588f67d924daa Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 21 Sep 2022 14:33:42 -0700 Subject: [PATCH] AIR: add call_async_alloc instruction This is for async calls that also act as an alloca. This helps avoid unnecessarily complicated machinery for the simple case of `var a = async b();`. The `call_async` instruction has a frame pointer and returns void always, which will be used for the other form: `a = async b();`. --- src/Air.zig | 18 ++++++-- src/Liveness.zig | 85 +++++++++++++++++++++++++++--------- src/Liveness/Verify.zig | 18 +++++++- src/Sema.zig | 6 +-- src/arch/aarch64/CodeGen.zig | 1 + src/arch/arm/CodeGen.zig | 1 + src/arch/riscv64/CodeGen.zig | 1 + src/arch/sparc64/CodeGen.zig | 1 + src/arch/wasm/CodeGen.zig | 1 + src/arch/x86_64/CodeGen.zig | 1 + src/codegen/c.zig | 1 + src/codegen/llvm.zig | 19 +++++--- src/print_air.zig | 18 ++++++-- 13 files changed, 132 insertions(+), 39 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 37001e002ceb9c7d3e2bb71c0d2508b745bd4f71..e08b94e56447e53022c83fd6df6e709875775673 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -310,9 +310,14 @@ 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. + /// Async function call, using a provided frame pointer. + /// Uses `pl_op` field with the `AsyncCall` payload. operand is the callee. + /// Result type is always void. call_async, + /// Async function call, which allocates the frame for the callee on the stack. + /// This instruction also acts as an alloc. + /// Uses `ty_pl` field with the `AsyncCallAlloc` payload. + call_async_alloc, /// 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. @@ -1076,6 +1081,11 @@ pub const Call = struct { /// Trailing is a list of `Inst.Ref` for every `args_len`. pub const AsyncCall = struct { frame_ptr: Inst.Ref, + args_len: u32, +}; + +/// Trailing is a list of `Inst.Ref` for every `args_len`. +pub const AsyncCallAlloc = struct { callee: Inst.Ref, args_len: u32, }; @@ -1350,7 +1360,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) .ptr_add, .ptr_sub, .try_ptr, - .call_async, + .call_async_alloc, => return air.getRefType(datas[inst].ty_pl.ty), .interned => return ip.typeOf(datas[inst].interned).toType(), @@ -1429,6 +1439,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) .set_err_return_trace, .vector_store_elem, .c_va_end, + .call_async, => return Type.void, .int_from_ptr, @@ -1595,6 +1606,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { .call_never_tail, .call_never_inline, .call_async, + .call_async_alloc, .cond_br, .switch_br, .@"try", diff --git a/src/Liveness.zig b/src/Liveness.zig index dbe92e5a5ebf4269a1c7688c6b8aecea02d8f95d..6844f8216798e5a10fe12535c42fdb3911e90104 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -484,15 +484,25 @@ 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 frame_ptr: Air.Inst.Ref = .none; const args: []const Air.Inst.Ref = @ptrCast(air.extra[extra.end..][0..extra.data.args_len]); - return categorizeOperandCall(l, inst, operand_ref, callee, args); + return categorizeOperandCall(l, inst, operand_ref, frame_ptr, callee, args); }, .call_async => { - const inst_data = air_datas[inst].ty_pl; + const inst_data = air_datas[inst].pl_op; const extra = air.extraData(Air.AsyncCall, inst_data.payload); + const callee = inst_data.operand; + const frame_ptr = extra.data.frame_ptr; + const args: []const Air.Inst.Ref = @ptrCast(air.extra[extra.end..][0..extra.data.args_len]); + return categorizeOperandCall(l, inst, operand_ref, frame_ptr, callee, args); + }, + .call_async_alloc => { + const inst_data = air_datas[inst].ty_pl; + const extra = air.extraData(Air.AsyncCallAlloc, inst_data.payload); const callee = extra.data.callee; + const frame_ptr: Air.Inst.Ref = .none; const args: []const Air.Inst.Ref = @ptrCast(air.extra[extra.end..][0..extra.data.args_len]); - return categorizeOperandCall(l, inst, operand_ref, callee, args); + return categorizeOperandCall(l, inst, operand_ref, frame_ptr, callee, args); }, .select => { const pl_op = air_datas[inst].pl_op; @@ -661,21 +671,37 @@ pub fn categorizeOperand( } } -fn categorizeOperandCall( +pub fn categorizeOperandCall( l: Liveness, inst: Air.Inst.Index, operand_ref: Air.Inst.Ref, + frame_ptr: 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); + const total = args.len + 1 + @intFromBool(frame_ptr != .none); + if (total <= bpi - 1) { + var op_index: OperandInt = 0; + if (frame_ptr != .none) { + if (frame_ptr == operand_ref) return matchOperandSmallIndex(l, inst, op_index, .write); + op_index += 1; + } + if (callee == operand_ref) return matchOperandSmallIndex(l, inst, op_index, .write); + + for (args) |arg| { + op_index += 1; + if (arg == operand_ref) return matchOperandSmallIndex(l, inst, op_index, .write); } return .write; } var bt = l.iterateBigTomb(inst); + if (frame_ptr != .none) { + if (bt.feed()) { + if (frame_ptr == operand_ref) return .tomb; + } else { + if (frame_ptr == operand_ref) return .write; + } + } if (bt.feed()) { if (callee == operand_ref) return .tomb; } else { @@ -1122,18 +1148,28 @@ fn analyzeInst( }, .call, .call_always_tail, .call_never_tail, .call_never_inline => { + const pl_op = inst_datas[inst].pl_op; + const callee = pl_op.operand; + const frame_ptr: Air.Inst.Ref = .none; + const extra = a.air.extraData(Air.Call, pl_op.payload); + const args: []const Air.Inst.Ref = @ptrCast(a.air.extra[extra.end..][0..extra.data.args_len]); + return analyzeInstCall(a, pass, data, inst, frame_ptr, callee, args); + }, + .call_async => { 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: []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 = inst_data.operand; + const args: []const Air.Inst.Ref = @ptrCast(a.air.extra[extra.end..][0..extra.data.args_len]); + const frame_ptr = extra.data.frame_ptr; + return analyzeInstCall(a, pass, data, inst, frame_ptr, callee, args); + }, + .call_async_alloc => { + const ty_pl = inst_datas[inst].ty_pl; + const extra = a.air.extraData(Air.AsyncCallAlloc, ty_pl.payload); const callee = extra.data.callee; + const frame_ptr: Air.Inst.Ref = .none; 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); + return analyzeInstCall(a, pass, data, inst, frame_ptr, callee, args); }, .select => { const pl_op = inst_datas[inst].pl_op; @@ -1267,24 +1303,33 @@ fn analyzeInstCall( comptime pass: LivenessPass, data: *LivenessPassData(pass), inst: Air.Inst.Index, + frame_ptr: Air.Inst.Ref, callee: Air.Inst.Ref, args: []const Air.Inst.Ref, ) Allocator.Error!void { - if (args.len + 1 <= bpi - 1) { + const total = args.len + 1 + @intFromBool(frame_ptr != .none); + if (total <= bpi - 1) { var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1); - buf[0] = callee; - @memcpy(buf[1..][0..args.len], args); + var op_index: OperandInt = 0; + if (frame_ptr != .none) { + buf[op_index] = frame_ptr; + op_index += 1; + } + buf[op_index] = callee; + op_index += 1; + @memcpy(buf[op_index..][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(); + if (frame_ptr != .none) try big.feed(frame_ptr); + try big.feed(callee); var i: usize = args.len; while (i > 0) { i -= 1; try big.feed(args[i]); } - try big.feed(callee); return big.finish(); } diff --git a/src/Liveness/Verify.zig b/src/Liveness/Verify.zig index 5082c070e2424456d7a93108bb9644bfef3aa92a..2d0d694761193721aa1d988900710fe4f64d9c9c 100644 --- a/src/Liveness/Verify.zig +++ b/src/Liveness/Verify.zig @@ -350,8 +350,24 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { try self.verifyInst(inst); }, .call_async => { + const pl_op = data[inst].pl_op; + const extra = self.air.extraData(Air.AsyncCall, pl_op.payload); + const args: []const Air.Inst.Ref = @ptrCast( + self.air.extra[extra.end..][0..extra.data.args_len], + ); + const callee = pl_op.operand; + + var bt = self.liveness.iterateBigTomb(inst); + try self.verifyOperand(inst, extra.data.frame_ptr, bt.feed()); + try self.verifyOperand(inst, callee, bt.feed()); + for (args) |arg| { + try self.verifyOperand(inst, arg, bt.feed()); + } + try self.verifyInst(inst); + }, + .call_async_alloc => { const ty_pl = data[inst].ty_pl; - const extra = self.air.extraData(Air.AsyncCall, ty_pl.payload); + const extra = self.air.extraData(Air.AsyncCallAlloc, ty_pl.payload); const args: []const Air.Inst.Ref = @ptrCast( self.air.extra[extra.end..][0..extra.data.args_len], ); diff --git a/src/Sema.zig b/src/Sema.zig index 2f60982dd2c54b0c577b0fbe71f9dbc928cf0f3b..9fb9e4daf70d6105b384ab35861addee0c168a6f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7299,18 +7299,16 @@ fn addAsyncCallInst( ) Allocator.Error!Air.Inst.Ref { const mod = sema.mod; const ptr_frame_ty = try mod.singleMutPtrType(try mod.asyncFrameType(callee_fn)); - const frame_ptr = try block.addTy(.alloc, ptr_frame_ty); const ptr_frame_ty_ref = try sema.addType(ptr_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, + .tag = .call_async_alloc, .data = .{ .ty_pl = .{ .ty = ptr_frame_ty_ref, - .payload = sema.addExtraAssumeCapacity(Air.AsyncCall{ - .frame_ptr = frame_ptr, + .payload = sema.addExtraAssumeCapacity(Air.AsyncCallAlloc{ .callee = callee, .args_len = @intCast(args.len), }), diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index f2199c1a57033e8696f9193fe208decee8ea5431..d5d5c3ec18a07be0a6e171a65ad34d3333856165 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -820,6 +820,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .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), + .call_async_alloc => try self.airCall(inst, .async_kw), .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index e58725648beb2c8cd13e537c5321685dff9a9730..d845be6afbfb60a7881eda577b57dbe9a1e58b11 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -804,6 +804,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .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), + .call_async_alloc => try self.airCall(inst, .async_kw), .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 810279b956287aecb8b2eb5978e3d7487c060bf8..80858e6ce4676c4a637337627b81d0205299138b 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -639,6 +639,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .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), + .call_async_alloc => try self.airCall(inst, .async_kw), .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index ca256ea8243280965612b98c1aa88ed610aa71d0..f30c128b2bdda51f4c4046f27f975d155b8add69 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -652,6 +652,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .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), + .call_async_alloc => 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)"), diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index e691e51bbbbb6d63a108659d63455e2d15167d95..92f1be615870dec177bef110ca5c5d764dd77f82 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1931,6 +1931,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { .call_never_tail => func.airCall(inst, .never_tail), .call_never_inline => func.airCall(inst, .never_inline), .call_async => func.airCall(inst, .async_kw), + .call_async_alloc => func.airCall(inst, .async_kw), .is_err => func.airIsErr(inst, .i32_ne), .is_non_err => func.airIsErr(inst, .i32_eq), diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index be9f465cc859410ecab4f4fa6f30e7e788d68330..f373b5708020799bb0c5a812cb762bc378068fc8 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1902,6 +1902,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .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), + .call_async_alloc => try self.airCall(inst, .async_kw), .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 19df11efdaea2a2aab257b7ac2ebeb67a336ab57..968d567121a023e5268b9d5e9bad6cbc38835a11 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -3001,6 +3001,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .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), + .call_async_alloc => 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 2cb15bc374a65e6a35ec09d67b63b69810bb65b9..32e1d383ffb199476d9170e4acd6e84cc2367646 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3035,12 +3035,11 @@ pub const Object = struct { 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"); - //} + 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), } @@ -4522,6 +4521,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_alloc => try self.airCallAsyncAlloc(inst), .call_async => try self.airCallAsync(inst), .ptr_slice_ptr_ptr => try self.airPtrSliceFieldPtr(inst, 0), @@ -4990,7 +4990,12 @@ pub const FuncGen = struct { fn airCallAsync(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { _ = inst; - return self.todo("lower async call", .{}); + return self.todo("lower call_async", .{}); + } + + fn airCallAsyncAlloc(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { + _ = inst; + return self.todo("lower call_async_alloc", .{}); } fn airRet(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { diff --git a/src/print_air.zig b/src/print_air.zig index 9fb488282afd1fc3617e7e6e6b96b7915eb80e14..6064da44412cf934c2db8d37fb82c8cba6a551b4 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -330,6 +330,7 @@ const Writer = struct { .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), + .call_async_alloc => try w.writeCallAsyncAlloc(s, inst), .dbg_block_begin, .dbg_block_end => {}, @@ -705,11 +706,20 @@ const Writer = struct { } 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]); + const pl_op = w.air.instructions.items(.data)[inst].pl_op; + const extra = w.air.extraData(Air.AsyncCall, pl_op.payload); + const callee = pl_op.operand; const frame_ptr = extra.data.frame_ptr; + const args: []const Air.Inst.Ref = @ptrCast(w.air.extra[extra.end..][0..extra.data.args_len]); + return finishWriteCall(w, s, inst, frame_ptr, callee, args); + } + + fn writeCallAsyncAlloc(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.AsyncCallAlloc, ty_pl.payload); + const callee = extra.data.callee; + const frame_ptr: Air.Inst.Ref = .none; + const args: []const Air.Inst.Ref = @ptrCast(w.air.extra[extra.end..][0..extra.data.args_len]); return finishWriteCall(w, s, inst, frame_ptr, callee, args); } -- 2.54.0