Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
421 changes: 363 additions & 58 deletions .github/workflows/ci_sim.yml

Large diffs are not rendered by default.

91 changes: 61 additions & 30 deletions lib/PTO/Transforms/FoldTileBufIntrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ static bool shouldFoldAddrFamily(FoldIntrinsicMode mode) {
return mode == FoldIntrinsicMode::All || mode == FoldIntrinsicMode::AddrOnly;
}

static void eraseDeadAllocTileOps(func::FuncOp func) {
static bool eraseDeadAllocTileOps(func::FuncOp func) {
SmallVector<pto::AllocTileOp> deadAllocs;
func.walk([&](pto::AllocTileOp alloc) {
if (alloc.getResult().use_empty())
Expand All @@ -88,6 +88,65 @@ static void eraseDeadAllocTileOps(func::FuncOp func) {

for (pto::AllocTileOp alloc : llvm::reverse(deadAllocs))
alloc.erase();
return !deadAllocs.empty();
Comment thread
jimmychou0 marked this conversation as resolved.
}

static bool isPTOViewBridgeType(Type type) {
return isa<pto::PartitionTensorViewType, pto::TensorViewType,
pto::TileBufType>(type);
}

static bool eraseDeadViewBridgeCasts(func::FuncOp func) {
SmallVector<UnrealizedConversionCastOp, 8> deadCasts;
func.walk([&](UnrealizedConversionCastOp castOp) {
if (!castOp.use_empty() || castOp.getNumOperands() != 1 ||
castOp.getNumResults() != 1)
return;

Type srcTy = castOp.getOperand(0).getType();
Type dstTy = castOp.getResult(0).getType();
if ((isa<MemRefType>(srcTy) && isPTOViewBridgeType(dstTy)) ||
(isPTOViewBridgeType(srcTy) && isa<MemRefType>(dstTy)))
deadCasts.push_back(castOp);
});

for (auto castOp : llvm::reverse(deadCasts))
castOp.erase();
return !deadCasts.empty();
}

static bool eraseDeadMemrefViewOps(func::FuncOp func) {
SmallVector<Operation *, 8> deadMemrefOps;
func.walk([&](Operation *op) {
if ((isa<memref::SubViewOp>(op) ||
isa<memref::ReinterpretCastOp>(op)) &&
op->use_empty())
deadMemrefOps.push_back(op);
});

for (Operation *op : llvm::reverse(deadMemrefOps))
op->erase();
return !deadMemrefOps.empty();
}

static bool eraseDeadTensorViewOps(func::FuncOp func) {
SmallVector<Operation *, 8> deadViewOps;
func.walk([&](Operation *op) {
if ((isa<pto::PartitionViewOp>(op) ||
isa<pto::MakeTensorViewOp>(op)) &&
op->use_empty())
deadViewOps.push_back(op);
});

for (Operation *op : llvm::reverse(deadViewOps))
op->erase();
return !deadViewOps.empty();
}

static void eraseDeadViewChains(func::FuncOp func) {
while (eraseDeadViewBridgeCasts(func) || eraseDeadMemrefViewOps(func) ||
eraseDeadTensorViewOps(func) || eraseDeadAllocTileOps(func)) {
}
}

struct TileHandleInfo {
Expand Down Expand Up @@ -728,34 +787,6 @@ struct FoldTileBufIntrinsicsPass
}
}

// Clean up dead unrealized_conversion_cast ops that bridged
// memref -> partition_tensor_view / tile_buf and are now unused
// after folding.
SmallVector<UnrealizedConversionCastOp, 8> deadCasts;
func.walk([&](UnrealizedConversionCastOp castOp) {
if (castOp.use_empty() && castOp.getNumOperands() == 1 &&
isa<MemRefType>(castOp.getOperand(0).getType()) &&
isa<pto::PartitionTensorViewType, pto::TileBufType>(
castOp.getResult(0).getType()))
deadCasts.push_back(castOp);
});
for (auto castOp : llvm::reverse(deadCasts))
castOp.erase();

while (true) {
SmallVector<Operation *, 8> deadMemrefOps;
func.walk([&](Operation *op) {
if ((isa<memref::SubViewOp>(op) ||
isa<memref::ReinterpretCastOp>(op)) &&
op->use_empty())
deadMemrefOps.push_back(op);
});
if (deadMemrefOps.empty())
break;
for (auto *op : llvm::reverse(deadMemrefOps))
op->erase();
}

// Erase pto.set_validshape ops. Every valid-shape reader
// (get_validshape / tile_valid_{rows,cols} / tile_buf_addr) has been
// folded above, so the runtime metadata writes have no remaining
Expand Down Expand Up @@ -788,7 +819,7 @@ struct FoldTileBufIntrinsicsPass
}
}

eraseDeadAllocTileOps(func);
eraseDeadViewChains(func);
}
};

