diff --git a/mlirlite/__function.vp b/mlirlite/__function.vp index 67903ec..9ff1fa3 100644 --- a/mlirlite/__function.vp +++ b/mlirlite/__function.vp @@ -90,6 +90,7 @@ novirtual class Function(GSListNode[Function]): AttrGroupId: int # attribute group id (>0 emits #N on the function line and attributes #N at module end) NameLen: sizet # function name length (computed once at creation, always == strlen(Name); appended last so existing field offsets stay) DbgRef: str # DISubprogram ref (kept as-is; ignored by MLIR printing, e.g. "!3") + PeepDone: int # 1=local peephole already applied to this body (runs once; see FunctionPrint) # ============================================================ @@ -150,8 +151,8 @@ def new_param(pool: memhub.MemBuddy*, ty: LLVMType*, return ptr -# Function hardcoded size: Next(8) + Name(8) + RetTy(8) + Params(8) + Blocks(8) + IsDeclared(4) + IsVarArg(4) + Attrs(8) + AttrGroup(8) + AttrGroupId(8) + NameLen(8) + DbgRef(8) = 88 bytes (same layout as llvmlite) -define FUNC_SIZE = 88 +# Function hardcoded size: Next(8) + Name(8) + RetTy(8) + Params(8) + Blocks(8) + IsDeclared(4) + IsVarArg(4) + Attrs(8) + AttrGroup(8) + AttrGroupId(8) + NameLen(8) + DbgRef(8) + PeepDone(8) = 96 bytes (same layout as llvmlite) +define FUNC_SIZE = 96 def new_function(pool: memhub.MemBuddy*, name: str, ret_ty: LLVMType*) -> Function*: @@ -167,6 +168,7 @@ def new_function(pool: memhub.MemBuddy*, name: str, ptr.Attrs = None ptr.AttrGroup = None ptr.AttrGroupId = 0 + ptr.PeepDone = 0 # NameLen computed once: Name never changes, so NameLen == strlen(Name) (avoids repeated strlen). # name=None sets 0, matching new_line's null convention (explicit for fixed semantics, not allocator behavior). if name is not None: @@ -3209,8 +3211,13 @@ def FunctionPrint(buf: str, size: sizet, # no-op bitcast removal): peepholes match by name and delete lines by setting Buf=None. # They act on the builders' intermediate instruction text and are orthogonal to MLIR rendering; # RenumberSSA internally skips declare functions (IsDeclared==1). - if func.IsDeclared == 0 and G_PeepEnable != 0: + # Run at most ONCE per body: the module is printed several times (STUB/TEXT/FULL, see PrintModule) + # and the T2 forwarding is NOT idempotent — a second pass over the already-optimized text can + # drop an entry alloca whose use was only reachable through a line the first pass had removed, + # leaving dangling SSA names (mlir-opt: "op using value defined outside the region"). + if func.IsDeclared == 0 and G_PeepEnable != 0 and func.PeepDone == 0: _peephole_opt(func, pool) + func.PeepDone = 1 RenumberSSA(func, pool) _MlirTablesInit(pool)