| 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,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, |
| 1444 | 1458 | ||
| 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 | } |
| 1184 | 1184 | ||
| 1185 | fn suspendExpr( | 1185 | fn 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, |
| 2794 | 2790 | ||
| 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, |
| 346 | 346 | ||
| 347 | .fence => return .write, | 347 | .suspend_begin, |
| 348 | .suspend_end, | ||
| 349 | .fence, | ||
| 350 | => return .write, | ||
| 348 | 351 | ||
| 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; |
| 5743 | 5743 | ||
| 5744 | func.state = .success; | 5744 | func.state = .success; |
| 5745 | if (func.async_status == .unknown) { | ||
| 5746 | func.async_status = .not_async; | ||
| 5747 | } | ||
| 5745 | 5748 | ||
| 5746 | // Finally we must resolve the return type and parameter types so that backends | 5749 | // 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 avoid | 1218 | // 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 | } |
| 5596 | 5600 | ||
| 5597 | fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 5601 | fn 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 | } |
| 5602 | 5612 | ||
| 5603 | fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index, force_comptime: bool) CompileError!Air.Inst.Ref { | 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,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), |
| 824 | 824 | ||
| 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 | } |
| 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 | fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { | 4258 | fn 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), |
| 808 | 808 | ||
| 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 | } |
| 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 | fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { | 4231 | fn 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), |
| 643 | 643 | ||
| 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 | } |
| 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 | fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { | 1722 | fn 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), |
| 656 | 656 | ||
| 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 | } |
| 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 | fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { | 1309 | fn 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), |
| 1935 | 1935 | ||
| 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), |
| 1938 | 1941 | ||
| ... | @@ -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 | } |
| 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 | fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void { | 2196 | fn 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), |
| 1906 | 1906 | ||
| 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 | } |
| 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 | fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { | 8074 | fn 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), |
| 3005 | 3005 | ||
| 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 | } |
| 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 | fn airCall( | 4103 | fn 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(); |
| 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 | 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, |
| 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 | /// 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) = .{}, |
| 4313 | 4345 | ||
| ... | @@ -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(), | ||
| 4554 | 4589 | ||
| 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 | } |
| 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 | _ = inst; | 4886 | _ = inst; |
| 4824 | return self.todo("lower call_async", .{}); | 4887 | return fg.todo("lower call_async", .{}); |
| 4825 | } | 4888 | } |
| 4826 | 4889 | ||
| 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 { |
| 259 | 259 | ||
| 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 | }; |
| 263 | 266 | ||
| 264 | pub const Type = opaque { | 267 | pub 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), |
| 221 | 223 | ||
| 222 | .alloc, | 224 | .alloc, |