authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-09-21 20:12:54-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-06-26 15:57:07-07:00
loge45b10f3d453f3bd8326631ee786a2ef247e8953
tree92a8b593bbb1d06f696ce57826fc74438dfc1b1d
parentf1b7c76ae9c015eeadeeff6ffcdf1523afce43f5

stage2: implement suspend blocks

* AstGen: suspend blocks are always void * add new AIR instructions: suspend_begin and suspend_end * Module: after a function's body is analyzed, conclude that it is not async if it wasn't proven async during analysis. * LLVM: implement lowering of suspend_begin and suspend_end. There is a lot to do in this branch. I started a branch-local TODO list in the BRANCH_TODO file. All of these tasks should be finished before merging into master.

17 files changed, 220 insertions(+), 16 deletions(-)

BRANCH_TODO created+13
...@@ -0,0 +1,13 @@
1 * detect when a called function is async and make the caller async too
2 * generate the async frame type *after* lowering the function to LLVM IR
3 * calculate frame size after llvm lowering, ability to inspect with `@sizeOf`
4 * spill only values that span across suspension points based on Liveness info
5 - handle variable lifetimes correctly - don't die until end curly brace
6 - don't pay for spill bytes that are not used - if an i32 spans across suspension point 1
7 and a different value which is an f32 spans across suspension point 2, only 4 bytes
8 of spill data should be allocated.
9 - detect when first N spilled values are the same and avoid redundant stores
10 * solve safety panics for bad resume
11 * use function pointers instead of resume index to...
12 - reduce the number of runtime branches from 2 to 1
13 - pass function arguments as normal arguments to the first segment
src/Air.zig+16
...@@ -318,6 +318,18 @@ pub const Inst = struct {...@@ -318,6 +318,18 @@ pub const Inst = struct {
318 /// This instruction also acts as an alloc.318 /// This instruction also acts as an alloc.
319 /// Uses `ty_pl` field with the `AsyncCallAlloc` payload.319 /// Uses `ty_pl` field with the `AsyncCallAlloc` payload.
320 call_async_alloc,320 call_async_alloc,
321 /// Enter a suspend block. After this instruction, the function is
322 /// still executing but is considered to be in a "suspended" state, and
323 /// can be resumed, which will send control flow to just after the next
324 /// suspend_end instruction.
325 /// Result type is always void.
326 /// Uses the `no_op` field.
327 suspend_begin,
328 /// Exit a suspend block. This causes an async function to return
329 /// control flow to the resumer.
330 /// Result type is always void.
331 /// Uses the `no_op` field.
332 suspend_end,
321 /// Count leading zeroes of an integer according to its representation in twos complement.333 /// Count leading zeroes of an integer according to its representation in twos complement.
322 /// Result type will always be an unsigned integer big enough to fit the answer.334 /// Result type will always be an unsigned integer big enough to fit the answer.
323 /// Uses the `ty_op` field.335 /// Uses the `ty_op` field.
...@@ -1440,6 +1452,8 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)...@@ -1440,6 +1452,8 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
1440 .vector_store_elem,1452 .vector_store_elem,
1441 .c_va_end,1453 .c_va_end,
1442 .call_async,1454 .call_async,
1455 .suspend_begin,
1456 .suspend_end,
1443 => return Type.void,1457 => return Type.void,
14441458
1445 .int_from_ptr,1459 .int_from_ptr,
...@@ -1632,6 +1646,8 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {...@@ -1632,6 +1646,8 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
1632 .cmpxchg_weak,1646 .cmpxchg_weak,
1633 .cmpxchg_strong,1647 .cmpxchg_strong,
1634 .fence,1648 .fence,
1649 .suspend_begin,
1650 .suspend_end,
1635 .atomic_store_unordered,1651 .atomic_store_unordered,
1636 .atomic_store_monotonic,1652 .atomic_store_monotonic,
1637 .atomic_store_release,1653 .atomic_store_release,
src/AstGen.zig+2-6
...@@ -1182,11 +1182,7 @@ fn nosuspendExpr(...@@ -1182,11 +1182,7 @@ fn nosuspendExpr(
1182 return expr(gz, scope, ri, body_node);1182 return expr(gz, scope, ri, body_node);
1183}1183}
11841184
1185fn suspendExpr(1185fn suspendExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
1186 gz: *GenZir,
1187 scope: *Scope,
1188 node: Ast.Node.Index,
1189) InnerError!Zir.Inst.Ref {
1190 const astgen = gz.astgen;1186 const astgen = gz.astgen;
1191 const gpa = astgen.gpa;1187 const gpa = astgen.gpa;
1192 const tree = astgen.tree;1188 const tree = astgen.tree;
...@@ -2562,7 +2558,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2562,7 +2558,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2562 .block,2558 .block,
2563 .block_comptime,2559 .block_comptime,
2564 .block_inline,2560 .block_inline,
2565 .suspend_block,
2566 .loop,2561 .loop,
2567 .bool_br_and,2562 .bool_br_and,
2568 .bool_br_or,2563 .bool_br_or,
...@@ -2790,6 +2785,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2790,6 +2785,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2790 .validate_deref,2785 .validate_deref,
2791 .save_err_ret_index,2786 .save_err_ret_index,
2792 .restore_err_ret_index,2787 .restore_err_ret_index,
2788 .suspend_block,
2793 => break :b true,2789 => break :b true,
27942790
2795 .@"defer" => unreachable,2791 .@"defer" => unreachable,
src/Liveness.zig+6-1
...@@ -344,7 +344,10 @@ pub fn categorizeOperand(...@@ -344,7 +344,10 @@ pub fn categorizeOperand(
344 .work_group_id,344 .work_group_id,
345 => return .none,345 => return .none,
346346
347 .fence => return .write,347 .suspend_begin,
348 .suspend_end,
349 .fence,
350 => return .write,
348351
349 .not,352 .not,
350 .bitcast,353 .bitcast,
...@@ -1013,6 +1016,8 @@ fn analyzeInst(...@@ -1013,6 +1016,8 @@ fn analyzeInst(
1013 .dbg_block_begin,1016 .dbg_block_begin,
1014 .dbg_block_end,1017 .dbg_block_end,
1015 .fence,1018 .fence,
1019 .suspend_begin,
1020 .suspend_end,
1016 .ret_addr,1021 .ret_addr,
1017 .frame_addr,1022 .frame_addr,
1018 .wasm_memory_size,1023 .wasm_memory_size,
src/Liveness/Verify.zig+2
...@@ -52,6 +52,8 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {...@@ -52,6 +52,8 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
52 .dbg_block_begin,52 .dbg_block_begin,
53 .dbg_block_end,53 .dbg_block_end,
54 .fence,54 .fence,
55 .suspend_begin,
56 .suspend_end,
55 .ret_addr,57 .ret_addr,
56 .frame_addr,58 .frame_addr,
57 .wasm_memory_size,59 .wasm_memory_size,
src/Module.zig+3
...@@ -5742,6 +5742,9 @@ pub fn analyzeFnBody(mod: *Module, func_index: Fn.Index, arena: Allocator) SemaE...@@ -5742,6 +5742,9 @@ pub fn analyzeFnBody(mod: *Module, func_index: Fn.Index, arena: Allocator) SemaE
5742 sema.air_extra.items[@intFromEnum(Air.ExtraIndex.main_block)] = main_block_index;5742 sema.air_extra.items[@intFromEnum(Air.ExtraIndex.main_block)] = main_block_index;
57435743
5744 func.state = .success;5744 func.state = .success;
5745 if (func.async_status == .unknown) {
5746 func.async_status = .not_async;
5747 }
57455748
5746 // Finally we must resolve the return type and parameter types so that backends5749 // Finally we must resolve the return type and parameter types so that backends
5747 // have full access to type information.5750 // have full access to type information.
src/Sema.zig+15-5
...@@ -932,7 +932,6 @@ fn analyzeBodyInner(...@@ -932,7 +932,6 @@ fn analyzeBodyInner(
932 .bit_not => try sema.zirBitNot(block, inst),932 .bit_not => try sema.zirBitNot(block, inst),
933 .bit_or => try sema.zirBitwise(block, inst, .bit_or),933 .bit_or => try sema.zirBitwise(block, inst, .bit_or),
934 .bitcast => try sema.zirBitcast(block, inst),934 .bitcast => try sema.zirBitcast(block, inst),
935 .suspend_block => try sema.zirSuspendBlock(block, inst),
936 .bool_not => try sema.zirBoolNot(block, inst),935 .bool_not => try sema.zirBoolNot(block, inst),
937 .bool_br_and => try sema.zirBoolBr(block, inst, false),936 .bool_br_and => try sema.zirBoolBr(block, inst, false),
938 .bool_br_or => try sema.zirBoolBr(block, inst, true),937 .bool_br_or => try sema.zirBoolBr(block, inst, true),
...@@ -1218,6 +1217,11 @@ fn analyzeBodyInner(...@@ -1218,6 +1217,11 @@ fn analyzeBodyInner(
1218 // continue the loop.1217 // continue the loop.
1219 // We also know that they cannot be referenced later, so we avoid1218 // We also know that they cannot be referenced later, so we avoid
1220 // putting them into the map.1219 // putting them into the map.
1220 .suspend_block => {
1221 try sema.zirSuspendBlock(block, inst);
1222 i += 1;
1223 continue;
1224 },
1221 .dbg_stmt => {1225 .dbg_stmt => {
1222 try sema.zirDbgStmt(block, inst);1226 try sema.zirDbgStmt(block, inst);
1223 i += 1;1227 i += 1;
...@@ -5594,10 +5598,16 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr...@@ -5594,10 +5598,16 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
5594 return sema.addConstant(file_root_decl.val);5598 return sema.addConstant(file_root_decl.val);
5595}5599}
55965600
5597fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {5601fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!void {
5598 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;5602 const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
5599 const src = inst_data.src();5603 const src = pl_node.src();
5600 return sema.failWithUseOfAsync(parent_block, src);5604 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
5605 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
5606 _ = src;
5607 sema.owner_func.?.async_status = .yes_async;
5608 _ = try parent_block.addNoOp(.suspend_begin);
5609 _ = try sema.resolveBody(parent_block, body, inst);
5610 _ = try parent_block.addNoOp(.suspend_end);
5601}5611}
56025612
5603fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index, force_comptime: bool) CompileError!Air.Inst.Ref {5613fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index, force_comptime: bool) CompileError!Air.Inst.Ref {
src/arch/aarch64/CodeGen.zig+13
...@@ -822,6 +822,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -822,6 +822,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
822 .call_async => try self.airCall(inst, .async_kw),822 .call_async => try self.airCall(inst, .async_kw),
823 .call_async_alloc => try self.airCall(inst, .async_kw),823 .call_async_alloc => try self.airCall(inst, .async_kw),
824824
825 .suspend_begin => try airSuspendBegin(self, inst),
826 .suspend_end => try airSuspendEnd (self, inst),
827
825 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),828 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
826 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),829 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
827 .atomic_store_release => try self.airAtomicStore(inst, .Release),830 .atomic_store_release => try self.airAtomicStore(inst, .Release),
...@@ -4242,6 +4245,16 @@ fn airFence(self: *Self) !void {...@@ -4242,6 +4245,16 @@ fn airFence(self: *Self) !void {
4242 //return self.finishAirBookkeeping();4245 //return self.finishAirBookkeeping();
4243}4246}
42444247
4248fn airSuspendBegin(self: *Self, inst: Air.Inst.Index) !void {
4249 _ = inst;
4250 return self.fail("TODO implement suspend_begin for aarch64", .{});
4251}
4252
4253fn airSuspendEnd(self: *Self, inst: Air.Inst.Index) !void {
4254 _ = inst;
4255 return self.fail("TODO implement suspend_end for aarch64", .{});
4256}
4257
4245fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {4258fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {
4246 if (modifier == .always_tail) return self.fail("TODO implement tail calls for aarch64", .{});4259 if (modifier == .always_tail) return self.fail("TODO implement tail calls for aarch64", .{});
4247 if (modifier == .async_kw) return self.fail("TODO implement async calls for aarch64", .{});4260 if (modifier == .async_kw) return self.fail("TODO implement async calls for aarch64", .{});
src/arch/arm/CodeGen.zig+13
...@@ -806,6 +806,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -806,6 +806,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
806 .call_async => try self.airCall(inst, .async_kw),806 .call_async => try self.airCall(inst, .async_kw),
807 .call_async_alloc => try self.airCall(inst, .async_kw),807 .call_async_alloc => try self.airCall(inst, .async_kw),
808808
809 .suspend_begin => try airSuspendBegin(self, inst),
810 .suspend_end => try airSuspendEnd (self, inst),
811
809 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),812 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
810 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),813 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
811 .atomic_store_release => try self.airAtomicStore(inst, .Release),814 .atomic_store_release => try self.airAtomicStore(inst, .Release),
...@@ -4215,6 +4218,16 @@ fn airFence(self: *Self) !void {...@@ -4215,6 +4218,16 @@ fn airFence(self: *Self) !void {
4215 //return self.finishAirBookkeeping();4218 //return self.finishAirBookkeeping();
4216}4219}
42174220
4221fn airSuspendBegin(self: *Self, inst: Air.Inst.Index) !void {
4222 _ = inst;
4223 return self.fail("TODO implement suspend_begin for arm", .{});
4224}
4225
4226fn airSuspendEnd(self: *Self, inst: Air.Inst.Index) !void {
4227 _ = inst;
4228 return self.fail("TODO implement suspend_end for arm", .{});
4229}
4230
4218fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {4231fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {
4219 if (modifier == .always_tail) return self.fail("TODO implement tail calls for arm", .{});4232 if (modifier == .always_tail) return self.fail("TODO implement tail calls for arm", .{});
4220 if (modifier == .async_kw) return self.fail("TODO implement async calls for arm", .{});4233 if (modifier == .async_kw) return self.fail("TODO implement async calls for arm", .{});
src/arch/riscv64/CodeGen.zig+13
...@@ -641,6 +641,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -641,6 +641,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
641 .call_async => try self.airCall(inst, .async_kw),641 .call_async => try self.airCall(inst, .async_kw),
642 .call_async_alloc => try self.airCall(inst, .async_kw),642 .call_async_alloc => try self.airCall(inst, .async_kw),
643643
644 .suspend_begin => try airSuspendBegin(self, inst),
645 .suspend_end => try airSuspendEnd (self, inst),
646
644 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),647 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
645 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),648 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
646 .atomic_store_release => try self.airAtomicStore(inst, .Release),649 .atomic_store_release => try self.airAtomicStore(inst, .Release),
...@@ -1706,6 +1709,16 @@ fn airFence(self: *Self) !void {...@@ -1706,6 +1709,16 @@ fn airFence(self: *Self) !void {
1706 //return self.finishAirBookkeeping();1709 //return self.finishAirBookkeeping();
1707}1710}
17081711
1712fn airSuspendBegin(self: *Self, inst: Air.Inst.Index) !void {
1713 _ = inst;
1714 return self.fail("TODO implement suspend_begin for riscv64", .{});
1715}
1716
1717fn airSuspendEnd(self: *Self, inst: Air.Inst.Index) !void {
1718 _ = inst;
1719 return self.fail("TODO implement suspend_end for riscv64", .{});
1720}
1721
1709fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {1722fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {
1710 const mod = self.bin_file.options.module.?;1723 const mod = self.bin_file.options.module.?;
1711 if (modifier == .always_tail) return self.fail("TODO implement tail calls for riscv64", .{});1724 if (modifier == .always_tail) return self.fail("TODO implement tail calls for riscv64", .{});
src/arch/sparc64/CodeGen.zig+13
...@@ -654,6 +654,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -654,6 +654,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
654 .call_async => try self.airCall(inst, .async_kw),654 .call_async => try self.airCall(inst, .async_kw),
655 .call_async_alloc => try self.airCall(inst, .async_kw),655 .call_async_alloc => try self.airCall(inst, .async_kw),
656656
657 .suspend_begin => try airSuspendBegin(self, inst),
658 .suspend_end => try airSuspendEnd (self, inst),
659
657 .atomic_store_unordered => @panic("TODO try self.airAtomicStore(inst, .Unordered)"),660 .atomic_store_unordered => @panic("TODO try self.airAtomicStore(inst, .Unordered)"),
658 .atomic_store_monotonic => @panic("TODO try self.airAtomicStore(inst, .Monotonic)"),661 .atomic_store_monotonic => @panic("TODO try self.airAtomicStore(inst, .Monotonic)"),
659 .atomic_store_release => @panic("TODO try self.airAtomicStore(inst, .Release)"),662 .atomic_store_release => @panic("TODO try self.airAtomicStore(inst, .Release)"),
...@@ -1293,6 +1296,16 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {...@@ -1293,6 +1296,16 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void {
1293 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1296 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1294}1297}
12951298
1299fn airSuspendBegin(self: *Self, inst: Air.Inst.Index) !void {
1300 _ = inst;
1301 return self.fail("TODO implement suspend_begin for sparc64", .{});
1302}
1303
1304fn airSuspendEnd(self: *Self, inst: Air.Inst.Index) !void {
1305 _ = inst;
1306 return self.fail("TODO implement suspend_end for sparc64", .{});
1307}
1308
1296fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {1309fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {
1297 if (modifier == .always_tail) return self.fail("TODO implement tail calls for {}", .{self.target.cpu.arch});1310 if (modifier == .always_tail) return self.fail("TODO implement tail calls for {}", .{self.target.cpu.arch});
1298 if (modifier == .async_kw) return self.fail("TODO implement async calls for {}", .{self.target.cpu.arch});1311 if (modifier == .async_kw) return self.fail("TODO implement async calls for {}", .{self.target.cpu.arch});
src/arch/wasm/CodeGen.zig+13
...@@ -1933,6 +1933,9 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1933,6 +1933,9 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
1933 .call_async => func.airCall(inst, .async_kw),1933 .call_async => func.airCall(inst, .async_kw),
1934 .call_async_alloc => func.airCall(inst, .async_kw),1934 .call_async_alloc => func.airCall(inst, .async_kw),
19351935
1936 .suspend_begin => func.airSuspendBegin(inst),
1937 .suspend_end => func.airSuspendEnd(inst),
1938
1936 .is_err => func.airIsErr(inst, .i32_ne),1939 .is_err => func.airIsErr(inst, .i32_ne),
1937 .is_non_err => func.airIsErr(inst, .i32_eq),1940 .is_non_err => func.airIsErr(inst, .i32_eq),
19381941
...@@ -2180,6 +2183,16 @@ fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2180,6 +2183,16 @@ fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2180 return func.finishAir(inst, .none, &.{un_op});2183 return func.finishAir(inst, .none, &.{un_op});
2181}2184}
21822185
2186fn airSuspendBegin(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2187 _ = inst;
2188 return func.fail("TODO implement suspend_begin for wasm", .{});
2189}
2190
2191fn airSuspendEnd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2192 _ = inst;
2193 return func.fail("TODO implement suspend_end for wasm", .{});
2194}
2195
2183fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void {2196fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void {
2184 if (modifier == .always_tail) return func.fail("TODO implement tail calls for wasm", .{});2197 if (modifier == .always_tail) return func.fail("TODO implement tail calls for wasm", .{});
2185 if (modifier == .async_kw) return func.fail("TODO implement async calls for wasm", .{});2198 if (modifier == .async_kw) return func.fail("TODO implement async calls for wasm", .{});
src/arch/x86_64/CodeGen.zig+13
...@@ -1904,6 +1904,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -1904,6 +1904,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
1904 .call_async => try self.airCall(inst, .async_kw),1904 .call_async => try self.airCall(inst, .async_kw),
1905 .call_async_alloc => try self.airCall(inst, .async_kw),1905 .call_async_alloc => try self.airCall(inst, .async_kw),
19061906
1907 .suspend_begin => try airSuspendBegin(self, inst),
1908 .suspend_end => try airSuspendEnd (self, inst),
1909
1907 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),1910 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
1908 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),1911 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
1909 .atomic_store_release => try self.airAtomicStore(inst, .Release),1912 .atomic_store_release => try self.airAtomicStore(inst, .Release),
...@@ -8058,6 +8061,16 @@ fn airFence(self: *Self, inst: Air.Inst.Index) !void {...@@ -8058,6 +8061,16 @@ fn airFence(self: *Self, inst: Air.Inst.Index) !void {
8058 return self.finishAirBookkeeping();8061 return self.finishAirBookkeeping();
8059}8062}
80608063
8064fn airSuspendBegin(self: *Self, inst: Air.Inst.Index) !void {
8065 _ = inst;
8066 return self.fail("TODO implement suspend_begin for x86_64", .{});
8067}
8068
8069fn airSuspendEnd(self: *Self, inst: Air.Inst.Index) !void {
8070 _ = inst;
8071 return self.fail("TODO implement suspend_end for x86_64", .{});
8072}
8073
8061fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {8074fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {
8062 const mod = self.bin_file.options.module.?;8075 const mod = self.bin_file.options.module.?;
8063 if (modifier == .always_tail) return self.fail("TODO implement tail calls for x86_64", .{});8076 if (modifier == .always_tail) return self.fail("TODO implement tail calls for x86_64", .{});
src/codegen/c.zig+13
...@@ -3003,6 +3003,9 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,...@@ -3003,6 +3003,9 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
3003 .call_async => try airCall(f, inst, .async_kw),3003 .call_async => try airCall(f, inst, .async_kw),
3004 .call_async_alloc => try airCall(f, inst, .async_kw),3004 .call_async_alloc => try airCall(f, inst, .async_kw),
30053005
3006 .suspend_begin => try airSuspendBegin(f, inst),
3007 .suspend_end => try airSuspendEnd (f, inst),
3008
3006 .float_from_int,3009 .float_from_int,
3007 .int_from_float,3010 .int_from_float,
3008 .fptrunc,3011 .fptrunc,
...@@ -4087,6 +4090,16 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4087,6 +4090,16 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {
4087 return local;4090 return local;
4088}4091}
40894092
4093fn airSuspendBegin(f: *Function, inst: Air.Inst.Index) !CValue {
4094 _ = inst;
4095 return f.fail("TODO: C backend: lower suspend_begin", .{});
4096}
4097
4098fn airSuspendEnd(f: *Function, inst: Air.Inst.Index) !CValue {
4099 _ = inst;
4100 return f.fail("TODO: C backend: lower suspend_end", .{});
4101}
4102
4090fn airCall(4103fn airCall(
4091 f: *Function,4104 f: *Function,
4092 inst: Air.Inst.Index,4105 inst: Air.Inst.Index,
src/codegen/llvm.zig+67-4
...@@ -1207,9 +1207,35 @@ pub const Object = struct {...@@ -1207,9 +1207,35 @@ pub const Object = struct {
1207 .prev_dbg_line = 0,1207 .prev_dbg_line = 0,
1208 .prev_dbg_column = 0,1208 .prev_dbg_column = 0,
1209 .err_ret_trace = err_ret_trace,1209 .err_ret_trace = err_ret_trace,
1210
1211 .resume_block_index = 0,
1212 .resume_bb = undefined,
1213 .async_switch = undefined,
1214 .resume_index_ptr = undefined,
1210 };1215 };
1211 defer fg.deinit();1216 defer fg.deinit();
12121217
1218 if (func.isAsync()) {
1219 const frame_ty = try mod.asyncFrameType(func_index);
1220 const frame_size = frame_ty.abiSize(mod);
1221 const llvm_usize = dg.context.intType(target.ptrBitWidth());
1222 const size_val = llvm_usize.constInt(frame_size, .False);
1223 llvm_func.functionSetPrefixData(size_val);
1224
1225 const async_preamble_bb = dg.context.appendBasicBlock(llvm_func, "AsyncSwitch");
1226 const bad_resume_bb = dg.context.appendBasicBlock(llvm_func, "BadResume");
1227 builder.positionBuilderAtEnd(bad_resume_bb);
1228 _ = builder.buildUnreachable(); // TODO make this a safety panic
1229
1230 builder.positionBuilderAtEnd(async_preamble_bb);
1231 const l = asyncFrameLayout();
1232 const frame_llvm_ty = try dg.lowerType(frame_ty);
1233 const frame_ptr = llvm_func.getParam(0);
1234 fg.resume_index_ptr = builder.buildStructGEP(frame_llvm_ty, frame_ptr, l.resume_index, "");
1235 const resume_index = builder.buildLoad(llvm_usize, fg.resume_index_ptr, "");
1236 fg.async_switch = builder.buildSwitch(resume_index, bad_resume_bb, 4);
1237 }
1238
1213 fg.genBody(air.getMainBody()) catch |err| switch (err) {1239 fg.genBody(air.getMainBody()) catch |err| switch (err) {
1214 error.CodegenFail => {1240 error.CodegenFail => {
1215 decl.analysis = .codegen_failure;1241 decl.analysis = .codegen_failure;
...@@ -4308,6 +4334,12 @@ pub const FuncGen = struct {...@@ -4308,6 +4334,12 @@ pub const FuncGen = struct {
4308 prev_dbg_line: c_uint,4334 prev_dbg_line: c_uint,
4309 prev_dbg_column: c_uint,4335 prev_dbg_column: c_uint,
43104336
4337 // async stuff
4338 resume_block_index: u32,
4339 resume_bb: *llvm.BasicBlock,
4340 async_switch: *llvm.Value,
4341 resume_index_ptr: *llvm.Value,
4342
4311 /// Stack of locations where a call was inlined.4343 /// Stack of locations where a call was inlined.
4312 dbg_inlined: std.ArrayListUnmanaged(DbgState) = .{},4344 dbg_inlined: std.ArrayListUnmanaged(DbgState) = .{},
43134345
...@@ -4549,8 +4581,11 @@ pub const FuncGen = struct {...@@ -4549,8 +4581,11 @@ pub const FuncGen = struct {
4549 .call_always_tail => try self.airCall(inst, .AlwaysTail),4581 .call_always_tail => try self.airCall(inst, .AlwaysTail),
4550 .call_never_tail => try self.airCall(inst, .NeverTail),4582 .call_never_tail => try self.airCall(inst, .NeverTail),
4551 .call_never_inline => try self.airCall(inst, .NeverInline),4583 .call_never_inline => try self.airCall(inst, .NeverInline),
4552 .call_async_alloc => try self.airCallAsyncAlloc(inst),4584
4553 .call_async => try self.airCallAsync(inst),4585 .call_async_alloc => try self.airCallAsyncAlloc(inst),
4586 .call_async => try self.airCallAsync(inst),
4587 .suspend_begin => try self.airSuspendBegin(),
4588 .suspend_end => try self.airSuspendEnd(),
45544589
4555 .ptr_slice_ptr_ptr => try self.airPtrSliceFieldPtr(inst, 0),4590 .ptr_slice_ptr_ptr => try self.airPtrSliceFieldPtr(inst, 0),
4556 .ptr_slice_len_ptr => try self.airPtrSliceFieldPtr(inst, 1),4591 .ptr_slice_len_ptr => try self.airPtrSliceFieldPtr(inst, 1),
...@@ -4819,9 +4854,37 @@ pub const FuncGen = struct {...@@ -4819,9 +4854,37 @@ pub const FuncGen = struct {
4819 }4854 }
4820 }4855 }
48214856
4822 fn airCallAsync(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {4857 fn airSuspendBegin(fg: *FuncGen) !?*llvm.Value {
4858 fg.resume_bb = genSuspendBegin(fg, "SuspendResume");
4859 return null;
4860 }
4861
4862 fn genSuspendBegin(fg: *FuncGen, name_hint: [*:0]const u8) *llvm.BasicBlock {
4863 const target = fg.getTarget();
4864 const llvm_usize = fg.dg.context.intType(target.ptrBitWidth());
4865 const resume_bb = fg.context.appendBasicBlock(fg.llvm_func, name_hint);
4866 const new_block_index = fg.resume_block_index;
4867 fg.resume_block_index += 1;
4868 const new_block_index_llvm_val = llvm_usize.constInt(new_block_index, .False);
4869 fg.async_switch.addCase(new_block_index_llvm_val, resume_bb);
4870 _ = fg.builder.buildStore(new_block_index_llvm_val, fg.resume_index_ptr);
4871 return resume_bb;
4872 }
4873
4874 fn airSuspendEnd(fg: *FuncGen) !?*llvm.Value {
4875 _ = fg.builder.buildRetVoid();
4876 fg.builder.positionBuilderAtEnd(fg.resume_bb);
4877 fg.resume_bb = undefined;
4878
4879 // TODO safety, store the index of the "not suspended" panic basic
4880 // block into resume_index_ptr
4881
4882 return null;
4883 }
4884
4885 fn airCallAsync(fg: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
4823 _ = inst;4886 _ = inst;
4824 return self.todo("lower call_async", .{});4887 return fg.todo("lower call_async", .{});
4825 }4888 }
48264889
4827 fn airCallAsyncAlloc(fg: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {4890 fn airCallAsyncAlloc(fg: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
src/codegen/llvm/bindings.zig+3
...@@ -259,6 +259,9 @@ pub const Value = opaque {...@@ -259,6 +259,9 @@ pub const Value = opaque {
259259
260 pub const attachMetaData = ZigLLVMAttachMetaData;260 pub const attachMetaData = ZigLLVMAttachMetaData;
261 extern fn ZigLLVMAttachMetaData(GlobalVar: *Value, DIG: *DIGlobalVariableExpression) void;261 extern fn ZigLLVMAttachMetaData(GlobalVar: *Value, DIG: *DIGlobalVariableExpression) void;
262
263 pub const functionSetPrefixData = ZigLLVMFunctionSetPrefixData;
264 extern fn ZigLLVMFunctionSetPrefixData(func: *Value, data: *Value) void;
262};265};
263266
264pub const Type = opaque {267pub const Type = opaque {
src/print_air.zig+2
...@@ -217,6 +217,8 @@ const Writer = struct {...@@ -217,6 +217,8 @@ const Writer = struct {
217 .ret_addr,217 .ret_addr,
218 .frame_addr,218 .frame_addr,
219 .save_err_return_trace_index,219 .save_err_return_trace_index,
220 .suspend_begin,
221 .suspend_end,
220 => try w.writeNoOp(s, inst),222 => try w.writeNoOp(s, inst),
221223
222 .alloc,224 .alloc,