build(phaseb): run ASan instrumentation in-process too
--asan used to force the legacy PhaseB because the instrumentation step was a separate `opt -passes=forceattrs,asan -force-attribute=sanitize_address -asan-globals=false` child process. ircore.dll now exposes ircore_mlir_to_obj_asan / ircore_llvmir_to_obj_asan, which run the same passes through LLVM's C pass builder (LLVMRunPasses) between MLIR lowering and codegen, so ASan builds also avoid the three child processes. - the two cl::opt values are parsed once (std::call_once) before the first pass run - PhaseB loads the asan entry point together with the others and calls it when _asan_mode is set; the legacy `opt` path stays for --phaseb-legacy and is unchanged - --traceback (llc -frame-pointer=all) and raw-IR modules (llvm-link/opt) still take the legacy flow Verified on a real VKernel module: the ircore-built object and the `opt`-instrumented one reference the same ASan runtime symbols (69 __asan_report / 4 asan.module_ctor), and a deliberate heap overflow is reported identically by both builds.
This commit is contained in:
1 parent
ef4582dcfc
commit
eba9e9b6cc
1 file changed
+15
-7
@@ -79,6 +79,7 @@ _phaseb_backend: int = PHASEB_BACKEND_IRCORE
|
||||
_ircore_lib: win32.win32base.HANDLE = None
|
||||
_ircore_ready: int = 0
|
||||
_ircore_obj_fn: callable[[char*, sizet, char**, sizet*, char**, sizet*], int] = None
|
||||
_ircore_asan_fn: callable[[char*, sizet, char**, sizet*, char**, sizet*], int] = None
|
||||
_ircore_free_fn: callable[[char*], int] = None
|
||||
|
||||
# ircore.dll imports the exact LLVM build it was compiled with (MSYS2 clang64 22.1.x).
|
||||
@@ -90,8 +91,8 @@ extern def SetDllDirectoryA(lpPathName: LPCSTR) -> BOOL
|
||||
|
||||
|
||||
def _ircore_load() -> int:
|
||||
"""Load ircore.dll and resolve its two entry points; 1 = available, 0 = not available."""
|
||||
global _ircore_lib, _ircore_obj_fn, _ircore_free_fn
|
||||
"""Load ircore.dll and resolve its entry points; 1 = available, 0 = not available."""
|
||||
global _ircore_lib, _ircore_obj_fn, _ircore_asan_fn, _ircore_free_fn
|
||||
if _ircore_lib is not None: return 1
|
||||
h: win32.win32base.HANDLE = win32.win32base.LoadLibraryA("ircore.dll")
|
||||
if h is None:
|
||||
@@ -101,11 +102,13 @@ def _ircore_load() -> int:
|
||||
if h is None:
|
||||
return 0
|
||||
p_obj: win32.win32base.VOIDPTR = win32.win32base.GetProcAddress(h, "ircore_mlir_to_obj")
|
||||
p_asan: win32.win32base.VOIDPTR = win32.win32base.GetProcAddress(h, "ircore_mlir_to_obj_asan")
|
||||
p_free: win32.win32base.VOIDPTR = win32.win32base.GetProcAddress(h, "ircore_free")
|
||||
if p_obj is None or p_free is None:
|
||||
if p_obj is None or p_asan is None or p_free is None:
|
||||
win32.win32base.FreeLibrary(h)
|
||||
return 0
|
||||
_ircore_obj_fn = p_obj
|
||||
_ircore_asan_fn = p_asan
|
||||
_ircore_free_fn = p_free
|
||||
_ircore_lib = h
|
||||
return 1
|
||||
@@ -545,7 +548,12 @@ def _ircore_compile_module(ir_path: str, obj_path: str, module_name: str) -> int
|
||||
out_len: sizet = 0
|
||||
err: char* = None
|
||||
err_len: sizet = 0
|
||||
rc: int = _ircore_obj_fn(src, sizet(fsize), &out_obj, &out_len, &err, &err_len)
|
||||
# --asan: the DLL runs forceattrs,asan before codegen (same passes/options as the legacy
|
||||
# `opt -passes=forceattrs,asan ...` step); the project still links the ASan runtime.
|
||||
if _asan_mode != 0:
|
||||
rc: int = _ircore_asan_fn(src, sizet(fsize), &out_obj, &out_len, &err, &err_len)
|
||||
else:
|
||||
rc: int = _ircore_obj_fn(src, sizet(fsize), &out_obj, &out_len, &err, &err_len)
|
||||
stdlib.free(src)
|
||||
|
||||
if rc != 0:
|
||||
@@ -583,7 +591,7 @@ def compile_ll_to_obj(ir_path: str, output_dir: str, module_name: str, cc_cmd: s
|
||||
# IRCore backend: one in-process call for plain modules. ASan instrumentation, raw-IR
|
||||
# merging (llvm-link/opt), --traceback frame pointers and --phase2command still need the
|
||||
# external tools, so they fall through to the legacy command flow below (kept intact).
|
||||
if _phaseb_ircore_active() != 0 and _asan_mode == 0 and _traceback_mode == 0 \
|
||||
if _phaseb_ircore_active() != 0 and _traceback_mode == 0 \
|
||||
and _phase2_cmd_path is None:
|
||||
raw_sidecar: str = _ir_sibling_path(ir_path, ".raw.ll")
|
||||
inline_marker: str = _ir_sibling_path(ir_path, ".inline")
|
||||
@@ -832,7 +840,7 @@ def init_llc_task(task: LlcTask*, ir_path: str, output_dir: str, module_name: st
|
||||
# IRCore backend: plain modules compile in-process (the commands above stay allocated for
|
||||
# the legacy path / reporting but are not run). ASan instrumentation, raw-IR merging and
|
||||
# --phase2command keep the external tools.
|
||||
if _phaseb_ircore_active() != 0 and _asan_mode == 0 and _traceback_mode == 0 \
|
||||
if _phaseb_ircore_active() != 0 and _traceback_mode == 0 \
|
||||
and _phase2_cmd_path is None and has_raw == 0 and need_inline == 0:
|
||||
task.UseIrcore = 1
|
||||
task.IrPath = stdlib.malloc(len(ir_path) + 1)
|
||||
@@ -2271,7 +2279,7 @@ def _tb_read_whole(mb: memhub.MemBuddy*, path: str) -> str:
|
||||
if buf is None:
|
||||
f.close()
|
||||
return None
|
||||
# 容量全程用 sizet(与 sz 同宽):旧写法 ULONG(sz) + ULONG(2) 会在 sz >= 4GiB 时静默回绕
|
||||
# 容量全程�?sizet(与 sz 同宽):旧写�?ULONG(sz) + ULONG(2) 会在 sz >= 4GiB 时静默回�?
|
||||
got: LONG = f.read_all(buf, sizet(sz) + 2)
|
||||
f.close()
|
||||
if got < 0: return None
|
||||
|
||||
Reference in new issue
Block a user