Expand Down
6 changes: 4 additions & 2 deletions ptodsl/docs/user_guide/01-introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,10 @@ def my_kernel(
barriers by hand, and work with raw pointers — useful when you need to
hand-tune instruction schedules or overlap DMA with compute.

`mode` only affects what you can write inside the function body. It doesn't
change how you compile or launch the kernel.
For native launch builds, `mode` also selects the default PTOAS build policy:
`mode="auto"` keeps the PTOAS default build level and enables sync insertion,
while `mode="explicit"` uses `--pto-level=level3` and leaves synchronization
under user control by default.

#### `backend`: VPTO vs EmitC

Expand Down
6 changes: 4 additions & 2 deletions ptodsl/docs/user_guide/03-kernel-entry-and-subkernels.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@ The **`backend`** parameter selects the compilation target:
rejected at decoration time with an actionable diagnostic.

The **`mode`** parameter selects the programming model within the kernel body
(see Section 3.4). `mode` only affects what you can write inside the function —
it doesn't change how you compile or launch the kernel.
(see Section 3.4). For native launch builds, `mode="auto"` keeps PTOAS default
build level and enables sync insertion by default, while `mode="explicit"` uses
`--pto-level=level3` and disables sync insertion by default. This matches the
manual-address, user-managed staging contract of explicit kernels.

`@pto.jit` owns compilation (tracing + lowering), caching, and — for
`entry=True` — runtime launch binding. The compute-unit decorators
Expand Down
10 changes: 10 additions & 0 deletions ptodsl/ptodsl/_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,15 @@ def illegal_inline_subkernel_placement_error(role: str, outer_role: str | None)
)


def subkernel_kernel_kind_mismatch_error(role: str, kernel_kind: str) -> RuntimeError:
"""Return one diagnostic for mixing explicit @pto.jit kernel kind with the opposite subkernel kind."""
return RuntimeError(
f"@pto.{role} cannot be lowered inside an explicit @pto.jit(kernel_kind={kernel_kind!r}) "
"module. Remove the explicit kernel_kind so PTOAS can split cube/vector sections, "
"or keep subkernel scopes in the same physical kind."
)


