Skip to content

Commit d9cd072

Browse files
Add LLVM optimizer
1 parent a949a90 commit d9cd072

1 file changed

Lines changed: 63 additions & 4 deletions

File tree

‎pythonbpf/codegen.py‎

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
)
1616
from .debuginfo import DW_LANG_C11, DwarfBehaviorEnum, DebugInfoGenerator
1717
import os
18+
import shutil
1819
import subprocess
1920
import inspect
2021
from pathlib import Path
@@ -175,8 +176,66 @@ def compile_to_ir(filename: str, output: str, loglevel=logging.INFO):
175176
return output, structs_sym_tab, maps_sym_tab
176177

177178

179+
MIN_OPT_VERSION = 15
180+
_OPT_VERSIONED_RE = re.compile(r"opt-(\d+)")
181+
182+
183+
def _find_opt():
184+
"""Locate an LLVM opt binary.
185+
186+
Prefers a plain ``opt`` on PATH. Otherwise falls back to the newest
187+
versioned ``opt-N`` (N >= MIN_OPT_VERSION) that distros such as
188+
Debian/Ubuntu install, e.g. ``opt-18``. Returns None if none is found.
189+
"""
190+
opt = shutil.which("opt")
191+
if opt:
192+
return opt
193+
194+
best_version, best_path = -1, None
195+
for directory in os.environ.get("PATH", "").split(os.pathsep):
196+
try:
197+
entries = os.listdir(directory or ".")
198+
except OSError:
199+
continue
200+
for entry in entries:
201+
match = _OPT_VERSIONED_RE.fullmatch(entry)
202+
if not match:
203+
continue
204+
version = int(match.group(1))
205+
if version < MIN_OPT_VERSION or version <= best_version:
206+
continue
207+
path = shutil.which(entry, path=directory or ".")
208+
if path:
209+
best_version, best_path = version, path
210+
return best_path
211+
212+
213+
def _run_opt(ll_file):
214+
"""Run the O2 pipeline over the IR and return it as bitcode, or None if
215+
no opt binary is available."""
216+
217+
opt = _find_opt()
218+
if opt is None:
219+
logger.warning(
220+
f"No LLVM 'opt' (or 'opt-N', N >= {MIN_OPT_VERSION}) found on PATH; "
221+
"skipping IR optimization."
222+
)
223+
return None
224+
225+
logger.info(f"Optimizing IR with {opt} -O2: {ll_file}")
226+
result = subprocess.run(
227+
[opt, "-O2", str(ll_file), "-o", "-"],
228+
check=True,
229+
capture_output=True,
230+
)
231+
return result.stdout
232+
233+
178234
def _run_llc(ll_file, obj_file):
179-
"""Compile LLVM IR to BPF object file using llc."""
235+
"""Optimize LLVM IR with opt (when available) and compile it to a BPF
236+
object file using llc."""
237+
238+
bitcode = _run_opt(ll_file)
180239

181240
logger.info(f"Compiling IR to object: {ll_file} -> {obj_file}")
182241
result = subprocess.run(
@@ -185,20 +244,20 @@ def _run_llc(ll_file, obj_file):
185244
"-march=bpf",
186245
"-filetype=obj",
187246
"-O2",
188-
str(ll_file),
247+
"-" if bitcode is not None else str(ll_file),
189248
"-o",
190249
str(obj_file),
191250
],
251+
input=bitcode,
192252
check=True,
193253
capture_output=True,
194-
text=True,
195254
)
196255

197256
if result.returncode == 0:
198257
logger.info(f"Object file written to {obj_file}")
199258
return True
200259
else:
201-
logger.error(f"llc compilation failed: {result.stderr}")
260+
logger.error(f"llc compilation failed: {result.stderr.decode()}")
202261
return False
203262

204263

0 commit comments

Comments
 (0)