feat(coro): M4——异常传播(帧 exc 槽 + raise + await 传播)+ gather 并发调度

- 帧布局加固定 exc 槽(i32,0=无异常),协程内 raise 存异常码并终止
- await 子协程后检测异常并传播到父帧(父协程带异常终止,状态机短路)
- return await x:await 值直接存父帧 ret 槽(挂起前),修复 child_ret 跨块
  dominate 错误;挂起不再写 ret=0(避免覆盖返回值)
- coro.coro_gather(帧...):并发推进全部协程到完成(含 coro_sleep 轮询),
  返回完成数
- coro.vp 追加 coro_exception/coro_gather 占位
- CoroTest 验证:异常传播 exc=42;gather=2 r1=2 r2=3
This commit is contained in:
TermiNexus committed 2026-08-21 14:26:19 +08:00
1 parent c979f0f94d
commit 411e0bb774
4 files changed
+201 -26

No files matched your search

+14 -10
View File
@@ -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
+8
View File
@@ -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
+171 -16
View File
@@ -18,7 +18,7 @@ class AsyncFunctionHandle(BaseHandle):
- coro_destroy(帧):释放帧内存(free)。
M2 能力:async 参数、嵌套 await 提升(__await_N 槽)、If/While 控制流、动态状态号恢复块。
帧布局:%Coro_<name> = type { i32 state, i32 done, i32 ret, <参数槽>, <局部槽>, <额外槽(wake/for/await)> }
帧布局:%Coro_<name> = type { i32 state, i32 done, i32 ret, i32 exc, <参数槽>, <局部槽>, <额外槽(wake/for/await)> }
状态机:create_<name>(args...) 分配帧 + 存参数;resume_<name>(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)],
+8
View File
@@ -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):