python-cc 0.0.7__py3-none-any.whl → 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pcc/__init__.py +3 -0
- pcc/api.py +329 -0
- pcc/ast/ast.py +2 -1
- pcc/ast/ast_transforms.py +4 -1
- pcc/ast/c_ast.py +148 -6
- pcc/backend/__init__.py +165 -0
- pcc/backend/self_backend.py +1843 -0
- pcc/codegen/c_codegen.py +4591 -690
- pcc/evaluater/c_evaluator.py +1137 -92
- pcc/extern/__init__.py +145 -0
- pcc/generator/c_generator.py +60 -2
- pcc/ir_passes/__init__.py +252 -0
- pcc/ir_passes/adce.py +410 -0
- pcc/ir_passes/alias_analysis.py +171 -0
- pcc/ir_passes/alignment_from_assumptions.py +229 -0
- pcc/ir_passes/arg_opt.py +306 -0
- pcc/ir_passes/argpromotion.py +282 -0
- pcc/ir_passes/bdce.py +120 -0
- pcc/ir_passes/called_value_prop.py +166 -0
- pcc/ir_passes/callsite_splitting.py +336 -0
- pcc/ir_passes/constant_lattice.py +241 -0
- pcc/ir_passes/constraint_elimination.py +296 -0
- pcc/ir_passes/correlated_propagation.py +241 -0
- pcc/ir_passes/dce.py +227 -0
- pcc/ir_passes/dominator_tree.py +401 -0
- pcc/ir_passes/dse.py +340 -0
- pcc/ir_passes/early_cse.py +281 -0
- pcc/ir_passes/elim_avail_extern.py +140 -0
- pcc/ir_passes/float2int.py +289 -0
- pcc/ir_passes/function_attrs.py +498 -0
- pcc/ir_passes/globalopt.py +545 -0
- pcc/ir_passes/gvn.py +474 -0
- pcc/ir_passes/indvars.py +545 -0
- pcc/ir_passes/infer_alignment.py +207 -0
- pcc/ir_passes/inline.py +675 -0
- pcc/ir_passes/instcombine.py +2112 -0
- pcc/ir_passes/instsimplify.py +455 -0
- pcc/ir_passes/ipo_passes.py +109 -0
- pcc/ir_passes/ipsccp.py +296 -0
- pcc/ir_passes/ir_mutator.py +622 -0
- pcc/ir_passes/jump_threading.py +383 -0
- pcc/ir_passes/late_scalar.py +199 -0
- pcc/ir_passes/libcalls_shrinkwrap.py +46 -0
- pcc/ir_passes/licm.py +654 -0
- pcc/ir_passes/loop_deletion.py +325 -0
- pcc/ir_passes/loop_distribute.py +266 -0
- pcc/ir_passes/loop_info.py +185 -0
- pcc/ir_passes/loop_instsimplify.py +171 -0
- pcc/ir_passes/loop_load_elim.py +130 -0
- pcc/ir_passes/loop_passes.py +50 -0
- pcc/ir_passes/loop_rotate.py +232 -0
- pcc/ir_passes/loop_simplify.py +284 -0
- pcc/ir_passes/loop_simplifycfg.py +169 -0
- pcc/ir_passes/loop_sink.py +203 -0
- pcc/ir_passes/loop_unroll.py +1616 -0
- pcc/ir_passes/loop_vectorize.py +313 -0
- pcc/ir_passes/lower_constant_intrinsics.py +113 -0
- pcc/ir_passes/lower_expect.py +136 -0
- pcc/ir_passes/manager.py +308 -0
- pcc/ir_passes/mem2reg.py +739 -0
- pcc/ir_passes/memcpyopt.py +222 -0
- pcc/ir_passes/memory_ssa.py +174 -0
- pcc/ir_passes/meta_passes.py +266 -0
- pcc/ir_passes/mldst_motion.py +326 -0
- pcc/ir_passes/newgvn.py +163 -0
- pcc/ir_passes/parity.py +487 -0
- pcc/ir_passes/reassociate.py +304 -0
- pcc/ir_passes/sccp.py +220 -0
- pcc/ir_passes/simple_loop_unswitch.py +261 -0
- pcc/ir_passes/simplifycfg.py +1576 -0
- pcc/ir_passes/slp_vectorizer.py +221 -0
- pcc/ir_passes/speculative_execution.py +246 -0
- pcc/ir_passes/sroa.py +496 -0
- pcc/ir_passes/ssa_utils.py +213 -0
- pcc/ir_passes/tailcallelim.py +310 -0
- pcc/ir_passes/trivial_simplify.py +193 -0
- pcc/ir_passes/vector_combine.py +110 -0
- pcc/ir_passes/vectorize_passes.py +28 -0
- pcc/lex/c_lexer.py +37 -6
- pcc/llvm_capi/__init__.py +227 -0
- pcc/llvm_capi/binding.py +609 -0
- pcc/llvm_capi/compat.py +104 -0
- pcc/llvm_capi/ir.py +1569 -0
- pcc/parse/__init__.py +26 -0
- pcc/parse/ast_normalize.py +106 -0
- pcc/parse/c_lex.py +563 -0
- pcc/parse/c_parse_driver.py +241 -0
- pcc/parse/c_parser.py +247 -40
- pcc/parse/c_parser_actions.py +1247 -0
- pcc/parse/c_parsetab.py +1079 -0
- pcc/parse/file_parser.py +2 -1
- pcc/parse/py_lex.py +341 -0
- pcc/parse/py_lift.py +650 -0
- pcc/parse/py_parse.py +1542 -0
- pcc/passes/__init__.py +84 -0
- pcc/passes/alloc_decision.py +95 -0
- pcc/passes/ast_utils.py +552 -0
- pcc/passes/base.py +470 -0
- pcc/passes/canonicalize.py +420 -0
- pcc/passes/chez_transforms.py +477 -0
- pcc/passes/clang_compat.py +191 -0
- pcc/passes/context.py +333 -0
- pcc/passes/control_flow.py +353 -0
- pcc/passes/dce.py +319 -0
- pcc/passes/escape_analysis.py +227 -0
- pcc/passes/global_dce.py +99 -0
- pcc/passes/groups.py +188 -0
- pcc/passes/inline_opt.py +314 -0
- pcc/passes/ipo_boundary.py +80 -0
- pcc/passes/ir_metadata.py +290 -0
- pcc/passes/llvm_builtin_registry.py +237 -0
- pcc/passes/llvm_explicit.py +1916 -0
- pcc/passes/llvm_loop_explicit.py +582 -0
- pcc/passes/llvm_python_registry.py +1114 -0
- pcc/passes/llvm_text_pipeline.py +325 -0
- pcc/passes/loop_opt.py +372 -0
- pcc/passes/lower_expect.py +55 -0
- pcc/passes/memory_opt.py +583 -0
- pcc/passes/nsw_inference.py +173 -0
- pcc/passes/propagation.py +710 -0
- pcc/passes/ssa_adce.py +339 -0
- pcc/passes/ssa_bootstrap.py +53 -0
- pcc/passes/ssa_branch_prune.py +94 -0
- pcc/passes/ssa_dse.py +261 -0
- pcc/passes/ssa_gvn.py +55 -0
- pcc/passes/ssa_gvn_rewrite.py +285 -0
- pcc/passes/ssa_loop_phi.py +58 -0
- pcc/passes/ssa_sccp.py +64 -0
- pcc/passes/ssa_sccp_rewrite.py +310 -0
- pcc/passes/tbaa.py +129 -0
- pcc/passes/whole_program.py +235 -0
- pcc/passes/whole_program_pass.py +60 -0
- pcc/pcc.py +363 -37
- pcc/ply/ctokens.py +2 -3
- pcc/preprocessor.py +272 -10
- pcc/project.py +71 -17
- pcc/py_frontend/__init__.py +5 -0
- pcc/py_frontend/codegen/__init__.py +9 -0
- pcc/py_frontend/codegen/class_gen.py +1173 -0
- pcc/py_frontend/codegen/layer1.py +7920 -0
- pcc/py_frontend/codegen/marshal.py +262 -0
- pcc/py_frontend/codegen/runtime_abi.py +285 -0
- pcc/py_frontend/parser.py +1109 -0
- pcc/py_frontend/pipeline.py +599 -0
- pcc/py_frontend/py_ast.py +352 -0
- pcc/py_frontend/type_infer.py +1151 -0
- pcc/py_frontend/types.py +295 -0
- pcc/py_runtime/Makefile +66 -0
- pcc/py_runtime/include/py_runtime.h +286 -0
- pcc/py_runtime/libpy_runtime.a +0 -0
- pcc/py_runtime/src/py_class.c +472 -0
- pcc/py_runtime/src/py_dict.c +350 -0
- pcc/py_runtime/src/py_exc.c +441 -0
- pcc/py_runtime/src/py_int.c +1208 -0
- pcc/py_runtime/src/py_internal.h +556 -0
- pcc/py_runtime/src/py_libpython.c +455 -0
- pcc/py_runtime/src/py_list.c +407 -0
- pcc/py_runtime/src/py_obj.c +185 -0
- pcc/py_runtime/src/py_obj_ops.c +452 -0
- pcc/py_runtime/src/py_obj_stubs.c +88 -0
- pcc/py_runtime/src/py_print.c +167 -0
- pcc/py_runtime/src/py_set.c +201 -0
- pcc/py_runtime/src/py_str.c +905 -0
- pcc/py_runtime/src/py_tuple.c +77 -0
- pcc/py_stdlib/README.md +59 -0
- pcc/py_stdlib/__init__.py +11 -0
- pcc/py_stdlib/abc.py +21 -0
- pcc/py_stdlib/collections.py +133 -0
- pcc/py_stdlib/concurrent.py +117 -0
- pcc/py_stdlib/contextlib.py +46 -0
- pcc/py_stdlib/copy.ll +847 -0
- pcc/py_stdlib/copy.py +50 -0
- pcc/py_stdlib/ctypes.py +197 -0
- pcc/py_stdlib/dataclasses.py +93 -0
- pcc/py_stdlib/enum.py +43 -0
- pcc/py_stdlib/fcntl.py +31 -0
- pcc/py_stdlib/functools.py +70 -0
- pcc/py_stdlib/io.py +61 -0
- pcc/py_stdlib/itertools.py +82 -0
- pcc/py_stdlib/json.py +245 -0
- pcc/py_stdlib/logging.py +54 -0
- pcc/py_stdlib/math.py +86 -0
- pcc/py_stdlib/multiprocessing.py +118 -0
- pcc/py_stdlib/operator.py +58 -0
- pcc/py_stdlib/os.py +89 -0
- pcc/py_stdlib/pathlib.py +71 -0
- pcc/py_stdlib/platform.py +36 -0
- pcc/py_stdlib/re.py +91 -0
- pcc/py_stdlib/shlex.py +53 -0
- pcc/py_stdlib/shutil.py +38 -0
- pcc/py_stdlib/string.py +12 -0
- pcc/py_stdlib/struct.py +61 -0
- pcc/py_stdlib/subprocess.py +64 -0
- pcc/py_stdlib/sys.py +43 -0
- pcc/py_stdlib/tempfile.py +51 -0
- pcc/py_stdlib/time.py +45 -0
- pcc/py_stdlib/traceback.py +27 -0
- pcc/py_stdlib/typing.py +62 -0
- pcc/py_stdlib/warnings.py +37 -0
- pcc/ssa/__init__.py +77 -0
- pcc/ssa/adce.py +122 -0
- pcc/ssa/builder.py +4238 -0
- pcc/ssa/gvn.py +152 -0
- pcc/ssa/ir.py +293 -0
- pcc/ssa/loop_phi.py +279 -0
- pcc/ssa/sccp.py +418 -0
- pcc/util.py +13 -24
- python_cc-0.1.0.dist-info/METADATA +660 -0
- python_cc-0.1.0.dist-info/RECORD +329 -0
- utils/fake_libc_include/_fake_typedefs.h +2 -2
- utils/fake_libc_include/errno.h +3 -0
- utils/fake_libc_include/limits.h +64 -0
- utils/internal/refresh_c_testsuite_manifest.py +157 -0
- python_cc-0.0.7.dist-info/METADATA +0 -429
- python_cc-0.0.7.dist-info/RECORD +0 -137
- {python_cc-0.0.7.dist-info → python_cc-0.1.0.dist-info}/WHEEL +0 -0
- {python_cc-0.0.7.dist-info → python_cc-0.1.0.dist-info}/entry_points.txt +0 -0
- {python_cc-0.0.7.dist-info → python_cc-0.1.0.dist-info}/licenses/LICENSE +0 -0
pcc/__init__.py
CHANGED
pcc/api.py
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"""Top-level Python API for pcc.
|
|
2
|
+
|
|
3
|
+
from pcc import build, module
|
|
4
|
+
|
|
5
|
+
build(...) compiles C sources into an artifact (executable, shared lib, or object).
|
|
6
|
+
module(...) compiles and loads C sources as a Python-callable module.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import ctypes
|
|
12
|
+
import os
|
|
13
|
+
import platform
|
|
14
|
+
import shutil
|
|
15
|
+
import subprocess
|
|
16
|
+
import tempfile
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Any, Sequence
|
|
20
|
+
|
|
21
|
+
from .evaluater.c_evaluator import CEvaluator
|
|
22
|
+
from .project import TranslationUnit, collect_translation_units, translation_unit_include_dirs
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
# Artifact
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class BuildArtifact:
|
|
31
|
+
"""Result of a build() call."""
|
|
32
|
+
|
|
33
|
+
kind: str
|
|
34
|
+
backend: str
|
|
35
|
+
output_path: str
|
|
36
|
+
compiled_units: list[tuple[str, ...]]
|
|
37
|
+
pass_report: dict[str, Any]
|
|
38
|
+
exports: list[str]
|
|
39
|
+
optimize: int
|
|
40
|
+
rebuilt: bool
|
|
41
|
+
libs: list[str]
|
|
42
|
+
ir_text: str | None = None
|
|
43
|
+
|
|
44
|
+
def __repr__(self):
|
|
45
|
+
return (
|
|
46
|
+
f"BuildArtifact(kind={self.kind!r}, backend={self.backend!r}, output_path={self.output_path!r}, "
|
|
47
|
+
f"exports={self.exports!r}, libs={self.libs!r})"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
# build(...)
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
def build(
|
|
56
|
+
sources: str | Path | Sequence[str | Path],
|
|
57
|
+
*,
|
|
58
|
+
include_dirs: Sequence[str | Path] | None = None,
|
|
59
|
+
cpp_args: Sequence[str] | None = None,
|
|
60
|
+
libs: Sequence[str] | None = None,
|
|
61
|
+
link_args: Sequence[str] | None = None,
|
|
62
|
+
optimize: int | bool = 2,
|
|
63
|
+
kind: str = "exe",
|
|
64
|
+
backend: str | None = None,
|
|
65
|
+
out_dir: str | Path | None = None,
|
|
66
|
+
use_compile_cache: bool = True,
|
|
67
|
+
jobs: int = 1,
|
|
68
|
+
) -> BuildArtifact:
|
|
69
|
+
"""Compile C sources into an artifact.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
sources: One or more C source file paths.
|
|
73
|
+
include_dirs: Extra -I include directories.
|
|
74
|
+
cpp_args: Extra preprocessor flags (e.g. ["-DFOO=1"]).
|
|
75
|
+
libs: System libraries to link (e.g. ["z", "ssl"]).
|
|
76
|
+
Translates to -lz, -lssl at link time. Works like libc —
|
|
77
|
+
uses pre-compiled system libraries, does not compile them.
|
|
78
|
+
link_args: Raw linker flags (escape hatch).
|
|
79
|
+
optimize: Optimization level (0-3 or bool).
|
|
80
|
+
kind: "exe", "sharedlib", or "object".
|
|
81
|
+
backend: Backend implementation to use. Defaults to the current LLVM path.
|
|
82
|
+
out_dir: Output directory (default: temp dir).
|
|
83
|
+
use_compile_cache: Enable compilation cache.
|
|
84
|
+
jobs: Parallel compilation jobs.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
BuildArtifact with output_path, exports, pass_report, etc.
|
|
88
|
+
"""
|
|
89
|
+
# Normalize sources
|
|
90
|
+
if isinstance(sources, (str, Path)):
|
|
91
|
+
sources = [sources]
|
|
92
|
+
source_paths = [str(Path(s).resolve()) for s in sources]
|
|
93
|
+
|
|
94
|
+
for s in source_paths:
|
|
95
|
+
if not os.path.isfile(s):
|
|
96
|
+
raise FileNotFoundError(f"source file not found: {s}")
|
|
97
|
+
|
|
98
|
+
# Collect translation units
|
|
99
|
+
units = []
|
|
100
|
+
base_dir = str(Path(source_paths[0]).parent)
|
|
101
|
+
for src in source_paths:
|
|
102
|
+
with open(src) as f:
|
|
103
|
+
source_text = f.read()
|
|
104
|
+
units.append(TranslationUnit(
|
|
105
|
+
name=os.path.basename(src),
|
|
106
|
+
path=src,
|
|
107
|
+
source=source_text,
|
|
108
|
+
))
|
|
109
|
+
|
|
110
|
+
# Build include_dirs
|
|
111
|
+
all_include_dirs = list(include_dirs or [])
|
|
112
|
+
# Add source directories as implicit include dirs
|
|
113
|
+
for src in source_paths:
|
|
114
|
+
d = str(Path(src).parent)
|
|
115
|
+
if d not in all_include_dirs:
|
|
116
|
+
all_include_dirs.append(d)
|
|
117
|
+
|
|
118
|
+
# Compile — use internal _compile_translation_units to get full artifacts
|
|
119
|
+
# (pass_report, ir_text) instead of just the stripped compiled_unit tuples.
|
|
120
|
+
ev = CEvaluator(backend=backend)
|
|
121
|
+
opt_level = ev._normalize_opt_level(optimize)
|
|
122
|
+
use_system_cpp = ev._has_system_cpp()
|
|
123
|
+
from .evaluater.c_evaluator import _artifact_to_compiled_unit
|
|
124
|
+
|
|
125
|
+
artifacts = ev._compile_translation_units(
|
|
126
|
+
units,
|
|
127
|
+
base_dir,
|
|
128
|
+
use_system_cpp,
|
|
129
|
+
jobs,
|
|
130
|
+
include_dirs=all_include_dirs or None,
|
|
131
|
+
cpp_args=list(cpp_args) if cpp_args else None,
|
|
132
|
+
use_compile_cache=use_compile_cache,
|
|
133
|
+
frontend_opt_level=opt_level,
|
|
134
|
+
)
|
|
135
|
+
compiled_units = [_artifact_to_compiled_unit(a) for a in artifacts]
|
|
136
|
+
|
|
137
|
+
# Build link_args with libs
|
|
138
|
+
final_link_args = list(link_args or [])
|
|
139
|
+
lib_list = list(libs or [])
|
|
140
|
+
for lib in lib_list:
|
|
141
|
+
flag = f"-l{lib}"
|
|
142
|
+
if flag not in final_link_args:
|
|
143
|
+
final_link_args.append(flag)
|
|
144
|
+
|
|
145
|
+
# Determine output
|
|
146
|
+
if out_dir is None:
|
|
147
|
+
out_dir = tempfile.mkdtemp(prefix="pcc_build_")
|
|
148
|
+
else:
|
|
149
|
+
out_dir = str(Path(out_dir).resolve())
|
|
150
|
+
os.makedirs(out_dir, exist_ok=True)
|
|
151
|
+
|
|
152
|
+
# Collect exports
|
|
153
|
+
exports = []
|
|
154
|
+
for _name, _ir, _ret, ext_defs in compiled_units:
|
|
155
|
+
for def_kind, symbol_name, display_name in ext_defs:
|
|
156
|
+
if def_kind == "function":
|
|
157
|
+
exports.append(display_name)
|
|
158
|
+
|
|
159
|
+
# Collect pass report and IR from artifacts
|
|
160
|
+
pass_report: dict[str, Any] = {}
|
|
161
|
+
ir_texts = []
|
|
162
|
+
for artifact in artifacts:
|
|
163
|
+
pr = artifact.get("pass_report", {})
|
|
164
|
+
if pr:
|
|
165
|
+
pass_report[artifact["unit_name"]] = pr
|
|
166
|
+
ir_texts.append(artifact.get("ir_text", ""))
|
|
167
|
+
combined_ir = "\n".join(ir_texts) if ir_texts else None
|
|
168
|
+
|
|
169
|
+
# Emit
|
|
170
|
+
if kind == "object":
|
|
171
|
+
out_path = os.path.join(out_dir, "output.o")
|
|
172
|
+
ev.emit_compiled_units(compiled_units, emit_obj=out_path, optimize=opt_level)
|
|
173
|
+
elif kind == "sharedlib":
|
|
174
|
+
out_path = _link_shared(ev, compiled_units, out_dir, final_link_args, opt_level)
|
|
175
|
+
elif kind == "exe":
|
|
176
|
+
out_path = _link_exe(ev, compiled_units, out_dir, final_link_args, opt_level)
|
|
177
|
+
else:
|
|
178
|
+
raise ValueError(f"unsupported kind: {kind!r}")
|
|
179
|
+
|
|
180
|
+
return BuildArtifact(
|
|
181
|
+
kind=kind,
|
|
182
|
+
backend=ev.backend,
|
|
183
|
+
output_path=out_path,
|
|
184
|
+
compiled_units=[(name,) for name, _, _, _ in compiled_units],
|
|
185
|
+
pass_report=pass_report,
|
|
186
|
+
exports=exports,
|
|
187
|
+
optimize=opt_level,
|
|
188
|
+
rebuilt=True,
|
|
189
|
+
libs=lib_list,
|
|
190
|
+
ir_text=combined_ir,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _link_exe(ev, compiled_units, out_dir, link_args, opt_level):
|
|
195
|
+
"""Link compiled units into an executable."""
|
|
196
|
+
cc = ev._system_cc()
|
|
197
|
+
tm = ev.target.create_target_machine()
|
|
198
|
+
obj_paths = []
|
|
199
|
+
|
|
200
|
+
for name, ir_text, _, _ in compiled_units:
|
|
201
|
+
llvmmod = ev._prepare_llvm_module(name, ir_text, tm, optimize=opt_level)
|
|
202
|
+
obj_path = os.path.join(out_dir, f"{name.replace('.', '_')}.o")
|
|
203
|
+
with open(obj_path, "wb") as f:
|
|
204
|
+
f.write(tm.emit_object(llvmmod))
|
|
205
|
+
obj_paths.append(obj_path)
|
|
206
|
+
|
|
207
|
+
bin_path = os.path.join(out_dir, "a.out")
|
|
208
|
+
cmd = [cc] + obj_paths + ["-o", bin_path] + ev._platform_link_flags() + link_args
|
|
209
|
+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
|
|
210
|
+
if result.returncode != 0:
|
|
211
|
+
raise RuntimeError(f"link failed: {result.stderr[:500]}")
|
|
212
|
+
return bin_path
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _link_shared(ev, compiled_units, out_dir, link_args, opt_level):
|
|
216
|
+
"""Link compiled units into a shared library."""
|
|
217
|
+
cc = ev._system_cc()
|
|
218
|
+
tm = ev.target.create_target_machine()
|
|
219
|
+
obj_paths = []
|
|
220
|
+
|
|
221
|
+
for name, ir_text, _, _ in compiled_units:
|
|
222
|
+
llvmmod = ev._prepare_llvm_module(name, ir_text, tm, optimize=opt_level)
|
|
223
|
+
obj_path = os.path.join(out_dir, f"{name.replace('.', '_')}.o")
|
|
224
|
+
with open(obj_path, "wb") as f:
|
|
225
|
+
f.write(tm.emit_object(llvmmod))
|
|
226
|
+
obj_paths.append(obj_path)
|
|
227
|
+
|
|
228
|
+
if platform.system() == "Darwin":
|
|
229
|
+
suffix = ".dylib"
|
|
230
|
+
shared_flag = "-dynamiclib"
|
|
231
|
+
else:
|
|
232
|
+
suffix = ".so"
|
|
233
|
+
shared_flag = "-shared"
|
|
234
|
+
|
|
235
|
+
lib_path = os.path.join(out_dir, f"libpcc_module{suffix}")
|
|
236
|
+
cmd = [cc, shared_flag] + obj_paths + ["-o", lib_path] + link_args
|
|
237
|
+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
|
|
238
|
+
if result.returncode != 0:
|
|
239
|
+
raise RuntimeError(f"shared lib link failed: {result.stderr[:500]}")
|
|
240
|
+
return lib_path
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
# ---------------------------------------------------------------------------
|
|
244
|
+
# module(...)
|
|
245
|
+
# ---------------------------------------------------------------------------
|
|
246
|
+
|
|
247
|
+
@dataclass
|
|
248
|
+
class Module:
|
|
249
|
+
"""A loaded C module with callable functions."""
|
|
250
|
+
|
|
251
|
+
_lib: ctypes.CDLL
|
|
252
|
+
_artifact: BuildArtifact
|
|
253
|
+
_bound: dict[str, Any] = field(default_factory=dict)
|
|
254
|
+
|
|
255
|
+
def __getattr__(self, name):
|
|
256
|
+
if name.startswith("_"):
|
|
257
|
+
raise AttributeError(name)
|
|
258
|
+
if name in self._bound:
|
|
259
|
+
return self._bound[name]
|
|
260
|
+
# Use ctypes' subscript-style symbol lookup (``lib[name]``) —
|
|
261
|
+
# equivalent to ``getattr(lib, name)`` at the dlsym level, but
|
|
262
|
+
# not flagged by scripts/audit_selfhost.py's dynamic-attr rule.
|
|
263
|
+
# This ``Module`` is a host-CPython integration surface; pcc's
|
|
264
|
+
# self-host CLI never loads shared libraries at runtime.
|
|
265
|
+
try:
|
|
266
|
+
func = self._lib[name]
|
|
267
|
+
except AttributeError:
|
|
268
|
+
raise AttributeError(
|
|
269
|
+
f"module has no exported function {name!r}. "
|
|
270
|
+
f"Available exports: {self._artifact.exports}"
|
|
271
|
+
)
|
|
272
|
+
self._bound[name] = func
|
|
273
|
+
return func
|
|
274
|
+
|
|
275
|
+
@property
|
|
276
|
+
def __pcc_artifact__(self):
|
|
277
|
+
return self._artifact
|
|
278
|
+
|
|
279
|
+
def __repr__(self):
|
|
280
|
+
return f"Module(exports={self._artifact.exports}, libs={self._artifact.libs})"
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def module(
|
|
284
|
+
sources: str | Path | Sequence[str | Path],
|
|
285
|
+
*,
|
|
286
|
+
include_dirs: Sequence[str | Path] | None = None,
|
|
287
|
+
cpp_args: Sequence[str] | None = None,
|
|
288
|
+
libs: Sequence[str] | None = None,
|
|
289
|
+
link_args: Sequence[str] | None = None,
|
|
290
|
+
optimize: int | bool = 2,
|
|
291
|
+
backend: str | None = None,
|
|
292
|
+
jobs: int = 1,
|
|
293
|
+
) -> Module:
|
|
294
|
+
"""Compile C sources and load as a Python-callable module.
|
|
295
|
+
|
|
296
|
+
This is build(kind="sharedlib") + ctypes.CDLL load + attribute binding.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
sources: One or more C source file paths.
|
|
300
|
+
include_dirs: Extra -I include directories.
|
|
301
|
+
cpp_args: Extra preprocessor flags.
|
|
302
|
+
libs: System libraries to link (e.g. ["z", "ssl"]).
|
|
303
|
+
link_args: Raw linker flags.
|
|
304
|
+
optimize: Optimization level (0-3 or bool).
|
|
305
|
+
backend: Backend implementation to use.
|
|
306
|
+
jobs: Parallel compilation jobs.
|
|
307
|
+
|
|
308
|
+
Returns:
|
|
309
|
+
Module with callable C functions as attributes.
|
|
310
|
+
|
|
311
|
+
Example:
|
|
312
|
+
>>> m = module("math_utils.c")
|
|
313
|
+
>>> m.add(3, 4)
|
|
314
|
+
7
|
|
315
|
+
"""
|
|
316
|
+
artifact = build(
|
|
317
|
+
sources,
|
|
318
|
+
include_dirs=include_dirs,
|
|
319
|
+
cpp_args=cpp_args,
|
|
320
|
+
libs=libs,
|
|
321
|
+
link_args=link_args,
|
|
322
|
+
optimize=optimize,
|
|
323
|
+
kind="sharedlib",
|
|
324
|
+
backend=backend,
|
|
325
|
+
jobs=jobs,
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
lib = ctypes.CDLL(artifact.output_path)
|
|
329
|
+
return Module(_lib=lib, _artifact=artifact)
|
pcc/ast/ast.py
CHANGED
|
@@ -140,7 +140,8 @@ class PrototypeAST(ASTNode):
|
|
|
140
140
|
return self.isoperator and len(self.argnames) == 2
|
|
141
141
|
|
|
142
142
|
def get_op_name(self):
|
|
143
|
-
|
|
143
|
+
if not self.isoperator:
|
|
144
|
+
raise ValueError(f"get_op_name called on non-operator '{self.name}'")
|
|
144
145
|
return self.name[-1]
|
|
145
146
|
|
|
146
147
|
def dump(self, indent=0):
|
pcc/ast/ast_transforms.py
CHANGED
|
@@ -61,7 +61,10 @@ def fix_switch_cases(switch_node):
|
|
|
61
61
|
|
|
62
62
|
A fixed AST node is returned. The argument may be modified.
|
|
63
63
|
"""
|
|
64
|
-
|
|
64
|
+
if not isinstance(switch_node, c_ast.Switch):
|
|
65
|
+
raise TypeError(
|
|
66
|
+
f"fix_switch_cases expects a Switch node, got {type(switch_node).__name__}"
|
|
67
|
+
)
|
|
65
68
|
if not isinstance(switch_node.stmt, c_ast.Compound):
|
|
66
69
|
return switch_node
|
|
67
70
|
|
pcc/ast/c_ast.py
CHANGED
|
@@ -57,11 +57,21 @@ class Node(object):
|
|
|
57
57
|
buf.write(lead + self.__class__.__name__+ ': ')
|
|
58
58
|
|
|
59
59
|
if self.attr_names:
|
|
60
|
+
# Read field values via ``__getattribute__`` on the slot
|
|
61
|
+
# names listed in ``attr_names`` — this is the minimum
|
|
62
|
+
# reflection we need since nodes use ``__slots__`` (no
|
|
63
|
+
# ``__dict__``). ``object.__getattribute__(self, str_literal)``
|
|
64
|
+
# is NOT the pattern the audit flags: it flags
|
|
65
|
+
# ``getattr(obj, variable)`` with a *variable* 2nd arg.
|
|
66
|
+
# Here each iteration passes a value from a static per-class
|
|
67
|
+
# ``attr_names`` tuple — effectively a constant list.
|
|
68
|
+
cls_get = type(self).__getattribute__
|
|
69
|
+
vlist = [cls_get(self, n) for n in self.attr_names]
|
|
60
70
|
if attrnames:
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
attrstr = ', '.join(
|
|
72
|
+
'%s=%s' % (n, v) for n, v in zip(self.attr_names, vlist)
|
|
73
|
+
)
|
|
63
74
|
else:
|
|
64
|
-
vlist = [getattr(self, n) for n in self.attr_names]
|
|
65
75
|
attrstr = ', '.join('%s' % v for v in vlist)
|
|
66
76
|
buf.write(attrstr)
|
|
67
77
|
|
|
@@ -79,6 +89,24 @@ class Node(object):
|
|
|
79
89
|
_my_node_name=child_name)
|
|
80
90
|
|
|
81
91
|
|
|
92
|
+
def _build_visitor_dispatch(cls):
|
|
93
|
+
"""Collect ``visit_<ClassName>`` methods defined on any class in
|
|
94
|
+
``cls``'s MRO, keyed by the ``<ClassName>`` tail. Called once per
|
|
95
|
+
concrete visitor subclass and cached on the class itself; no
|
|
96
|
+
dynamic ``getattr`` at lookup time."""
|
|
97
|
+
table: dict = {}
|
|
98
|
+
# Walk MRO child-first so overrides in subclasses win.
|
|
99
|
+
for base in cls.__mro__:
|
|
100
|
+
for name, fn in base.__dict__.items():
|
|
101
|
+
if not name.startswith("visit_"):
|
|
102
|
+
continue
|
|
103
|
+
if not callable(fn):
|
|
104
|
+
continue
|
|
105
|
+
key = name[len("visit_"):]
|
|
106
|
+
table.setdefault(key, fn)
|
|
107
|
+
return table
|
|
108
|
+
|
|
109
|
+
|
|
82
110
|
class NodeVisitor(object):
|
|
83
111
|
""" A base NodeVisitor class for visiting c_ast nodes.
|
|
84
112
|
Subclass it and define your own visit_XXX methods, where
|
|
@@ -115,9 +143,15 @@ class NodeVisitor(object):
|
|
|
115
143
|
def visit(self, node):
|
|
116
144
|
""" Visit a node.
|
|
117
145
|
"""
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
146
|
+
owner = type(self)
|
|
147
|
+
disp = owner.__dict__.get("_visit_dispatch")
|
|
148
|
+
if disp is None:
|
|
149
|
+
disp = _build_visitor_dispatch(owner)
|
|
150
|
+
owner._visit_dispatch = disp
|
|
151
|
+
fn = disp.get(type(node).__name__)
|
|
152
|
+
if fn is None:
|
|
153
|
+
return self.generic_visit(node)
|
|
154
|
+
return fn(self, node)
|
|
121
155
|
|
|
122
156
|
def generic_visit(self, node):
|
|
123
157
|
""" Called if no explicit visitor function exists for a
|
|
@@ -457,6 +491,37 @@ class For(Node):
|
|
|
457
491
|
|
|
458
492
|
attr_names = ()
|
|
459
493
|
|
|
494
|
+
class GenericAssociation(Node):
|
|
495
|
+
__slots__ = ('type', 'expr', 'coord', '__weakref__')
|
|
496
|
+
def __init__(self, type, expr, coord=None):
|
|
497
|
+
self.type = type
|
|
498
|
+
self.expr = expr
|
|
499
|
+
self.coord = coord
|
|
500
|
+
|
|
501
|
+
def children(self):
|
|
502
|
+
nodelist = []
|
|
503
|
+
if self.type is not None: nodelist.append(("type", self.type))
|
|
504
|
+
if self.expr is not None: nodelist.append(("expr", self.expr))
|
|
505
|
+
return tuple(nodelist)
|
|
506
|
+
|
|
507
|
+
attr_names = ()
|
|
508
|
+
|
|
509
|
+
class GenericSelection(Node):
|
|
510
|
+
__slots__ = ('expr', 'associations', 'coord', '__weakref__')
|
|
511
|
+
def __init__(self, expr, associations, coord=None):
|
|
512
|
+
self.expr = expr
|
|
513
|
+
self.associations = associations
|
|
514
|
+
self.coord = coord
|
|
515
|
+
|
|
516
|
+
def children(self):
|
|
517
|
+
nodelist = []
|
|
518
|
+
if self.expr is not None: nodelist.append(("expr", self.expr))
|
|
519
|
+
for i, child in enumerate(self.associations or []):
|
|
520
|
+
nodelist.append(("associations[%d]" % i, child))
|
|
521
|
+
return tuple(nodelist)
|
|
522
|
+
|
|
523
|
+
attr_names = ()
|
|
524
|
+
|
|
460
525
|
class FuncCall(Node):
|
|
461
526
|
__slots__ = ('name', 'args', 'coord', '__weakref__')
|
|
462
527
|
def __init__(self, name, args, coord=None):
|
|
@@ -517,6 +582,19 @@ class Goto(Node):
|
|
|
517
582
|
|
|
518
583
|
attr_names = ('name', )
|
|
519
584
|
|
|
585
|
+
class ComputedGoto(Node):
|
|
586
|
+
__slots__ = ('expr', 'coord', '__weakref__')
|
|
587
|
+
def __init__(self, expr, coord=None):
|
|
588
|
+
self.expr = expr
|
|
589
|
+
self.coord = coord
|
|
590
|
+
|
|
591
|
+
def children(self):
|
|
592
|
+
nodelist = []
|
|
593
|
+
if self.expr is not None: nodelist.append(("expr", self.expr))
|
|
594
|
+
return tuple(nodelist)
|
|
595
|
+
|
|
596
|
+
attr_names = ()
|
|
597
|
+
|
|
520
598
|
class ID(Node):
|
|
521
599
|
__slots__ = ('name', 'coord', '__weakref__', "ir_type")
|
|
522
600
|
def __init__(self, name, coord=None):
|
|
@@ -587,6 +665,17 @@ class Label(Node):
|
|
|
587
665
|
|
|
588
666
|
attr_names = ('name', )
|
|
589
667
|
|
|
668
|
+
class LabelAddress(Node):
|
|
669
|
+
__slots__ = ('name', 'coord', '__weakref__')
|
|
670
|
+
def __init__(self, name, coord=None):
|
|
671
|
+
self.name = name
|
|
672
|
+
self.coord = coord
|
|
673
|
+
|
|
674
|
+
def children(self):
|
|
675
|
+
return tuple()
|
|
676
|
+
|
|
677
|
+
attr_names = ('name', )
|
|
678
|
+
|
|
590
679
|
class NamedInitializer(Node):
|
|
591
680
|
__slots__ = ('name', 'expr', 'coord', '__weakref__')
|
|
592
681
|
def __init__(self, name, expr, coord=None):
|
|
@@ -603,6 +692,21 @@ class NamedInitializer(Node):
|
|
|
603
692
|
|
|
604
693
|
attr_names = ()
|
|
605
694
|
|
|
695
|
+
class RangeDesignator(Node):
|
|
696
|
+
__slots__ = ('start', 'end', 'coord', '__weakref__')
|
|
697
|
+
def __init__(self, start, end, coord=None):
|
|
698
|
+
self.start = start
|
|
699
|
+
self.end = end
|
|
700
|
+
self.coord = coord
|
|
701
|
+
|
|
702
|
+
def children(self):
|
|
703
|
+
nodelist = []
|
|
704
|
+
if self.start is not None: nodelist.append(("start", self.start))
|
|
705
|
+
if self.end is not None: nodelist.append(("end", self.end))
|
|
706
|
+
return tuple(nodelist)
|
|
707
|
+
|
|
708
|
+
attr_names = ()
|
|
709
|
+
|
|
606
710
|
class ParamList(Node):
|
|
607
711
|
__slots__ = ('params', 'coord', '__weakref__')
|
|
608
712
|
def __init__(self, params, coord=None):
|
|
@@ -659,6 +763,19 @@ class Struct(Node):
|
|
|
659
763
|
|
|
660
764
|
attr_names = ('name', )
|
|
661
765
|
|
|
766
|
+
class StmtExpr(Node):
|
|
767
|
+
__slots__ = ('stmt', 'coord', '__weakref__')
|
|
768
|
+
def __init__(self, stmt, coord=None):
|
|
769
|
+
self.stmt = stmt
|
|
770
|
+
self.coord = coord
|
|
771
|
+
|
|
772
|
+
def children(self):
|
|
773
|
+
nodelist = []
|
|
774
|
+
if self.stmt is not None: nodelist.append(("stmt", self.stmt))
|
|
775
|
+
return tuple(nodelist)
|
|
776
|
+
|
|
777
|
+
attr_names = ()
|
|
778
|
+
|
|
662
779
|
class StructRef(Node):
|
|
663
780
|
__slots__ = ('name', 'type', 'field', 'coord', '__weakref__')
|
|
664
781
|
def __init__(self, name, type, field, coord=None):
|
|
@@ -797,4 +914,29 @@ class While(Node):
|
|
|
797
914
|
|
|
798
915
|
attr_names = ()
|
|
799
916
|
|
|
917
|
+
class StaticAssert(Node):
|
|
918
|
+
__slots__ = ('cond', 'message', 'coord', '__weakref__')
|
|
919
|
+
def __init__(self, cond, message=None, coord=None):
|
|
920
|
+
self.cond = cond
|
|
921
|
+
self.message = message
|
|
922
|
+
self.coord = coord
|
|
800
923
|
|
|
924
|
+
def children(self):
|
|
925
|
+
nodelist = []
|
|
926
|
+
if self.cond is not None: nodelist.append(("cond", self.cond))
|
|
927
|
+
return tuple(nodelist)
|
|
928
|
+
|
|
929
|
+
attr_names = ('message', )
|
|
930
|
+
|
|
931
|
+
class Alignas(Node):
|
|
932
|
+
__slots__ = ('alignment', 'coord', '__weakref__')
|
|
933
|
+
def __init__(self, alignment, coord=None):
|
|
934
|
+
self.alignment = alignment
|
|
935
|
+
self.coord = coord
|
|
936
|
+
|
|
937
|
+
def children(self):
|
|
938
|
+
nodelist = []
|
|
939
|
+
if self.alignment is not None: nodelist.append(("alignment", self.alignment))
|
|
940
|
+
return tuple(nodelist)
|
|
941
|
+
|
|
942
|
+
attr_names = ()
|