diff --git a/Test/CoroTest/App/main.vp b/Test/CoroTest/App/main.vp index b9ff30d..1cffc5e 100644 --- a/Test/CoroTest/App/main.vp +++ b/Test/CoroTest/App/main.vp @@ -4,35 +4,38 @@ import stdio import coro +class MyError(Exception): + pass + + async def leaf(x: int) -> int: return x + 1 -async def boom(kind: int) -> int: - if kind == 1: - raise ValueError - raise KeyError +async def boom() -> int: + raise MyError("boom msg") return 0 -async def task(kind: int) -> int: - caught: int = 0 +async def task() -> int: + fin: int = 0 try: - await boom(kind) - except ValueError as e: - caught = 1 - except KeyError: - caught = 2 - return caught + fin = 1 + await boom() + finally: + fin = fin + 1 + stdio.printf("finally ran, fin = %d\n", fin) + return 99 def main() -> t.CInt: - a: coro.Coroutine = task(1) - b: coro.Coroutine = task(2) - 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) + n: t.CInt = 0 + for i in range(3): + tt: coro.Coroutine = task() + r: t.CInt = coro.coro_run(tt) + e: t.CInt = coro.coro_exception(tt) + m: t.CPtr = coro.coro_exception_msg(tt) + stdio.printf("[%d] r = %d, exc = %d, msg = %s\n", i, r, e, m) + coro.coro_destroy(tt) + n = n + 1 return 0 \ No newline at end of file diff --git a/includes/coro.vp b/includes/coro.vp index b87c01b..54e1491 100644 --- a/includes/coro.vp +++ b/includes/coro.vp @@ -44,3 +44,7 @@ def coro_exception(c: Coroutine) -> t.CInt: def coro_gather(a: Coroutine) -> t.CInt: # M4 占位:编译器将 coro.coro_gather(帧...) 内联翻译为并发推进循环。 return 0 + +def coro_exception_msg(c: Coroutine) -> t.CPtr: + # M7 占位:编译器将 coro.coro_exception_msg(帧) 内联翻译为帧 msg 槽读取。 + return null diff --git a/lib/core/Handles/HandlesAsyncFunctions.py b/lib/core/Handles/HandlesAsyncFunctions.py index 28b0b9e..bce6107 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, i32 exc, <参数槽>, <局部槽>, <额外槽(wake/for/await)> } + 帧布局:%Coro_ = type { i32 state, i32 done, i32 ret, i32 exc, i8* msg, <参数槽>, <局部槽>, <额外槽(wake/for/await)> } 状态机:create_(args...) 分配帧 + 存参数;resume_(frame) -> i32 (0=SUSPEND 1=DONE)按 state 分发到主入口 state0 与各恢复块。 """ @@ -97,6 +97,7 @@ class AsyncFunctionHandle(BaseHandle): 'wake_ptr': getattr(Gen, '_coro_wake_ptr', None), 'exc_ptr': getattr(Gen, '_coro_exc_ptr', None), 'try_stack': getattr(Gen, '_coro_try_stack', None), + 'msg_ptr': getattr(Gen, '_coro_msg_ptr', None), } try: self.Trans.VarScopes.append({}) @@ -141,6 +142,7 @@ class AsyncFunctionHandle(BaseHandle): Gen._coro_wake_ptr = saved_coro_state['wake_ptr'] Gen._coro_exc_ptr = saved_coro_state['exc_ptr'] Gen._coro_try_stack = saved_coro_state['try_stack'] + Gen._coro_msg_ptr = saved_coro_state['msg_ptr'] # ------------------------------------------------------------------ # 帧结构体与局部变量收集 @@ -243,6 +245,7 @@ class AsyncFunctionHandle(BaseHandle): ir.IntType(32), # 1: done ir.IntType(32), # 2: ret ir.IntType(32), # 3: exc(异常码,0=无异常) + ir.PointerType(ir.IntType(8)), # 4: msg(异常消息,null=无) ] slot_vars: list[str] = [] for pname, pty in params: @@ -336,6 +339,17 @@ class AsyncFunctionHandle(BaseHandle): # create_(args...) -> %Coro_* # ------------------------------------------------------------------ + def _GetCoroPool(self, name: str, Gen: LlvmCodeGenerator) -> ir.GlobalVariable: + """每帧类型的全局 free-list 池头(i8*),跨 create/destroy 复用帧内存。""" + pool_name: str = f'coro_pool_{Gen._mangle_func_name(name)}' + for gv in Gen.module.global_values: + if getattr(gv, 'name', None) == pool_name: + return gv + gv: ir.GlobalVariable = ir.GlobalVariable(Gen.module, ir.PointerType(ir.IntType(8)), pool_name) + gv.initializer = ir.Constant(ir.PointerType(ir.IntType(8)), None) + gv.linkage = 'internal' + return gv + def _GetMalloc(self, Gen: LlvmCodeGenerator) -> ir.Function: for fn in Gen.module.global_values: if getattr(fn, 'name', None) == 'malloc': @@ -364,19 +378,45 @@ class AsyncFunctionHandle(BaseHandle): entry: ir.Block = create_fn.append_basic_block('entry') Gen.builder = ir.IRBuilder(entry) Gen.func = create_fn - 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) + null_i8p: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None) + # 帧池:pop(非空则复用,空则 malloc) + pool_gv: ir.GlobalVariable = self._GetCoroPool(name, Gen) + pool_head: ir.Value = Gen.builder.load(pool_gv, name='pool_head') + pool_hit_bb: ir.Block = create_fn.append_basic_block('pool_hit') + pool_miss_bb: ir.Block = create_fn.append_basic_block('pool_miss') + join_bb: ir.Block = create_fn.append_basic_block('pool_join') + head_is_null: ir.Value = Gen.builder.icmp_signed('==', pool_head, null_i8p, name='pool_empty') + Gen.builder.cbranch(head_is_null, pool_miss_bb, pool_hit_bb) + # pool_hit:弹出一个池节点(帧头部存 next) + Gen.builder.position_at_start(pool_hit_bb) + next_head: ir.Value = Gen.builder.load( + Gen.builder.bitcast(pool_head, ir.PointerType(ir.PointerType(ir.IntType(8))), name='pool_node_next_ptr'), + name='pool_next') + Gen.builder.store(next_head, pool_gv) + hit_frame: ir.Value = Gen.builder.bitcast(pool_head, ir.PointerType(frame_ty), name='frame') + Gen.builder.branch(join_bb) + # pool_miss:malloc + 状态清零 + Gen.builder.position_at_start(pool_miss_bb) + raw: ir.Value = Gen.builder.call(malloc_fn, [ir.Constant(ir.IntType(64), frame_size)], name='frame_raw') + miss_frame: ir.Value = Gen.builder.bitcast(raw, ir.PointerType(frame_ty), name='frame') 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}') + slot_ptr: ir.Value = Gen.builder.gep(miss_frame, [zero32, ir.Constant(ir.IntType(32), i)], name=f'init_slot{i}') Gen._store(zero32, slot_ptr) - # 参数存入帧槽(4 + i) + msg_slot: ir.Value = Gen.builder.gep(miss_frame, [zero32, ir.Constant(ir.IntType(32), 4)], name='init_msg_slot') + Gen._store(null_i8p, msg_slot) + Gen.builder.branch(join_bb) + # join:phi 选择帧 + 参数存入(池取/新建都要写参数槽) + Gen.builder.position_at_start(join_bb) + frame_phi: Any = Gen.builder.phi(ir.PointerType(frame_ty), name='frame') + frame_phi.add_incoming(hit_frame, pool_hit_bb) + frame_phi.add_incoming(miss_frame, pool_miss_bb) for i in range(len(params)): - slot_ptr = Gen.builder.gep(frame_ptr, [zero32, ir.Constant(ir.IntType(32), 4 + i)], name=f'param{i}_slot') + slot_ptr = Gen.builder.gep(frame_phi, [zero32, ir.Constant(ir.IntType(32), 5 + 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) - Gen.builder.ret(frame_ptr) + Gen.builder.ret(frame_phi) Gen.builder = None Gen.func = None @@ -406,7 +446,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), 4 + idx)], + [zero32, ir.Constant(ir.IntType(32), 5 + idx)], name=f'{var}_slot', ) local_slot_ptrs[var] = slot_ptr @@ -419,6 +459,8 @@ class AsyncFunctionHandle(BaseHandle): 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 + msg_ptr: ir.Value = Gen.builder.gep(frame_arg, [zero32, ir.Constant(ir.IntType(32), 4)], name='msg_ptr') + Gen._coro_msg_ptr = msg_ptr Gen._coro_exit_bb = exit_bb Gen._coro_frame_ptr = frame_arg switch_instr: Any = Gen.builder.switch(state_val, exit_bb) @@ -475,7 +517,10 @@ class AsyncFunctionHandle(BaseHandle): self._HandleCoroWhile(stmt, Gen) continue if isinstance(stmt, ast.Try): - self._HandleCoroTry(stmt, Gen) + if stmt.finalbody and not stmt.handlers: + self._HandleCoroTryFinally(stmt, Gen) + else: + self._HandleCoroTry(stmt, Gen) continue if isinstance(stmt, ast.For): self._HandleCoroFor(stmt, Gen) @@ -683,24 +728,32 @@ class AsyncFunctionHandle(BaseHandle): frame_ptr = Gen.builder.inttoptr(frame_val, ir.PointerType(frame_ty), name='coro_frame') else: return ir.Constant(ir.IntType(32), 0) - free_fn: ir.Function = self._GetFree(Gen) - raw: ir.Value = Gen.builder.bitcast(frame_ptr, ir.IntType(8).as_pointer(), name='coro_free_ptr') - Gen.builder.call(free_fn, [raw]) + # 帧回池(free-list):帧头部存当前池头,池头指向本帧 + pool_gv: ir.GlobalVariable = self._GetCoroPool(async_name, Gen) + pool_head: ir.Value = Gen.builder.load(pool_gv, name='pool_head') + node_next_ptr: ir.Value = Gen.builder.bitcast( + frame_ptr, ir.PointerType(ir.PointerType(ir.IntType(8))), name='pool_node_next') + Gen.builder.store(pool_head, node_next_ptr) + Gen.builder.store( + Gen.builder.bitcast(frame_ptr, ir.IntType(8).as_pointer(), name='pool_node'), pool_gv) return ir.Constant(ir.IntType(32), 0) - def _ResolveRaiseCode(self, exc_node: ast.expr) -> int | None: - """解析 raise 目标的异常类型码(注册类/内置异常);普通表达式返回 None 走值路径。""" + def _ResolveRaiseInfo(self, exc_node: ast.expr) -> tuple[int, ast.expr | None] | None: + """解析 raise 目标的异常类型(返回 (异常码, 消息表达式));普通表达式返回 None 走值路径。""" name: str | None = None + msg_node: ast.expr | None = None if isinstance(exc_node, ast.Name): name = exc_node.id elif isinstance(exc_node, ast.Call) and isinstance(exc_node.func, ast.Name): name = exc_node.func.id + if exc_node.args: + msg_node = exc_node.args[0] elif isinstance(exc_node, ast.Attribute): name = exc_node.attr if name: if name in EXCEPTION_CODE_MAP or name in getattr(self.Trans, 'exception_registry', {}): try: - return self.Trans.RaiseHandler._get_exception_code(name) + return (self.Trans.RaiseHandler._get_exception_code(name), msg_node) except Exception: return None return None @@ -710,9 +763,15 @@ class AsyncFunctionHandle(BaseHandle): exc_code: ir.Value = ir.Constant(ir.IntType(32), 1) if stmt.exc is not None: v: ir.Value | None = None - resolved: int | None = self._ResolveRaiseCode(stmt.exc) + resolved: tuple[int, ast.expr | None] | None = self._ResolveRaiseInfo(stmt.exc) if resolved is not None: - exc_code = ir.Constant(ir.IntType(32), resolved) + exc_code = ir.Constant(ir.IntType(32), resolved[0]) + msg_node: ast.expr | None = resolved[1] + if msg_node is not None: + msg_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(msg_node) + msg_ptr_r: ir.Value | None = getattr(Gen, '_coro_msg_ptr', None) + if msg_val is not None and isinstance(msg_val.type, ir.PointerType) and msg_ptr_r is not None: + Gen._store(msg_val, msg_ptr_r) else: v = self.Trans.ExprHandler.HandleExprLlvm(stmt.exc) if v is not None: @@ -768,6 +827,35 @@ class AsyncFunctionHandle(BaseHandle): ) return Gen._load(exc_ptr, name='coro_exc') + def _HandleCoroExceptionMsgLlvm(self, Node: ast.Call, Gen: LlvmCodeGenerator) -> ir.Value: + """coro.coro_exception_msg(帧):返回帧 msg 槽(异常消息指针,null=无)。""" + if not Node.args: + return ir.Constant(ir.PointerType(ir.IntType(8)), None) + 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.PointerType(ir.IntType(8)), None) + frame_ty: ir.IdentifiedStructType | None = getattr(Gen, '_coro_frame_types', {}).get(async_name) + if not frame_ty: + return ir.Constant(ir.PointerType(ir.IntType(8)), None) + frame_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(arg) + if frame_val is None: + return ir.Constant(ir.PointerType(ir.IntType(8)), None) + 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.PointerType(ir.IntType(8)), None) + msg_ptr: ir.Value = Gen.builder.gep( + frame_ptr, + [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 4)], + name='coro_msg_ptr', + ) + return Gen._load(msg_ptr, name='coro_msg') + 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: @@ -837,6 +925,15 @@ class AsyncFunctionHandle(BaseHandle): 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) + child_msg_ptr: ir.Value = Gen.builder.gep( + child, + [zero32, ir.Constant(ir.IntType(32), 4)], + name='child_msg_ptr', + ) + child_msg: ir.Value = Gen._load(child_msg_ptr, name='child_msg') + parent_msg_ptr: ir.Value | None = getattr(Gen, '_coro_msg_ptr', None) + if parent_msg_ptr is not None: + Gen._store(child_msg, parent_msg_ptr) coro_try: list[ir.Block] | None = getattr(Gen, '_coro_try_stack', None) if coro_try: Gen.builder.branch(coro_try[-1]) @@ -1058,6 +1155,49 @@ class AsyncFunctionHandle(BaseHandle): else: Gen.builder.position_at_end(merge_bb) + def _HandleCoroTryFinally(self, Node: ast.Try, Gen: LlvmCodeGenerator) -> None: + """协程内 try/finally(无 except):正常/异常路径都执行 fbody; + 异常时 finally 后重新传播(done=1 + br exit);finally 内暂不支持 await。""" + if any(isinstance(n, ast.Await) for s in Node.finalbody for n in ast.walk(s)): + raise SyntaxError( + f"[coro-M7] finally 内暂不支持 await (line {getattr(Node, 'lineno', 0)})" + ) + fin_bb: ir.Block = Gen.func.append_basic_block('coro_fin') + fin_exc_bb: ir.Block = Gen.func.append_basic_block('coro_fin_exc') + merge_bb: ir.Block = Gen.func.append_basic_block('coro_fin_merge') + if not getattr(Gen, '_coro_try_stack', None): + Gen._coro_try_stack = [] + Gen._coro_try_stack.append(fin_bb) + self._HandleCoroSegment(Node.body, Gen) + if Gen.builder is not None and not Gen.builder.block.is_terminated: + Gen.builder.branch(fin_bb) + Gen._coro_try_stack.pop() + # fin_bb:记录异常标志,执行 fbody + Gen.builder.position_at_start(fin_bb) + exc_ptr_f: ir.Value | None = getattr(Gen, '_coro_exc_ptr', None) + if exc_ptr_f is not None: + was_exc: ir.Value = Gen.builder.icmp_signed( + '!=', Gen._load(exc_ptr_f, name='coro_fin_exc'), + ir.Constant(ir.IntType(32), 0), name='coro_fin_was_exc') + else: + was_exc = ir.Constant(ir.IntType(1), 0) + self._HandleCoroSegment(Node.finalbody, Gen) + if Gen.builder is not None and not Gen.builder.block.is_terminated: + Gen.builder.cbranch(was_exc, fin_exc_bb, merge_bb) + # fin_exc_bb:finally 后异常继续传播 + Gen.builder.position_at_start(fin_exc_bb) + if not fin_exc_bb.is_terminated: + done_ptr_f: ir.Value | None = getattr(Gen, '_coro_done_ptr', None) + if done_ptr_f is not None: + Gen._store(ir.Constant(ir.IntType(32), 1), done_ptr_f) + exit_bb_f: ir.Block | None = getattr(Gen, '_coro_exit_bb', None) + if exit_bb_f is not None: + Gen.builder.branch(exit_bb_f) + if not merge_bb.is_terminated: + Gen.builder.position_at_start(merge_bb) + else: + Gen.builder.position_at_end(merge_bb) + def _HandleCoroFor(self, Node: ast.For, Gen: LlvmCodeGenerator) -> None: if any(isinstance(n, ast.Await) for n in ast.walk(Node)): if not self._IsRangeFor(Node): @@ -1067,7 +1207,7 @@ class AsyncFunctionHandle(BaseHandle): ) self._HandleCoroForRange(Node, Gen) return - if any(isinstance(n, ast.Return) for n in ast.walk(Node.body)): + if any(isinstance(n, ast.Return) for st in Node.body for n in ast.walk(st)): raise SyntaxError( f"[coro-M2] For 循环体内暂不支持 return(M3 实现)(line {getattr(Node, 'lineno', 0)})" ) @@ -1185,7 +1325,7 @@ class AsyncFunctionHandle(BaseHandle): 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') + wake_idx = 5 + 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') @@ -1248,7 +1388,7 @@ 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 = 4 + slot_vars.index('__coro_wake_us') + wake_idx = 5 + 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') diff --git a/lib/core/Handles/HandlesExprCall.py b/lib/core/Handles/HandlesExprCall.py index 78ce48f..5aa6726 100644 --- a/lib/core/Handles/HandlesExprCall.py +++ b/lib/core/Handles/HandlesExprCall.py @@ -286,6 +286,10 @@ class ExprCallHandle(BaseHandle): 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_exception_msg(帧) 内联翻译为帧 msg 槽读取 + if isinstance(Node.func, ast.Attribute) and Node.func.attr == 'coro_exception_msg': + if isinstance(Node.func.value, ast.Name) and self.Trans.AsyncHandler._IsCoroModule(Node.func.value.id): + return self.Trans.AsyncHandler._HandleCoroExceptionMsgLlvm(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):