2098 lines
114 KiB
Plaintext
2098 lines
114 KiB
Plaintext
import lib.core.Gen.GenTypes as GenTypes
|
||
from stdint import *
|
||
import ast
|
||
import mlirlite
|
||
from mlirlite import LLVMType
|
||
import memhub
|
||
import string
|
||
import viperlib
|
||
import stdio
|
||
import stdlib
|
||
import lib.core.VLogger as VLogger
|
||
import lib.core.Gen.Translator as HT
|
||
import lib.core.Gen.Call.CallGenBase as CallGenBase
|
||
|
||
|
||
# ============================================================
|
||
# AsyncGenMixin - VPC-M1 coroutine translation (async def / await / coro.coro_run)
|
||
define SCOPE_FUNCTION = 1
|
||
novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||
"""Coroutine translation mixin (VPC-M1 state machine)"""
|
||
|
||
# Entry: async function definition. Called by FuncGenDef.translate_function_def after
|
||
# detecting fd.flags & FLAG_IS_ASYNC; early-returns so it never enters the normal path.
|
||
def translate_async_function(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||
"""Translate an async function definition; returns 0 on success"""
|
||
fd: ast.FunctionDef* = ast.FunctionDef*(node)
|
||
if fd is None or fd.name is None or trans is None: return 0
|
||
name: str = fd.name
|
||
pool: memhub.MemBuddy* = trans.Pool
|
||
mod: mlirlite.LLVMModule* = trans.Module
|
||
if pool is None or mod is None: return 0
|
||
|
||
# avoid duplicate generation: create_<name> already exists (multi-pass / dup def)
|
||
cname_buf: str = pool.alloc(64)
|
||
if cname_buf is not None: viperlib.snprintf(cname_buf, 64, "create_%s", name)
|
||
create_mangled: str = self.Gen.FuncGenBase._mangle_func_name(trans, cname_buf, 0)
|
||
if self.Gen.CallGenBase.find_func_in_module(mod, create_mangled) is not None: return 0
|
||
|
||
# register the async bare name (await target check / coro_run reverse lookup)
|
||
self._register_async_func(trans, name)
|
||
|
||
# M2: collect params -> trans._coro_params (char* array, 8 bytes per item)
|
||
trans._coro_param_count = 0
|
||
nparams: int = self._collect_param_names(trans, fd)
|
||
|
||
# collect locals (top-level AnnAssign/Assign Name targets -> frame slots)
|
||
trans._coro_local_count = 0
|
||
nlocals: int = self._collect_local_names(trans, fd)
|
||
|
||
# M3-M7: pre-scan extra slots (wake/for/await type tables + position arrays) + state ids.
|
||
# _coro_await_count = resume-block count; nseg = 1 entry + await_count.
|
||
trans._coro_extra_count = 0
|
||
trans._coro_hoist_count = 0
|
||
trans._coro_await_count = 0
|
||
trans._coro_uses_sleep = 0
|
||
trans._coro_scan_for_count = 0
|
||
trans._coro_for_counter = 0
|
||
self._scan_sleep_usage(trans, fd)
|
||
self._pre_scan_awaits(trans, fd)
|
||
nextra: int = trans._coro_extra_count
|
||
trans._coro_nseg = 1 + trans._coro_await_count
|
||
|
||
# frame struct %Coro_<name>
|
||
frame_ty: mlirlite.LLVMType* = self._build_frame_ty(pool, mod, trans, name, nparams, nlocals, nextra)
|
||
if frame_ty is None: return 0
|
||
self._register_coro_frame(trans, name, frame_ty)
|
||
# M3: register the wake-slot index (coro_run polling; wake is extra slot 0)
|
||
if trans._coro_uses_sleep != 0: self._register_coro_wake(trans, name, trans._coro_extra_base + 0)
|
||
|
||
# enter function scope so resume slot registration stays isolated from module scope
|
||
self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_FUNCTION)
|
||
|
||
self._emit_create(pool, mod, trans, name, frame_ty, nparams)
|
||
self._emit_resume(pool, mod, trans, name, fd, frame_ty, nparams, nlocals, nextra)
|
||
|
||
self.Gen.VarGen.exit_scope(trans.SymTab)
|
||
return 0
|
||
|
||
# ============================================================
|
||
# M2: parameter collection (into frame param slots)
|
||
def _collect_param_names(self, trans: HT.Translator*, fd: ast.FunctionDef*) -> int:
|
||
"""Collect async parameter names into trans._coro_params (8-byte slots); slot index = 3 + i"""
|
||
if fd is None or fd.args is None or trans is None: return 0
|
||
if trans._coro_params is None: return 0
|
||
ags: ast.Arguments* = ast.Arguments*(fd.args)
|
||
cnt: int = 0
|
||
if ags.args is None: return 0
|
||
bn: sizet = len(ags.args)
|
||
for i in range(int(bn)):
|
||
a0: ast.AST* = ags.args.get(i)
|
||
if a0 is None: continue
|
||
aname: str = None
|
||
if a0.kind() == ast.ASTKind.Arg:
|
||
ar: ast.Arg* = ast.Arg*(a0)
|
||
aname = ar.arg
|
||
elif a0.kind() == ast.ASTKind.Name:
|
||
anm: ast.Name* = ast.Name*(a0)
|
||
aname = anm.id
|
||
if aname is None: continue
|
||
if cnt >= HT.MAX_CORO_VARS: break
|
||
addr: uint64 = uint64(trans._coro_params) + cnt * 8
|
||
slot: str* = (str*)(addr)
|
||
slot[0] = aname
|
||
cnt = cnt + 1
|
||
trans._coro_param_count = cnt
|
||
return cnt
|
||
|
||
# M3-M7: pre-scan extra slots + state ids in the same DFS order as translation.
|
||
# Extra-slot table _coro_extra_types (1=i32, 2=i64); position arrays map slot -> relative index.
|
||
def _pre_scan_awaits(self, trans: HT.Translator*, fd: ast.FunctionDef*) -> int:
|
||
"""Pre-scan the coroutine body to fill the extra-slot table and return its count"""
|
||
if fd is None or fd.children is None or trans is None: return 0
|
||
trans._coro_extra_count = 0
|
||
trans._coro_hoist_count = 0
|
||
trans._coro_await_count = 0
|
||
trans._coro_scan_for_count = 0
|
||
if trans._coro_uses_sleep != 0:
|
||
trans._coro_extra_types[0] = 2 # __coro_wake_us (i64) relative index 0
|
||
trans._coro_extra_count = 1
|
||
self._scan_stmts(trans, fd.children)
|
||
return trans._coro_extra_count
|
||
|
||
# ---------- pass 1: coro_sleep detection (decides the wake slot) ----------
|
||
def _scan_sleep_usage(self, trans: HT.Translator*, fd: ast.FunctionDef*):
|
||
if fd is None or fd.children is None or trans is None: return
|
||
self._scan_sleep_stmts(trans, fd.children)
|
||
|
||
def _scan_sleep_stmts(self, trans: HT.Translator*, stmts: list[ast.AST*]*):
|
||
if stmts is None or trans._coro_uses_sleep != 0: return
|
||
bn: sizet = len(stmts)
|
||
i: sizet = 0
|
||
for i in range(int(bn)): self._scan_sleep_stmt(trans, stmts.get(i))
|
||
|
||
def _scan_sleep_stmt(self, trans: HT.Translator*, stmt: ast.AST*):
|
||
if stmt is None or trans._coro_uses_sleep != 0: return
|
||
sk: int = stmt.kind()
|
||
if sk == ast.ASTKind.If:
|
||
ifn: ast.If* = ast.If*(stmt)
|
||
self._scan_sleep_expr(trans, ifn.test)
|
||
self._scan_sleep_stmts(trans, stmt.children)
|
||
self._scan_sleep_stmts(trans, ifn.orelse)
|
||
return
|
||
if sk == ast.ASTKind.While:
|
||
whn: ast.While* = ast.While*(stmt)
|
||
self._scan_sleep_expr(trans, whn.test)
|
||
self._scan_sleep_stmts(trans, stmt.children)
|
||
self._scan_sleep_stmts(trans, whn.orelse)
|
||
return
|
||
if sk == ast.ASTKind.For:
|
||
fn2: ast.For* = ast.For*(stmt)
|
||
self._scan_sleep_expr(trans, fn2.iter)
|
||
self._scan_sleep_stmts(trans, stmt.children)
|
||
self._scan_sleep_stmts(trans, fn2.orelse)
|
||
return
|
||
if sk == ast.ASTKind.Try:
|
||
trn: ast.Try* = ast.Try*(stmt)
|
||
self._scan_sleep_stmts(trans, stmt.children)
|
||
if trn.handlers is not None:
|
||
nh: sizet = len(trn.handlers)
|
||
hh: sizet = 0
|
||
for hh in range(int(nh)):
|
||
h0: ast.AST* = trn.handlers.get(hh)
|
||
if h0 is not None: self._scan_sleep_stmts(trans, h0.children)
|
||
self._scan_sleep_stmts(trans, trn.orelse)
|
||
self._scan_sleep_stmts(trans, trn.finalbody)
|
||
return
|
||
if sk == ast.ASTKind.Return:
|
||
rv: ast.Return* = ast.Return*(stmt)
|
||
self._scan_sleep_expr(trans, rv.value)
|
||
return
|
||
if sk == ast.ASTKind.AnnAssign:
|
||
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
|
||
self._scan_sleep_expr(trans, aa.value)
|
||
return
|
||
if sk == ast.ASTKind.Assign:
|
||
as_: ast.Assign* = ast.Assign*(stmt)
|
||
self._scan_sleep_expr(trans, as_.value)
|
||
return
|
||
if sk == ast.ASTKind.Expr:
|
||
e: ast.Expr* = ast.Expr*(stmt)
|
||
self._scan_sleep_expr(trans, e.value)
|
||
return
|
||
|
||
def _scan_sleep_expr(self, trans: HT.Translator*, node: ast.AST*):
|
||
if node is None or trans._coro_uses_sleep != 0: return
|
||
k: int = node.kind()
|
||
if k == ast.ASTKind.Call:
|
||
cl: ast.Call* = ast.Call*(node)
|
||
if cl.func is not None:
|
||
if cl.func.kind() == ast.ASTKind.Name:
|
||
nm: ast.Name* = ast.Name*(cl.func)
|
||
if nm.id is not None and string.strcmp(nm.id, "coro_sleep") == 0:
|
||
trans._coro_uses_sleep = 1
|
||
return
|
||
elif cl.func.kind() == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||
if at.attr is not None and string.strcmp(at.attr, "coro_sleep") == 0:
|
||
trans._coro_uses_sleep = 1
|
||
return
|
||
self._scan_sleep_expr(trans, cl.func)
|
||
if cl.args is not None:
|
||
nb: sizet = len(cl.args)
|
||
j: sizet = 0
|
||
for j in range(int(nb)): self._scan_sleep_expr(trans, cl.args.get(j))
|
||
return
|
||
if k == ast.ASTKind.BinOp:
|
||
b: ast.BinOp* = ast.BinOp*(node)
|
||
self._scan_sleep_expr(trans, b.left)
|
||
self._scan_sleep_expr(trans, b.right)
|
||
return
|
||
if k == ast.ASTKind.UnaryOp:
|
||
u: ast.UnaryOp* = ast.UnaryOp*(node)
|
||
self._scan_sleep_expr(trans, u.operand)
|
||
return
|
||
if k == ast.ASTKind.Compare:
|
||
cm: ast.Compare* = ast.Compare*(node)
|
||
self._scan_sleep_expr(trans, cm.left)
|
||
if cm.comparators is not None:
|
||
nb2: sizet = len(cm.comparators)
|
||
j2: sizet = 0
|
||
for j2 in range(int(nb2)): self._scan_sleep_expr(trans, cm.comparators.get(j2))
|
||
return
|
||
if k == ast.ASTKind.BoolOp:
|
||
bo: ast.BoolOp* = ast.BoolOp*(node)
|
||
if bo.values is not None:
|
||
nb3: sizet = len(bo.values)
|
||
j3: sizet = 0
|
||
for j3 in range(int(nb3)): self._scan_sleep_expr(trans, bo.values.get(j3))
|
||
return
|
||
if k == ast.ASTKind.IfExp:
|
||
ie: ast.IfExp* = ast.IfExp*(node)
|
||
self._scan_sleep_expr(trans, ie.test)
|
||
self._scan_sleep_expr(trans, ie.body)
|
||
self._scan_sleep_expr(trans, ie.orelse)
|
||
return
|
||
if k == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(node)
|
||
self._scan_sleep_expr(trans, at.value)
|
||
return
|
||
if k == ast.ASTKind.Subscript:
|
||
sb: ast.Subscript* = ast.Subscript*(node)
|
||
self._scan_sleep_expr(trans, sb.value)
|
||
self._scan_sleep_expr(trans, sb.slice)
|
||
return
|
||
if k == ast.ASTKind.Await:
|
||
aw: ast.Await* = ast.Await*(node)
|
||
self._scan_sleep_expr(trans, aw.value)
|
||
return
|
||
|
||
# ---------- pass 2: fill the extra-slot table + count state ids ----------
|
||
def _scan_stmts(self, trans: HT.Translator*, stmts: list[ast.AST*]*):
|
||
if stmts is None: return
|
||
bn: sizet = len(stmts)
|
||
i: sizet = 0
|
||
for i in range(int(bn)): self._scan_stmt(trans, stmts.get(i))
|
||
|
||
def _scan_stmt(self, trans: HT.Translator*, stmt: ast.AST*):
|
||
if stmt is None: return
|
||
sk: int = stmt.kind()
|
||
if sk == ast.ASTKind.If:
|
||
ifn: ast.If* = ast.If*(stmt)
|
||
if ifn.test is not None: self._scan_expr(trans, ifn.test)
|
||
self._scan_stmts(trans, stmt.children)
|
||
self._scan_stmts(trans, ifn.orelse)
|
||
return
|
||
if sk == ast.ASTKind.While:
|
||
whn: ast.While* = ast.While*(stmt)
|
||
if whn.test is not None: self._scan_expr(trans, whn.test)
|
||
self._scan_stmts(trans, stmt.children)
|
||
self._scan_stmts(trans, whn.orelse)
|
||
return
|
||
if sk == ast.ASTKind.For:
|
||
fn_: ast.For* = ast.For*(stmt)
|
||
if self._for_deep_await(stmt) != 0:
|
||
# contains await: must be a range-For (M3)
|
||
if self._is_range_for(stmt) == 0:
|
||
err_b2: str = trans.Pool.alloc(256)
|
||
if err_b2 is not None:
|
||
viperlib.snprintf(err_b2, 256, "[coro-M3] For inside a coroutine supports only range iteration (including await)")
|
||
self.Gen.TypeGenErr.fatal_error(stmt, err_b2, "SyntaxError")
|
||
return
|
||
# fill __f_idx_N / __f_len_N (i32)
|
||
if trans._coro_scan_for_count < HT.MAX_CORO_VARS: trans._coro_for_slots[trans._coro_scan_for_count] = trans._coro_extra_count
|
||
trans._coro_extra_types[trans._coro_extra_count] = 1
|
||
trans._coro_extra_count = trans._coro_extra_count + 1
|
||
trans._coro_extra_types[trans._coro_extra_count] = 1
|
||
trans._coro_extra_count = trans._coro_extra_count + 1
|
||
trans._coro_scan_for_count = trans._coro_scan_for_count + 1
|
||
self._scan_stmts(trans, stmt.children)
|
||
self._scan_stmts(trans, fn_.orelse)
|
||
return
|
||
# no await: delegate to standard For; recurse (nested For/Try may contain await)
|
||
self._scan_stmts(trans, stmt.children)
|
||
self._scan_stmts(trans, fn_.orelse)
|
||
return
|
||
if sk == ast.ASTKind.Try:
|
||
tr2: ast.Try* = ast.Try*(stmt)
|
||
self._scan_stmts(trans, stmt.children)
|
||
if tr2.handlers is not None:
|
||
nh2: sizet = len(tr2.handlers)
|
||
hh2: sizet = 0
|
||
for hh2 in range(int(nh2)):
|
||
h2: ast.AST* = tr2.handlers.get(hh2)
|
||
if h2 is not None: self._scan_stmts(trans, h2.children)
|
||
self._scan_stmts(trans, tr2.orelse)
|
||
self._scan_stmts(trans, tr2.finalbody)
|
||
return
|
||
if sk == ast.ASTKind.Raise:
|
||
rz: ast.Raise* = ast.Raise*(stmt)
|
||
if rz.exc is not None: self._scan_expr(trans, rz.exc)
|
||
return
|
||
if sk == ast.ASTKind.Return:
|
||
rv: ast.Return* = ast.Return*(stmt)
|
||
if rv.value is not None: self._scan_expr(trans, rv.value)
|
||
return
|
||
if sk == ast.ASTKind.Break or sk == ast.ASTKind.Continue: return
|
||
# top-level await (AnnAssign/Assign/Expr value is Await): count without hoisting
|
||
if sk == ast.ASTKind.AnnAssign or sk == ast.ASTKind.Assign or sk == ast.ASTKind.Expr:
|
||
if self._is_await_tail(stmt) != 0:
|
||
trans._coro_await_count = trans._coro_await_count + 1
|
||
return
|
||
# nested await inside a statement: DFS the value expression
|
||
self._scan_stmt_exprs(trans, stmt)
|
||
|
||
def _scan_stmt_exprs(self, trans: HT.Translator*, stmt: ast.AST*):
|
||
"""Scan statement value expressions for nested awaits"""
|
||
if stmt is None: return
|
||
sk: int = stmt.kind()
|
||
if sk == ast.ASTKind.AnnAssign:
|
||
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
|
||
if aa.value is not None: self._scan_expr(trans, aa.value)
|
||
return
|
||
if sk == ast.ASTKind.Assign:
|
||
as_: ast.Assign* = ast.Assign*(stmt)
|
||
if as_.value is not None: self._scan_expr(trans, as_.value)
|
||
return
|
||
if sk == ast.ASTKind.Expr:
|
||
e: ast.Expr* = ast.Expr*(stmt)
|
||
if e.value is not None: self._scan_expr(trans, e.value)
|
||
return
|
||
# other statement types: fail fast at translation
|
||
|
||
def _scan_expr(self, trans: HT.Translator*, node: ast.AST*):
|
||
"""DFS the expression for Await: allocate a __await_N slot and count the state number"""
|
||
if node is None: return
|
||
k: int = node.kind()
|
||
if k == ast.ASTKind.Await:
|
||
# M3: await coro_sleep suspends via the wake slot, not __await_N (state id only)
|
||
aw_s: ast.Await* = ast.Await*(node)
|
||
if self._expr_await_is_sleep(aw_s) != 0:
|
||
trans._coro_await_count = trans._coro_await_count + 1
|
||
return
|
||
if trans._coro_hoist_count < HT.MAX_CORO_VARS: trans._coro_await_slots[trans._coro_hoist_count] = trans._coro_extra_count
|
||
trans._coro_extra_types[trans._coro_extra_count] = 1
|
||
trans._coro_extra_count = trans._coro_extra_count + 1
|
||
trans._coro_hoist_count = trans._coro_hoist_count + 1
|
||
trans._coro_await_count = trans._coro_await_count + 1
|
||
return
|
||
if k == ast.ASTKind.BinOp:
|
||
b: ast.BinOp* = ast.BinOp*(node)
|
||
self._scan_expr(trans, b.left)
|
||
self._scan_expr(trans, b.right)
|
||
return
|
||
if k == ast.ASTKind.Call:
|
||
cl: ast.Call* = ast.Call*(node)
|
||
if cl.func is not None: self._scan_expr(trans, cl.func)
|
||
if cl.args is not None:
|
||
nb: sizet = len(cl.args)
|
||
j: sizet = 0
|
||
for j in range(int(nb)): self._scan_expr(trans, cl.args.get(j))
|
||
return
|
||
if k == ast.ASTKind.UnaryOp:
|
||
u: ast.UnaryOp* = ast.UnaryOp*(node)
|
||
if u.operand is not None: self._scan_expr(trans, u.operand)
|
||
return
|
||
if k == ast.ASTKind.Compare:
|
||
cm: ast.Compare* = ast.Compare*(node)
|
||
if cm.left is not None: self._scan_expr(trans, cm.left)
|
||
if cm.comparators is not None:
|
||
nb2: sizet = len(cm.comparators)
|
||
j2: sizet = 0
|
||
for j2 in range(int(nb2)): self._scan_expr(trans, cm.comparators.get(j2))
|
||
return
|
||
if k == ast.ASTKind.BoolOp:
|
||
bo: ast.BoolOp* = ast.BoolOp*(node)
|
||
if bo.values is not None:
|
||
nb3: sizet = len(bo.values)
|
||
j3: sizet = 0
|
||
for j3 in range(int(nb3)): self._scan_expr(trans, bo.values.get(j3))
|
||
return
|
||
if k == ast.ASTKind.IfExp:
|
||
ie: ast.IfExp* = ast.IfExp*(node)
|
||
self._scan_expr(trans, ie.test)
|
||
self._scan_expr(trans, ie.body)
|
||
self._scan_expr(trans, ie.orelse)
|
||
return
|
||
if k == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(node)
|
||
if at.value is not None: self._scan_expr(trans, at.value)
|
||
return
|
||
if k == ast.ASTKind.Subscript:
|
||
sb: ast.Subscript* = ast.Subscript*(node)
|
||
if sb.value is not None: self._scan_expr(trans, sb.value)
|
||
if sb.slice is not None: self._scan_expr(trans, sb.slice)
|
||
return
|
||
# leaves like Name / Constant: no nested await
|
||
|
||
# ---------- For helpers (M3) ----------
|
||
def _for_deep_await(self, stmt: ast.AST*) -> int:
|
||
"""Return whether a For statement tree contains await"""
|
||
if stmt is None: return 0
|
||
fn3: ast.For* = ast.For*(stmt)
|
||
if fn3.iter is not None and self._find_expr_await(fn3.iter) is not None: return 1
|
||
if stmt.children is not None:
|
||
bn4: sizet = len(stmt.children)
|
||
i4: sizet = 0
|
||
for i4 in range(int(bn4)):
|
||
if self._stmt_deep_await(stmt.children.get(i4)) != 0: return 1
|
||
if fn3.orelse is not None:
|
||
bn5: sizet = len(fn3.orelse)
|
||
i5: sizet = 0
|
||
for i5 in range(int(bn5)):
|
||
if self._stmt_deep_await(fn3.orelse.get(i5)) != 0: return 1
|
||
return 0
|
||
|
||
def _stmt_deep_await(self, stmt: ast.AST*) -> int:
|
||
"""Return whether a statement tree contains await"""
|
||
if stmt is None: return 0
|
||
sk2: int = stmt.kind()
|
||
if sk2 == ast.ASTKind.If:
|
||
ifn2: ast.If* = ast.If*(stmt)
|
||
if ifn2.test is not None and self._find_expr_await(ifn2.test) is not None: return 1
|
||
if self._stmts_deep_await(stmt.children) != 0: return 1
|
||
return self._stmts_deep_await(ifn2.orelse)
|
||
if sk2 == ast.ASTKind.While:
|
||
whn2: ast.While* = ast.While*(stmt)
|
||
if whn2.test is not None and self._find_expr_await(whn2.test) is not None: return 1
|
||
if self._stmts_deep_await(stmt.children) != 0: return 1
|
||
return self._stmts_deep_await(whn2.orelse)
|
||
if sk2 == ast.ASTKind.For: return self._for_deep_await(stmt)
|
||
if sk2 == ast.ASTKind.Try:
|
||
tr3: ast.Try* = ast.Try*(stmt)
|
||
if self._stmts_deep_await(stmt.children) != 0: return 1
|
||
if tr3.handlers is not None:
|
||
nh3: sizet = len(tr3.handlers)
|
||
hh3: sizet = 0
|
||
for hh3 in range(int(nh3)):
|
||
h3: ast.AST* = tr3.handlers.get(hh3)
|
||
if h3 is not None and self._stmts_deep_await(h3.children) != 0: return 1
|
||
if self._stmts_deep_await(tr3.orelse) != 0: return 1
|
||
return self._stmts_deep_await(tr3.finalbody)
|
||
return self._stmt_has_await(stmt)
|
||
|
||
def _stmts_deep_await(self, stmts: list[ast.AST*]*) -> int:
|
||
if stmts is None: return 0
|
||
bn6: sizet = len(stmts)
|
||
i6: sizet = 0
|
||
for i6 in range(int(bn6)):
|
||
if self._stmt_deep_await(stmts.get(i6)) != 0: return 1
|
||
return 0
|
||
|
||
def _is_range_for(self, stmt: ast.AST*) -> int:
|
||
"""Return whether a for-loop is a range-for with a plain Name target and no await in its args"""
|
||
if stmt is None: return 0
|
||
fn4: ast.For* = ast.For*(stmt)
|
||
if fn4.target is None or fn4.target.kind() != ast.ASTKind.Name: return 0
|
||
if fn4.iter is None or fn4.iter.kind() != ast.ASTKind.Call: return 0
|
||
cl4: ast.Call* = ast.Call*(fn4.iter)
|
||
if cl4.func is None or cl4.func.kind() != ast.ASTKind.Name: return 0
|
||
nm4: ast.Name* = ast.Name*(cl4.func)
|
||
if nm4.id is None or string.strcmp(nm4.id, "range") != 0: return 0
|
||
if cl4.args is None: return 0
|
||
nb7: sizet = len(cl4.args)
|
||
if nb7 != 1 and nb7 != 2: return 0
|
||
j7: sizet = 0
|
||
for j7 in range(int(nb7)):
|
||
if self._find_expr_await(cl4.args.get(j7)) is not None: return 0
|
||
return 1
|
||
|
||
# ============================================================
|
||
# Local-variable collection (top-level AnnAssign / Assign Name targets)
|
||
def _collect_local_names(self, trans: HT.Translator*, fd: ast.FunctionDef*) -> int:
|
||
"""Collect frame local variable names into trans._coro_locals (8-byte slots)"""
|
||
if fd is None or fd.children is None or trans is None: return 0
|
||
if trans._coro_locals is None: return 0
|
||
trans._coro_local_count = 0
|
||
self._collect_locals_stmts(trans, fd.children)
|
||
return trans._coro_local_count
|
||
|
||
def _collect_locals_stmts(self, trans: HT.Translator*, stmts: list[ast.AST*]*):
|
||
if stmts is None: return
|
||
bn: sizet = len(stmts)
|
||
i: sizet = 0
|
||
for i in range(int(bn)): self._collect_locals_stmt(trans, stmts.get(i))
|
||
|
||
def _collect_locals_stmt(self, trans: HT.Translator*, stmt: ast.AST*):
|
||
if stmt is None: return
|
||
sk: int = stmt.kind()
|
||
if sk == ast.ASTKind.AnnAssign:
|
||
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
|
||
if aa.target is not None and aa.target.kind() == ast.ASTKind.Name:
|
||
tn: ast.Name* = ast.Name*(aa.target)
|
||
self._add_local(trans, tn.id)
|
||
return
|
||
if sk == ast.ASTKind.Assign:
|
||
as_: ast.Assign* = ast.Assign*(stmt)
|
||
if as_.targets is not None and len(as_.targets) == 1:
|
||
t0: ast.AST* = as_.targets.get(0)
|
||
if t0 is not None and t0.kind() == ast.ASTKind.Name:
|
||
tn2: ast.Name* = ast.Name*(t0)
|
||
self._add_local(trans, tn2.id)
|
||
return
|
||
if sk == ast.ASTKind.If:
|
||
self._collect_locals_stmts(trans, stmt.children)
|
||
ifn: ast.If* = ast.If*(stmt)
|
||
self._collect_locals_stmts(trans, ifn.orelse)
|
||
return
|
||
if sk == ast.ASTKind.While:
|
||
self._collect_locals_stmts(trans, stmt.children)
|
||
whn: ast.While* = ast.While*(stmt)
|
||
self._collect_locals_stmts(trans, whn.orelse)
|
||
return
|
||
if sk == ast.ASTKind.For:
|
||
fn: ast.For* = ast.For*(stmt)
|
||
if self._for_deep_await(stmt) != 0:
|
||
if fn.target is not None and fn.target.kind() == ast.ASTKind.Name:
|
||
tn3: ast.Name* = ast.Name*(fn.target)
|
||
self._add_local(trans, tn3.id)
|
||
self._collect_locals_stmts(trans, stmt.children)
|
||
self._collect_locals_stmts(trans, fn.orelse)
|
||
return
|
||
if sk == ast.ASTKind.Try:
|
||
tr: ast.Try* = ast.Try*(stmt)
|
||
self._collect_locals_stmts(trans, stmt.children)
|
||
if tr.handlers is not None:
|
||
nh: sizet = len(tr.handlers)
|
||
hh: sizet = 0
|
||
for hh in range(int(nh)):
|
||
h0: ast.AST* = tr.handlers.get(hh)
|
||
if h0 is None: continue
|
||
hd: ast.ExceptHandler* = ast.ExceptHandler*(h0)
|
||
self._add_local(trans, hd.name)
|
||
self._collect_locals_stmts(trans, h0.children)
|
||
self._collect_locals_stmts(trans, tr.orelse)
|
||
self._collect_locals_stmts(trans, tr.finalbody)
|
||
return
|
||
|
||
def _add_local(self, trans: HT.Translator*, name: str):
|
||
if name is None: return
|
||
cnt: int = trans._coro_local_count
|
||
if cnt >= HT.MAX_CORO_VARS: return
|
||
addr: uint64 = uint64(trans._coro_locals) + cnt * 8
|
||
slot: str* = (str*)(addr)
|
||
slot[0] = name
|
||
trans._coro_local_count = cnt + 1
|
||
|
||
# Frame struct %Coro_<name> = { i32 state, i32 done, i32 ret, ... };
|
||
# slot index: 0=state 1=done 2=ret 3=exc 4=msg, then params / locals / extra slots.
|
||
def _build_frame_ty(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, name: str, nparams: int, nlocals: int, nextra: int) -> mlirlite.LLVMType*:
|
||
"""Build the frame struct, register it on the module and struct table, and return its LLVMType"""
|
||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||
i64_ty: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||
# slot base (used by resume/await to compute slot indexes)
|
||
trans._coro_local_base = 5 + nparams
|
||
trans._coro_extra_base = trans._coro_local_base + nlocals
|
||
total: int = 5 + nparams + nlocals + nextra
|
||
head: mlirlite.ParamNode* = None
|
||
prev: mlirlite.ParamNode* = None
|
||
fi: int = 0
|
||
for fi in range(int(total)):
|
||
fty: mlirlite.LLVMType* = i32_ty
|
||
if fi == 4: fty = i8_ptr_ty
|
||
elif fi >= trans._coro_extra_base:
|
||
tidx: int = fi - trans._coro_extra_base
|
||
if tidx < nextra and trans._coro_extra_types is not None:
|
||
if trans._coro_extra_types[tidx] == 2: fty = i64_ty
|
||
pnode: mlirlite.ParamNode* = pool.alloc(mlirlite.ParamNode.__sizeof__())
|
||
if pnode is None: continue
|
||
string.memset(pnode, 0, mlirlite.ParamNode.__sizeof__())
|
||
pnode.Ty = fty
|
||
if head is None: head = pnode
|
||
if prev is not None: prev.Next = pnode
|
||
prev = pnode
|
||
ty_name: str = pool.alloc(128)
|
||
if ty_name is not None: viperlib.snprintf(ty_name, 128, "Coro_%s", name)
|
||
frame_ty: mlirlite.LLVMType* = mlirlite.Struct(pool, head, total, ty_name)
|
||
if frame_ty is None: return None
|
||
mlirlite.module_add_named_type(mod, pool, frame_ty)
|
||
self.Gen.StructGen.register_struct(pool, ty_name, frame_ty, trans.ModuleSha1)
|
||
return frame_ty
|
||
|
||
# ============================================================
|
||
# create_<name>() -> %Coro_<name>*
|
||
def _emit_create(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, name: str, frame_ty: mlirlite.LLVMType*, nparams: int) -> int:
|
||
"""Emit create_<name>(args...): pop or malloc a frame, zero the header, store args, return it"""
|
||
if frame_ty is None: return 0
|
||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||
i64_ty: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty)
|
||
i64_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i64_ty)
|
||
frame_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, frame_ty)
|
||
|
||
cname_buf2: str = pool.alloc(64)
|
||
if cname_buf2 is not None: viperlib.snprintf(cname_buf2, 64, "create_%s", name)
|
||
mangled: str = self.Gen.FuncGenBase._mangle_func_name(trans, cname_buf2, 0)
|
||
if self.Gen.CallGenBase.find_func_in_module(mod, mangled) is not None: return 0
|
||
func: mlirlite.Function* = mlirlite.create_function(pool, mod, mangled, frame_ptr_ty)
|
||
if func is None: return 0
|
||
# params (i32, frame slot 5+i). VPC has no Python "%" formatting ("%" is modulo),
|
||
# so names are built with snprintf ("%%arg" -> "%arg", matching resume's "%frame").
|
||
pi: int = 0
|
||
argn_buf: str = pool.alloc(16)
|
||
for pi in range(int(nparams)):
|
||
if argn_buf is not None: viperlib.snprintf(argn_buf, 16, "%%arg%d", pi)
|
||
mlirlite.add_param(pool, func, i32_ty, argn_buf)
|
||
# register in the function table (bare name create_<name>)
|
||
funcs_ptr: GenTypes.FuncEntry* = trans._funcs
|
||
cur_count: int = trans._func_count
|
||
if self.Gen.CallGenLeafKit.add_func_to_table(funcs_ptr, cur_count, cname_buf2, func, 1024, None, 0, 0, trans.Pool) == 0: trans._func_count = cur_count + 1
|
||
|
||
entry: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "entry")
|
||
if entry is None: return 0
|
||
builder: mlirlite.IRBuilder* = mlirlite.new_builder(pool, func)
|
||
if builder is None: return 0
|
||
mlirlite.position_at_end(builder, entry)
|
||
|
||
frame_size: int = self.Gen.ClassGenEnum._get_type_size(frame_ty)
|
||
size_val: mlirlite.Value* = mlirlite.const_int64(pool, frame_size)
|
||
if size_val is None: return 0
|
||
size_val.Next = None
|
||
|
||
# M7 frame pool: i64 head (0=empty); first 8 bytes hold next (state/done zeroed on reuse)
|
||
pool_gv: mlirlite.GlobalVariable* = self._get_coro_pool(pool, mod, trans, name)
|
||
# GlobalVariable is not a Value: build a Value view (Ty=Ptr(i64), Name="@coro_pool_<name>")
|
||
pool_ref_name: str = pool.alloc(string.strlen(name) + 16)
|
||
if pool_ref_name is not None: viperlib.snprintf(pool_ref_name, string.strlen(name) + 16, "@coro_pool_%s", name)
|
||
pool_ref: mlirlite.Value* = mlirlite.SSAValue(pool, i64_ptr_ty, pool_ref_name)
|
||
pool_head: mlirlite.Value* = mlirlite.build_load(builder, i64_ty, pool_ref)
|
||
if pool_head is None: return 0
|
||
zero64: mlirlite.Value* = mlirlite.const_int64(pool, 0)
|
||
empty_cond: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_EQ, pool_head, zero64)
|
||
if empty_cond is None: return 0
|
||
hit_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "pool.hit")
|
||
miss_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "pool.miss")
|
||
join_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "pool.join")
|
||
if hit_bb is None or miss_bb is None or join_bb is None: return 0
|
||
mlirlite.build_cond_br(builder, empty_cond, miss_bb, hit_bb)
|
||
# hit: pop from pool (first 8 bytes are next)
|
||
mlirlite.position_at_end(builder, hit_bb)
|
||
node_next_ptr: mlirlite.Value* = mlirlite.build_inttoptr(builder, pool_head, i64_ptr_ty)
|
||
next_head: mlirlite.Value* = mlirlite.build_load(builder, i64_ty, node_next_ptr)
|
||
if next_head is None: return 0
|
||
mlirlite.build_store(builder, next_head, pool_ref)
|
||
hit_frame: mlirlite.Value* = mlirlite.build_inttoptr(builder, pool_head, frame_ptr_ty)
|
||
if hit_frame is None: return 0
|
||
mlirlite.build_br(builder, join_bb)
|
||
# miss: malloc + zero the first 8 bytes (clear stale next from pooled frames)
|
||
mlirlite.position_at_end(builder, miss_bb)
|
||
raw: mlirlite.Value* = mlirlite.build_call(builder, "malloc", size_val, 1, i8_ptr_ty, 0)
|
||
if raw is None: return 0
|
||
miss_frame: mlirlite.Value* = mlirlite.build_bitcast(builder, raw, frame_ptr_ty)
|
||
if miss_frame is None: return 0
|
||
miss_head_ptr: mlirlite.Value* = mlirlite.build_bitcast(builder, miss_frame, i64_ptr_ty)
|
||
if miss_head_ptr is not None: mlirlite.build_store(builder, zero64, miss_head_ptr)
|
||
mlirlite.build_br(builder, join_bb)
|
||
# join: phi-select the frame + zero the 5 header fields + store params into slots
|
||
mlirlite.position_at_end(builder, join_bb)
|
||
inc1: mlirlite.PhiIncoming* = mlirlite.new_phi_incoming(pool, hit_frame, hit_bb)
|
||
inc2: mlirlite.PhiIncoming* = mlirlite.new_phi_incoming(pool, miss_frame, miss_bb)
|
||
if inc1 is None or inc2 is None: return 0
|
||
inc1.Next = inc2
|
||
frame_ref: mlirlite.Value* = mlirlite.build_phi(builder, frame_ptr_ty, inc1, 2)
|
||
if frame_ref is None: return 0
|
||
zero: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||
null_p: mlirlite.Value* = mlirlite.ConstNull(pool, i8_ptr_ty, "null")
|
||
si: int = 0
|
||
for si in range(5):
|
||
if si == 4:
|
||
mslot: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i8_ptr_ty, frame_ref, 4)
|
||
if mslot is not None: mlirlite.build_store(builder, null_p, mslot)
|
||
else:
|
||
slot: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i32_ty, frame_ref, si)
|
||
if slot is not None: mlirlite.build_store(builder, zero, slot)
|
||
# store params into frame slots (index 5+i)
|
||
pj: int = 0
|
||
for pj in range(int(nparams)):
|
||
pslot: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i32_ty, frame_ref, 5 + pj)
|
||
if pslot is not None:
|
||
arnb: str = pool.alloc(16)
|
||
if arnb is not None: viperlib.snprintf(arnb, 16, "%%arg%d", pj)
|
||
arg_ref: mlirlite.Value* = mlirlite.SSAValue(pool, i32_ty, arnb)
|
||
if arg_ref is not None: mlirlite.build_store(builder, arg_ref, pslot)
|
||
mlirlite.build_ret(builder, frame_ref)
|
||
return 0
|
||
|
||
# ============================================================
|
||
# M7: per-frame-type global free-list pool head (i64, 0 = empty)
|
||
def _get_coro_pool(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, name: str) -> mlirlite.GlobalVariable*:
|
||
"""Get or create the coro_pool_<name> global (i64 pool head)"""
|
||
pn_buf: str = pool.alloc(string.strlen(name) + 16)
|
||
if pn_buf is not None: viperlib.snprintf(pn_buf, string.strlen(name) + 16, "coro_pool_%s", name)
|
||
# idempotent: create/destroy both call this, so scan the module global list (no find_global API)
|
||
g: mlirlite.GlobalVariable* = mod.GlobalHead
|
||
while g is not None:
|
||
if g.Name is not None and string.strcmp(g.Name, pn_buf) == 0: return g
|
||
g = g.Next
|
||
return mlirlite.create_global_int(pool, mod, pn_buf, 64, 0)
|
||
|
||
def _register_coro_wake(self, trans: HT.Translator*, name: str, wake_idx: int):
|
||
"""Record the wake-slot index for an async name"""
|
||
if trans is None or name is None or trans._coro_wake_names is None: return
|
||
cnt: int = trans._coro_wake_count
|
||
if cnt >= HT.MAX_CORO_FUNCS: return
|
||
addr: uint64 = uint64(trans._coro_wake_names) + cnt * 8
|
||
slot: str* = (str*)(addr)
|
||
slot[0] = name
|
||
trans._coro_wake_idxs[cnt] = wake_idx
|
||
trans._coro_wake_count = cnt + 1
|
||
|
||
def _lookup_coro_wake(self, trans: HT.Translator*, name: str) -> int:
|
||
"""Return the wake-slot index for an async name, or -1 if none"""
|
||
if trans is None or name is None or trans._coro_wake_names is None: return -1
|
||
i: int = 0
|
||
for i in range(int(trans._coro_wake_count)):
|
||
addr: uint64 = uint64(trans._coro_wake_names) + i * 8
|
||
slot: str* = (str*)(addr)
|
||
if slot[0] is not None and string.strcmp(slot[0], name) == 0: return trans._coro_wake_idxs[i]
|
||
return -1
|
||
|
||
# ============================================================
|
||
# resume_<name>(%Coro_<name>*) -> i32 state machine (static segment splitting)
|
||
def _emit_resume(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, name: str, fd: ast.FunctionDef*, frame_ty: mlirlite.LLVMType*, nparams: int, nlocals: int, nextra: int) -> int:
|
||
"""Emit resume_<name>(frame) -> i32 state machine returning done (0=SUSPEND, 1=DONE)"""
|
||
if frame_ty is None: return 0
|
||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||
frame_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, frame_ty)
|
||
|
||
rname_buf: str = pool.alloc(64)
|
||
if rname_buf is not None: viperlib.snprintf(rname_buf, 64, "resume_%s", name)
|
||
mangled: str = self.Gen.FuncGenBase._mangle_func_name(trans, rname_buf, 0)
|
||
func: mlirlite.Function* = mlirlite.create_function(pool, mod, mangled, i32_ty)
|
||
if func is None: return 0
|
||
mlirlite.add_param(pool, func, frame_ptr_ty, "%frame")
|
||
funcs_ptr: GenTypes.FuncEntry* = trans._funcs
|
||
cur_count: int = trans._func_count
|
||
if self.Gen.CallGenLeafKit.add_func_to_table(funcs_ptr, cur_count, rname_buf, func, 1024, None, 0, 1, trans.Pool) == 0:
|
||
trans._func_count = cur_count + 1
|
||
|
||
entry: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "entry")
|
||
if entry is None: return 0
|
||
builder: mlirlite.IRBuilder* = mlirlite.new_builder(pool, func)
|
||
if builder is None: return 0
|
||
mlirlite.position_at_end(builder, entry)
|
||
|
||
frame_arg: mlirlite.Value* = mlirlite.SSAValue(pool, frame_ptr_ty, "%frame")
|
||
if frame_arg is None: return 0
|
||
|
||
# GEP the 5 header slots (M4 exc / M7 msg)
|
||
i8_ptr_ty_r: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||
i64_ty_r: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||
state_ptr: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i32_ty, frame_arg, 0)
|
||
done_ptr: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i32_ty, frame_arg, 1)
|
||
ret_ptr: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i32_ty, frame_arg, 2)
|
||
exc_ptr: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i32_ty, frame_arg, 3)
|
||
msg_ptr: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i8_ptr_ty_r, frame_arg, 4)
|
||
|
||
# GEP param slots -> register in the symbol table (index 5+i)
|
||
pj: int = 0
|
||
for pj in range(int(nparams)):
|
||
pname: str = self._read_param_name(trans, pj)
|
||
if pname is None: continue
|
||
pslot: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i32_ty, frame_arg, 5 + pj)
|
||
if pslot is not None: self.Gen.VarGen.define_var(trans.SymTab, pname, pslot)
|
||
|
||
# GEP local slots -> register in the symbol table
|
||
li: int = 0
|
||
for li in range(int(nlocals)):
|
||
lname: str = self._read_local_name(trans, li)
|
||
if lname is None: continue
|
||
slot: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i32_ty, frame_arg, trans._coro_local_base + li)
|
||
if slot is not None: self.Gen.VarGen.define_var(trans.SymTab, lname, slot)
|
||
|
||
# M3-M7: register extra slots (wake i64 / __f_idx/__f_len/__await i32).
|
||
# Name reverse lookup: wake=uses_sleep && ei==0; for=_coro_for_slots; else __await_M.
|
||
ei: int = 0
|
||
for ei in range(int(nextra)):
|
||
ety: int = 1
|
||
if trans._coro_extra_types is not None: ety = trans._coro_extra_types[ei]
|
||
ety_llvm: mlirlite.LLVMType* = i32_ty
|
||
if ety == 2: ety_llvm = i64_ty_r
|
||
eslot: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, ety_llvm, frame_arg, trans._coro_extra_base + ei)
|
||
if eslot is None: continue
|
||
# wake slot: store the pointer only, no SymTab entry (used by coro_run polling)
|
||
if trans._coro_uses_sleep != 0 and ei == 0:
|
||
trans._coro_wake_ptr = eslot
|
||
continue
|
||
ename: str = pool.alloc(16)
|
||
# for slot: _coro_for_slots[N] is __f_idx_N, +1 is __f_len_N
|
||
is_for: int = 0
|
||
is_len: int = 0
|
||
kf: int = 0
|
||
for kf in range(int(trans._coro_scan_for_count)):
|
||
if trans._coro_for_slots[kf] == ei:
|
||
is_for = 1
|
||
break
|
||
if trans._coro_for_slots[kf] + 1 == ei:
|
||
is_for = 1
|
||
is_len = 1
|
||
break
|
||
if is_for != 0:
|
||
if ename is not None:
|
||
if is_len != 0: viperlib.snprintf(ename, 16, "__f_len_%d", kf)
|
||
else: viperlib.snprintf(ename, 16, "__f_idx_%d", kf)
|
||
else:
|
||
# __await_M: reverse lookup the position array
|
||
found: int = 0
|
||
km: int = 0
|
||
for km in range(int(nextra)):
|
||
if trans._coro_await_slots[km] == ei:
|
||
found = 1
|
||
break
|
||
if found == 0: continue
|
||
if ename is not None: viperlib.snprintf(ename, 16, "__await_%d", km)
|
||
if ename is not None: self.Gen.VarGen.define_var(trans.SymTab, ename, eslot)
|
||
|
||
# state value + exit block
|
||
state_val: mlirlite.Value* = mlirlite.build_load(builder, i32_ty, state_ptr)
|
||
exit_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.exit")
|
||
if exit_bb is None: return 0
|
||
|
||
# save resume context (shared by await suspension / resume blocks)
|
||
trans._coro_state_ptr = state_ptr
|
||
trans._coro_done_ptr = done_ptr
|
||
trans._coro_ret_ptr = ret_ptr
|
||
trans._coro_exc_ptr = exc_ptr
|
||
trans._coro_msg_ptr = msg_ptr
|
||
trans._coro_wake_ptr = None
|
||
trans._coro_exit_bb = exit_bb
|
||
trans._coro_frame = frame_arg
|
||
trans._coro_await_slot = None
|
||
# state0 is the entry; await states start at 1 (same DFS order as pre-scan)
|
||
trans._coro_next_state = 1
|
||
# translation-time indices (same order as pre-scan)
|
||
trans._coro_hoist_count = 0
|
||
trans._coro_for_counter = 0
|
||
trans._coro_try_stack_count = 0
|
||
trans._coro_break_bb = None
|
||
trans._coro_continue_bb = None
|
||
|
||
nseg: int = trans._coro_nseg
|
||
if nseg <= 0: nseg = 1
|
||
# pre-build state blocks + switch cases (one-shot; resume blocks are positioned later)
|
||
state_bbs: sizet* = pool.alloc(nseg * 8)
|
||
if state_bbs is None: return 0
|
||
trans._coro_state_bbs = state_bbs
|
||
sw_head: mlirlite.SwitchCase* = None
|
||
sw_tail: mlirlite.SwitchCase* = None
|
||
sw_count: int = 0
|
||
bb_name: str = pool.alloc(32)
|
||
si: int = 0
|
||
for si in range(int(nseg)):
|
||
if bb_name is not None: viperlib.snprintf(bb_name, 32, "state%d", si)
|
||
sbb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, bb_name)
|
||
if sbb is None: continue
|
||
state_bbs[si] = sizet(sbb)
|
||
sc: mlirlite.SwitchCase* = mlirlite.new_switch_case(pool, mlirlite.const_int32(pool, si), sbb)
|
||
if sc is not None:
|
||
if sw_head is None:
|
||
sw_head = sc
|
||
sw_tail = sc
|
||
else:
|
||
sw_tail.Next = sc
|
||
sw_tail = sc
|
||
sw_count = sw_count + 1
|
||
mlirlite.build_switch(builder, state_val, exit_bb, sw_head, sw_count)
|
||
|
||
# ---- state0 entry: recursively translate the whole body (If/While awaits resume dynamically).
|
||
# Set trans._cur_builder/_cur_func here; later position_at_end reuse the same builder object.
|
||
trans._cur_func = func
|
||
trans._cur_builder = builder
|
||
# P2 willFree 失效表:协程体是独立帧 → 入口同样复位
|
||
self.Gen.VarGen.freed_clear()
|
||
seg_bb: mlirlite.BasicBlock* = mlirlite.BasicBlock*(state_bbs[0])
|
||
mlirlite.position_at_end(builder, seg_bb)
|
||
# M3 硬约束锚点:登记"正在翻译其 body 的协程函数",CallGenCtor._translate_new_expr 据此对
|
||
# 协程体内的 `new heap slife` fail-fast(协程体被状态机拆分,释放点无法确定)。
|
||
# 保存/恢复旧值:协程里再定义协程时不会把外层的标记冲掉。
|
||
old_coro_body: mlirlite.Function* = trans._coro_body_func
|
||
trans._coro_body_func = func
|
||
self._handle_segment(trans, builder, pool, mod, fd.children)
|
||
trans._coro_body_func = old_coro_body
|
||
# body fell through -> implicit return (ret=0 + done=1 + br exit)
|
||
if mlirlite.builder_cur_block_is_terminated(builder) == 0:
|
||
zret: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||
done_one: mlirlite.Value* = mlirlite.const_int32(pool, 1)
|
||
mlirlite.build_store(builder, zret, ret_ptr)
|
||
mlirlite.build_store(builder, done_one, done_ptr)
|
||
mlirlite.build_br(builder, exit_bb)
|
||
|
||
# exit block: load done and return (0=SUSPEND 1=DONE)
|
||
mlirlite.position_at_end(builder, exit_bb)
|
||
done_val: mlirlite.Value* = mlirlite.build_load(builder, i32_ty, done_ptr)
|
||
mlirlite.build_ret(builder, done_val)
|
||
return 0
|
||
|
||
# ============================================================
|
||
# M2: recursive coroutine-body translation (Return/top-level await/If/While/nested await/plain)
|
||
def _handle_segment(self, trans: HT.Translator*, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, stmts: list[ast.AST*]*):
|
||
"""Recursively translate coroutine body statements into the current builder block"""
|
||
if stmts is None: return
|
||
bn: sizet = len(stmts)
|
||
i: sizet = 0
|
||
for i in range(int(bn)):
|
||
stmt: ast.AST* = stmts.get(i)
|
||
if stmt is None: continue
|
||
# current block is terminated (return/suspend) -> stop this segment
|
||
if mlirlite.builder_cur_block_is_terminated(builder) != 0: return
|
||
sk: int = stmt.kind()
|
||
if sk == ast.ASTKind.Return:
|
||
self._emit_coro_return(builder, pool, mod, trans, stmt)
|
||
continue
|
||
if sk == ast.ASTKind.If:
|
||
self._handle_coro_if(trans, builder, pool, mod, stmt)
|
||
continue
|
||
if sk == ast.ASTKind.While:
|
||
self._handle_coro_while(trans, builder, pool, mod, stmt)
|
||
continue
|
||
if sk == ast.ASTKind.Raise:
|
||
self._handle_coro_raise(builder, pool, mod, trans, stmt)
|
||
continue
|
||
if sk == ast.ASTKind.Try:
|
||
tr_: ast.Try* = ast.Try*(stmt)
|
||
has_fin: int = 0
|
||
has_exc: int = 0
|
||
if tr_.finalbody is not None and len(tr_.finalbody) > 0: has_fin = 1
|
||
if tr_.handlers is not None and len(tr_.handlers) > 0: has_exc = 1
|
||
if has_fin != 0 and has_exc == 0: self._handle_coro_try_finally(trans, builder, pool, mod, stmt)
|
||
else: self._handle_coro_try(trans, builder, pool, mod, stmt)
|
||
continue
|
||
if sk == ast.ASTKind.For:
|
||
if self._for_deep_await(stmt) != 0: self._handle_coro_for(trans, builder, pool, mod, stmt)
|
||
else:
|
||
# no await: delegate to the standard For
|
||
self.Gen.StmtGen.translate_stmt(trans, stmt)
|
||
continue
|
||
if sk == ast.ASTKind.Break:
|
||
if trans._coro_break_bb is not None: mlirlite.build_br(builder, trans._coro_break_bb)
|
||
continue
|
||
if sk == ast.ASTKind.Continue:
|
||
if trans._coro_continue_bb is not None: mlirlite.build_br(builder, trans._coro_continue_bb)
|
||
continue
|
||
if sk == ast.ASTKind.Pass: continue
|
||
# top-level await: AnnAssign/Assign/Expr value is Await -> suspend
|
||
if sk == ast.ASTKind.AnnAssign or sk == ast.ASTKind.Assign or sk == ast.ASTKind.Expr:
|
||
if self._is_await_tail(stmt) != 0:
|
||
target_name: str = self._get_assign_target_name(stmt)
|
||
if target_name is not None: trans._coro_await_slot = self.Gen.VarGen.lookup_var(trans.SymTab, target_name)
|
||
else:
|
||
# Expr(await ...): no target slot (discard return), clear stale slot
|
||
trans._coro_await_slot = None
|
||
aw_node: ast.Await* = self._get_await_value(stmt)
|
||
call_name: str = None
|
||
if aw_node is not None: call_name = self._extract_await_call_name(aw_node)
|
||
if call_name is not None and self.is_async_func(trans, call_name) != 0: self._emit_await(builder, pool, mod, trans, aw_node, call_name)
|
||
else:
|
||
if call_name is None: call_name = "(unknown)"
|
||
err_buf: str = pool.alloc(256)
|
||
if err_buf is not None:
|
||
viperlib.snprintf(err_buf, 256, "[coro-M2] await target '%s' is not an async function", call_name)
|
||
self.Gen.TypeGenErr.fatal_error(stmt, err_buf, "SyntaxError")
|
||
continue
|
||
# nested await in the statement -> hoist to __await_N
|
||
if self._stmt_has_await(stmt) != 0:
|
||
self._handle_nested_await_stmt(trans, builder, pool, mod, stmt)
|
||
continue
|
||
# other statements -> standard translation
|
||
self.Gen.StmtGen.translate_stmt(trans, stmt)
|
||
|
||
# ============================================================
|
||
# M2: coroutine If (awaits inside branches suspend/resume in place)
|
||
def _handle_coro_if(self, trans: HT.Translator*, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, stmt: ast.AST*):
|
||
ifn: ast.If* = ast.If*(stmt)
|
||
if ifn is None or ifn.test is None: return
|
||
# cif/celif (internal c.@CIf marker): compile-time prune, then translate only the taken branch
|
||
# so awaits inside it still suspend in place. The marker must never be lowered as a runtime value.
|
||
if self.Gen.StmtGenCif._is_cif_call(ifn.test) == 1:
|
||
if self.Gen.StmtGenCif._prune_cif_test(pool, trans, ifn.test) != 0:
|
||
self._handle_segment(trans, builder, pool, mod, ifn.children)
|
||
else:
|
||
self._handle_segment(trans, builder, pool, mod, ifn.orelse)
|
||
return
|
||
if self._find_expr_await(ifn.test) is not None:
|
||
err_b: str = pool.alloc(256)
|
||
if err_b is not None:
|
||
viperlib.snprintf(err_b, 256, "[coro-M2] await is not supported in an if condition")
|
||
self.Gen.TypeGenErr.fatal_error(stmt, err_b, "SyntaxError")
|
||
return
|
||
cond: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, ifn.test, None, 0, trans)
|
||
if cond is None: return
|
||
cond1: mlirlite.Value* = self._normalize_cond(builder, pool, mod, cond, trans)
|
||
func: mlirlite.Function* = trans._cur_func
|
||
then_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.then")
|
||
merge_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.end")
|
||
has_else: int = 0
|
||
if ifn.orelse is not None and len(ifn.orelse) > 0: has_else = 1
|
||
else_bb: mlirlite.BasicBlock* = None
|
||
if has_else != 0: else_bb = mlirlite.create_block(pool, func, "coro.else")
|
||
if cond1 is None or then_bb is None or merge_bb is None: return
|
||
mlirlite.build_cond_br(builder, cond1, then_bb, else_bb if else_bb is not None else merge_bb)
|
||
# then branch
|
||
mlirlite.position_at_end(builder, then_bb)
|
||
self._handle_segment(trans, builder, pool, mod, stmt.children)
|
||
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, merge_bb)
|
||
# else branch
|
||
if else_bb is not None:
|
||
mlirlite.position_at_end(builder, else_bb)
|
||
self._handle_segment(trans, builder, pool, mod, ifn.orelse)
|
||
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, merge_bb)
|
||
mlirlite.position_at_end(builder, merge_bb)
|
||
|
||
# ============================================================
|
||
# M2: coroutine While (condition block + body; awaits in the body suspend/resume in place)
|
||
def _handle_coro_while(self, trans: HT.Translator*, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, stmt: ast.AST*):
|
||
whn: ast.While* = ast.While*(stmt)
|
||
if whn is None: return
|
||
if whn.test is not None and self._find_expr_await(whn.test) is not None:
|
||
err_c: str = pool.alloc(256)
|
||
if err_c is not None:
|
||
viperlib.snprintf(err_c, 256, "[coro-M2] await is not supported in a while condition")
|
||
self.Gen.TypeGenErr.fatal_error(stmt, err_c, "SyntaxError")
|
||
return
|
||
func: mlirlite.Function* = trans._cur_func
|
||
cond_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.wcond")
|
||
body_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.wbody")
|
||
after_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.wend")
|
||
if cond_bb is None or body_bb is None or after_bb is None: return
|
||
# break/continue targets (M2, single level)
|
||
prev_break: mlirlite.BasicBlock* = trans._coro_break_bb
|
||
prev_cont: mlirlite.BasicBlock* = trans._coro_continue_bb
|
||
trans._coro_break_bb = after_bb
|
||
trans._coro_continue_bb = cond_bb
|
||
mlirlite.build_br(builder, cond_bb)
|
||
# condition block
|
||
mlirlite.position_at_end(builder, cond_bb)
|
||
cond: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, whn.test, None, 0, trans)
|
||
if cond is None: cond = mlirlite.const_int32(pool, 0)
|
||
cond1: mlirlite.Value* = self._normalize_cond(builder, pool, mod, cond, trans)
|
||
mlirlite.build_cond_br(builder, cond1, body_bb, after_bb)
|
||
# loop body
|
||
mlirlite.position_at_end(builder, body_bb)
|
||
self._handle_segment(trans, builder, pool, mod, stmt.children)
|
||
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, cond_bb)
|
||
trans._coro_break_bb = prev_break
|
||
trans._coro_continue_bb = prev_cont
|
||
# loop exit block
|
||
mlirlite.position_at_end(builder, after_bb)
|
||
|
||
# ============================================================
|
||
# M2: nested await hoisting: a = b + await f() -> __await_N = await f(); a = b + __await_N
|
||
def _handle_nested_await_stmt(self, trans: HT.Translator*, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, stmt: ast.AST*):
|
||
"""Hoist all nested awaits in a statement to top-level temporaries in DFS order"""
|
||
guard: int = 0
|
||
while True:
|
||
aw_node: ast.Await* = self._extract_first_await(stmt)
|
||
if aw_node is None: break
|
||
if guard >= HT.MAX_CORO_VARS: break
|
||
guard = guard + 1
|
||
temp_name: str = pool.alloc(32)
|
||
if temp_name is not None: viperlib.snprintf(temp_name, 32, "__await_%d", trans._coro_hoist_count)
|
||
# target slot = __await_N extra slot (pre-scan positions, wake/for slots precede)
|
||
slot_idx: int = trans._coro_extra_base + trans._coro_await_slots[trans._coro_hoist_count]
|
||
trans._coro_hoist_count = trans._coro_hoist_count + 1
|
||
slot: mlirlite.Value* = mlirlite.build_gep_struct(builder, self._lookup_coro_frame_by_trans(trans), mlirlite.Int32(pool), trans._coro_frame, slot_idx)
|
||
trans._coro_await_slot = slot
|
||
call_name: str = self._extract_await_call_name(aw_node)
|
||
if call_name is None: break
|
||
self._emit_await(builder, pool, mod, trans, aw_node, call_name)
|
||
self._replace_await_with_name(pool, stmt, temp_name)
|
||
# hoisting done -> standard translation (resume block continues the rest of the expression)
|
||
self.Gen.StmtGen.translate_stmt(trans, stmt)
|
||
|
||
# ============================================================
|
||
# Nested-await detection / extraction / replacement (DFS order matches pre-scan)
|
||
def _stmt_has_await(self, stmt: ast.AST*) -> int:
|
||
aw: ast.Await* = self._extract_first_await(stmt)
|
||
if aw is not None: return 1
|
||
return 0
|
||
|
||
def _extract_first_await(self, stmt: ast.AST*) -> ast.Await*:
|
||
"""Return the first Await node in a statement (DFS field order), or None"""
|
||
if stmt is None: return None
|
||
sk: int = stmt.kind()
|
||
if sk == ast.ASTKind.AnnAssign:
|
||
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
|
||
return self._find_expr_await(aa.value)
|
||
if sk == ast.ASTKind.Assign:
|
||
as_: ast.Assign* = ast.Assign*(stmt)
|
||
return self._find_expr_await(as_.value)
|
||
if sk == ast.ASTKind.Expr:
|
||
e: ast.Expr* = ast.Expr*(stmt)
|
||
return self._find_expr_await(e.value)
|
||
if sk == ast.ASTKind.Return:
|
||
rv: ast.Return* = ast.Return*(stmt)
|
||
return self._find_expr_await(rv.value)
|
||
return None
|
||
|
||
def _find_expr_await(self, node: ast.AST*) -> ast.Await*:
|
||
"""DFS the expression tree for the first Await"""
|
||
if node is None: return None
|
||
k: int = node.kind()
|
||
if k == ast.ASTKind.Await: return ast.Await*(node)
|
||
if k == ast.ASTKind.BinOp:
|
||
b: ast.BinOp* = ast.BinOp*(node)
|
||
r: ast.Await* = self._find_expr_await(b.left)
|
||
if r is not None: return r
|
||
return self._find_expr_await(b.right)
|
||
if k == ast.ASTKind.Call:
|
||
cl: ast.Call* = ast.Call*(node)
|
||
if cl.func is not None:
|
||
r2: ast.Await* = self._find_expr_await(cl.func)
|
||
if r2 is not None: return r2
|
||
if cl.args is not None:
|
||
nb: sizet = len(cl.args)
|
||
j: sizet = 0
|
||
for j in range(int(nb)):
|
||
r3: ast.Await* = self._find_expr_await(cl.args.get(j))
|
||
if r3 is not None: return r3
|
||
return None
|
||
if k == ast.ASTKind.UnaryOp:
|
||
u: ast.UnaryOp* = ast.UnaryOp*(node)
|
||
return self._find_expr_await(u.operand)
|
||
if k == ast.ASTKind.Compare:
|
||
cm: ast.Compare* = ast.Compare*(node)
|
||
r4: ast.Await* = self._find_expr_await(cm.left)
|
||
if r4 is not None: return r4
|
||
if cm.comparators is not None:
|
||
nb2: sizet = len(cm.comparators)
|
||
j2: sizet = 0
|
||
for j2 in range(int(nb2)):
|
||
r5: ast.Await* = self._find_expr_await(cm.comparators.get(j2))
|
||
if r5 is not None: return r5
|
||
return None
|
||
if k == ast.ASTKind.BoolOp:
|
||
bo: ast.BoolOp* = ast.BoolOp*(node)
|
||
if bo.values is not None:
|
||
nb3: sizet = len(bo.values)
|
||
j3: sizet = 0
|
||
for j3 in range(int(nb3)):
|
||
r6: ast.Await* = self._find_expr_await(bo.values.get(j3))
|
||
if r6 is not None: return r6
|
||
return None
|
||
if k == ast.ASTKind.IfExp:
|
||
ie: ast.IfExp* = ast.IfExp*(node)
|
||
r7: ast.Await* = self._find_expr_await(ie.test)
|
||
if r7 is not None: return r7
|
||
r8: ast.Await* = self._find_expr_await(ie.body)
|
||
if r8 is not None: return r8
|
||
return self._find_expr_await(ie.orelse)
|
||
if k == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(node)
|
||
return self._find_expr_await(at.value)
|
||
if k == ast.ASTKind.Subscript:
|
||
sb: ast.Subscript* = ast.Subscript*(node)
|
||
r9: ast.Await* = self._find_expr_await(sb.value)
|
||
if r9 is not None: return r9
|
||
return self._find_expr_await(sb.slice)
|
||
return None
|
||
|
||
def _replace_await_with_name(self, pool: memhub.MemBuddy*, stmt: ast.AST*, temp_name: str) -> int:
|
||
"""Replace the first Await in a statement tree with Name(temp_name) in DFS order"""
|
||
if stmt is None or temp_name is None: return 0
|
||
sk: int = stmt.kind()
|
||
if sk == ast.ASTKind.AnnAssign:
|
||
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
|
||
if aa.value is not None: return self._replace_expr_await(pool, aa.value, temp_name)
|
||
return 0
|
||
if sk == ast.ASTKind.Assign:
|
||
as_: ast.Assign* = ast.Assign*(stmt)
|
||
if as_.value is not None: return self._replace_expr_await(pool, as_.value, temp_name)
|
||
return 0
|
||
if sk == ast.ASTKind.Expr:
|
||
e: ast.Expr* = ast.Expr*(stmt)
|
||
if e.value is not None: return self._replace_expr_await(pool, e.value, temp_name)
|
||
return 0
|
||
if sk == ast.ASTKind.Return:
|
||
rv: ast.Return* = ast.Return*(stmt)
|
||
if rv.value is not None: return self._replace_expr_await(pool, rv.value, temp_name)
|
||
return 0
|
||
return 0
|
||
|
||
def _replace_expr_await(self, pool: memhub.MemBuddy*, node: ast.AST*, temp_name: str) -> int:
|
||
"""Replace the first Await in an expression with Name(temp_name); returns 1/0/2"""
|
||
if node is None: return 0
|
||
k: int = node.kind()
|
||
if k == ast.ASTKind.Await: return 2
|
||
if k == ast.ASTKind.BinOp:
|
||
b: ast.BinOp* = ast.BinOp*(node)
|
||
r: int = self._replace_expr_await(pool, b.left, temp_name)
|
||
if r == 1: return 1
|
||
if r == 2:
|
||
b.left = ast.Name(pool, temp_name, 0)
|
||
return 1
|
||
r2: int = self._replace_expr_await(pool, b.right, temp_name)
|
||
if r2 == 1: return 1
|
||
if r2 == 2:
|
||
b.right = ast.Name(pool, temp_name, 0)
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.Call:
|
||
cl: ast.Call* = ast.Call*(node)
|
||
if cl.func is not None:
|
||
r3: int = self._replace_expr_await(pool, cl.func, temp_name)
|
||
if r3 == 1: return 1
|
||
if r3 == 2:
|
||
cl.func = ast.Name(pool, temp_name, 0)
|
||
return 1
|
||
if cl.args is not None:
|
||
nb: sizet = len(cl.args)
|
||
j: sizet = 0
|
||
for j in range(int(nb)):
|
||
r4: int = self._replace_expr_await(pool, cl.args.get(j), temp_name)
|
||
if r4 == 1: return 1
|
||
if r4 == 2:
|
||
cl.args.set(j, ast.Name(pool, temp_name, 0))
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.UnaryOp:
|
||
u: ast.UnaryOp* = ast.UnaryOp*(node)
|
||
r5: int = self._replace_expr_await(pool, u.operand, temp_name)
|
||
if r5 == 1: return 1
|
||
if r5 == 2:
|
||
u.operand = ast.Name(pool, temp_name, 0)
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.Compare:
|
||
cm: ast.Compare* = ast.Compare*(node)
|
||
r6: int = self._replace_expr_await(pool, cm.left, temp_name)
|
||
if r6 == 1: return 1
|
||
if r6 == 2:
|
||
cm.left = ast.Name(pool, temp_name, 0)
|
||
return 1
|
||
if cm.comparators is not None:
|
||
nb2: sizet = len(cm.comparators)
|
||
j2: sizet = 0
|
||
for j2 in range(int(nb2)):
|
||
r7: int = self._replace_expr_await(pool, cm.comparators.get(j2), temp_name)
|
||
if r7 == 1: return 1
|
||
if r7 == 2:
|
||
cm.comparators.set(j2, ast.Name(pool, temp_name, 0))
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.BoolOp:
|
||
bo: ast.BoolOp* = ast.BoolOp*(node)
|
||
if bo.values is not None:
|
||
nb3: sizet = len(bo.values)
|
||
j3: sizet = 0
|
||
for j3 in range(int(nb3)):
|
||
r8: int = self._replace_expr_await(pool, bo.values.get(j3), temp_name)
|
||
if r8 == 1: return 1
|
||
if r8 == 2:
|
||
bo.values.set(j3, ast.Name(pool, temp_name, 0))
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.IfExp:
|
||
ie: ast.IfExp* = ast.IfExp*(node)
|
||
r9: int = self._replace_expr_await(pool, ie.test, temp_name)
|
||
if r9 == 1: return 1
|
||
if r9 == 2:
|
||
ie.test = ast.Name(pool, temp_name, 0)
|
||
return 1
|
||
r10: int = self._replace_expr_await(pool, ie.body, temp_name)
|
||
if r10 == 1: return 1
|
||
if r10 == 2:
|
||
ie.body = ast.Name(pool, temp_name, 0)
|
||
return 1
|
||
r11: int = self._replace_expr_await(pool, ie.orelse, temp_name)
|
||
if r11 == 1: return 1
|
||
if r11 == 2:
|
||
ie.orelse = ast.Name(pool, temp_name, 0)
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(node)
|
||
r12: int = self._replace_expr_await(pool, at.value, temp_name)
|
||
if r12 == 1: return 1
|
||
if r12 == 2:
|
||
at.value = ast.Name(pool, temp_name, 0)
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.Subscript:
|
||
sb: ast.Subscript* = ast.Subscript*(node)
|
||
r13: int = self._replace_expr_await(pool, sb.value, temp_name)
|
||
if r13 == 1: return 1
|
||
if r13 == 2:
|
||
sb.value = ast.Name(pool, temp_name, 0)
|
||
return 1
|
||
r14: int = self._replace_expr_await(pool, sb.slice, temp_name)
|
||
if r14 == 1: return 1
|
||
if r14 == 2:
|
||
sb.slice = ast.Name(pool, temp_name, 0)
|
||
return 1
|
||
return 0
|
||
return 0
|
||
|
||
# ============================================================
|
||
# Condition normalization (i1 passes through; otherwise icmp ne 0)
|
||
def _normalize_cond(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, cond: mlirlite.Value*, trans: HT.Translator*) -> mlirlite.Value*:
|
||
"""Normalize a coroutine condition to i1, preferring __bool__ dispatch"""
|
||
return self.Gen.ExprGenOps.coerce_cond_to_bool(pool, builder, mod, cond, trans)
|
||
|
||
# ============================================================
|
||
# Parameter-name read (from the _coro_params table)
|
||
def _read_param_name(self, trans: HT.Translator*, idx: int) -> str:
|
||
if trans is None or trans._coro_params is None: return None
|
||
addr: uint64 = uint64(trans._coro_params) + idx * 8
|
||
slot: str* = (str*)(addr)
|
||
return slot[0]
|
||
|
||
# ============================================================
|
||
# Resume-block lookup (state{N} from the pre-built array)
|
||
def _get_state_bb(self, trans: HT.Translator*, state_no: int) -> mlirlite.BasicBlock*:
|
||
if trans is None or trans._coro_state_bbs is None: return None
|
||
if state_no < 0 or state_no >= trans._coro_nseg: return None
|
||
# state_bbs holds block pointers (sizet): index it, never take an element address,
|
||
# or position lands on an invalid block and the IR chain corrupts (RenumberSSA crash).
|
||
return mlirlite.BasicBlock*(trans._coro_state_bbs[state_no])
|
||
|
||
def _lookup_coro_frame_by_trans(self, trans: HT.Translator*) -> mlirlite.LLVMType*:
|
||
"""Return the frame type of the current coroutine, looked up from the registered frames"""
|
||
# resume GEP needs the frame type: take the Pointee of _coro_frame.Ty (Ptr(frame))
|
||
if trans is None or trans._coro_frame is None: return None
|
||
ft: mlirlite.LLVMType* = trans._coro_frame.Ty
|
||
if ft is None: return None
|
||
match ft:
|
||
case LLVMType.Ptr():
|
||
return ft.Pointee
|
||
case _:
|
||
return None
|
||
|
||
# ============================================================
|
||
# M3: await coro_sleep(ms) - suspend via the wake slot (coro_run polls to resume)
|
||
def _emit_await_sleep(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, node: ast.AST*) -> mlirlite.Value*:
|
||
"""Emit await coro_sleep(ms): store the wake deadline in the frame and suspend the parent"""
|
||
i64_ty: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||
if node is None or trans is None or trans._coro_wake_ptr is None: return mlirlite.const_int32(pool, 0)
|
||
ms_val: mlirlite.Value* = mlirlite.const_int64(pool, 0)
|
||
aw_s: ast.Await* = ast.Await*(node)
|
||
if aw_s.value is not None and aw_s.value.kind() == ast.ASTKind.Call:
|
||
cl_s: ast.Call* = ast.Call*(aw_s.value)
|
||
if cl_s.args is not None and len(cl_s.args) > 0:
|
||
ms_v: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cl_s.args.get(0), None, 0, trans)
|
||
if ms_v is not None: ms_val = ms_v
|
||
# GetTickCount64() + ms(i64)
|
||
now_fn: mlirlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, "GetTickCount64")
|
||
if now_fn is None: now_fn = mlirlite.create_declare(pool, mod, "GetTickCount64", i64_ty)
|
||
if now_fn is None: return mlirlite.const_int32(pool, 0)
|
||
now_val: mlirlite.Value* = mlirlite.build_call(builder, "GetTickCount64", None, 0, i64_ty, 0)
|
||
if now_val is None: return mlirlite.const_int32(pool, 0)
|
||
wake_val: mlirlite.Value* = mlirlite.build_add(builder, now_val, ms_val)
|
||
if wake_val is None: return mlirlite.const_int32(pool, 0)
|
||
mlirlite.build_store(builder, wake_val, trans._coro_wake_ptr)
|
||
# parent suspend: new state id + br exit + position the resume block
|
||
new_state_s: int = trans._coro_next_state
|
||
trans._coro_next_state = new_state_s + 1
|
||
st_v_s: mlirlite.Value* = mlirlite.const_int32(pool, new_state_s)
|
||
mlirlite.build_store(builder, st_v_s, trans._coro_state_ptr)
|
||
mlirlite.build_br(builder, trans._coro_exit_bb)
|
||
resume_bb_s: mlirlite.BasicBlock* = self._get_state_bb(trans, new_state_s)
|
||
if resume_bb_s is not None: mlirlite.position_at_end(builder, resume_bb_s)
|
||
return mlirlite.const_int32(pool, 0)
|
||
|
||
# ============================================================
|
||
# M4-M6: raise inside a coroutine (exception type/value -> frame exc slot)
|
||
def _handle_coro_raise(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, stmt: ast.AST*):
|
||
"""Handle raise inside a coroutine: store the exception code/message, then jump to the handler or exit"""
|
||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||
exc_code: mlirlite.Value* = mlirlite.const_int32(pool, 1)
|
||
rz: ast.Raise* = ast.Raise*(stmt)
|
||
if rz is not None and rz.exc is not None:
|
||
code: int = self._resolve_raise_code(pool, rz.exc)
|
||
if code != 0:
|
||
exc_code = mlirlite.const_int32(pool, code)
|
||
# M7: exception message (first arg of Exception("msg"))
|
||
msg_v: mlirlite.Value* = self._resolve_raise_msg(builder, pool, mod, trans, rz.exc)
|
||
if msg_v is not None and trans._coro_msg_ptr is not None: mlirlite.build_store(builder, msg_v, trans._coro_msg_ptr)
|
||
else:
|
||
v: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, rz.exc, None, 0, trans)
|
||
if v is not None: exc_code = v
|
||
if trans._coro_exc_ptr is not None: mlirlite.build_store(builder, exc_code, trans._coro_exc_ptr)
|
||
# jump to try/except or terminate
|
||
if trans._coro_try_stack_count > 0:
|
||
tbb_r: mlirlite.BasicBlock* = mlirlite.BasicBlock*(trans._coro_try_stack[trans._coro_try_stack_count - 1])
|
||
mlirlite.build_br(builder, tbb_r)
|
||
else:
|
||
done_r: mlirlite.Value* = mlirlite.const_int32(pool, 1)
|
||
mlirlite.build_store(builder, done_r, trans._coro_done_ptr)
|
||
mlirlite.build_br(builder, trans._coro_exit_bb)
|
||
|
||
def _resolve_raise_code(self, pool: memhub.MemBuddy*, exc_node: ast.AST*) -> int:
|
||
"""Resolve the exception code of a raise target; return 0 for non-exception classes"""
|
||
if exc_node is None: return 0
|
||
name: str = None
|
||
if exc_node.kind() == ast.ASTKind.Name:
|
||
nm: ast.Name* = ast.Name*(exc_node)
|
||
name = nm.id
|
||
elif exc_node.kind() == ast.ASTKind.Call:
|
||
cl: ast.Call* = ast.Call*(exc_node)
|
||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
|
||
nm2: ast.Name* = ast.Name*(cl.func)
|
||
name = nm2.id
|
||
elif exc_node.kind() == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(exc_node)
|
||
name = at.attr
|
||
if name is None: return 0
|
||
return self.Gen.ClassGenBase.lookup_exception_code(name)
|
||
|
||
def _resolve_raise_msg(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, exc_node: ast.AST*) -> mlirlite.Value*:
|
||
"""Return the exception message expression (first arg of Exception), or None"""
|
||
if exc_node is None or exc_node.kind() != ast.ASTKind.Call: return None
|
||
cl: ast.Call* = ast.Call*(exc_node)
|
||
if cl.args is None or len(cl.args) < 1: return None
|
||
return self.Gen.ExprGen.translate_value(builder, pool, mod, cl.args.get(0), None, 0, trans)
|
||
|
||
# ============================================================
|
||
# M5-M6: coroutine try/except (chained multi-except match + type codes + `as e` binding)
|
||
def _handle_coro_try(self, trans: HT.Translator*, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, stmt: ast.AST*):
|
||
"""Handle try/except inside a coroutine: match handlers in order and propagate unmatched exceptions"""
|
||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||
tr: ast.Try* = ast.Try*(stmt)
|
||
if tr is None: return
|
||
nhandlers: sizet = 0
|
||
if tr.handlers is not None: nhandlers = len(tr.handlers)
|
||
if nhandlers == 0:
|
||
err_t: str = pool.alloc(256)
|
||
if err_t is not None:
|
||
viperlib.snprintf(err_t, 256, "[coro-M6] try inside a coroutine requires an except clause")
|
||
self.Gen.TypeGenErr.fatal_error(stmt, err_t, "SyntaxError")
|
||
return
|
||
func: mlirlite.Function* = trans._cur_func
|
||
except_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.try_except")
|
||
merge_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.try_merge")
|
||
if except_bb is None or merge_bb is None: return
|
||
# push the try stack
|
||
if trans._coro_try_stack_count < HT.MAX_CORO_TRY:
|
||
trans._coro_try_stack[trans._coro_try_stack_count] = sizet(except_bb)
|
||
trans._coro_try_stack_count = trans._coro_try_stack_count + 1
|
||
# try body
|
||
self._handle_segment(trans, builder, pool, mod, stmt.children)
|
||
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, merge_bb)
|
||
if trans._coro_try_stack_count > 0: trans._coro_try_stack_count = trans._coro_try_stack_count - 1
|
||
# ---- dispatch: match the exc code against each except in order ----
|
||
mlirlite.position_at_end(builder, except_bb)
|
||
exc_val: mlirlite.Value* = mlirlite.build_load(builder, i32_ty, trans._coro_exc_ptr)
|
||
# pre-build check/handler/unmatched blocks
|
||
check_bbs: sizet* = pool.alloc((nhandlers + 1) * 8)
|
||
handler_bbs: sizet* = pool.alloc(nhandlers * 8)
|
||
if check_bbs is None or handler_bbs is None: return
|
||
check_bbs[0] = sizet(except_bb)
|
||
hi: int = 0
|
||
for hi in range(int(nhandlers)):
|
||
# [FIX 2026-08-31] build block names with snprintf: "%" is not string formatting here,
|
||
# so the old "%d" form produced i32 garbage and triggered a false int->ptr cast error.
|
||
chk_name: str = pool.alloc(64)
|
||
exc_name: str = pool.alloc(64)
|
||
if chk_name is None or exc_name is None: return
|
||
viperlib.snprintf(chk_name, 64, "coro.exc_check_%d", hi)
|
||
viperlib.snprintf(exc_name, 64, "coro.except_%d", hi)
|
||
check_bbs[hi + 1] = sizet(mlirlite.create_block(pool, func, chk_name))
|
||
handler_bbs[hi] = sizet(mlirlite.create_block(pool, func, exc_name))
|
||
unmatched_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.try_reraise")
|
||
codes_buf: int* = pool.alloc(256)
|
||
# per handler: the check chain
|
||
for hi in range(int(nhandlers)):
|
||
h_node: ast.AST* = tr.handlers.get(hi)
|
||
check_bb: mlirlite.BasicBlock* = (mlirlite.BasicBlock*)(check_bbs[hi])
|
||
mlirlite.position_at_end(builder, check_bb)
|
||
catch_all: int = 0
|
||
ncodes: int = 0
|
||
if h_node is not None:
|
||
hd: ast.ExceptHandler* = ast.ExceptHandler*(h_node)
|
||
if hd.type is None: catch_all = 1
|
||
else:
|
||
ename: str = self._exception_type_name(pool, hd.type)
|
||
if ename is not None and string.strcmp(ename, "Exception") == 0: catch_all = 1
|
||
else: ncodes = self.Gen.ClassGenBase.collect_descendant_codes(ename, codes_buf, 64)
|
||
next_check: mlirlite.BasicBlock* = None
|
||
if hi + 1 < nhandlers: next_check = mlirlite.BasicBlock*(check_bbs[hi + 1])
|
||
else: next_check = unmatched_bb
|
||
handler_bb: mlirlite.BasicBlock* = (mlirlite.BasicBlock*)(handler_bbs[hi])
|
||
if catch_all != 0: mlirlite.build_br(builder, handler_bb)
|
||
else:
|
||
match_cond: mlirlite.Value* = None
|
||
ci: int = 0
|
||
for ci in range(int(ncodes)):
|
||
eq_v: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_EQ, exc_val, mlirlite.const_int32(pool, codes_buf[ci]))
|
||
if match_cond is None: match_cond = eq_v
|
||
else: match_cond = mlirlite.build_or(builder, match_cond, eq_v)
|
||
if match_cond is None: mlirlite.build_br(builder, next_check)
|
||
else: mlirlite.build_cond_br(builder, match_cond, handler_bb, next_check)
|
||
# no match: keep propagating (terminate the coroutine again)
|
||
mlirlite.position_at_end(builder, unmatched_bb)
|
||
done_u: mlirlite.Value* = mlirlite.const_int32(pool, 1)
|
||
mlirlite.build_store(builder, done_u, trans._coro_done_ptr)
|
||
mlirlite.build_br(builder, trans._coro_exit_bb)
|
||
# ---- each except handler ----
|
||
for hi in range(int(nhandlers)):
|
||
h_node2: ast.AST* = tr.handlers.get(hi)
|
||
handler_bb2: mlirlite.BasicBlock* = (mlirlite.BasicBlock*)(handler_bbs[hi])
|
||
mlirlite.position_at_end(builder, handler_bb2)
|
||
if h_node2 is not None:
|
||
hd2: ast.ExceptHandler* = ast.ExceptHandler*(h_node2)
|
||
# `as e`: bind the exception code to e's slot (a local collected by _collect_local_names)
|
||
if hd2.name is not None and trans._coro_exc_ptr is not None:
|
||
e_slot: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, hd2.name)
|
||
if e_slot is not None:
|
||
hv: mlirlite.Value* = mlirlite.build_load(builder, i32_ty, trans._coro_exc_ptr)
|
||
mlirlite.build_store(builder, hv, e_slot)
|
||
# clear the frame exc after catching
|
||
z_clr: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||
mlirlite.build_store(builder, z_clr, trans._coro_exc_ptr)
|
||
self._handle_segment(trans, builder, pool, mod, h_node2.children)
|
||
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, merge_bb)
|
||
mlirlite.position_at_end(builder, merge_bb)
|
||
|
||
def _exception_type_name(self, pool: memhub.MemBuddy*, type_node: ast.AST*) -> str:
|
||
"""Return the except type name (Name/Attribute), or None if unavailable"""
|
||
if type_node is None: return None
|
||
if type_node.kind() == ast.ASTKind.Name:
|
||
nm: ast.Name* = ast.Name*(type_node)
|
||
return nm.id
|
||
if type_node.kind() == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(type_node)
|
||
return at.attr
|
||
return None
|
||
|
||
# ============================================================
|
||
# M7: coroutine try/finally (no except)
|
||
def _handle_coro_try_finally(self, trans: HT.Translator*, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, stmt: ast.AST*):
|
||
"""Handle try/finally inside a coroutine: always run fbody, then re-propagate any pending exception"""
|
||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||
tr: ast.Try* = ast.Try*(stmt)
|
||
if tr is None: return
|
||
if tr.finalbody is not None and self._stmts_deep_await(tr.finalbody) != 0:
|
||
err_f: str = pool.alloc(256)
|
||
if err_f is not None:
|
||
viperlib.snprintf(err_f, 256, "[coro-M7] await is not yet supported inside finally")
|
||
self.Gen.TypeGenErr.fatal_error(stmt, err_f, "SyntaxError")
|
||
return
|
||
func: mlirlite.Function* = trans._cur_func
|
||
fin_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.fin")
|
||
fin_exc_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.fin_exc")
|
||
merge_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.fin_merge")
|
||
if fin_bb is None or fin_exc_bb is None or merge_bb is None: return
|
||
# push the try stack (exceptions jump to finally)
|
||
if trans._coro_try_stack_count < HT.MAX_CORO_TRY:
|
||
trans._coro_try_stack[trans._coro_try_stack_count] = sizet(fin_bb)
|
||
trans._coro_try_stack_count = trans._coro_try_stack_count + 1
|
||
self._handle_segment(trans, builder, pool, mod, stmt.children)
|
||
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, fin_bb)
|
||
if trans._coro_try_stack_count > 0: trans._coro_try_stack_count = trans._coro_try_stack_count - 1
|
||
# fin_bb: record the exception flag + run fbody
|
||
mlirlite.position_at_end(builder, fin_bb)
|
||
was_exc: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_NE, mlirlite.build_load(builder, i32_ty, trans._coro_exc_ptr), mlirlite.const_int32(pool, 0))
|
||
self._handle_segment(trans, builder, pool, mod, tr.finalbody)
|
||
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_cond_br(builder, was_exc, fin_exc_bb, merge_bb)
|
||
# fin_exc: keep propagating the exception after finally
|
||
mlirlite.position_at_end(builder, fin_exc_bb)
|
||
done_f: mlirlite.Value* = mlirlite.const_int32(pool, 1)
|
||
mlirlite.build_store(builder, done_f, trans._coro_done_ptr)
|
||
mlirlite.build_br(builder, trans._coro_exit_bb)
|
||
mlirlite.position_at_end(builder, merge_bb)
|
||
|
||
# ============================================================
|
||
# M3: coroutine range-For (with await) turned into While (__f_idx/__f_len frame slots)
|
||
def _handle_coro_for(self, trans: HT.Translator*, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, stmt: ast.AST*):
|
||
"""Lower a range-for loop with await into a while loop over frame index/length slots"""
|
||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||
if self._is_range_for(stmt) == 0:
|
||
err_g: str = pool.alloc(256)
|
||
if err_g is not None:
|
||
viperlib.snprintf(err_g, 256, "[coro-M3] For inside a coroutine supports only range iteration (including await)")
|
||
self.Gen.TypeGenErr.fatal_error(stmt, err_g, "SyntaxError")
|
||
return
|
||
fn: ast.For* = ast.For*(stmt)
|
||
k: int = trans._coro_for_counter
|
||
trans._coro_for_counter = k + 1
|
||
idx_idx: int = trans._coro_extra_base + trans._coro_for_slots[k]
|
||
len_idx: int = idx_idx + 1
|
||
idx_slot: mlirlite.Value* = mlirlite.build_gep_struct(builder, self._lookup_coro_frame_by_trans(trans), i32_ty, trans._coro_frame, idx_idx)
|
||
len_slot: mlirlite.Value* = mlirlite.build_gep_struct(builder, self._lookup_coro_frame_by_trans(trans), i32_ty, trans._coro_frame, len_idx)
|
||
if idx_slot is None or len_slot is None: return
|
||
# start/end (range 1 or 2 args)
|
||
cl: ast.Call* = ast.Call*(fn.iter)
|
||
start_val: mlirlite.Value* = None
|
||
end_val: mlirlite.Value* = None
|
||
nargs: int = int(len(cl.args))
|
||
if nargs == 1:
|
||
start_val = mlirlite.const_int32(pool, 0)
|
||
end_val = self.Gen.ExprGen.translate_value(builder, pool, mod, cl.args.get(0), None, 0, trans)
|
||
else:
|
||
start_val = self.Gen.ExprGen.translate_value(builder, pool, mod, cl.args.get(0), None, 0, trans)
|
||
end_val = self.Gen.ExprGen.translate_value(builder, pool, mod, cl.args.get(1), None, 0, trans)
|
||
if start_val is None: start_val = mlirlite.const_int32(pool, 0)
|
||
if end_val is None: end_val = mlirlite.const_int32(pool, 0)
|
||
mlirlite.build_store(builder, start_val, idx_slot)
|
||
mlirlite.build_store(builder, end_val, len_slot)
|
||
# loop structure
|
||
func: mlirlite.Function* = trans._cur_func
|
||
cond_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.fcond")
|
||
body_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.fbody")
|
||
after_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.fend")
|
||
if cond_bb is None or body_bb is None or after_bb is None: return
|
||
prev_break: mlirlite.BasicBlock* = trans._coro_break_bb
|
||
prev_cont: mlirlite.BasicBlock* = trans._coro_continue_bb
|
||
trans._coro_break_bb = after_bb
|
||
trans._coro_continue_bb = cond_bb
|
||
mlirlite.build_br(builder, cond_bb)
|
||
# condition block
|
||
mlirlite.position_at_end(builder, cond_bb)
|
||
idx_v: mlirlite.Value* = mlirlite.build_load(builder, i32_ty, idx_slot)
|
||
len_v: mlirlite.Value* = mlirlite.build_load(builder, i32_ty, len_slot)
|
||
cmp_v: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_SLT, idx_v, len_v)
|
||
mlirlite.build_cond_br(builder, cmp_v, body_bb, after_bb)
|
||
# loop body: target = idx; <body>; idx += 1
|
||
mlirlite.position_at_end(builder, body_bb)
|
||
if fn.target is not None and fn.target.kind() == ast.ASTKind.Name:
|
||
tn: ast.Name* = ast.Name*(fn.target)
|
||
tgt_slot: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, tn.id)
|
||
if tgt_slot is not None:
|
||
idx_v2: mlirlite.Value* = mlirlite.build_load(builder, i32_ty, idx_slot)
|
||
mlirlite.build_store(builder, idx_v2, tgt_slot)
|
||
self._handle_segment(trans, builder, pool, mod, stmt.children)
|
||
if mlirlite.builder_cur_block_is_terminated(builder) == 0:
|
||
idx_v3: mlirlite.Value* = mlirlite.build_load(builder, i32_ty, idx_slot)
|
||
one_v: mlirlite.Value* = mlirlite.const_int32(pool, 1)
|
||
nxt_v: mlirlite.Value* = mlirlite.build_add(builder, idx_v3, one_v)
|
||
mlirlite.build_store(builder, nxt_v, idx_slot)
|
||
mlirlite.build_br(builder, cond_bb)
|
||
trans._coro_break_bb = prev_break
|
||
trans._coro_continue_bb = prev_cont
|
||
mlirlite.position_at_end(builder, after_bb)
|
||
|
||
# ============================================================
|
||
# M3/M4/M7: coro.coro_destroy / coro_exception / coro_exception_msg / coro_gather
|
||
def translate_coro_destroy(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, cl: ast.Call*, trans: HT.Translator*) -> mlirlite.Value*:
|
||
"""coro.coro_destroy(frame): return the frame to the free-list pool"""
|
||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||
i64_ty: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||
if cl is None or trans is None or cl.args is None or len(cl.args) < 1: return mlirlite.const_int32(pool, 0)
|
||
async_name: str = self._var_to_async(trans, cl.args.get(0))
|
||
if async_name is None: return mlirlite.const_int32(pool, 0)
|
||
frame_ty: mlirlite.LLVMType* = self._lookup_coro_frame(trans, async_name)
|
||
if frame_ty is None: return mlirlite.const_int32(pool, 0)
|
||
frame_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, frame_ty)
|
||
frame_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cl.args.get(0), None, 0, trans)
|
||
if frame_val is None: return mlirlite.const_int32(pool, 0)
|
||
frame_ptr: mlirlite.Value* = mlirlite.build_bitcast(builder, frame_val, frame_ptr_ty)
|
||
if frame_ptr is None: return mlirlite.const_int32(pool, 0)
|
||
# return to pool: frame's first 8 bytes hold the old head; head = this frame (i64)
|
||
pool_gv: mlirlite.GlobalVariable* = self._get_coro_pool(pool, mod, trans, async_name)
|
||
# GlobalVariable is not a Value: build a Value view (Ty=Ptr(i64), Name="@coro_pool_<name>")
|
||
i64_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i64_ty)
|
||
pool_ref_name: str = pool.alloc(string.strlen(async_name) + 16)
|
||
if pool_ref_name is not None: viperlib.snprintf(pool_ref_name, string.strlen(async_name) + 16, "@coro_pool_%s", async_name)
|
||
pool_ref: mlirlite.Value* = mlirlite.SSAValue(pool, i64_ptr_ty, pool_ref_name)
|
||
pool_head: mlirlite.Value* = mlirlite.build_load(builder, i64_ty, pool_ref)
|
||
node_next: mlirlite.Value* = mlirlite.build_bitcast(builder, frame_ptr, i64_ptr_ty)
|
||
mlirlite.build_store(builder, pool_head, node_next)
|
||
frame_i64: mlirlite.Value* = mlirlite.build_ptrtoint(builder, frame_ptr, i64_ty)
|
||
mlirlite.build_store(builder, frame_i64, pool_ref)
|
||
return mlirlite.const_int32(pool, 0)
|
||
|
||
def translate_coro_exception(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, cl: ast.Call*, trans: HT.Translator*) -> mlirlite.Value*:
|
||
"""coro.coro_exception(frame): return the frame exception code (0 = none)"""
|
||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||
if cl is None or trans is None or cl.args is None or len(cl.args) < 1: return mlirlite.const_int32(pool, 0)
|
||
async_name: str = self._var_to_async(trans, cl.args.get(0))
|
||
if async_name is None: return mlirlite.const_int32(pool, 0)
|
||
frame_ty: mlirlite.LLVMType* = self._lookup_coro_frame(trans, async_name)
|
||
if frame_ty is None: return mlirlite.const_int32(pool, 0)
|
||
frame_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, frame_ty)
|
||
frame_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cl.args.get(0), None, 0, trans)
|
||
if frame_val is None: return mlirlite.const_int32(pool, 0)
|
||
frame_ptr: mlirlite.Value* = mlirlite.build_bitcast(builder, frame_val, frame_ptr_ty)
|
||
if frame_ptr is None: return mlirlite.const_int32(pool, 0)
|
||
exc_ptr: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i32_ty, frame_ptr, 3)
|
||
return mlirlite.build_load(builder, i32_ty, exc_ptr)
|
||
|
||
def translate_coro_exception_msg(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, cl: ast.Call*, trans: HT.Translator*) -> mlirlite.Value*:
|
||
"""coro.coro_exception_msg(frame): return the frame exception message pointer, or null"""
|
||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||
if cl is None or trans is None or cl.args is None or len(cl.args) < 1: return mlirlite.ConstNull(pool, i8_ptr_ty, "null")
|
||
async_name: str = self._var_to_async(trans, cl.args.get(0))
|
||
if async_name is None: return mlirlite.ConstNull(pool, i8_ptr_ty, "null")
|
||
frame_ty: mlirlite.LLVMType* = self._lookup_coro_frame(trans, async_name)
|
||
if frame_ty is None: return mlirlite.ConstNull(pool, i8_ptr_ty, "null")
|
||
frame_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, frame_ty)
|
||
frame_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cl.args.get(0), None, 0, trans)
|
||
if frame_val is None: return mlirlite.ConstNull(pool, i8_ptr_ty, "null")
|
||
frame_ptr: mlirlite.Value* = mlirlite.build_bitcast(builder, frame_val, frame_ptr_ty)
|
||
if frame_ptr is None: return mlirlite.ConstNull(pool, i8_ptr_ty, "null")
|
||
msg_ptr: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i8_ptr_ty, frame_ptr, 4)
|
||
return mlirlite.build_load(builder, i8_ptr_ty, msg_ptr)
|
||
|
||
def translate_coro_gather(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, cl: ast.Call*, trans: HT.Translator*) -> mlirlite.Value*:
|
||
"""coro.coro_gather(frames...): resume all coroutines to completion and return the finished count"""
|
||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||
if cl is None or trans is None or cl.args is None: return mlirlite.const_int32(pool, 0)
|
||
nb: sizet = len(cl.args)
|
||
j: sizet = 0
|
||
for j in range(int(nb)):
|
||
arg0: ast.AST* = cl.args.get(j)
|
||
async_name: str = self._var_to_async(trans, arg0)
|
||
if async_name is None: continue
|
||
frame_ty: mlirlite.LLVMType* = self._lookup_coro_frame(trans, async_name)
|
||
if frame_ty is None: continue
|
||
frame_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, frame_ty)
|
||
frame_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, arg0, None, 0, trans)
|
||
if frame_val is None: continue
|
||
frame_ptr: mlirlite.Value* = mlirlite.build_bitcast(builder, frame_val, frame_ptr_ty)
|
||
if frame_ptr is None: continue
|
||
rn_buf: str = pool.alloc(64)
|
||
if rn_buf is not None: viperlib.snprintf(rn_buf, 64, "resume_%s", async_name)
|
||
resume_mangled: str = self.Gen.FuncGenBase._mangle_func_name(trans, rn_buf, 0)
|
||
if self.Gen.CallGenBase.find_func_in_module(mod, resume_mangled) is None: continue
|
||
func: mlirlite.Function* = trans._cur_func
|
||
loop_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "gather.loop")
|
||
after_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "gather.after")
|
||
if loop_bb is None or after_bb is None: continue
|
||
mlirlite.build_br(builder, loop_bb)
|
||
mlirlite.position_at_end(builder, loop_bb)
|
||
frame_ptr.Next = None
|
||
status: mlirlite.Value* = mlirlite.build_call(builder, resume_mangled, frame_ptr, 1, i32_ty, 0)
|
||
done_cond: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_NE, status, mlirlite.const_int32(pool, 0))
|
||
mlirlite.build_cond_br(builder, done_cond, after_bb, loop_bb)
|
||
mlirlite.position_at_end(builder, after_bb)
|
||
return mlirlite.const_int32(pool, 0)
|
||
|
||
def _var_to_async(self, trans: HT.Translator*, arg0: ast.AST*) -> str:
|
||
"""Map a variable node to its async function name, or None if not found"""
|
||
if trans is None or arg0 is None or arg0.kind() != ast.ASTKind.Name: return None
|
||
nm: ast.Name* = ast.Name*(arg0)
|
||
if nm.id is None: return None
|
||
return self._lookup_coro_var(trans, nm.id)
|
||
|
||
# ============================================================
|
||
# await expression translation (shared by segment tail / translate_value Await branch)
|
||
def _emit_await(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, node: ast.AST*, call_name: str) -> mlirlite.Value*:
|
||
"""Inline-translate await async_fn(args...): create the child frame, resume it to DONE, store its result"""
|
||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||
i8_ptr_ty_a: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||
if node is None or call_name is None or trans is None: return mlirlite.const_int32(pool, 0)
|
||
# M3: await coro_sleep(ms) - suspend until the wake time (frame wake slot)
|
||
if string.strcmp(call_name, "coro_sleep") == 0: return self._emit_await_sleep(builder, pool, mod, trans, node)
|
||
child_frame_ty: mlirlite.LLVMType* = self._lookup_coro_frame(trans, call_name)
|
||
if child_frame_ty is None: return mlirlite.const_int32(pool, 0)
|
||
child_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, child_frame_ty)
|
||
|
||
cc_buf: str = pool.alloc(64)
|
||
if cc_buf is not None: viperlib.snprintf(cc_buf, 64, "create_%s", call_name)
|
||
create_mangled: str = self.Gen.FuncGenBase._mangle_func_name(trans, cc_buf, 0)
|
||
rc_buf: str = pool.alloc(64)
|
||
if rc_buf is not None: viperlib.snprintf(rc_buf, 64, "resume_%s", call_name)
|
||
resume_mangled: str = self.Gen.FuncGenBase._mangle_func_name(trans, rc_buf, 0)
|
||
if self.Gen.CallGenBase.find_func_in_module(mod, create_mangled) is None or self.Gen.CallGenBase.find_func_in_module(mod, resume_mangled) is None:
|
||
# async functions must be defined first (no forward refs); silently return 0
|
||
return mlirlite.const_int32(pool, 0)
|
||
|
||
# M2: child-call args (await fn(a, b) -> create_fn(a, b))
|
||
args_head: mlirlite.Value* = None
|
||
args_tail: mlirlite.Value* = None
|
||
args_count: int = 0
|
||
aw_n: ast.Await* = ast.Await*(node)
|
||
if aw_n.value is not None and aw_n.value.kind() == ast.ASTKind.Call:
|
||
cl_a: ast.Call* = ast.Call*(aw_n.value)
|
||
if cl_a.args is not None:
|
||
nb: sizet = len(cl_a.args)
|
||
j: sizet = 0
|
||
for j in range(int(nb)):
|
||
av: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cl_a.args.get(j), None, 0, trans)
|
||
if av is None: av = mlirlite.const_int32(pool, 0)
|
||
av.Next = None
|
||
if args_head is None:
|
||
args_head = av
|
||
args_tail = av
|
||
else:
|
||
args_tail.Next = av
|
||
args_tail = av
|
||
args_count = args_count + 1
|
||
|
||
# child create (M2 with args) + resume
|
||
child: mlirlite.Value* = mlirlite.build_call(builder, create_mangled, args_head, args_count, child_ptr_ty, 0)
|
||
if child is None: return mlirlite.const_int32(pool, 0)
|
||
child.Next = None
|
||
mlirlite.build_call(builder, resume_mangled, child, 1, i32_ty, 0)
|
||
# GEP the child ret slot [0,2] and load
|
||
child_ret_ptr: mlirlite.Value* = mlirlite.build_gep_struct(builder, child_frame_ty, i32_ty, child, 2)
|
||
child_ret: mlirlite.Value* = mlirlite.build_load(builder, i32_ty, child_ret_ptr)
|
||
# M4: child exception propagation - child exc != 0 -> parent exc/msg + jump try or terminate
|
||
child_exc_ptr: mlirlite.Value* = mlirlite.build_gep_struct(builder, child_frame_ty, i32_ty, child, 3)
|
||
child_exc: mlirlite.Value* = mlirlite.build_load(builder, i32_ty, child_exc_ptr)
|
||
zero32_x: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||
exc_ok_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, trans._cur_func, "coro.ok")
|
||
exc_bad_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, trans._cur_func, "coro.exc")
|
||
if exc_ok_bb is None or exc_bad_bb is None: return mlirlite.const_int32(pool, 0)
|
||
exc_cond: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_EQ, child_exc, zero32_x)
|
||
mlirlite.build_cond_br(builder, exc_cond, exc_ok_bb, exc_bad_bb)
|
||
# exc path: parent exc/msg slots + jump to try or terminate
|
||
mlirlite.position_at_end(builder, exc_bad_bb)
|
||
if trans._coro_exc_ptr is not None: mlirlite.build_store(builder, child_exc, trans._coro_exc_ptr)
|
||
if trans._coro_msg_ptr is not None:
|
||
child_msg_ptr: mlirlite.Value* = mlirlite.build_gep_struct(builder, child_frame_ty, i8_ptr_ty_a, child, 4)
|
||
child_msg: mlirlite.Value* = mlirlite.build_load(builder, i8_ptr_ty_a, child_msg_ptr)
|
||
mlirlite.build_store(builder, child_msg, trans._coro_msg_ptr)
|
||
if trans._coro_try_stack_count > 0:
|
||
tbb: mlirlite.BasicBlock* = mlirlite.BasicBlock*(trans._coro_try_stack[trans._coro_try_stack_count - 1])
|
||
mlirlite.build_br(builder, tbb)
|
||
else:
|
||
done_x: mlirlite.Value* = mlirlite.const_int32(pool, 1)
|
||
mlirlite.build_store(builder, done_x, trans._coro_done_ptr)
|
||
mlirlite.build_br(builder, trans._coro_exit_bb)
|
||
# normal path: store into the parent await slot (if any) + suspend
|
||
mlirlite.position_at_end(builder, exc_ok_bb)
|
||
slot: mlirlite.Value* = trans._coro_await_slot
|
||
if slot is not None: mlirlite.build_store(builder, child_ret, slot)
|
||
# parent suspend: new state id (matching the pre-scan DFS order)
|
||
new_state: int = trans._coro_next_state
|
||
trans._coro_next_state = new_state + 1
|
||
st_v: mlirlite.Value* = mlirlite.const_int32(pool, new_state)
|
||
mlirlite.build_store(builder, st_v, trans._coro_state_ptr)
|
||
mlirlite.build_br(builder, trans._coro_exit_bb)
|
||
# resume block state{new_state}: position immediately (pre-built case; translation continues)
|
||
resume_bb: mlirlite.BasicBlock* = self._get_state_bb(trans, new_state)
|
||
if resume_bb is not None: mlirlite.position_at_end(builder, resume_bb)
|
||
return child_ret
|
||
|
||
# ============================================================
|
||
# coro.coro_run(frame) inline translation
|
||
def translate_coro_run(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, cl: ast.Call*, trans: HT.Translator*) -> mlirlite.Value*:
|
||
"""Inline-translate coro.coro_run(frame): resolve the async name, resume to DONE, and return the result"""
|
||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||
if cl is None or trans is None: return mlirlite.const_int32(pool, 0)
|
||
if cl.args is None or len(cl.args) < 1: return mlirlite.const_int32(pool, 0)
|
||
arg0: ast.AST* = cl.args.get(0)
|
||
if arg0 is None or arg0.kind() != ast.ASTKind.Name: return mlirlite.const_int32(pool, 0)
|
||
nm: ast.Name* = ast.Name*(arg0)
|
||
if nm.id is None: return mlirlite.const_int32(pool, 0)
|
||
async_name: str = self._lookup_coro_var(trans, nm.id)
|
||
if async_name is None: return mlirlite.const_int32(pool, 0)
|
||
frame_ty: mlirlite.LLVMType* = self._lookup_coro_frame(trans, async_name)
|
||
if frame_ty is None: return mlirlite.const_int32(pool, 0)
|
||
frame_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, frame_ty)
|
||
|
||
# frame variable value (i8* / Ptr(frame)) -> bitcast to Ptr(frame)
|
||
frame_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, arg0, None, 0, trans)
|
||
if frame_val is None: return mlirlite.const_int32(pool, 0)
|
||
frame_ptr: mlirlite.Value* = mlirlite.build_bitcast(builder, frame_val, frame_ptr_ty)
|
||
if frame_ptr is None: return mlirlite.const_int32(pool, 0)
|
||
|
||
func: mlirlite.Function* = trans._cur_func
|
||
if func is None: return mlirlite.const_int32(pool, 0)
|
||
rn_buf: str = pool.alloc(64)
|
||
if rn_buf is not None: viperlib.snprintf(rn_buf, 64, "resume_%s", async_name)
|
||
resume_mangled: str = self.Gen.FuncGenBase._mangle_func_name(trans, rn_buf, 0)
|
||
if self.Gen.CallGenBase.find_func_in_module(mod, resume_mangled) is None: return mlirlite.const_int32(pool, 0)
|
||
|
||
loop_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.loop")
|
||
check_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.check")
|
||
after_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.after")
|
||
if loop_bb is None or check_bb is None or after_bb is None: return mlirlite.const_int32(pool, 0)
|
||
mlirlite.build_br(builder, loop_bb)
|
||
mlirlite.position_at_end(builder, loop_bb)
|
||
frame_ptr.Next = None
|
||
status: mlirlite.Value* = mlirlite.build_call(builder, resume_mangled, frame_ptr, 1, i32_ty, 0)
|
||
zero_cmp: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||
done_cond: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_NE, status, zero_cmp)
|
||
mlirlite.build_cond_br(builder, done_cond, after_bb, check_bb)
|
||
# M3: SUSPEND -> wake polling (continue if GetTickCount64 >= wake or wake==0, else Sleep(1))
|
||
mlirlite.position_at_end(builder, check_bb)
|
||
wake_idx_r: int = self._lookup_coro_wake(trans, async_name)
|
||
if wake_idx_r < 0: mlirlite.build_br(builder, loop_bb)
|
||
else:
|
||
i64_ty_r2: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||
wake_ptr_r: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i64_ty_r2, frame_ptr, wake_idx_r)
|
||
wake_v_r: mlirlite.Value* = mlirlite.build_load(builder, i64_ty_r2, wake_ptr_r)
|
||
now_v_r: mlirlite.Value* = mlirlite.build_call(builder, "GetTickCount64", None, 0, i64_ty_r2, 0)
|
||
not_wake_r: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_EQ, wake_v_r, mlirlite.const_int64(pool, 0))
|
||
due_r: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_UGE, now_v_r, wake_v_r)
|
||
cont_r: mlirlite.Value* = mlirlite.build_or(builder, not_wake_r, due_r)
|
||
wsleep_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.wsleep")
|
||
wdue_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.wdue")
|
||
mlirlite.build_cond_br(builder, cont_r, wdue_bb, wsleep_bb)
|
||
mlirlite.position_at_end(builder, wsleep_bb)
|
||
sleep_one: mlirlite.Value* = mlirlite.const_int32(pool, 1)
|
||
mlirlite.build_call(builder, "Sleep", sleep_one, 1, mlirlite.Void(pool), 0)
|
||
mlirlite.build_br(builder, loop_bb)
|
||
mlirlite.position_at_end(builder, wdue_bb)
|
||
mlirlite.build_br(builder, loop_bb)
|
||
mlirlite.position_at_end(builder, after_bb)
|
||
ret_ptr: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i32_ty, frame_ptr, 2)
|
||
ret_val: mlirlite.Value* = mlirlite.build_load(builder, i32_ty, ret_ptr)
|
||
return ret_val
|
||
|
||
# ============================================================
|
||
# Public queries / registration
|
||
def is_async_func(self, trans: HT.Translator*, name: str) -> int:
|
||
"""Return whether name is registered as an async function (1 = yes, 0 = no)"""
|
||
if trans is None or name is None or trans._async_funcs is None: return 0
|
||
cnt: int = trans._async_func_count
|
||
i: int = 0
|
||
for i in range(int(cnt)):
|
||
addr: uint64 = uint64(trans._async_funcs) + i * 8
|
||
slot: str* = (str*)(addr)
|
||
if slot[0] is not None and string.strcmp(slot[0], name) == 0: return 1
|
||
return 0
|
||
|
||
def register_coro_var(self, trans: HT.Translator*, var_name: str, func_name: str) -> int:
|
||
"""Record the variable-name to async-function-name mapping used by coro.coro_run"""
|
||
if trans is None or var_name is None or func_name is None: return 1
|
||
if trans._coro_var_async is None: return 1
|
||
cnt: int = trans._coro_var_async_count
|
||
i: int = 0
|
||
for i in range(int(cnt)):
|
||
addr: uint64 = uint64(trans._coro_var_async) + i * 16
|
||
pair: str* = (str*)(addr)
|
||
if pair[0] is not None and string.strcmp(pair[0], var_name) == 0: return 0
|
||
if cnt >= HT.MAX_CORO_VARS: return 1
|
||
pair2_addr: uint64 = uint64(trans._coro_var_async) + cnt * 16
|
||
pair2: str* = (str*)(pair2_addr)
|
||
pair2[0] = var_name
|
||
pair2[1] = func_name
|
||
trans._coro_var_async_count = cnt + 1
|
||
return 0
|
||
|
||
def _register_async_func(self, trans: HT.Translator*, name: str) -> int:
|
||
"""Register an async function bare name in the _async_funcs table"""
|
||
if trans is None or name is None or trans._async_funcs is None: return 1
|
||
cnt: int = trans._async_func_count
|
||
i: int = 0
|
||
for i in range(int(cnt)):
|
||
addr: uint64 = uint64(trans._async_funcs) + i * 8
|
||
slot: str* = (str*)(addr)
|
||
if slot[0] is not None and string.strcmp(slot[0], name) == 0: return 0
|
||
if cnt >= HT.MAX_CORO_FUNCS: return 1
|
||
addr2: uint64 = uint64(trans._async_funcs) + cnt * 8
|
||
slot2: str* = (str*)(addr2)
|
||
slot2[0] = name
|
||
trans._async_func_count = cnt + 1
|
||
return 0
|
||
|
||
def _register_coro_frame(self, trans: HT.Translator*, name: str, frame_ty: mlirlite.LLVMType*) -> int:
|
||
"""Register an async name to frame type in the _coro_frames table"""
|
||
if trans is None or name is None or frame_ty is None: return 1
|
||
if trans._coro_frames is None: return 1
|
||
cnt: int = trans._coro_frame_count
|
||
i: int = 0
|
||
for i in range(int(cnt)):
|
||
addr: uint64 = uint64(trans._coro_frames) + i * 16
|
||
pair: str* = (str*)(addr)
|
||
if pair[0] is not None and string.strcmp(pair[0], name) == 0:
|
||
pair[1] = str(frame_ty)
|
||
return 0
|
||
if cnt >= HT.MAX_CORO_FUNCS: return 1
|
||
pair2_addr: uint64 = uint64(trans._coro_frames) + cnt * 16
|
||
pair2: str* = (str*)(pair2_addr)
|
||
pair2[0] = name
|
||
pair2[1] = str(frame_ty)
|
||
trans._coro_frame_count = cnt + 1
|
||
return 0
|
||
|
||
def _lookup_coro_frame(self, trans: HT.Translator*, name: str) -> mlirlite.LLVMType*:
|
||
"""Look up the frame type by async name, or None if not found"""
|
||
if trans is None or name is None or trans._coro_frames is None: return None
|
||
cnt: int = trans._coro_frame_count
|
||
i: int = 0
|
||
for i in range(int(cnt)):
|
||
addr: uint64 = uint64(trans._coro_frames) + i * 16
|
||
pair: str* = (str*)(addr)
|
||
if pair[0] is not None and string.strcmp(pair[0], name) == 0:
|
||
if pair[1] is not None:
|
||
ty_addr: sizet = sizet(pair[1])
|
||
return (mlirlite.LLVMType*)(ty_addr)
|
||
return None
|
||
return None
|
||
|
||
def _lookup_coro_var(self, trans: HT.Translator*, var_name: str) -> str:
|
||
"""Reverse-look-up the async function name by variable name, or None if not found"""
|
||
if trans is None or var_name is None or trans._coro_var_async is None: return None
|
||
cnt: int = trans._coro_var_async_count
|
||
i: int = 0
|
||
for i in range(int(cnt)):
|
||
addr: uint64 = uint64(trans._coro_var_async) + i * 16
|
||
pair: str* = (str*)(addr)
|
||
if pair[0] is not None and string.strcmp(pair[0], var_name) == 0: return pair[1]
|
||
return None
|
||
|
||
# ============================================================
|
||
# Segment-translation helpers
|
||
def _emit_coro_return(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, stmt: ast.AST*) -> None:
|
||
"""Special-case Return inside a coroutine: store the return slot, set done, and branch to exit"""
|
||
rv_node: ast.Return* = ast.Return*(stmt)
|
||
ret_val: mlirlite.Value* = None
|
||
if rv_node is not None and rv_node.value is not None:
|
||
if rv_node.value.kind() != ast.ASTKind.Await: ret_val = self.Gen.ExprGen.translate_value(builder, pool, mod, rv_node.value, None, 0, trans)
|
||
if ret_val is None: ret_val = mlirlite.const_int32(pool, 0)
|
||
mlirlite.build_store(builder, ret_val, trans._coro_ret_ptr)
|
||
done_one: mlirlite.Value* = mlirlite.const_int32(pool, 1)
|
||
mlirlite.build_store(builder, done_one, trans._coro_done_ptr)
|
||
mlirlite.build_br(builder, trans._coro_exit_bb)
|
||
|
||
def _expr_await_is_sleep(self, aw: ast.Await*) -> int:
|
||
"""Return whether an Await target is coro_sleep (Name or coro.coro_sleep)"""
|
||
if aw is None or aw.value is None: return 0
|
||
if aw.value.kind() != ast.ASTKind.Call: return 0
|
||
cl: ast.Call* = ast.Call*(aw.value)
|
||
if cl.func is None: return 0
|
||
if cl.func.kind() == ast.ASTKind.Name:
|
||
nm: ast.Name* = ast.Name*(cl.func)
|
||
if nm.id is not None and string.strcmp(nm.id, "coro_sleep") == 0: return 1
|
||
elif cl.func.kind() == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||
if at.attr is not None and string.strcmp(at.attr, "coro_sleep") == 0: return 1
|
||
return 0
|
||
|
||
def _is_await_tail(self, stmt: ast.AST*) -> int:
|
||
"""Return whether a statement is a segment tail, i.e. its value is directly an Await"""
|
||
if stmt is None: return 0
|
||
sk: int = stmt.kind()
|
||
if sk == ast.ASTKind.AnnAssign:
|
||
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
|
||
if aa.value is not None and aa.value.kind() == ast.ASTKind.Await: return 1
|
||
elif sk == ast.ASTKind.Assign:
|
||
as_: ast.Assign* = ast.Assign*(stmt)
|
||
if as_.value is not None and as_.value.kind() == ast.ASTKind.Await:
|
||
if as_.targets is not None and len(as_.targets) == 1: return 1
|
||
elif sk == ast.ASTKind.Expr:
|
||
e: ast.Expr* = ast.Expr*(stmt)
|
||
if e.value is not None and e.value.kind() == ast.ASTKind.Await: return 1
|
||
return 0
|
||
|
||
def _get_assign_target_name(self, stmt: ast.AST*) -> str:
|
||
"""Return the target name of an AnnAssign/Assign to a single Name, or None"""
|
||
if stmt is None: return None
|
||
sk: int = stmt.kind()
|
||
if sk == ast.ASTKind.AnnAssign:
|
||
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
|
||
if aa.target is not None and aa.target.kind() == ast.ASTKind.Name:
|
||
tn: ast.Name* = ast.Name*(aa.target)
|
||
return tn.id
|
||
elif sk == ast.ASTKind.Assign:
|
||
as_: ast.Assign* = ast.Assign*(stmt)
|
||
if as_.targets is not None and len(as_.targets) == 1:
|
||
t0: ast.AST* = as_.targets.get(0)
|
||
if t0 is not None and t0.kind() == ast.ASTKind.Name:
|
||
tn2: ast.Name* = ast.Name*(t0)
|
||
return tn2.id
|
||
return None
|
||
|
||
def _get_await_value(self, stmt: ast.AST*) -> ast.Await*:
|
||
"""Return the Await value node of a segment-tail statement, or None"""
|
||
if stmt is None: return None
|
||
sk: int = stmt.kind()
|
||
if sk == ast.ASTKind.AnnAssign:
|
||
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
|
||
if aa.value is not None and aa.value.kind() == ast.ASTKind.Await: return ast.Await*(aa.value)
|
||
elif sk == ast.ASTKind.Assign:
|
||
as_: ast.Assign* = ast.Assign*(stmt)
|
||
if as_.value is not None and as_.value.kind() == ast.ASTKind.Await: return ast.Await*(as_.value)
|
||
elif sk == ast.ASTKind.Expr:
|
||
ex: ast.Expr* = ast.Expr*(stmt)
|
||
if ex.value is not None and ex.value.kind() == ast.ASTKind.Await: return ast.Await*(ex.value)
|
||
return None
|
||
|
||
def _extract_await_call_name(self, aw_node: ast.Await*) -> str:
|
||
"""Extract the async function name or coro_sleep from an Await call, or None"""
|
||
if aw_node is None or aw_node.value is None: return None
|
||
if aw_node.value.kind() == ast.ASTKind.Call:
|
||
cl: ast.Call* = ast.Call*(aw_node.value)
|
||
if cl.func is not None:
|
||
if cl.func.kind() == ast.ASTKind.Name:
|
||
nm: ast.Name* = ast.Name*(cl.func)
|
||
return nm.id
|
||
if cl.func.kind() == ast.ASTKind.Attribute:
|
||
# coro.coro_sleep(ms): M3 sleep suspension
|
||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||
if at.attr is not None and string.strcmp(at.attr, "coro_sleep") == 0: return "coro_sleep"
|
||
return None
|
||
|
||
def _read_local_name(self, trans: HT.Translator*, idx: int) -> str:
|
||
"""Return the idx-th local variable name from trans._coro_locals, or None if out of range"""
|
||
if trans is None or trans._coro_locals is None: return None
|
||
addr: uint64 = uint64(trans._coro_locals) + idx * 8
|
||
slot: str* = (str*)(addr)
|
||
return slot[0]
|