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
5 changes: 3 additions & 2 deletions include/PTO/IR/PTOOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -6921,7 +6921,8 @@ def TPrintOp: PTO_TOp<"tprint", [
]> {
let summary = "TPRINT: Print the contents of a Tile or GlobalTensor for debugging purposes directly from device code.";
let description = [{
pto-isa overloads support `TPRINT(src)` and `TPRINT<Format>(src, tmp)`.
pto-isa debug wrappers support `TPRINT(src)` and
`TPRINT<Format>(src, tmp)`.
The optional tmp operand is used when printing Mat/Acc tiles through a
scratch GlobalTensor, while Vec tiles and GlobalTensors can be printed
without tmp.
Expand All @@ -6939,7 +6940,7 @@ def TPrintOp: PTO_TOp<"tprint", [
let hasCustomAssemblyFormat = 1;

let extraClassDeclaration = [{
::mlir::pto::PIPE getPipe() { return ::mlir::pto::PIPE::PIPE_V; }
::mlir::pto::PIPE getPipe() { return ::mlir::pto::PIPE::PIPE_S; }
}];
}

Expand Down
2 changes: 1 addition & 1 deletion include/PTO/Transforms/TileOpExpansionUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace mlir::pto {
inline bool isTileLibExpandableOp(Operation *op) {
if (!op || !isa<TileOpInterface>(op))
return false;
return !isa<TReshapeOp, TSyncOp, TAllocToAivOp, TAllocToAicOp,
return !isa<TReshapeOp, TPrintOp, TSyncOp, TAllocToAivOp, TAllocToAicOp,
TPushToAivOp, TPushToAicOp, TPopFromAicOp, TPopFromAivOp,
TFreeFromAicOp, TFreeFromAivOp>(op);
}
Expand Down
3 changes: 3 additions & 0 deletions include/pto-c/Dialect/PTO.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ MLIR_CAPI_EXPORTED int32_t mlirPTOSqrtPrecisionAttrGetValue(MlirAttribute attr);
MLIR_CAPI_EXPORTED MlirAttribute mlirPTOFmodPrecisionAttrGet(MlirContext ctx, int32_t value);
MLIR_CAPI_EXPORTED bool mlirPTOAttrIsAFmodPrecisionAttr(MlirAttribute attr);
MLIR_CAPI_EXPORTED int32_t mlirPTOFmodPrecisionAttrGetValue(MlirAttribute attr);
MLIR_CAPI_EXPORTED MlirAttribute mlirPTOPrintFormatAttrGet(MlirContext ctx, int32_t value);
MLIR_CAPI_EXPORTED bool mlirPTOAttrIsAPrintFormatAttr(MlirAttribute attr);
MLIR_CAPI_EXPORTED int32_t mlirPTOPrintFormatAttrGetValue(MlirAttribute attr);
MLIR_CAPI_EXPORTED MlirAttribute mlirPTOSaturationModeAttrGet(MlirContext ctx, int32_t value);
MLIR_CAPI_EXPORTED bool mlirPTOAttrIsASaturationModeAttr(MlirAttribute attr);
MLIR_CAPI_EXPORTED int32_t mlirPTOSaturationModeAttrGetValue(MlirAttribute attr);
Expand Down
9 changes: 9 additions & 0 deletions lib/Bindings/Python/PTOModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@ void mlir::pto::python::populatePTODialectBindings(pybind11::module_ &m) {
.value("Default", mlir::pto::FmodPrecision::Default)
.value("HighPrecision", mlir::pto::FmodPrecision::HighPrecision);

py::enum_<mlir::pto::PrintFormat>(m, "PrintFormat")
.value("Width8_Precision4", mlir::pto::PrintFormat::Width8_Precision4)
.value("Width8_Precision2", mlir::pto::PrintFormat::Width8_Precision2)
.value("Width10_Precision6", mlir::pto::PrintFormat::Width10_Precision6);

py::enum_<mlir::pto::SaturationMode>(m, "SaturationMode")
.value("ON", mlir::pto::SaturationMode::ON)
.value("OFF", mlir::pto::SaturationMode::OFF);
Expand Down Expand Up @@ -578,6 +583,10 @@ void mlir::pto::python::populatePTODialectBindings(pybind11::module_ &m) {
mlirPTOAttrIsAFmodPrecisionAttr,
mlirPTOFmodPrecisionAttrGet,
mlirPTOFmodPrecisionAttrGetValue);
bindPTOEnumAttr(m, "PrintFormatAttr", "PrintFormat",
mlirPTOAttrIsAPrintFormatAttr,
mlirPTOPrintFormatAttrGet,
mlirPTOPrintFormatAttrGetValue);

mlir_attribute_subclass(
m, "SaturationModeAttr",
Expand Down
1 change: 1 addition & 0 deletions lib/CAPI/Dialect/PTO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ DEFINE_PTO_ENUM_ATTR_CAPI(RemPrecision, RemPrecisionAttr, RemPrecision)
DEFINE_PTO_ENUM_ATTR_CAPI(RsqrtPrecision, RsqrtPrecisionAttr, RsqrtPrecision)
DEFINE_PTO_ENUM_ATTR_CAPI(SqrtPrecision, SqrtPrecisionAttr, SqrtPrecision)
DEFINE_PTO_ENUM_ATTR_CAPI(FmodPrecision, FmodPrecisionAttr, FmodPrecision)
DEFINE_PTO_ENUM_ATTR_CAPI(PrintFormat, PrintFormatAttr, PrintFormat)

#undef DEFINE_PTO_ENUM_ATTR_CAPI

Expand Down
2 changes: 2 additions & 0 deletions lib/PTO/IR/PTO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12733,10 +12733,12 @@ void mlir::pto::TPrintOp::print(OpAsmPrinter &p) {
mlir::LogicalResult mlir::pto::TPrintOp::verify() {
auto srcType = getSrc().getType();
Value tmp = getTPrintTmpIfPresent(*this);

auto printFormatAttr =
dyn_cast_or_null<pto::PrintFormatAttr>(getProperties().printFormat);
if (printFormatAttr && !tmp)
return emitOpError() << "expects printFormat only when tmp is present";

if (auto tb = mlir::dyn_cast<mlir::pto::TileBufType>(srcType)) {
auto elem = tb.getElementType();
if (!(elem.isF16() || elem.isF32() ||
Expand Down
15 changes: 15 additions & 0 deletions lib/PTO/Transforms/ExpandTileOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1257,6 +1257,21 @@ void ExpandTileOpPass::runOnOperation() {
ModuleOp mod = getOperation();
MLIRContext *ctx = &getContext();

bool isVPTOBackend = false;
if (auto backend = mod->getAttrOfType<StringAttr>("pto.backend"))
isVPTOBackend = backend.getValue() == "vpto";
if (isVPTOBackend) {
WalkResult unsupportedTPrint = mod.walk([&](pto::TPrintOp op) {
op.emitError("ExpandTileOp: pto.tprint is only supported by the "
"EmitC backend; VPTO lowering for TPRINT is not implemented");
return WalkResult::interrupt();
});
if (unsupportedTPrint.wasInterrupted()) {
signalPassFailure();
return;
}
}

bool hasExpandableOps = false;
mod.walk([&](Operation *op) {
if (pto::isTileLibExpandableOp(op)) {
Expand Down
102 changes: 75 additions & 27 deletions lib/PTO/Transforms/PTOToEmitC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13716,34 +13716,82 @@ struct EmitPTOManualPass
return signalPassFailure();
}

bool needsEventIdArrayHelper = false;
bool needsTRandomHelper = false;
bool needsGlobalTensorDataHelper = false;
mop.walk([&](Operation *op) {
if (isa<mlir::pto::DeclareEventIdArrayOp>(op))
needsEventIdArrayHelper = true;
if (isa<mlir::pto::TRandomOp>(op))
needsTRandomHelper = true;
if (auto cmo = dyn_cast<mlir::pto::CmoCacheInvalidOp>(op)) {
if (cmo.getAddr())
needsGlobalTensorDataHelper = true;
}
if (auto init = dyn_cast<mlir::pto::InitializeL2G2LPipeOp>(op)) {
if (isa<mlir::pto::TensorViewType>(init.getGmAddr().getType()))
needsGlobalTensorDataHelper = true;
}
if (isa<mlir::pto::PartitionViewOp>(op))
needsGlobalTensorDataHelper = true;
});
bool needsEventIdArrayHelper = false;
bool needsTRandomHelper = false;
bool needsGlobalTensorDataHelper = false;
bool needsTPrintInclude = false;
mop.walk([&](Operation *op) {
if (isa<mlir::pto::DeclareEventIdArrayOp>(op))
needsEventIdArrayHelper = true;
if (isa<mlir::pto::TRandomOp>(op))
needsTRandomHelper = true;
if (isa<mlir::pto::TPrintOp>(op))
needsTPrintInclude = true;
if (auto cmo = dyn_cast<mlir::pto::CmoCacheInvalidOp>(op)) {
if (cmo.getAddr())
needsGlobalTensorDataHelper = true;
}
if (auto init = dyn_cast<mlir::pto::InitializeL2G2LPipeOp>(op)) {
if (isa<mlir::pto::TensorViewType>(init.getGmAddr().getType()))
needsGlobalTensorDataHelper = true;
}
if (isa<mlir::pto::PartitionViewOp>(op))
needsGlobalTensorDataHelper = true;
});

// 1. 插入头文件
auto loc = mop->getLoc();
OpBuilder builder(ctx);
builder.setInsertionPointToStart(mop.getBody());
builder.create<emitc::IncludeOp>(
loc, "pto/pto-inst.hpp", /*is_standard_include=*/false);
builder.create<emitc::VerbatimOp>(
loc, builder.getStringAttr("using namespace pto;"));
auto loc = mop->getLoc();
OpBuilder builder(ctx);
builder.setInsertionPointToStart(mop.getBody());
if (needsTPrintInclude) {
// CANN 9.1 asc_printf.h defines a global conditional helper that can
// collide with std::conditional through unqualified lookup. Keep the
// workaround scoped to that include.
builder.create<emitc::IncludeOp>(
loc, "cstdint", /*is_standard_include=*/true);
builder.create<emitc::IncludeOp>(
loc, "type_traits", /*is_standard_include=*/true);
builder.create<emitc::VerbatimOp>(
loc, builder.getStringAttr(
"#define conditional PTOAS_ASC_PRINTF_CONDITIONAL"));
builder.create<emitc::IncludeOp>(
loc, "utils/debug/asc_printf.h",
/*is_standard_include=*/false);
builder.create<emitc::VerbatimOp>(
loc, builder.getStringAttr("#undef conditional"));
}
builder.create<emitc::IncludeOp>(
loc, "pto/pto-inst.hpp", /*is_standard_include=*/false);
if (needsTPrintInclude) {
builder.create<emitc::VerbatimOp>(
loc, builder.getStringAttr(R"cpp(namespace cce {
template <class... Args>
AICORE inline void printf(const __gm__ char *fmt, Args &&...args) {
__asc_aicore::printf(fmt, args...);
}
})cpp"));
builder.create<emitc::IncludeOp>(
loc,
targetArch == PTOArch::A5 ? "pto/npu/a5/TPrint.hpp"
: "pto/npu/a2a3/TPrint.hpp",
/*is_standard_include=*/false);
builder.create<emitc::VerbatimOp>(
loc, builder.getStringAttr(R"cpp(#if !defined(_DEBUG) && !defined(__CPU_SIM)
template <pto::PrintFormat Format = pto::PrintFormat::Width8_Precision4,
typename TileData>
AICORE inline void TPRINT(TileData &src) {
TPRINT_IMPL<Format>(src);
}

template <pto::PrintFormat Format = pto::PrintFormat::Width8_Precision4,
typename TileData, typename GlobalData>
AICORE inline void TPRINT(TileData &src, GlobalData &tmp) {
TPRINT_IMPL<Format>(src, tmp);
}
#endif
)cpp"));
}
builder.create<emitc::VerbatimOp>(
loc, builder.getStringAttr("using namespace pto;"));

// Emit a C++ definition for every !pto.struct used in the module, in
// dependency order (nested structs first) so there is no
Expand Down
32 changes: 32 additions & 0 deletions ptodsl/docs/user_guide/04-type-system-and-buffer.md
Original file line number Diff line number Diff line change
Expand Up @@ -382,3 +382,35 @@ scratch = pto.alloc_buffer((32,), pto.f32)
| `dtype` | Element type of the returned buffer, such as `pto.f32` or `pto.i32`. |

The returned value wraps the buffer address together with its allocation metadata: shape, dtype, element type, element count, and byte size.

## 4.11 Tile Debug Print

`pto.tile.print` emits a device-side debug print of a tile's contents through the PTO-ISA `TPRINT` wrapper. It is a pure side effect: it produces no value and writes nothing back to a tile, so the call result is `None` and the op is not fusible. Use it only for debugging. It is currently supported only by the EmitC backend, where it lowers directly to a native `TPRINT` device call and is skipped by tile-op expansion.

#### `pto.tile.print(src: Tile, *, tmp: View | None = None, print_format: str | pto.PrintFormat | None = None) -> None`

**Description**: Prints `src` from device code. A Unified-Buffer (`vec`) tile prints directly; an accumulator tile may pass a scratch GlobalTensor `tmp` to stage the copy to GM before printing. `print_format` maps to the C++ `TPRINT<Format>(...)` template argument and requires `tmp`. Supported string values are `"width8_precision4"` (default), `"width8_precision2"`, and `"width10_precision6"`.

**Parameters**:

| Parameter | Type | Description |
|-----------|------|-------------|
| `src` | `Tile` | Tile to print. Printable element types are `f32`, `f16`, `i32`, `i16`, `i8` |
| `tmp` | `View` or `None` | Optional scratch GlobalTensor view (Acc-tile path). Default `None` — direct Vec print |
| `print_format` | `str`, `pto.PrintFormat`, or `None` | Optional print format used with `tmp`. Default `None` uses `width8_precision4` |

**Constraints**:

- **Vec tiles print without `tmp`**: a tile printed without `tmp` must live in the `vec` (UB) address space.
- **`tmp` is for formatted printing and Mat/Acc staging**: `print_format` requires `tmp`; on A5, `tmp`-based printing is supported for `vec`/`acc` tiles (Mat-tile printing with `tmp` is A2/A3 only).
- **Backend support**: supported by the EmitC backend. VPTO lowering for `TPRINT` is not implemented.
- **Hardware mapping**: executes on the **Scalar pipeline** (`PIPE_S`).

**Example** — load a tile from GM and print it for debugging:

```python
src_view = pto.make_tensor_view(src_ptr, shape=[rows, cols], strides=[cols, 1])
src_tile = pto.alloc_tile(shape=[rows, cols], dtype=pto.f32)
pto.tile.load(src_view, src_tile)
pto.tile.print(src_tile)
```
64 changes: 63 additions & 1 deletion ptodsl/ptodsl/_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,20 @@ def _current_target_arch():
return getattr(current_module_spec, "target_arch", None)


def _current_backend():
try:
from ._tracing.active import current_session
session = current_session()
except Exception:
return None
if session is None:
return None
current_module_spec = getattr(
session, "current_function_module_spec", session.module_spec
)
return getattr(current_module_spec, "backend", None)


def _require_target_arch(surface: str, allowed: set[str]):
target = _current_target_arch()
if target is None:
Expand Down Expand Up @@ -3439,6 +3453,54 @@ def _tile_numel(shape, *, context: str):
return numel


_PRINT_FORMAT_ALIASES = {
"width8_precision4": "Width8_Precision4",
"width8_precision2": "Width8_Precision2",
"width10_precision6": "Width10_Precision6",
"Width8_Precision4": "Width8_Precision4",
"Width8_Precision2": "Width8_Precision2",
"Width10_Precision6": "Width10_Precision6",
}


def _coerce_print_format(print_format):
if print_format is None:
return None
if isinstance(print_format, Attribute):
return print_format
if isinstance(print_format, str):
enum_name = _PRINT_FORMAT_ALIASES.get(print_format)
if enum_name is None:
expected = ", ".join(sorted(_PRINT_FORMAT_ALIASES))
raise ValueError(f"pto.tile.print(print_format=...) expected one of: {expected}")
print_format = getattr(_pto.PrintFormat, enum_name)
return _pto.PrintFormatAttr.get(print_format)


def tprint(src, *, tmp=None, print_format=None):
"""``pto.tprint ins(src, tmp?)`` -- device-side debug print of a tile.

Pure side effect (``cce::printf``, no numeric result): a Vec tile prints
directly, while an Acc tile may pass a scratch GlobalTensor ``tmp`` to stage
the copy to GM. ``print_format`` requires ``tmp`` and accepts
``"width8_precision4"``, ``"width8_precision2"``,
``"width10_precision6"``, or ``pto.PrintFormat``.
"""
Comment thread
Crystal-wzy marked this conversation as resolved.
backend = _current_backend()
if backend == "vpto":
raise ValueError(
"pto.tile.print is only supported by the EmitC backend; "
"VPTO lowering for TPRINT is not implemented"
)
if print_format is not None and tmp is None:
raise ValueError("pto.tile.print(print_format=...) requires tmp")
_pto.tprint(
unwrap_surface_value(src),
tmp=None if tmp is None else unwrap_surface_value(tmp),
print_format=_coerce_print_format(print_format),
)


def treshape(src, *, shape, dtype=None, blayout=None):
"""``pto.treshape ins(src) -> result``."""
src_value = unwrap_surface_value(src)
Expand Down Expand Up @@ -6341,7 +6403,7 @@ def import_reserved_buffer(name, *, peer_func):
"trowsum", "trowmax", "trowmin", "trowprod", "trowargmax", "trowargmin",
"tcolsum", "tcolmax", "tcolmin", "tcolprod", "tcolargmax", "tcolargmin",
"tcmp", "tcmps",
"texpands", "treshape", "trowexpand", "tcolexpand",
"texpands", "tprint", "treshape", "trowexpand", "tcolexpand",
"trowexpandadd", "trowexpandsub", "trowexpandmul", "trowexpanddiv", "trowexpandmax", "trowexpandmin", "trowexpandexpdif",
"tcolexpandadd", "tcolexpandsub", "tcolexpandmul", "tcolexpanddiv", "tcolexpandmax", "tcolexpandmin", "tcolexpandexpdif",
"tsort32", "tmrgsort", "tgather", "tscatter",
Expand Down
8 changes: 8 additions & 0 deletions ptodsl/ptodsl/_runtime/native_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def _run_ptoas(
insert_sync: bool | None = None,
backend: str | None = None,
pto_level: str | None = None,
fatobj: bool = False,
) -> None:
ptoas = resolve_ptoas_binary()
cmd = [
Expand All @@ -59,6 +60,8 @@ def _run_ptoas(
cmd.append(f"--pto-level={pto_level}")
if insert_sync is True:
cmd.append("--enable-insert-sync")
if fatobj:
cmd.append("--fatobj")
cmd.extend([
"--enable-tile-op-expand",
str(mlir_path),
Expand Down Expand Up @@ -92,6 +95,7 @@ def _compile_config_text(
effective_insert_sync: bool,
effective_pto_level: str | None,
ptoas_overrides: dict,
fatobj: bool,
) -> str:
return "\n".join(
[
Expand All @@ -101,6 +105,7 @@ def _compile_config_text(
f"insert_sync={effective_insert_sync}",
f"pto_level={effective_pto_level}",
f"backend={ptoas_overrides.get('backend')}",
f"fatobj={fatobj}",
"enable_tile_op_expand=True",
]
)
Expand Down Expand Up @@ -219,11 +224,13 @@ def build_native_library(
)
effective_pto_level = _effective_pto_level(mode=module_spec.mode)
ptoas_overrides = _source_ptoas_overrides(module_spec)
fatobj = module_spec.backend == "emitc"
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,
fatobj=fatobj,
)
sim_mode = bool(os.environ.get("MSPROF_SIMULATOR_MODE"))
link_config_text = "\n".join(runtime_library_flags(sim_mode=sim_mode))
Expand All @@ -247,6 +254,7 @@ def build_native_library(
target_arch=module_spec.target_arch,
insert_sync=effective_insert_sync,
pto_level=effective_pto_level,
fatobj=fatobj,
**ptoas_overrides,
)

Expand Down
Loading
Loading