类别:a 编译器缺陷(同一根因的其它变体收口:函数声明创建的唯一入口 + fail-fast 不被吞)
背景(前几轮已修两处):llvmlite 的 `Function.__init__` 不是事务性的(名字在 `_set_name`
就进 module.scope,之后建 Argument/ReturnValue 才断言类型,最后才 add_global),中途失败会
留下悬空名字;本仓已加两道防线:全局守卫 `InstallFuncCreateGuard()`(回滚名字 + 带调用点的
FailFastError)与唯一幂等入口 `_CreateOrReuseFuncDecl`(创建前自检、同名同类型复用、异类型
带两侧签名 fail-fast)。但仍有 20+ 处**绕过入口直建**(同名时抛裸 DuplicatedNameError、且
名字被占/半注册时只能看到没有上下文的名字),以及若干处 `except Exception` 会把这颗
FailFastError 吞掉(声明静默缺失、真因丢失)。
改动:
1. 创建点收敛(21 处,行为等价或更严)——`X = ir.Function(<gen>.module, <fty>, name=<N>)`
→ `X = <gen>._CreateOrReuseFuncDecl(<N>, <fty>)`:
· Handles:HandlesAnnAssign[llvm.memcpy]、HandlesAssign[llvm.memcpy.p0i8.p0i8.i64 ×2]、
HandlesBase[GetOrCreateFuncDecl / GetOrCreateStubFuncDecl]、
HandlesExpr[malloc]、HandlesExprBuiltin[strlen / llvm.memcpy]、
HandlesExprCall[llvm.* 与普通 / win32 API / strlen / llvm.memcpy]、
HandlesAsyncFunctions[malloc / create_* / resume_* / CRT 声明 / free]、
HandlesExprLambda[lambda 包装]、HandlesFunctions[_replace_function_decl 的 old_func=None 分支]、
HandlesImports[output stub 按需加载]
· LLVMCG:BaseGen[_DeclareExternalFunc 与 6 处 --hotspot-sites 内部符号]、
ExprGen[llvm.pow.f64]、VaArg[llvm.va_start / llvm.va_end]
原有 globals/functions 预检查与随后的 `Gen.functions[...] = X` 赋值一律保留。
注:`_DeclareExternalFunc` 收敛后外部声明会同时登记进 `self.functions`(幂等入口固有行为,
与既有 `get_or_declare_c_func` 一致;该路径仅 --hotspot-sites 调试特性使用)。
2. fail-fast 不再被吞(3 处):在包裹创建的 `except Exception` 首部加
`isinstance(_e, FailFastError) → raise`:
· HandlesExpr[malloc 的 null 回退分支]、HandlesExprBuiltin[strlen]、HandlesExprCall[strlen]
(其余异常维持原行为,本轮不扩大 fatal 面。)
另有 HandlesFunctions / HandlesImports 的同类 handler 前几轮已具备 FFE 重抛,本次未重复改。
验证:13 个改动文件 `py_compile` 全部 EXIT=0;冒烟 4 例通过——同名同类型复用同一对象、
异签名 → FailFastError(报文含两侧签名 i64/i32)、名字被非函数全局占用 → FailFastError、
`llvm.va_start` 幂等。另:`ir.Function(` 在 lib 内仅剩 `BaseGen._CreateOrReuseFuncDecl` 内部
这一处(豁免点);`DecoratorPass.py` 的 `module` 为外部传入(非 Gen 的模块)故其两处保持原样。
94 lines
4.3 KiB
Python
94 lines
4.3 KiB
Python
from __future__ import annotations
|
|
import llvmlite.ir as ir
|
|
from lib.core.LLVMCG.BaseGen import VaArgInstruction
|
|
|
|
|
|
class VaArgMixin:
|
|
|
|
def _get_va_start_intrinsic(self) -> ir.Function:
|
|
if 'llvm.va_start' in self.functions:
|
|
return self.functions['llvm.va_start']
|
|
ftype: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.IntType(8).as_pointer()])
|
|
func: ir.Function = self._CreateOrReuseFuncDecl('llvm.va_start', ftype)
|
|
self.functions['llvm.va_start'] = func
|
|
return func
|
|
|
|
def _get_va_end_intrinsic(self) -> ir.Function:
|
|
if 'llvm.va_end' in self.functions:
|
|
return self.functions['llvm.va_end']
|
|
ftype: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.IntType(8).as_pointer()])
|
|
func: ir.Function = self._CreateOrReuseFuncDecl('llvm.va_end', ftype)
|
|
self.functions['llvm.va_end'] = func
|
|
return func
|
|
|
|
def emit_va_start(self, va_list_ptr: ir.Value) -> None:
|
|
if not self.builder:
|
|
return None
|
|
func: ir.Function = self._get_va_start_intrinsic()
|
|
i8ptr_type: ir.PointerType = ir.IntType(8).as_pointer()
|
|
if va_list_ptr.type == i8ptr_type:
|
|
self.builder.call(func, [va_list_ptr])
|
|
else:
|
|
ptr: ir.Value = self.builder.bitcast(va_list_ptr, i8ptr_type, name="va_list_i8ptr")
|
|
self.builder.call(func, [ptr])
|
|
return None
|
|
|
|
def _emit_stack_align(self, alignment: int = 16) -> None:
|
|
if not self.builder:
|
|
return None
|
|
void: ir.VoidType = ir.VoidType()
|
|
asm_type: ir.FunctionType = ir.FunctionType(void, [])
|
|
mask: int = -alignment
|
|
asm_str: str = f"andq $${mask}, %rsp"
|
|
asm_func: ir.InlineAsm = ir.InlineAsm(asm_type, asm_str, "~{dirflag},~{fpsr},~{flags},~{rsp}", side_effect=True)
|
|
self.builder.call(asm_func, [], name="align_stack")
|
|
return None
|
|
|
|
def emit_va_end(self, va_list_ptr: ir.Value) -> None:
|
|
if not self.builder:
|
|
return None
|
|
func: ir.Function = self._get_va_end_intrinsic()
|
|
i8ptr_type: ir.PointerType = ir.IntType(8).as_pointer()
|
|
if va_list_ptr.type == i8ptr_type:
|
|
self.builder.call(func, [va_list_ptr])
|
|
else:
|
|
ptr: ir.Value = self.builder.bitcast(va_list_ptr, i8ptr_type, name="va_list_i8ptr")
|
|
self.builder.call(func, [ptr])
|
|
return None
|
|
|
|
def emit_va_arg(self, va_list_ptr: ir.Value, arg_type: ir.Type) -> ir.Value:
|
|
if not self.builder:
|
|
return self._ZeroConst(arg_type)
|
|
if not hasattr(self, '_va_arg_counter'):
|
|
self._va_arg_counter: int = 0
|
|
self._va_arg_counter += 1
|
|
is_x86_64: bool = self._platform_info['is_x86_64']
|
|
if is_x86_64:
|
|
va_arg_type: ir.Type
|
|
if isinstance(arg_type, ir.PointerType):
|
|
va_arg_type = ir.IntType(64)
|
|
elif isinstance(arg_type, (ir.FloatType, ir.DoubleType)):
|
|
va_arg_type = ir.DoubleType()
|
|
elif isinstance(arg_type, ir.IntType) and arg_type.width < 64:
|
|
va_arg_type = ir.IntType(64)
|
|
else:
|
|
va_arg_type = arg_type
|
|
instr: ir.Instruction = VaArgInstruction(self.builder.block, va_arg_type, 'va_arg', (va_list_ptr,), name=f'va_arg_raw_{self._va_arg_counter}')
|
|
self.builder._insert(instr)
|
|
result: ir.Value
|
|
if va_arg_type != arg_type:
|
|
if isinstance(arg_type, ir.PointerType):
|
|
result = self.builder.inttoptr(instr, arg_type, name=f'va_arg_ptr_{self._va_arg_counter}')
|
|
elif isinstance(arg_type, ir.FloatType):
|
|
result = self.builder.fptrunc(instr, arg_type, name=f'va_arg_fptrunc_{self._va_arg_counter}')
|
|
else:
|
|
result = self.builder.trunc(instr, arg_type, name=f'va_arg_trunc_{self._va_arg_counter}')
|
|
else:
|
|
result = instr
|
|
return result
|
|
else:
|
|
if isinstance(arg_type, ir.IntType) and arg_type.width < 64:
|
|
arg_type = ir.IntType(64)
|
|
instr: ir.Instruction = VaArgInstruction(self.builder.block, arg_type, 'va_arg', (va_list_ptr,), name=f'va_arg_raw_{self._va_arg_counter}')
|
|
self.builder._insert(instr)
|
|
return instr |