diff --git a/Test/CoroTest/App/main.vp b/Test/CoroTest/App/main.vp index a9270e3..5708dc6 100644 --- a/Test/CoroTest/App/main.vp +++ b/Test/CoroTest/App/main.vp @@ -8,17 +8,21 @@ async def leaf(x: int) -> int: return x + 1 -async def task(base: int) -> int: - s: int = 0 - for i in range(base): - s = s + await leaf(i) - await coro_sleep(100) - return s +async def t1() -> int: + return await leaf(1) + + +async def t2() -> int: + return await leaf(2) def main() -> t.CInt: - tt: coro.Coroutine = task(5) - r: t.CInt = coro.coro_run(tt) - stdio.printf("coro result = %d\n", r) - coro.coro_destroy(tt) + a: coro.Coroutine = t1() + b: coro.Coroutine = t2() + n: t.CInt = coro.coro_gather(a, b) + r1: t.CInt = coro.coro_run(a) + r2: t.CInt = coro.coro_run(b) + stdio.printf("gather = %d, r1 = %d, r2 = %d\n", n, r1, r2) + coro.coro_destroy(a) + coro.coro_destroy(b) return 0 \ No newline at end of file diff --git a/includes/coro.vp b/includes/coro.vp index 3cfa02a..b87c01b 100644 --- a/includes/coro.vp +++ b/includes/coro.vp @@ -36,3 +36,11 @@ def coro_sleep(ms: t.CInt) -> t.CInt: def coro_destroy(c: Coroutine) -> t.CVoid: # M3 占位:编译器将 coro.coro_destroy(帧) 内联翻译为 free。 return + +def coro_exception(c: Coroutine) -> t.CInt: + # M4 占位:编译器将 coro.coro_exception(帧) 内联翻译为帧 exc 槽读取。 + return 0 + +def coro_gather(a: Coroutine) -> t.CInt: + # M4 占位:编译器将 coro.coro_gather(帧...) 内联翻译为并发推进循环。 + return 0 diff --git a/lib/core/Handles/HandlesAsyncFunctions.py b/lib/core/Handles/HandlesAsyncFunctions.py index 110a6af..f0a8031 100644 --- a/lib/core/Handles/HandlesAsyncFunctions.py +++ b/lib/core/Handles/HandlesAsyncFunctions.py @@ -18,7 +18,7 @@ class AsyncFunctionHandle(BaseHandle): - coro_destroy(帧):释放帧内存(free)。 M2 能力:async 参数、嵌套 await 提升(__await_N 槽)、If/While 控制流、动态状态号恢复块。 - 帧布局:%Coro_ = type { i32 state, i32 done, i32 ret, <参数槽>, <局部槽>, <额外槽(wake/for/await)> } + 帧布局:%Coro_ = type { i32 state, i32 done, i32 ret, i32 exc, <参数槽>, <局部槽>, <额外槽(wake/for/await)> } 状态机:create_(args...) 分配帧 + 存参数;resume_(frame) -> i32 (0=SUSPEND 1=DONE)按 state 分发到主入口 state0 与各恢复块。 """ @@ -95,6 +95,7 @@ class AsyncFunctionHandle(BaseHandle): 'hoist_counter': getattr(Gen, '_coro_hoist_counter', None), 'for_counter': getattr(Gen, '_coro_for_counter', None), 'wake_ptr': getattr(Gen, '_coro_wake_ptr', None), + 'exc_ptr': getattr(Gen, '_coro_exc_ptr', None), } try: self.Trans.VarScopes.append({}) @@ -137,6 +138,7 @@ class AsyncFunctionHandle(BaseHandle): Gen._coro_hoist_counter = saved_coro_state['hoist_counter'] Gen._coro_for_counter = saved_coro_state['for_counter'] Gen._coro_wake_ptr = saved_coro_state['wake_ptr'] + Gen._coro_exc_ptr = saved_coro_state['exc_ptr'] # ------------------------------------------------------------------ # 帧结构体与局部变量收集 @@ -224,6 +226,7 @@ class AsyncFunctionHandle(BaseHandle): ir.IntType(32), # 0: state ir.IntType(32), # 1: done ir.IntType(32), # 2: ret + ir.IntType(32), # 3: exc(异常码,0=无异常) ] slot_vars: list[str] = [] for pname, pty in params: @@ -339,12 +342,12 @@ class AsyncFunctionHandle(BaseHandle): raw: ir.Value = Gen.builder.call(malloc_fn, [ir.Constant(ir.IntType(64), frame_size)], name='frame_raw') frame_ptr: ir.Value = Gen.builder.bitcast(raw, ir.PointerType(frame_ty), name='frame') zero32: ir.Constant = ir.Constant(ir.IntType(32), 0) - for i in range(3): + for i in range(4): slot_ptr: ir.Value = Gen.builder.gep(frame_ptr, [zero32, ir.Constant(ir.IntType(32), i)], name=f'init_slot{i}') Gen._store(zero32, slot_ptr) - # 参数存入帧槽(3 + i) + # 参数存入帧槽(4 + i) for i in range(len(params)): - slot_ptr = Gen.builder.gep(frame_ptr, [zero32, ir.Constant(ir.IntType(32), 3 + i)], name=f'param{i}_slot') + slot_ptr = Gen.builder.gep(frame_ptr, [zero32, ir.Constant(ir.IntType(32), 4 + i)], name=f'param{i}_slot') arg_val: ir.Argument = create_fn.args[i] arg_val.name = f'arg{i}' Gen._store(arg_val, slot_ptr) @@ -378,7 +381,7 @@ class AsyncFunctionHandle(BaseHandle): for idx, var in enumerate(slot_vars): slot_ptr: ir.Value = Gen.builder.gep( frame_arg, - [zero32, ir.Constant(ir.IntType(32), 3 + idx)], + [zero32, ir.Constant(ir.IntType(32), 4 + idx)], name=f'{var}_slot', ) local_slot_ptrs[var] = slot_ptr @@ -389,6 +392,8 @@ class AsyncFunctionHandle(BaseHandle): Gen._coro_state_ptr = state_ptr Gen._coro_done_ptr = done_ptr Gen._coro_ret_field_ptr = ret_ptr + exc_ptr: ir.Value = Gen.builder.gep(frame_arg, [zero32, ir.Constant(ir.IntType(32), 3)], name='exc_ptr') + Gen._coro_exc_ptr = exc_ptr Gen._coro_exit_bb = exit_bb Gen._coro_frame_ptr = frame_arg switch_instr: Any = Gen.builder.switch(state_val, exit_bb) @@ -421,6 +426,9 @@ class AsyncFunctionHandle(BaseHandle): if isinstance(stmt, ast.Return): self._HandleCoroReturn(stmt, Gen) continue + if isinstance(stmt, ast.Raise): + self._HandleCoroRaiseLlvm(stmt, Gen) + continue if (isinstance(stmt, ast.AnnAssign) and isinstance(stmt.target, ast.Name) and isinstance(stmt.value, ast.Await)): @@ -548,7 +556,10 @@ class AsyncFunctionHandle(BaseHandle): def _HandleCoroAwaitCommon(self, target: str, await_node: ast.Await, Gen: LlvmCodeGenerator) -> None: """await 顶层翻译:设置目标槽后调用 _HandleAwaitLlvm(生成子帧/resume/挂起)。""" - Gen._coro_await_slot_ptr = Gen.variables.get(target) + if target == '__ret__': + Gen._coro_await_slot_ptr = getattr(Gen, '_coro_ret_field_ptr', None) + else: + Gen._coro_await_slot_ptr = Gen.variables.get(target) # 校验 await 目标:async 函数调用 或 coro_sleep(ms) call: Any = await_node.value if isinstance(call, ast.Call) and self._IsCoroSleepCall(call): @@ -616,9 +627,6 @@ class AsyncFunctionHandle(BaseHandle): state_ptr: ir.Value | None = getattr(Gen, '_coro_state_ptr', None) if state_ptr is not None: Gen._store(ir.Constant(ir.IntType(32), state_no), state_ptr) - ret_field_ptr: ir.Value | None = getattr(Gen, '_coro_ret_field_ptr', None) - if ret_field_ptr is not None: - Gen._store(zero32, ret_field_ptr) exit_bb: ir.Block | None = getattr(Gen, '_coro_exit_bb', None) if exit_bb is not None: Gen.builder.branch(exit_bb) @@ -652,6 +660,60 @@ class AsyncFunctionHandle(BaseHandle): Gen.builder.call(free_fn, [raw]) return ir.Constant(ir.IntType(32), 0) + def _HandleCoroRaiseLlvm(self, stmt: ast.Raise, Gen: LlvmCodeGenerator) -> None: + """协程内 raise:异常码存帧 exc 槽,done=1,终止(异常由 await/coro_exception 传播)。""" + exc_code: ir.Value = ir.Constant(ir.IntType(32), 1) + if stmt.exc is not None: + v: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(stmt.exc) + if v is not None: + if isinstance(v.type, ir.IntType): + if v.type.width < 32: + exc_code = Gen.builder.zext(v, ir.IntType(32), name='zext_exc') + elif v.type.width > 32: + exc_code = Gen.builder.trunc(v, ir.IntType(32), name='trunc_exc') + else: + exc_code = v + elif isinstance(v.type, ir.PointerType): + exc_code = Gen.builder.ptrtoint(v, ir.IntType(32), name='exc_ptr2int') + exc_ptr: ir.Value | None = getattr(Gen, '_coro_exc_ptr', None) + if exc_ptr is not None: + Gen._store(exc_code, exc_ptr) + done_ptr_r: ir.Value | None = getattr(Gen, '_coro_done_ptr', None) + if done_ptr_r is not None: + Gen._store(ir.Constant(ir.IntType(32), 1), done_ptr_r) + exit_bb: ir.Block | None = getattr(Gen, '_coro_exit_bb', None) + if exit_bb is not None: + Gen.builder.branch(exit_bb) + + def _HandleCoroExceptionLlvm(self, Node: ast.Call, Gen: LlvmCodeGenerator) -> ir.Value: + """coro.coro_exception(帧):返回帧 exc 槽(异常码,0=无异常)。""" + if not Node.args: + return ir.Constant(ir.IntType(32), 0) + arg: ast.expr = Node.args[0] + async_name: str | None = None + if isinstance(arg, ast.Name): + async_name = getattr(Gen, '_coro_var_async', {}).get(arg.id) + if not async_name: + return ir.Constant(ir.IntType(32), 0) + frame_ty: ir.IdentifiedStructType | None = getattr(Gen, '_coro_frame_types', {}).get(async_name) + if not frame_ty: + return ir.Constant(ir.IntType(32), 0) + frame_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(arg) + if frame_val is None: + return ir.Constant(ir.IntType(32), 0) + if isinstance(frame_val.type, ir.PointerType): + frame_ptr: ir.Value = Gen.builder.bitcast(frame_val, ir.PointerType(frame_ty), name='coro_frame') + elif isinstance(frame_val.type, ir.IntType): + frame_ptr = Gen.builder.inttoptr(frame_val, ir.PointerType(frame_ty), name='coro_frame') + else: + return ir.Constant(ir.IntType(32), 0) + exc_ptr: ir.Value = Gen.builder.gep( + frame_ptr, + [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 3)], + name='coro_exc_ptr', + ) + return Gen._load(exc_ptr, name='coro_exc') + def _CoerceArg(self, v: ir.Value | None, target_ty: ir.Type, Gen: LlvmCodeGenerator, name: str = 'arg') -> ir.Value: """实参转换到 create 形参类型(int 宽转换 / 指针 bitcast)。""" if v is None: @@ -706,7 +768,29 @@ class AsyncFunctionHandle(BaseHandle): name='child_ret_ptr', ) child_ret: ir.Value = Gen._load(child_ret_ptr, name='child_ret') - # 存父帧局部变量槽 + # M4: 子协程异常传播——子帧 exc != 0 时父协程带异常终止 + child_exc_ptr: ir.Value = Gen.builder.gep( + child, + [zero32, ir.Constant(ir.IntType(32), 3)], + name='child_exc_ptr', + ) + child_exc: ir.Value = Gen._load(child_exc_ptr, name='child_exc') + normal_bb: ir.Block = Gen.func.append_basic_block('coro_ok') + exc_bb: ir.Block = Gen.func.append_basic_block('coro_exc') + exc_cond: ir.Value = Gen.builder.icmp_signed('==', child_exc, zero32, name='child_exc_ok') + Gen.builder.cbranch(exc_cond, normal_bb, exc_bb) + Gen.builder.position_at_start(exc_bb) + parent_exc_ptr: ir.Value | None = getattr(Gen, '_coro_exc_ptr', None) + if parent_exc_ptr is not None: + Gen._store(child_exc, parent_exc_ptr) + parent_done_ptr: ir.Value | None = getattr(Gen, '_coro_done_ptr', None) + if parent_done_ptr is not None: + Gen._store(ir.Constant(ir.IntType(32), 1), parent_done_ptr) + exit_bb_ref: ir.Block | None = getattr(Gen, '_coro_exit_bb', None) + if exit_bb_ref is not None: + Gen.builder.branch(exit_bb_ref) + # 正常路径:存父帧局部变量槽 + 挂起 + Gen.builder.position_at_start(normal_bb) slot: ir.Value | None = getattr(Gen, '_coro_await_slot_ptr', None) if slot is not None: Gen._store(child_ret, slot) @@ -720,9 +804,6 @@ class AsyncFunctionHandle(BaseHandle): state_ptr: ir.Value | None = getattr(Gen, '_coro_state_ptr', None) if state_ptr is not None: Gen._store(ir.Constant(ir.IntType(32), state_no), state_ptr) - ret_field_ptr: ir.Value | None = getattr(Gen, '_coro_ret_field_ptr', None) - if ret_field_ptr is not None: - Gen._store(zero32, ret_field_ptr) exit_bb: ir.Block | None = getattr(Gen, '_coro_exit_bb', None) if exit_bb is not None: Gen.builder.branch(exit_bb) @@ -730,6 +811,16 @@ class AsyncFunctionHandle(BaseHandle): return child_ret def _HandleCoroReturn(self, stmt: ast.Return, Gen: LlvmCodeGenerator) -> None: + # return await x:await 直接存父帧 ret 槽(挂起前),恢复块仅完成协程 + if stmt.value is not None and isinstance(stmt.value, ast.Await): + self._HandleCoroAwaitCommon('__ret__', stmt.value, Gen) + done_ptr_r: ir.Value | None = getattr(Gen, '_coro_done_ptr', None) + if done_ptr_r is not None: + Gen._store(ir.Constant(ir.IntType(32), 1), done_ptr_r) + exit_bb_r: ir.Block | None = getattr(Gen, '_coro_exit_bb', None) + if exit_bb_r is not None: + Gen.builder.branch(exit_bb_r) + return val: ir.Value | None = None if stmt.value: val = self.Trans.ExprHandler.HandleExprLlvm(stmt.value) @@ -918,6 +1009,70 @@ class AsyncFunctionHandle(BaseHandle): # coro.coro_run(帧) 内联翻译 # ------------------------------------------------------------------ + def _HandleCoroGatherLlvm(self, Node: ast.Call, Gen: LlvmCodeGenerator) -> ir.Value: + """coro.coro_gather(帧...):并发推进全部协程到完成(含 coro_sleep 轮询),返回完成数。""" + zero32: ir.Constant = ir.Constant(ir.IntType(32), 0) + count: int = 0 + for arg in Node.args: + async_name: str | None = None + if isinstance(arg, ast.Name): + async_name = getattr(Gen, '_coro_var_async', {}).get(arg.id) + if not async_name: + continue + frame_ty: ir.IdentifiedStructType | None = getattr(Gen, '_coro_frame_types', {}).get(async_name) + resume_fn: ir.Function | None = Gen._get_function(f'resume_{async_name}') + if not frame_ty or not resume_fn: + continue + frame_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(arg) + if frame_val is None: + continue + if isinstance(frame_val.type, ir.PointerType): + frame_ptr: ir.Value = Gen.builder.bitcast(frame_val, ir.PointerType(frame_ty), name='gather_frame') + elif isinstance(frame_val.type, ir.IntType): + frame_ptr = Gen.builder.inttoptr(frame_val, ir.PointerType(frame_ty), name='gather_frame') + else: + continue + loop_bb: ir.Block = Gen.func.append_basic_block('gather_loop') + check_bb: ir.Block = Gen.func.append_basic_block('gather_wcheck') + after_bb: ir.Block = Gen.func.append_basic_block('gather_after') + Gen.builder.branch(loop_bb) + Gen.builder.position_at_start(loop_bb) + status: ir.Value = Gen.builder.call(resume_fn, [frame_ptr], name=f'gather_resume_{async_name}') + done_cond: ir.Value = Gen.builder.icmp_signed('!=', status, zero32, name='gather_done_cond') + Gen.builder.cbranch(done_cond, after_bb, check_bb) + # SUSPEND:coro_sleep 唤醒轮询 + Gen.builder.position_at_start(check_bb) + slot_vars: list[str] = getattr(Gen, '_coro_slot_vars', {}).get(async_name, []) + wake_idx: int | None = None + if '__coro_wake_us' in slot_vars: + wake_idx = 4 + slot_vars.index('__coro_wake_us') + if wake_idx is not None: + wsleep_bb: ir.Block = Gen.func.append_basic_block('gather_wsleep') + wdue_bb: ir.Block = Gen.func.append_basic_block('gather_wdue') + wake_ptr: ir.Value = Gen.builder.gep( + frame_ptr, + [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), wake_idx)], + name='gather_wake_ptr', + ) + wake_val: ir.Value = Gen._load(wake_ptr, name='gather_wake') + now_fn: ir.Function = self._GetWinFunc('GetTickCount64', Gen, ir.IntType(64)) + now_val: ir.Value = Gen.builder.call(now_fn, [], name='gather_tick_now') + not_wake: ir.Value = Gen.builder.icmp_signed('==', wake_val, ir.Constant(ir.IntType(64), 0), name='gather_wake_zero') + due: ir.Value = Gen.builder.icmp_unsigned('>=', now_val, wake_val, name='gather_wake_due') + sleep_cond: ir.Value = Gen.builder.or_(not_wake, due, name='gather_continue_cond') + Gen.builder.cbranch(sleep_cond, wdue_bb, wsleep_bb) + Gen.builder.position_at_start(wsleep_bb) + sleep_fn: ir.Function = self._GetWinFunc('Sleep', Gen, ir.VoidType(), [ir.IntType(32)]) + Gen.builder.call(sleep_fn, [ir.Constant(ir.IntType(32), 1)]) + Gen.builder.branch(loop_bb) + Gen.builder.position_at_start(wdue_bb) + Gen.builder.branch(loop_bb) + else: + Gen.builder.branch(loop_bb) + Gen.builder.position_at_start(after_bb) + count += 1 + return ir.Constant(ir.IntType(32), count) + def _HandleCoroRunLlvm(self, Node: ast.Call, Gen: LlvmCodeGenerator) -> ir.Value: if not Node.args: return ir.Constant(ir.IntType(32), 0) @@ -943,8 +1098,6 @@ class AsyncFunctionHandle(BaseHandle): loop_bb: ir.Block = Gen.func.append_basic_block('coro_loop') after_bb: ir.Block = Gen.func.append_basic_block('coro_after') check_bb: ir.Block = Gen.func.append_basic_block('coro_wcheck') - wake_wait_bb: ir.Block = Gen.func.append_basic_block('coro_wsleep') - wake_due_bb: ir.Block = Gen.func.append_basic_block('coro_wdue') Gen.builder.branch(loop_bb) Gen.builder.position_at_start(loop_bb) status: ir.Value = Gen.builder.call(resume_fn, [frame_ptr], name=f'resume_{async_name}') @@ -955,8 +1108,10 @@ class AsyncFunctionHandle(BaseHandle): wake_idx: int | None = None slot_vars: list[str] = getattr(Gen, '_coro_slot_vars', {}).get(async_name, []) if '__coro_wake_us' in slot_vars: - wake_idx = 3 + slot_vars.index('__coro_wake_us') + wake_idx = 4 + slot_vars.index('__coro_wake_us') if wake_idx is not None: + wake_wait_bb: ir.Block = Gen.func.append_basic_block('coro_wsleep') + wake_due_bb: ir.Block = Gen.func.append_basic_block('coro_wdue') wake_ptr: ir.Value = Gen.builder.gep( frame_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), wake_idx)], diff --git a/lib/core/Handles/HandlesExprCall.py b/lib/core/Handles/HandlesExprCall.py index 65df6b4..78ce48f 100644 --- a/lib/core/Handles/HandlesExprCall.py +++ b/lib/core/Handles/HandlesExprCall.py @@ -282,6 +282,14 @@ class ExprCallHandle(BaseHandle): if isinstance(Node.func, ast.Attribute) and Node.func.attr == 'coro_destroy': if isinstance(Node.func.value, ast.Name) and self.Trans.AsyncHandler._IsCoroModule(Node.func.value.id): return self.Trans.AsyncHandler._HandleCoroDestroyLlvm(Node, Gen) + # 协程:coro.coro_exception(帧) 内联翻译为帧 exc 槽读取 + if isinstance(Node.func, ast.Attribute) and Node.func.attr == 'coro_exception': + if isinstance(Node.func.value, ast.Name) and self.Trans.AsyncHandler._IsCoroModule(Node.func.value.id): + return self.Trans.AsyncHandler._HandleCoroExceptionLlvm(Node, Gen) + # 协程:coro.coro_gather(帧...) 内联翻译为并发推进循环 + if isinstance(Node.func, ast.Attribute) and Node.func.attr == 'coro_gather': + if isinstance(Node.func.value, ast.Name) and self.Trans.AsyncHandler._IsCoroModule(Node.func.value.id): + return self.Trans.AsyncHandler._HandleCoroGatherLlvm(Node, Gen) FuncAttr: str | None = None ModulePath: str | None = None if isinstance(Node.func, ast.Name):