authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-09-21 14:33:42-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-06-26 15:57:06-07:00
loga6eb83bd1b7739156ab01e3fc55588f67d924daa
treec89741b3b0e9fdfa1433031eab13e611634b5e1f
parente70c34cdb7ea7d9b7b6feddb2d8e6194ed1188a4

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();`.

13 files changed, 128 insertions(+), 35 deletions(-)

src/Air.zig+15-3
......@@ -310,9 +310,14 @@ pub const Inst = struct {
310310 call_never_tail,
311311 /// Same as `call` except with the `never_inline` attribute.
312312 call_never_inline,
313 /// Async function call.
314 /// Uses `ty_pl` field with the `AsyncCall` payload.
313 /// Async function call, using a provided frame pointer.
314 /// Uses `pl_op` field with the `AsyncCall` payload. operand is the callee.
315 /// Result type is always void.
315316 call_async,
317 /// Async function call, which allocates the frame for the callee on the stack.
318 /// This instruction also acts as an alloc.
319 /// Uses `ty_pl` field with the `AsyncCallAlloc` payload.
320 call_async_alloc,
316321 /// Count leading zeroes of an integer according to its representation in twos complement.
317322 /// Result type will always be an unsigned integer big enough to fit the answer.
318323 /// Uses the `ty_op` field.
......@@ -1076,6 +1081,11 @@ pub const Call = struct {
10761081/// Trailing is a list of `Inst.Ref` for every `args_len`.
10771082pub const AsyncCall = struct {
10781083 frame_ptr: Inst.Ref,
1084 args_len: u32,
1085};
1086
1087/// Trailing is a list of `Inst.Ref` for every `args_len`.
1088pub const AsyncCallAlloc = struct {
10791089 callee: Inst.Ref,
10801090 args_len: u32,
10811091};
......@@ -1350,7 +1360,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
13501360 .ptr_add,
13511361 .ptr_sub,
13521362 .try_ptr,
1353 .call_async,
1363 .call_async_alloc,
13541364 => return air.getRefType(datas[inst].ty_pl.ty),
13551365
13561366 .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)
14291439 .set_err_return_trace,
14301440 .vector_store_elem,
14311441 .c_va_end,
1442 .call_async,
14321443 => return Type.void,
14331444
14341445 .int_from_ptr,
......@@ -1595,6 +1606,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
15951606 .call_never_tail,
15961607 .call_never_inline,
15971608 .call_async,
1609 .call_async_alloc,
15981610 .cond_br,
15991611 .switch_br,
16001612 .@"try",
src/Liveness.zig+63-18
......@@ -484,15 +484,25 @@ pub fn categorizeOperand(
484484 const inst_data = air_datas[inst].pl_op;
485485 const callee = inst_data.operand;
486486 const extra = air.extraData(Air.Call, inst_data.payload);
487 const frame_ptr: Air.Inst.Ref = .none;
487488 const args: []const Air.Inst.Ref = @ptrCast(air.extra[extra.end..][0..extra.data.args_len]);
488 return categorizeOperandCall(l, inst, operand_ref, callee, args);
489 return categorizeOperandCall(l, inst, operand_ref, frame_ptr, callee, args);
489490 },
490491 .call_async => {
491 const inst_data = air_datas[inst].ty_pl;
492 const inst_data = air_datas[inst].pl_op;
492493 const extra = air.extraData(Air.AsyncCall, inst_data.payload);
494 const callee = inst_data.operand;
495 const frame_ptr = extra.data.frame_ptr;
496 const args: []const Air.Inst.Ref = @ptrCast(air.extra[extra.end..][0..extra.data.args_len]);
497 return categorizeOperandCall(l, inst, operand_ref, frame_ptr, callee, args);
498 },
499 .call_async_alloc => {
500 const inst_data = air_datas[inst].ty_pl;
501 const extra = air.extraData(Air.AsyncCallAlloc, inst_data.payload);
493502 const callee = extra.data.callee;
503 const frame_ptr: Air.Inst.Ref = .none;
494504 const args: []const Air.Inst.Ref = @ptrCast(air.extra[extra.end..][0..extra.data.args_len]);
495 return categorizeOperandCall(l, inst, operand_ref, callee, args);
505 return categorizeOperandCall(l, inst, operand_ref, frame_ptr, callee, args);
496506 },
497507 .select => {
498508 const pl_op = air_datas[inst].pl_op;
......@@ -661,21 +671,37 @@ pub fn categorizeOperand(
661671 }
662672}
663673
664fn categorizeOperandCall(
674pub fn categorizeOperandCall(
665675 l: Liveness,
666676 inst: Air.Inst.Index,
667677 operand_ref: Air.Inst.Ref,
678 frame_ptr: Air.Inst.Ref,
668679 callee: Air.Inst.Ref,
669680 args: []const Air.Inst.Ref,
670681) OperandCategory {
671 if (args.len + 1 <= bpi - 1) {
672 if (callee == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write);
673 for (args, 0..) |arg, i| {
674 if (arg == operand_ref) return matchOperandSmallIndex(l, inst, @intCast(i + 1), .write);
682 const total = args.len + 1 + @intFromBool(frame_ptr != .none);
683 if (total <= bpi - 1) {
684 var op_index: OperandInt = 0;
685 if (frame_ptr != .none) {
686 if (frame_ptr == operand_ref) return matchOperandSmallIndex(l, inst, op_index, .write);
687 op_index += 1;
688 }
689 if (callee == operand_ref) return matchOperandSmallIndex(l, inst, op_index, .write);
690
691 for (args) |arg| {
692 op_index += 1;
693 if (arg == operand_ref) return matchOperandSmallIndex(l, inst, op_index, .write);
675694 }
676695 return .write;
677696 }
678697 var bt = l.iterateBigTomb(inst);
698 if (frame_ptr != .none) {
699 if (bt.feed()) {
700 if (frame_ptr == operand_ref) return .tomb;
701 } else {
702 if (frame_ptr == operand_ref) return .write;
703 }
704 }
679705 if (bt.feed()) {
680706 if (callee == operand_ref) return .tomb;
681707 } else {
......@@ -1122,18 +1148,28 @@ fn analyzeInst(
11221148 },
11231149
11241150 .call, .call_always_tail, .call_never_tail, .call_never_inline => {
1125 const inst_data = inst_datas[inst].pl_op;
1126 const callee = inst_data.operand;
1127 const extra = a.air.extraData(Air.Call, inst_data.payload);
1151 const pl_op = inst_datas[inst].pl_op;
1152 const callee = pl_op.operand;
1153 const frame_ptr: Air.Inst.Ref = .none;
1154 const extra = a.air.extraData(Air.Call, pl_op.payload);
11281155 const args: []const Air.Inst.Ref = @ptrCast(a.air.extra[extra.end..][0..extra.data.args_len]);
1129 return analyzeInstCall(a, pass, data, inst, callee, args);
1156 return analyzeInstCall(a, pass, data, inst, frame_ptr, callee, args);
11301157 },
11311158 .call_async => {
1132 const inst_data = inst_datas[inst].ty_pl;
1159 const inst_data = inst_datas[inst].pl_op;
11331160 const extra = a.air.extraData(Air.AsyncCall, inst_data.payload);
1161 const callee = inst_data.operand;
1162 const args: []const Air.Inst.Ref = @ptrCast(a.air.extra[extra.end..][0..extra.data.args_len]);
1163 const frame_ptr = extra.data.frame_ptr;
1164 return analyzeInstCall(a, pass, data, inst, frame_ptr, callee, args);
1165 },
1166 .call_async_alloc => {
1167 const ty_pl = inst_datas[inst].ty_pl;
1168 const extra = a.air.extraData(Air.AsyncCallAlloc, ty_pl.payload);
11341169 const callee = extra.data.callee;
1170 const frame_ptr: Air.Inst.Ref = .none;
11351171 const args: []const Air.Inst.Ref = @ptrCast(a.air.extra[extra.end..][0..extra.data.args_len]);
1136 return analyzeInstCall(a, pass, data, inst, callee, args);
1172 return analyzeInstCall(a, pass, data, inst, frame_ptr, callee, args);
11371173 },
11381174 .select => {
11391175 const pl_op = inst_datas[inst].pl_op;
......@@ -1267,24 +1303,33 @@ fn analyzeInstCall(
12671303 comptime pass: LivenessPass,
12681304 data: *LivenessPassData(pass),
12691305 inst: Air.Inst.Index,
1306 frame_ptr: Air.Inst.Ref,
12701307 callee: Air.Inst.Ref,
12711308 args: []const Air.Inst.Ref,
12721309) Allocator.Error!void {
1273 if (args.len + 1 <= bpi - 1) {
1310 const total = args.len + 1 + @intFromBool(frame_ptr != .none);
1311 if (total <= bpi - 1) {
12741312 var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1);
1275 buf[0] = callee;
1276 @memcpy(buf[1..][0..args.len], args);
1313 var op_index: OperandInt = 0;
1314 if (frame_ptr != .none) {
1315 buf[op_index] = frame_ptr;
1316 op_index += 1;
1317 }
1318 buf[op_index] = callee;
1319 op_index += 1;
1320 @memcpy(buf[op_index..][0..args.len], args);
12771321 return analyzeOperands(a, pass, data, inst, buf);
12781322 }
12791323
12801324 var big = try AnalyzeBigOperands(pass).init(a, data, inst, args.len + 1);
12811325 defer big.deinit();
1326 if (frame_ptr != .none) try big.feed(frame_ptr);
1327 try big.feed(callee);
12821328 var i: usize = args.len;
12831329 while (i > 0) {
12841330 i -= 1;
12851331 try big.feed(args[i]);
12861332 }
1287 try big.feed(callee);
12881333 return big.finish();
12891334}
12901335
src/Liveness/Verify.zig+17-1
......@@ -350,8 +350,24 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
350350 try self.verifyInst(inst);
351351 },
352352 .call_async => {
353 const pl_op = data[inst].pl_op;
354 const extra = self.air.extraData(Air.AsyncCall, pl_op.payload);
355 const args: []const Air.Inst.Ref = @ptrCast(
356 self.air.extra[extra.end..][0..extra.data.args_len],
357 );
358 const callee = pl_op.operand;
359
360 var bt = self.liveness.iterateBigTomb(inst);
361 try self.verifyOperand(inst, extra.data.frame_ptr, bt.feed());
362 try self.verifyOperand(inst, callee, bt.feed());
363 for (args) |arg| {
364 try self.verifyOperand(inst, arg, bt.feed());
365 }
366 try self.verifyInst(inst);
367 },
368 .call_async_alloc => {
353369 const ty_pl = data[inst].ty_pl;
354 const extra = self.air.extraData(Air.AsyncCall, ty_pl.payload);
370 const extra = self.air.extraData(Air.AsyncCallAlloc, ty_pl.payload);
355371 const args: []const Air.Inst.Ref = @ptrCast(
356372 self.air.extra[extra.end..][0..extra.data.args_len],
357373 );
src/Sema.zig+2-4
......@@ -7299,18 +7299,16 @@ fn addAsyncCallInst(
72997299) Allocator.Error!Air.Inst.Ref {
73007300 const mod = sema.mod;
73017301 const ptr_frame_ty = try mod.singleMutPtrType(try mod.asyncFrameType(callee_fn));
7302 const frame_ptr = try block.addTy(.alloc, ptr_frame_ty);
73037302 const ptr_frame_ty_ref = try sema.addType(ptr_frame_ty);
73047303 try sema.air_extra.ensureUnusedCapacity(
73057304 sema.gpa,
73067305 @typeInfo(Air.AsyncCall).Struct.fields.len + args.len,
73077306 );
73087307 const call_inst = try block.addInst(.{
7309 .tag = .call_async,
7308 .tag = .call_async_alloc,
73107309 .data = .{ .ty_pl = .{
73117310 .ty = ptr_frame_ty_ref,
7312 .payload = sema.addExtraAssumeCapacity(Air.AsyncCall{
7313 .frame_ptr = frame_ptr,
7311 .payload = sema.addExtraAssumeCapacity(Air.AsyncCallAlloc{
73147312 .callee = callee,
73157313 .args_len = @intCast(args.len),
73167314 }),
src/arch/aarch64/CodeGen.zig+1
......@@ -820,6 +820,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
820820 .call_never_tail => try self.airCall(inst, .never_tail),
821821 .call_never_inline => try self.airCall(inst, .never_inline),
822822 .call_async => try self.airCall(inst, .async_kw),
823 .call_async_alloc => try self.airCall(inst, .async_kw),
823824
824825 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
825826 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
src/arch/arm/CodeGen.zig+1
......@@ -804,6 +804,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
804804 .call_never_tail => try self.airCall(inst, .never_tail),
805805 .call_never_inline => try self.airCall(inst, .never_inline),
806806 .call_async => try self.airCall(inst, .async_kw),
807 .call_async_alloc => try self.airCall(inst, .async_kw),
807808
808809 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
809810 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
src/arch/riscv64/CodeGen.zig+1
......@@ -639,6 +639,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
639639 .call_never_tail => try self.airCall(inst, .never_tail),
640640 .call_never_inline => try self.airCall(inst, .never_inline),
641641 .call_async => try self.airCall(inst, .async_kw),
642 .call_async_alloc => try self.airCall(inst, .async_kw),
642643
643644 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
644645 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
src/arch/sparc64/CodeGen.zig+1
......@@ -652,6 +652,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
652652 .call_never_tail => try self.airCall(inst, .never_tail),
653653 .call_never_inline => try self.airCall(inst, .never_inline),
654654 .call_async => try self.airCall(inst, .async_kw),
655 .call_async_alloc => try self.airCall(inst, .async_kw),
655656
656657 .atomic_store_unordered => @panic("TODO try self.airAtomicStore(inst, .Unordered)"),
657658 .atomic_store_monotonic => @panic("TODO try self.airAtomicStore(inst, .Monotonic)"),
src/arch/wasm/CodeGen.zig+1
......@@ -1931,6 +1931,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
19311931 .call_never_tail => func.airCall(inst, .never_tail),
19321932 .call_never_inline => func.airCall(inst, .never_inline),
19331933 .call_async => func.airCall(inst, .async_kw),
1934 .call_async_alloc => func.airCall(inst, .async_kw),
19341935
19351936 .is_err => func.airIsErr(inst, .i32_ne),
19361937 .is_non_err => func.airIsErr(inst, .i32_eq),
src/arch/x86_64/CodeGen.zig+1
......@@ -1902,6 +1902,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
19021902 .call_never_tail => try self.airCall(inst, .never_tail),
19031903 .call_never_inline => try self.airCall(inst, .never_inline),
19041904 .call_async => try self.airCall(inst, .async_kw),
1905 .call_async_alloc => try self.airCall(inst, .async_kw),
19051906
19061907 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
19071908 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
src/codegen/c.zig+1
......@@ -3001,6 +3001,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
30013001 .call_never_tail => try airCall(f, inst, .never_tail),
30023002 .call_never_inline => try airCall(f, inst, .never_inline),
30033003 .call_async => try airCall(f, inst, .async_kw),
3004 .call_async_alloc => try airCall(f, inst, .async_kw),
30043005
30053006 .float_from_int,
30063007 .int_from_float,
src/codegen/llvm.zig+12-7
......@@ -3035,12 +3035,11 @@ pub const Object = struct {
30353035 const llvm_struct_ty = o.context.structCreateNamed(name);
30363036 gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls
30373037
3038 return lowerAsyncFrameType(o, func, llvm_struct_ty);
3039 //if (func.isAsync()) {
3040 // return lowerAsyncFrameType(o, func, llvm_struct_ty);
3041 //} else {
3042 // @panic("lower llvm @Frame() type of non-async function");
3043 //}
3038 if (func.isAsync()) {
3039 return lowerAsyncFrameType(o, func, llvm_struct_ty);
3040 } else {
3041 @panic("lower llvm @Frame() type of non-async function");
3042 }
30443043 },
30453044 .AnyFrame => return o.context.pointerType(0),
30463045 }
......@@ -4522,6 +4521,7 @@ pub const FuncGen = struct {
45224521 .call_always_tail => try self.airCall(inst, .AlwaysTail),
45234522 .call_never_tail => try self.airCall(inst, .NeverTail),
45244523 .call_never_inline => try self.airCall(inst, .NeverInline),
4524 .call_async_alloc => try self.airCallAsyncAlloc(inst),
45254525 .call_async => try self.airCallAsync(inst),
45264526
45274527 .ptr_slice_ptr_ptr => try self.airPtrSliceFieldPtr(inst, 0),
......@@ -4990,7 +4990,12 @@ pub const FuncGen = struct {
49904990
49914991 fn airCallAsync(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
49924992 _ = inst;
4993 return self.todo("lower async call", .{});
4993 return self.todo("lower call_async", .{});
4994 }
4995
4996 fn airCallAsyncAlloc(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
4997 _ = inst;
4998 return self.todo("lower call_async_alloc", .{});
49944999 }
49955000
49965001 fn airRet(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
src/print_air.zig+12-2
......@@ -330,6 +330,7 @@ const Writer = struct {
330330 .cmp_vector, .cmp_vector_optimized => try w.writeCmpVector(s, inst),
331331 .vector_store_elem => try w.writeVectorStoreElem(s, inst),
332332 .call_async => try w.writeCallAsync(s, inst),
333 .call_async_alloc => try w.writeCallAsyncAlloc(s, inst),
333334
334335 .dbg_block_begin, .dbg_block_end => {},
335336
......@@ -705,11 +706,20 @@ const Writer = struct {
705706 }
706707
707708 fn writeCallAsync(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
709 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
710 const extra = w.air.extraData(Air.AsyncCall, pl_op.payload);
711 const callee = pl_op.operand;
712 const frame_ptr = extra.data.frame_ptr;
713 const args: []const Air.Inst.Ref = @ptrCast(w.air.extra[extra.end..][0..extra.data.args_len]);
714 return finishWriteCall(w, s, inst, frame_ptr, callee, args);
715 }
716
717 fn writeCallAsyncAlloc(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
708718 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
709 const extra = w.air.extraData(Air.AsyncCall, ty_pl.payload);
719 const extra = w.air.extraData(Air.AsyncCallAlloc, ty_pl.payload);
710720 const callee = extra.data.callee;
721 const frame_ptr: Air.Inst.Ref = .none;
711722 const args: []const Air.Inst.Ref = @ptrCast(w.air.extra[extra.end..][0..extra.data.args_len]);
712 const frame_ptr = extra.data.frame_ptr;
713723 return finishWriteCall(w, s, inst, frame_ptr, callee, args);
714724 }
715725