| author | |
| committer | |
| log | e45b10f3d453f3bd8326631ee786a2ef247e8953 |
| tree | 92a8b593bbb1d06f696ce57826fc74438dfc1b1d |
| parent | f1b7c76ae9c015eeadeeff6ffcdf1523afce43f5 |
* 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 | 318 | /// This instruction also acts as an alloc. |
| 319 | 319 | /// Uses `ty_pl` field with the `AsyncCallAlloc` payload. |
| 320 | 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 | 333 | /// Count leading zeroes of an integer according to its representation in twos complement. |
| 322 | 334 | /// Result type will always be an unsigned integer big enough to fit the answer. |
| 323 | 335 | /// Uses the `ty_op` field. |
| ... | ... | @@ -1440,6 +1452,8 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) |
| 1440 | 1452 | .vector_store_elem, |
| 1441 | 1453 | .c_va_end, |
| 1442 | 1454 | .call_async, |
| 1455 | .suspend_begin, | |
| 1456 | .suspend_end, | |
| 1443 | 1457 | => return Type.void, |
| 1444 | 1458 | |
| 1445 | 1459 | .int_from_ptr, |
| ... | ... | @@ -1632,6 +1646,8 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { |
| 1632 | 1646 | .cmpxchg_weak, |
| 1633 | 1647 | .cmpxchg_strong, |
| 1634 | 1648 | .fence, |
| 1649 | .suspend_begin, | |
| 1650 | .suspend_end, | |
| 1635 | 1651 | .atomic_store_unordered, |
| 1636 | 1652 | .atomic_store_monotonic, |
| 1637 | 1653 | .atomic_store_release, |
src/AstGen.zig+2-6| ... | ... | @@ -1182,11 +1182,7 @@ fn nosuspendExpr( |
| 1182 | 1182 | return expr(gz, scope, ri, body_node); |
| 1183 | 1183 | } |
| 1184 | 1184 | |
| 1185 | fn suspendExpr( | |
| 1186 | gz: *GenZir, | |
| 1187 | scope: *Scope, | |
| 1188 | node: Ast.Node.Index, | |
| 1189 | ) InnerError!Zir.Inst.Ref { | |
| 1185 | fn suspendExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { | |
| 1190 | 1186 | const astgen = gz.astgen; |
| 1191 | 1187 | const gpa = astgen.gpa; |
| 1192 | 1188 | const tree = astgen.tree; |
| ... | ... | @@ -2562,7 +2558,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2562 | 2558 | .block, |
| 2563 | 2559 | .block_comptime, |
| 2564 | 2560 | .block_inline, |
| 2565 | .suspend_block, | |
| 2566 | 2561 | .loop, |
| 2567 | 2562 | .bool_br_and, |
| 2568 | 2563 | .bool_br_or, |
| ... | ... | @@ -2790,6 +2785,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2790 | 2785 | .validate_deref, |
| 2791 | 2786 | .save_err_ret_index, |
| 2792 | 2787 | .restore_err_ret_index, |
| 2788 | .suspend_block, | |
| 2793 | 2789 | => break :b true, |
| 2794 | 2790 | |
| 2795 | 2791 | .@"defer" => unreachable, |
src/Liveness.zig+6-1| ... | ... | @@ -344,7 +344,10 @@ pub fn categorizeOperand( |
| 344 | 344 | .work_group_id, |
| 345 | 345 | => return .none, |
| 346 | 346 | |
| 347 | .fence => return .write, | |
| 347 | .suspend_begin, | |
| 348 | .suspend_end, | |
| 349 | .fence, | |
| 350 | => return .write, | |
| 348 | 351 | |
| 349 | 352 | .not, |
| 350 | 353 | .bitcast, |
| ... | ... | @@ -1013,6 +1016,8 @@ fn analyzeInst( |
| 1013 | 1016 | .dbg_block_begin, |
| 1014 | 1017 | .dbg_block_end, |
| 1015 | 1018 | .fence, |
| 1019 | .suspend_begin, | |
| 1020 | .suspend_end, | |
| 1016 | 1021 | .ret_addr, |
| 1017 | 1022 | .frame_addr, |
| 1018 | 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 | 52 | .dbg_block_begin, |
| 53 | 53 | .dbg_block_end, |
| 54 | 54 | .fence, |
| 55 | .suspend_begin, | |
| 56 | .suspend_end, | |
| 55 | 57 | .ret_addr, |
| 56 | 58 | .frame_addr, |
| 57 | 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 | 5742 | sema.air_extra.items[@intFromEnum(Air.ExtraIndex.main_block)] = main_block_index; |
| 5743 | 5743 | |
| 5744 | 5744 | func.state = .success; |
| 5745 | if (func.async_status == .unknown) { | |
| 5746 | func.async_status = .not_async; | |
| 5747 | } | |
| 5745 | 5748 | |
| 5746 | 5749 | // Finally we must resolve the return type and parameter types so that backends |
| 5747 | 5750 | // have full access to type information. |
src/Sema.zig+15-5| ... | ... | @@ -932,7 +932,6 @@ fn analyzeBodyInner( |
| 932 | 932 | .bit_not => try sema.zirBitNot(block, inst), |
| 933 | 933 | .bit_or => try sema.zirBitwise(block, inst, .bit_or), |
| 934 | 934 | .bitcast => try sema.zirBitcast(block, inst), |
| 935 | .suspend_block => try sema.zirSuspendBlock(block, inst), | |
| 936 | 935 | .bool_not => try sema.zirBoolNot(block, inst), |
| 937 | 936 | .bool_br_and => try sema.zirBoolBr(block, inst, false), |
| 938 | 937 | .bool_br_or => try sema.zirBoolBr(block, inst, true), |
| ... | ... | @@ -1218,6 +1217,11 @@ fn analyzeBodyInner( |
| 1218 | 1217 | // continue the loop. |
| 1219 | 1218 | // We also know that they cannot be referenced later, so we avoid |
| 1220 | 1219 | // putting them into the map. |
| 1220 | .suspend_block => { | |
| 1221 | try sema.zirSuspendBlock(block, inst); | |
| 1222 | i += 1; | |
| 1223 | continue; | |
| 1224 | }, | |
| 1221 | 1225 | .dbg_stmt => { |
| 1222 | 1226 | try sema.zirDbgStmt(block, inst); |
| 1223 | 1227 | i += 1; |
| ... | ... | @@ -5594,10 +5598,16 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr |
| 5594 | 5598 | return sema.addConstant(file_root_decl.val); |
| 5595 | 5599 | } |
| 5596 | 5600 | |
| 5597 | fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 5598 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 5599 | const src = inst_data.src(); | |
| 5600 | return sema.failWithUseOfAsync(parent_block, src); | |
| 5601 | fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!void { | |
| 5602 | const pl_node = sema.code.instructions.items(.data)[inst].pl_node; | |
| 5603 | const src = pl_node.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 | } |
| 5602 | 5612 | |
| 5603 | 5613 | fn 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 | 822 | .call_async => try self.airCall(inst, .async_kw), |
| 823 | 823 | .call_async_alloc => try self.airCall(inst, .async_kw), |
| 824 | 824 | |
| 825 | .suspend_begin => try airSuspendBegin(self, inst), | |
| 826 | .suspend_end => try airSuspendEnd (self, inst), | |
| 827 | ||
| 825 | 828 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), |
| 826 | 829 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), |
| 827 | 830 | .atomic_store_release => try self.airAtomicStore(inst, .Release), |
| ... | ... | @@ -4242,6 +4245,16 @@ fn airFence(self: *Self) !void { |
| 4242 | 4245 | //return self.finishAirBookkeeping(); |
| 4243 | 4246 | } |
| 4244 | 4247 | |
| 4248 | fn airSuspendBegin(self: *Self, inst: Air.Inst.Index) !void { | |
| 4249 | _ = inst; | |
| 4250 | return self.fail("TODO implement suspend_begin for aarch64", .{}); | |
| 4251 | } | |
| 4252 | ||
| 4253 | fn airSuspendEnd(self: *Self, inst: Air.Inst.Index) !void { | |
| 4254 | _ = inst; | |
| 4255 | return self.fail("TODO implement suspend_end for aarch64", .{}); | |
| 4256 | } | |
| 4257 | ||
| 4245 | 4258 | fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { |
| 4246 | 4259 | if (modifier == .always_tail) return self.fail("TODO implement tail calls for aarch64", .{}); |
| 4247 | 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 | 806 | .call_async => try self.airCall(inst, .async_kw), |
| 807 | 807 | .call_async_alloc => try self.airCall(inst, .async_kw), |
| 808 | 808 | |
| 809 | .suspend_begin => try airSuspendBegin(self, inst), | |
| 810 | .suspend_end => try airSuspendEnd (self, inst), | |
| 811 | ||
| 809 | 812 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), |
| 810 | 813 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), |
| 811 | 814 | .atomic_store_release => try self.airAtomicStore(inst, .Release), |
| ... | ... | @@ -4215,6 +4218,16 @@ fn airFence(self: *Self) !void { |
| 4215 | 4218 | //return self.finishAirBookkeeping(); |
| 4216 | 4219 | } |
| 4217 | 4220 | |
| 4221 | fn airSuspendBegin(self: *Self, inst: Air.Inst.Index) !void { | |
| 4222 | _ = inst; | |
| 4223 | return self.fail("TODO implement suspend_begin for arm", .{}); | |
| 4224 | } | |
| 4225 | ||
| 4226 | fn airSuspendEnd(self: *Self, inst: Air.Inst.Index) !void { | |
| 4227 | _ = inst; | |
| 4228 | return self.fail("TODO implement suspend_end for arm", .{}); | |
| 4229 | } | |
| 4230 | ||
| 4218 | 4231 | fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { |
| 4219 | 4232 | if (modifier == .always_tail) return self.fail("TODO implement tail calls for arm", .{}); |
| 4220 | 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 | 641 | .call_async => try self.airCall(inst, .async_kw), |
| 642 | 642 | .call_async_alloc => try self.airCall(inst, .async_kw), |
| 643 | 643 | |
| 644 | .suspend_begin => try airSuspendBegin(self, inst), | |
| 645 | .suspend_end => try airSuspendEnd (self, inst), | |
| 646 | ||
| 644 | 647 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), |
| 645 | 648 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), |
| 646 | 649 | .atomic_store_release => try self.airAtomicStore(inst, .Release), |
| ... | ... | @@ -1706,6 +1709,16 @@ fn airFence(self: *Self) !void { |
| 1706 | 1709 | //return self.finishAirBookkeeping(); |
| 1707 | 1710 | } |
| 1708 | 1711 | |
| 1712 | fn airSuspendBegin(self: *Self, inst: Air.Inst.Index) !void { | |
| 1713 | _ = inst; | |
| 1714 | return self.fail("TODO implement suspend_begin for riscv64", .{}); | |
| 1715 | } | |
| 1716 | ||
| 1717 | fn airSuspendEnd(self: *Self, inst: Air.Inst.Index) !void { | |
| 1718 | _ = inst; | |
| 1719 | return self.fail("TODO implement suspend_end for riscv64", .{}); | |
| 1720 | } | |
| 1721 | ||
| 1709 | 1722 | fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { |
| 1710 | 1723 | const mod = self.bin_file.options.module.?; |
| 1711 | 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 | 654 | .call_async => try self.airCall(inst, .async_kw), |
| 655 | 655 | .call_async_alloc => try self.airCall(inst, .async_kw), |
| 656 | 656 | |
| 657 | .suspend_begin => try airSuspendBegin(self, inst), | |
| 658 | .suspend_end => try airSuspendEnd (self, inst), | |
| 659 | ||
| 657 | 660 | .atomic_store_unordered => @panic("TODO try self.airAtomicStore(inst, .Unordered)"), |
| 658 | 661 | .atomic_store_monotonic => @panic("TODO try self.airAtomicStore(inst, .Monotonic)"), |
| 659 | 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 | 1296 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1294 | 1297 | } |
| 1295 | 1298 | |
| 1299 | fn airSuspendBegin(self: *Self, inst: Air.Inst.Index) !void { | |
| 1300 | _ = inst; | |
| 1301 | return self.fail("TODO implement suspend_begin for sparc64", .{}); | |
| 1302 | } | |
| 1303 | ||
| 1304 | fn airSuspendEnd(self: *Self, inst: Air.Inst.Index) !void { | |
| 1305 | _ = inst; | |
| 1306 | return self.fail("TODO implement suspend_end for sparc64", .{}); | |
| 1307 | } | |
| 1308 | ||
| 1296 | 1309 | fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { |
| 1297 | 1310 | if (modifier == .always_tail) return self.fail("TODO implement tail calls for {}", .{self.target.cpu.arch}); |
| 1298 | 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 | 1933 | .call_async => func.airCall(inst, .async_kw), |
| 1934 | 1934 | .call_async_alloc => func.airCall(inst, .async_kw), |
| 1935 | 1935 | |
| 1936 | .suspend_begin => func.airSuspendBegin(inst), | |
| 1937 | .suspend_end => func.airSuspendEnd(inst), | |
| 1938 | ||
| 1936 | 1939 | .is_err => func.airIsErr(inst, .i32_ne), |
| 1937 | 1940 | .is_non_err => func.airIsErr(inst, .i32_eq), |
| 1938 | 1941 | |
| ... | ... | @@ -2180,6 +2183,16 @@ fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2180 | 2183 | return func.finishAir(inst, .none, &.{un_op}); |
| 2181 | 2184 | } |
| 2182 | 2185 | |
| 2186 | fn airSuspendBegin(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 2187 | _ = inst; | |
| 2188 | return func.fail("TODO implement suspend_begin for wasm", .{}); | |
| 2189 | } | |
| 2190 | ||
| 2191 | fn airSuspendEnd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 2192 | _ = inst; | |
| 2193 | return func.fail("TODO implement suspend_end for wasm", .{}); | |
| 2194 | } | |
| 2195 | ||
| 2183 | 2196 | fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void { |
| 2184 | 2197 | if (modifier == .always_tail) return func.fail("TODO implement tail calls for wasm", .{}); |
| 2185 | 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 | 1904 | .call_async => try self.airCall(inst, .async_kw), |
| 1905 | 1905 | .call_async_alloc => try self.airCall(inst, .async_kw), |
| 1906 | 1906 | |
| 1907 | .suspend_begin => try airSuspendBegin(self, inst), | |
| 1908 | .suspend_end => try airSuspendEnd (self, inst), | |
| 1909 | ||
| 1907 | 1910 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), |
| 1908 | 1911 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), |
| 1909 | 1912 | .atomic_store_release => try self.airAtomicStore(inst, .Release), |
| ... | ... | @@ -8058,6 +8061,16 @@ fn airFence(self: *Self, inst: Air.Inst.Index) !void { |
| 8058 | 8061 | return self.finishAirBookkeeping(); |
| 8059 | 8062 | } |
| 8060 | 8063 | |
| 8064 | fn airSuspendBegin(self: *Self, inst: Air.Inst.Index) !void { | |
| 8065 | _ = inst; | |
| 8066 | return self.fail("TODO implement suspend_begin for x86_64", .{}); | |
| 8067 | } | |
| 8068 | ||
| 8069 | fn airSuspendEnd(self: *Self, inst: Air.Inst.Index) !void { | |
| 8070 | _ = inst; | |
| 8071 | return self.fail("TODO implement suspend_end for x86_64", .{}); | |
| 8072 | } | |
| 8073 | ||
| 8061 | 8074 | fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { |
| 8062 | 8075 | const mod = self.bin_file.options.module.?; |
| 8063 | 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 | 3003 | .call_async => try airCall(f, inst, .async_kw), |
| 3004 | 3004 | .call_async_alloc => try airCall(f, inst, .async_kw), |
| 3005 | 3005 | |
| 3006 | .suspend_begin => try airSuspendBegin(f, inst), | |
| 3007 | .suspend_end => try airSuspendEnd (f, inst), | |
| 3008 | ||
| 3006 | 3009 | .float_from_int, |
| 3007 | 3010 | .int_from_float, |
| 3008 | 3011 | .fptrunc, |
| ... | ... | @@ -4087,6 +4090,16 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4087 | 4090 | return local; |
| 4088 | 4091 | } |
| 4089 | 4092 | |
| 4093 | fn airSuspendBegin(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 4094 | _ = inst; | |
| 4095 | return f.fail("TODO: C backend: lower suspend_begin", .{}); | |
| 4096 | } | |
| 4097 | ||
| 4098 | fn airSuspendEnd(f: *Function, inst: Air.Inst.Index) !CValue { | |
| 4099 | _ = inst; | |
| 4100 | return f.fail("TODO: C backend: lower suspend_end", .{}); | |
| 4101 | } | |
| 4102 | ||
| 4090 | 4103 | fn airCall( |
| 4091 | 4104 | f: *Function, |
| 4092 | 4105 | inst: Air.Inst.Index, |
src/codegen/llvm.zig+67-4| ... | ... | @@ -1207,9 +1207,35 @@ pub const Object = struct { |
| 1207 | 1207 | .prev_dbg_line = 0, |
| 1208 | 1208 | .prev_dbg_column = 0, |
| 1209 | 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 | 1216 | defer fg.deinit(); |
| 1212 | 1217 | |
| 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 | 1239 | fg.genBody(air.getMainBody()) catch |err| switch (err) { |
| 1214 | 1240 | error.CodegenFail => { |
| 1215 | 1241 | decl.analysis = .codegen_failure; |
| ... | ... | @@ -4308,6 +4334,12 @@ pub const FuncGen = struct { |
| 4308 | 4334 | prev_dbg_line: c_uint, |
| 4309 | 4335 | prev_dbg_column: c_uint, |
| 4310 | 4336 | |
| 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 | 4343 | /// Stack of locations where a call was inlined. |
| 4312 | 4344 | dbg_inlined: std.ArrayListUnmanaged(DbgState) = .{}, |
| 4313 | 4345 | |
| ... | ... | @@ -4549,8 +4581,11 @@ pub const FuncGen = struct { |
| 4549 | 4581 | .call_always_tail => try self.airCall(inst, .AlwaysTail), |
| 4550 | 4582 | .call_never_tail => try self.airCall(inst, .NeverTail), |
| 4551 | 4583 | .call_never_inline => try self.airCall(inst, .NeverInline), |
| 4552 | .call_async_alloc => try self.airCallAsyncAlloc(inst), | |
| 4553 | .call_async => try self.airCallAsync(inst), | |
| 4584 | ||
| 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(), | |
| 4554 | 4589 | |
| 4555 | 4590 | .ptr_slice_ptr_ptr => try self.airPtrSliceFieldPtr(inst, 0), |
| 4556 | 4591 | .ptr_slice_len_ptr => try self.airPtrSliceFieldPtr(inst, 1), |
| ... | ... | @@ -4819,9 +4854,37 @@ pub const FuncGen = struct { |
| 4819 | 4854 | } |
| 4820 | 4855 | } |
| 4821 | 4856 | |
| 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 | 4886 | _ = inst; |
| 4824 | return self.todo("lower call_async", .{}); | |
| 4887 | return fg.todo("lower call_async", .{}); | |
| 4825 | 4888 | } |
| 4826 | 4889 | |
| 4827 | 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 | 259 | |
| 260 | 260 | pub const attachMetaData = ZigLLVMAttachMetaData; |
| 261 | 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 | }; |
| 263 | 266 | |
| 264 | 267 | pub const Type = opaque { |
src/print_air.zig+2| ... | ... | @@ -217,6 +217,8 @@ const Writer = struct { |
| 217 | 217 | .ret_addr, |
| 218 | 218 | .frame_addr, |
| 219 | 219 | .save_err_return_trace_index, |
| 220 | .suspend_begin, | |
| 221 | .suspend_end, | |
| 220 | 222 | => try w.writeNoOp(s, inst), |
| 221 | 223 | |
| 222 | 224 | .alloc, |