def inline_subkernel_value_escape_error(role: str, type_text: str) -> RuntimeError:
"""Return one diagnostic for outlined inline-scope values escaping their helper boundary."""
return RuntimeError(
Expand Down Expand Up @@ -503,6 +512,7 @@ def unsupported_public_surface_error(name: str) -> AttributeError:
"subkernel_host_tensor_boundary_error",
"subkernel_illegal_annotation_error",
"subkernel_illegal_parameter_kind_error",
"subkernel_kernel_kind_mismatch_error",
"subkernel_missing_annotation_error",
"subkernel_signature_boundary_error",
"tile_row_alignment_error",
Expand Down
28 changes: 22 additions & 6 deletions ptodsl/ptodsl/_jit.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@
_MODULE_ATTRS = ("pto.target_arch",)
_SUPPORTED_FRONTEND_OPTION_KEYS = {"ast_rewrite", "rewrite_part", "dump_rewritten_source"}
_SUPPORTED_REWRITE_PARTS = {"control_flow"}
_DEFAULT_KERNEL_KIND = "vector"


class _DefaultKernelKindSentinel:
def __repr__(self) -> str:
return repr(_DEFAULT_KERNEL_KIND)


_DEFAULT_KERNEL_KIND_SENTINEL = _DefaultKernelKindSentinel()


def _normalize_mode(mode: str, *, fn=None) -> str:
Expand Down Expand Up @@ -164,7 +173,7 @@ def jit(
name=None,
*,
target: str = "a5",
kernel_kind: str = "vector",
kernel_kind: str = _DEFAULT_KERNEL_KIND_SENTINEL,
backend: str = "vpto",
entry: bool = True,
mode: str = "auto",
Expand All @@ -180,10 +189,10 @@ def jit(
----------
name: IR function name (defaults to the Python function name).
target: Target architecture string, e.g. ``"a5"``.
kernel_kind: authored default physical kind, used for native build selection
and VPTO authoring intent. PTODSL now expresses physical regions
through ``pto.section.vector/cube`` instead of child-module
``pto.kernel_kind`` attributes.
kernel_kind: optional authored physical kind, used for native build selection
and explicit single-kind VPTO authoring intent. When omitted,
PTODSL keeps the historical vector default while allowing
subkernel sections to express mixed cube/vector regions.
backend: ``"vpto"`` or ``"emitc"`` – records the intended backend.
entry: ``True`` for launchable kernel entries, ``False`` for helpers.
mode: ``"auto"`` or ``"explicit"`` – feeds child compile policy.
Expand Down Expand Up @@ -238,12 +247,15 @@ def decorator(fn):
source_file = inspect.getsourcefile(fn) or inspect.getfile(fn)
except (OSError, TypeError):
source_file = None
kernel_kind_explicit = kernel_kind is not _DEFAULT_KERNEL_KIND_SENTINEL
effective_kernel_kind = kernel_kind if kernel_kind_explicit else _DEFAULT_KERNEL_KIND
compiler = KernelCompiler(
fn.__name__,
KernelModuleSpec(
function_name=fn_name,
target_arch=target,
kernel_kind=kernel_kind,
kernel_kind=effective_kernel_kind,
kernel_kind_explicit=kernel_kind_explicit,
backend=normalized_backend,
entry=entry,
mode=normalized_mode,
Expand Down Expand Up @@ -307,6 +319,10 @@ def __ptodsl_cache_signature__(self):
self._compiler._kernel_identity,
module_spec.function_name,
module_spec.entry,
module_spec.backend,
module_spec.mode,
module_spec.kernel_kind,
module_spec.kernel_kind_explicit,
)

def _build_default_module(self):
Expand Down
4 changes: 4 additions & 0 deletions ptodsl/ptodsl/_runtime/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def write_manifest(
launch_symbol: str,
mlir_digest: str,
launch_cpp_digest: str,
compile_config_digest: str,
link_config_digest: str,
) -> None:
artifacts.cache_dir.mkdir(parents=True, exist_ok=True)
Expand All @@ -76,6 +77,7 @@ def write_manifest(
"shared_library": str(artifacts.shared_library),
"mlir_digest": mlir_digest,
"launch_cpp_digest": launch_cpp_digest,
"compile_config_digest": compile_config_digest,
"link_config_digest": link_config_digest,
}
artifacts.manifest_path.write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
Expand All @@ -90,6 +92,7 @@ def is_native_build_current(
*,
mlir_text: str,
launch_cpp_text: str,
compile_config_text: str,
link_config_text: str,
) -> bool:
required = (
Expand All @@ -110,6 +113,7 @@ def is_native_build_current(
return (
manifest.get("mlir_digest") == _content_digest(mlir_text)
and manifest.get("launch_cpp_digest") == _content_digest(launch_cpp_text)
and manifest.get("compile_config_digest") == _content_digest(compile_config_text)
and manifest.get("link_config_digest") == _content_digest(link_config_text)
)

Expand Down
51 changes: 42 additions & 9 deletions ptodsl/ptodsl/_runtime/native_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,34 @@ def _effective_insert_sync(*, mode: str, insert_sync: bool | None) -> bool:
return mode != "explicit"


def _effective_pto_level(*, mode: str) -> str | None:
return "level3" if mode == "explicit" else None


def _source_ptoas_overrides(module_spec) -> dict:
if getattr(module_spec, "jit_source", None) is None:
return {}
overrides = {"backend": module_spec.backend}
if module_spec.mode == "explicit":
overrides["pto_level"] = "level3"
return overrides
return {"backend": module_spec.backend}


def _compile_config_text(
*,
module_spec,
effective_insert_sync: bool,
effective_pto_level: str | None,
ptoas_overrides: dict,
) -> str:
return "\n".join(
[
f"target_arch={module_spec.target_arch}",
f"kernel_kind={module_spec.kernel_kind}",
f"mode={module_spec.mode}",
f"insert_sync={effective_insert_sync}",
f"pto_level={effective_pto_level}",
f"backend={ptoas_overrides.get('backend')}",
"enable_tile_op_expand=True",
]
)


def _host_compile_flags() -> list[str]:
Expand Down Expand Up @@ -191,13 +212,26 @@ def build_native_library(
ir_function_name=ir_function_name,
kernel_signature=kernel_signature,
)
effective_insert_sync = _effective_insert_sync(
mode=module_spec.mode,
insert_sync=module_spec.insert_sync,
)
effective_pto_level = _effective_pto_level(mode=module_spec.mode)
ptoas_overrides = _source_ptoas_overrides(module_spec)
compile_config_text = _compile_config_text(
module_spec=module_spec,
effective_insert_sync=effective_insert_sync,
effective_pto_level=effective_pto_level,
ptoas_overrides=ptoas_overrides,
)
sim_mode = bool(os.environ.get("MSPROF_SIMULATOR_MODE"))
link_config_text = "\n".join(runtime_library_flags(sim_mode=sim_mode))

if is_native_build_current(
artifacts,
mlir_text=mlir_text,
launch_cpp_text=launch_cpp_text,
compile_config_text=compile_config_text,
link_config_text=link_config_text,
):
return artifacts.shared_library, launch_symbol
Expand All @@ -210,11 +244,9 @@ def build_native_library(
artifacts.mlir_path,
artifacts.kernel_object,
target_arch=module_spec.target_arch,
insert_sync=_effective_insert_sync(
mode=module_spec.mode,
insert_sync=module_spec.insert_sync,
),
**_source_ptoas_overrides(module_spec),
insert_sync=effective_insert_sync,
pto_level=effective_pto_level,
**ptoas_overrides,
)

launch_object = artifacts.cache_dir / "launch.o"
Expand All @@ -237,6 +269,7 @@ def build_native_library(
launch_symbol=launch_symbol,
mlir_digest=_content_digest(mlir_text),
launch_cpp_digest=_content_digest(launch_cpp_text),
compile_config_digest=_content_digest(compile_config_text),
link_config_digest=_content_digest(link_config_text),
)
return artifacts.shared_library, launch_symbol
Expand Down
12 changes: 12 additions & 0 deletions ptodsl/ptodsl/_runtime/toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@


def resolve_ptoas_binary() -> Path:
env_override = os.environ.get("PTOAS_BIN")
if env_override:
candidate = Path(env_override)
if candidate.is_file():
return candidate
from_path = shutil.which(env_override)
if from_path:
return Path(from_path)
raise FileNotFoundError(
f"PTOAS_BIN is set but does not resolve to an existing executable: {env_override}"
)

repo_root = Path(__file__).resolve().parents[4]
candidates = [
repo_root / "build" / "tools" / "ptoas" / "ptoas",
Expand Down
1 change: 1 addition & 0 deletions ptodsl/ptodsl/_tracing/module_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class KernelModuleSpec:
function_name: str
target_arch: str
kernel_kind: str
kernel_kind_explicit: bool = False
backend: str = "vpto"
entry: bool = True
mode: str = "auto"
Expand Down
Loading
Loading