545 Commits

Author SHA1 Message Date
Tim Gymnich
16f41cb1b8
[mlir][amdgpu] Add Python bindings for TDM types (#172309)
Add bindings for:
- `TDMBaseType`
- `TDMDescriptorType`
- `TDMGatherBaseType`
2025-12-16 10:43:08 +00:00
Rolf Morel
f12fcf030c
[MLIR][Transform][Python] transform.foreach wrapper and .owner OpViews (#172228)
Friendlier wrapper for transform.foreach.

To facilitate that friendliness, makes it so that OpResult.owner returns
the relevant OpView instead of Operation. For good measure, also changes
Value.owner to return OpView instead of Operation, thereby ensuring
consistency. That is, makes it is so that all op-returning .owner
accessors return OpView (and thereby give access to all goodies
available on registered OpViews.)

Reland of #171544 due to fixup for integration test.
2025-12-14 22:10:31 +00:00
Mehdi Amini
b9fe6532a7
Revert "[MLIR][Transform][Python] transform.foreach wrapper and .owner OpViews" (#172225)
Reverts llvm/llvm-project#171544 ; bots are broken.
2025-12-14 21:27:02 +00:00
Rolf Morel
4cdec92827
[MLIR][Transform][Python] transform.foreach wrapper and .owner OpViews (#171544)
Friendlier wrapper for `transform.foreach`.

To facilitate that friendliness, makes it so that `OpResult.owner`
returns the relevant `OpView` instead of `Operation`. For good measure,
also changes `Value.owner` to return `OpView` instead of `Operation`,
thereby ensuring consistency. That is, makes it is so that all
op-returning `.owner` accessors return `OpView` (and thereby give access
to all goodies available on registered `OpView`s.)
2025-12-14 20:44:15 +00:00
Rolf Morel
b33354f272
[MLIR][Python][Transform] Print diagnostics also upon success (#172188)
If we do not collect the diagnostics from the
CollectDiagnosticsToStringScope, even when the named_sequence applied
successfully, the Scope object's destructor will assert (with a
unhelpful message).
2025-12-14 00:35:52 +00:00
Hongzheng Chen
86cc934b4a
[python] Expose replaceUsesOfWith C API (#171892)
This PR exposes the `replaceUsesOfWith` C API to Python
2025-12-11 16:09:18 -08:00
Mehdi Amini
d6e6661f69 [MLIR] Apply clang-tidy fixes for llvm-include-order in TransformInterpreter.cpp (NFC) 2025-12-10 01:24:05 -08:00
Tianqi Chen
11fd760e3a
[MLIR][ExecutionEngine] Enable PIC option (#170995)
This PR enables the MLIR execution engine to dump object file as PIC
code, which is needed when the object file is later bundled into a dynamic
shared library.

---------

Co-authored-by: Mehdi Amini <joker.eph@gmail.com>
2025-12-07 17:07:17 +00:00
Maksim Levental
e6d1deae3e
[MLIR][Python] make Sliceable inherit from Sequence (#170551)
Generates type stubs like

```python
class RegionSequence(Sequence[Region]):
    def __add__(self, arg: RegionSequence, /) -> list[Region]: ...

    def __iter__(self) -> RegionIterator:
        """Returns an iterator over the regions in the sequence."""
```
2025-12-05 05:41:55 -08:00
Ingo Müller
eb48a61cc1
[mlir:python] Add manual typing annotations to mlir.register_* functions. (#170627)
This PR adds a manual typing annotations to the `register_operation` and
`register_(type|value)_caster` functions in the main `mlir` module.
Since those functions return the result `nb::cpp_function`, which is of
type `nb::object`, the automatic typing annocations are of the form `def
f() -> object`. This isn't particularly precise and leads to type
checking errors when the functions are used. Manually defining the
annotation with `nb::sig` solves the problem.

Signed-off-by: Ingo Müller <ingomueller@google.com>
2025-12-05 08:29:05 +00:00
Benjamin Chetioui
012721d320
[mlir][python] Propagate error diagnostics when an op couldn't be created. (#169499) 2025-11-25 17:41:01 +00:00
Maksim Levental
740d0bd385
[MLIR][Python] add GetTypeID for llvm.struct_type and llvm.ptr and enable downcasting (#169383) 2025-11-24 18:39:15 +00:00
Jacques Pienaar
5ab49edde2
[mlir][py][c] Enable setting block arg locations. (#169033)
This enables changing the location of a block argument. Follows the
approach for updating type of block arg.
2025-11-21 13:31:46 +00:00
Sergei Lebedev
31536e6e9a
[MLIR] [Python] ir.Value is now generic in the type of the value it holds (#166148)
This makes it similar to `mlir::TypedValue` in the MLIR C++ API and
allows users to be more specific about the values they produce or
accept.

Co-authored-by: Maksim Levental <maksim.levental@gmail.com>
2025-11-13 13:23:40 +00:00
Bangtian Liu
a5a78d0bb4
[mlir][linalg][python] Add Python Bindings for Inferring Contraction Dimensions from Affine Maps (#167587)
This PR exposes `linalg::inferContractionDims(ArrayRef<AffineMap>)` to
Python, allowing users to infer contraction dimensions (batch/m/n/k)
directly from a list of affine maps without needing an operation.

---------

Signed-off-by: Bangtian Liu <liubangtian@gmail.com>
2025-11-12 13:35:04 -05:00
Maksim Levental
8346a772bc
[MLIR][Python] fix PyRegionList __iter__ (#167466)
Fixes https://github.com/llvm/llvm-project/issues/167455
2025-11-11 07:25:50 -08:00
Maksim Levental
2d381bf65d
[MLIR][Python] add/fix docstrings in IRCore (#167063)
This PR adds all the missing doc strings in IRCore.cpp. It also

1. Normalizes all doc strings to have proper punctuation;
2. Inlines non-duplicated docstrings which are currently at the top of
the source file (and thereby possibly out of sync).

Follow-up PRs will do the same for the rest of the modules/source files.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-10 15:44:08 +00:00
Maksim Levental
c05ce9b005
[MLIR][Python] fix getOwner to return (typed) nb::object instead of abstract PyOpView (#165053)
https://github.com/llvm/llvm-project/pull/157930 changed `nb::object
getOwner()` to `PyOpView getOwner()` which implicitly constructs the
generic OpView against from a (possibly) concrete OpView. This PR fixes
that.
2025-10-26 01:48:46 +00:00
Maksim Levental
5a112dedff
[MLIR][Python] expose translate_module_to_llvmir (#163881)
This PR exposes `translate_module_to_llvmir` in the Python bindings.
2025-10-20 09:14:52 -07:00
Ingo Müller
907335c00c
[mlir:python] Prevent crash in DenseElementsAttr. (#163564)
This PR fixes a crash in the `bf_getbuffer` implementation of
`PyDenseElementsAttribute` that occurred when an element type was not
supported, such as `bf16`. I believe that supportion `bf16` is not
possible with that protocol but that's out of the scope of this PR.
Previsouly, the code raised an `std::exception` out of `bf_getbuffer`
that nanobind does not catch (see also pybind/pybind11#3336). The PR
makes the function catch all `std::exception`s and manually raises a
Python exception instead.

Signed-off-by: Ingo Müller <ingomueller@google.com>
2025-10-20 15:13:00 +02:00
Shenghang Tsai
7be89bb07b
[MLIR] Fix typo of the word "pattern" in CAPI and docs (#163780)
This includes the rename from `mlirOpRewritePattenCreate` to `mlirOpRewritePatternCreate` in CAPI, and other typo fixes in docs and code comments.
2025-10-17 12:57:59 +08:00
Perry Gibson
35cd291427
[mlir][python] add dict-style to IR attributes (#163200)
It makes sense that Attribute dicts/maps should behave like dicts in the
Python bindings. Previously this was not the case.
2025-10-16 18:42:05 +01:00
Twice
06e2c78680
[MLIR][Python] Pass OpView subclasses instead of Operation in rewrite patterns (#163080)
This is a follow-up PR for #162699.

Currently, in the function where we define rewrite patterns, the `op` we
receive is of type `ir.Operation` rather than a specific `OpView` type
(such as `arith.AddIOp`). This means we can’t conveniently access
certain parts of the operation — for example, we need to use
`op.operands[0]` instead of `op.lhs`. The following example code
illustrates this situation.

```python
def to_muli(op, rewriter):
  # op is typed ir.Operation instead of arith.AddIOp
  pass

patterns.add(arith.AddIOp, to_muli)
```

In this PR, we convert the operation to its corresponding `OpView`
subclass before invoking the rewrite pattern callback, making it much
easier to write patterns.

---------

Co-authored-by: Maksim Levental <maksim.levental@gmail.com>
2025-10-13 11:56:57 +08:00
Twice
24ac5066dd
[MLIR][NFC] Clean up rewrite CAPI implementation and Python bindings (#162974)
This is a follow-up PR of #162699.

In this PR we clean CAPI and Python bindings of MLIR rewrite part by:
- remove all manually-defined `wrap`/`unwrap` functions;
- remove useless nanobind-defined Python class `RewritePattern`.
2025-10-11 19:11:20 +08:00
Twice
7aec3f2864
[MLIR][Python] Support Python-defined rewrite patterns (#162699)
This PR adds support for defining custom **`RewritePattern`**
implementations directly in the Python bindings.

Previously, users could define similar patterns using the PDL dialect’s
bindings. However, for more complex patterns, this often required
writing multiple Python callbacks as PDL native constraints or rewrite
functions, which made the overall logic less intuitive—though it could
be more performant than a pure Python implementation (especially for
simple patterns).

With this change, we introduce an additional, straightforward way to
define patterns purely in Python, complementing the existing PDL-based
approach.

### Example

```python
def to_muli(op, rewriter):
    with rewriter.ip:
        new_op = arith.muli(op.operands[0], op.operands[1], loc=op.location)
    rewriter.replace_op(op, new_op.owner)

with Context():
    patterns = RewritePatternSet()
    patterns.add(arith.AddIOp, to_muli) # a pattern that rewrites arith.addi to arith.muli
    frozen = patterns.freeze()

    module = ...
    apply_patterns_and_fold_greedily(module, frozen)
```

---------

Co-authored-by: Maksim Levental <maksim.levental@gmail.com>
2025-10-11 11:28:45 +08:00
Twice
d12b0e2539
[MLIR][Python] Expose PassManager::enableStatistics to CAPI and Python (#162591)
`PassManager::enableStatistics` seems currently missing in both C API
and Python bindings. So here we added them in this PR, which includes
the `PassDisplayMode` enum type and the `EnableStatistics` method.
2025-10-09 20:11:19 +08:00
Twice
b6bf196cdc
[MLIR][Python] Make the TypeID allocator globally defined in PassManager.add (#162594)
Previously, each time we called `PassManager.add(python_pass_callable)`,
a new `TypeID` allocator was created and never released afterward. This
approach could potentially lead to some issues. In this PR, we introduce
a global `TypeIDAllocator` that is shared across all `add` calls to
allocate IDs.
2025-10-09 19:18:53 +08:00
Twice
8181c3deae
[MLIR][Python] Expose the insertion point of pattern rewriter (#161001)
In [#160520](https://github.com/llvm/llvm-project/pull/160520), we
discussed the current limitations of PDL rewriting in Python (see [this
comment](https://github.com/llvm/llvm-project/pull/160520#issuecomment-3332326184)).
At the moment, we cannot create new operations in PDL native (python)
rewrite functions because the `PatternRewriter` APIs are not exposed.

This PR introduces bindings to retrieve the insertion point of the
`PatternRewriter`, enabling users to create new operations within Python
rewrite functions. With this capability, more complex rewrites e.g. with
branching and loops that involve op creations become possible.

---------

Co-authored-by: Maksim Levental <maksim.levental@gmail.com>
2025-10-05 11:12:11 +08:00
Maksim Levental
a3594cd644
[MLIR][Python] fixup Context and Location stubs and NanobindAdaptors (#161433)
add correct names for `NB_TYPE_CASTER(..., name)` so users of
`NanobindAdaptors.h` can generate the correct hints. Also fix a few
straggler stubs.
2025-10-02 08:18:47 -07:00
Maksim Levental
fea2cca4d6
[MLIR][Python] expose Operation::setLoc (#161594) 2025-10-01 21:57:10 -07:00
Mehdi Amini
0d0cc06afe [MLIR] Apply clang-tidy fixes for performance-unnecessary-value-param in Rewrite.cpp (NFC) 2025-10-01 04:50:14 -07:00
Billy Zhu
a615249d02
[MLIR][Python] Fix PDLResultList bindings (#161102)
Adds argument names to the method stubs for PDLResultList (from
https://github.com/llvm/llvm-project/pull/159926).
2025-09-29 09:11:47 -07:00
Maksim Levental
3834c5428d
[MLIR][Python] add unchecked gettors (#160954)
Some of the current gettors require passing locations (i.e., there be an
active location) because they're using the "checked" APIs. This PR adds
"unchecked" gettors which only require an active context.
2025-09-27 13:54:33 -05:00
Twice
440d6d0f78
[MLIR][Python] Add bindings for PDL constraint function registering (#160520)
This is a follow-up to #159926.

That PR (#159926) exposed native rewrite function registration in PDL
through the C API and Python, enabling use with
`pdl.apply_native_rewrite`.

In this PR, we add support for native constraint functions in PDL via
`pdl.apply_native_constraint`, further completing the PDL API.
2025-09-25 14:38:03 +08:00
Twice
b5daf76798
[MLIR][Python] Add bindings for PDL native rewrite function registering (#159926)
In the MLIR Python bindings, we can currently use PDL to define simple
patterns and then execute them with the greedy rewrite driver. However,
when dealing with more complex patterns—such as constant folding for
integer addition—we find that we need `apply_native_rewrite` to actually
perform arithmetic (i.e., compute the sum of two constants). For
example, consider the following PDL pseudocode:

```mlir
pdl.pattern : benefit(1) {
  %a0 = pdl.attribute
  %a1 = pdl.attribute
  %c0 = pdl.operation "arith.constant" {value = %a0}
  %c1 = pdl.operation "arith.constant" {value = %a1}

  %op = pdl.operation "arith.addi"(%c0, %c1)

  %sum = pdl.apply_native_rewrite "addIntegers"(%a0, %a1)
  %new_cst = pdl.operation "arith.constant" {value = %sum}

  pdl.replace %op with %new_cst
}
```

Here, `addIntegers` cannot be expressed in PDL alone—it requires a
*native rewrite function*. This PR introduces a mechanism to support
exactly that, allowing complex rewrite patterns to be expressed in
Python and enabling many passes to be implemented directly in Python as
well.

As a test case, we defined two new operations (`myint.constant` and
`myint.add`) in Python and implemented a constant-folding rewrite
pattern for them. The core code looks like this:

```python
m = Module.create()
with InsertionPoint(m.body):

    @pdl.pattern(benefit=1, sym_name="myint_add_fold")
    def pat():
        ...
        op0 = pdl.OperationOp(name="myint.add", args=[v0, v1], types=[t])

        @pdl.rewrite()
        def rew():
            sum = pdl.apply_native_rewrite(
                [pdl.AttributeType.get()], "add_fold", [a0, a1]
            )
            newOp = pdl.OperationOp(
                name="myint.constant", attributes={"value": sum}, types=[t]
            )
            pdl.ReplaceOp(op0, with_op=newOp)

def add_fold(rewriter, results, values):
    a0, a1 = values
    results.push_back(IntegerAttr.get(i32, a0.value + a1.value))

pdl_module = PDLModule(m)
pdl_module.register_rewrite_function("add_fold", add_fold)
```

The idea is previously discussed in Discord #mlir-python channel with
@makslevental.

---------

Co-authored-by: Maksim Levental <maksim.levental@gmail.com>
2025-09-24 09:17:24 +08:00
Maksim Levental
0d08ffd22c
[MLIR][Python] use nb::typed for return signatures (#160221)
https://github.com/llvm/llvm-project/pull/160183 removed `nb::typed`
annotation to fix bazel but it turned out to be simply a matter of not
using the correct version of nanobind (see
https://github.com/llvm/llvm-project/pull/160183#issuecomment-3321429155).
This PR restores those annotations but (mostly) moves to the return
positions of the actual methods.
2025-09-23 10:54:22 -07:00
Maksim Levental
4a9df48cf8
[MLIR][Python] restore APIs in terms of Mlir* types (#160203)
https://github.com/llvm/llvm-project/pull/157930 changed a few APIs from
`Mlir*` to `Py*` and broke users that were using them (see
https://github.com/llvm/llvm-project/pull/160183#issuecomment-3321383969).
This PR restores those APIs.
2025-09-22 15:00:57 -07:00
Maksim Levental
81cbd970cf
[MLIR][Python] remove nb::typed to fix bazel build (#160183)
https://github.com/llvm/llvm-project/pull/157930 broke bazel build (see
https://github.com/llvm/llvm-project/pull/157930#issuecomment-3318681217)
because bazel is stricter on implicit conversions (some difference in
flags passed to clang). This PR fixes by moving/removing `nb::typed`.

EDIT: and also the overlay...
2025-09-22 12:55:43 -07:00
Rolf Morel
d8b84be107
[MLIR][Transform][SMT] Introduce transform.smt.constrain_params (#159450)
Introduces a Transform-dialect SMT-extension so that we can have an op
to express constrains on Transform-dialect params, in particular when
these params are knobs -- see transform.tune.knob -- and can hence be
seen as symbolic variables. This op allows expressing joint constraints
over multiple params/knobs together.

While the op's semantics are clearly defined, per SMTLIB, the interpreted
semantics -- i.e. the `apply()` method -- for now just defaults to failure. In
the future we should support attaching an implementation so that users
can Bring Your Own Solver and thereby control performance of 
interpreting the op. For now the main usage is to walk schedule IR and 
collect these constraints so that knobs can be rewritten to constants that
satisfy the constraints.
2025-09-21 20:32:45 +00:00
Maksim Levental
efd96afedf
[MLIR][Python] reland (narrower) type stub generation (#157930)
This a reland of https://github.com/llvm/llvm-project/pull/155741 which
was reverted at https://github.com/llvm/llvm-project/pull/157831. This
version is narrower in scope - it only turns on automatic stub
generation for `MLIRPythonExtension.Core._mlir` and **does not do
anything automatically**. Specifically, the only CMake code added to
`AddMLIRPython.cmake` is the `mlir_generate_type_stubs` function which
is then used only in a manual way. The API for
`mlir_generate_type_stubs` is:

```
Arguments:
  MODULE_NAME: The fully-qualified name of the extension module (used for importing in python).
  DEPENDS_TARGETS: List of targets these type stubs depend on being built; usually corresponding to the
    specific extension module (e.g., something like StandalonePythonModules.extension._standaloneDialectsNanobind.dso)
    and the core bindings extension module (e.g., something like StandalonePythonModules.extension._mlir.dso).
  OUTPUT_DIR: The root output directory to emit the type stubs into.
  OUTPUTS: List of expected outputs.
  DEPENDS_TARGET_SRC_DEPS: List of cpp sources for extension library (for generating a DEPFILE).
  IMPORT_PATHS: List of paths to add to PYTHONPATH for stubgen.
  PATTERN_FILE: (Optional) Pattern file (see https://nanobind.readthedocs.io/en/latest/typing.html#pattern-files).
Outputs:
  NB_STUBGEN_CUSTOM_TARGET: The target corresponding to generation which other targets can depend on.
```

Downstream users should use `mlir_generate_type_stubs` in coordination
with `declare_mlir_python_sources` to turn on stub generation for their
own downstream dialect extensions and upstream dialect extensions if
they so choose. Standalone example shows an example.

Note, downstream will also need to set
`-DMLIR_PYTHON_PACKAGE_PREFIX=...` correctly for their bindings.
2025-09-20 18:47:32 +00:00
Maksim Levental
67f43c6ee2
[MLIR][Python] add type hints for accessors (#158455)
This PR adds type hints for accessors in the generated builders.
2025-09-18 21:12:35 -05:00
Twice
e5114a2016
[MLIR][Python] Add python bindings for IRDL dialect (#158488)
In this PR we add basic python bindings for IRDL dialect, so that python
users can create and load IRDL dialects in python. This allows users, to
some extent, to define dialects in Python without having to modify
MLIR’s CMake/TableGen/C++ code and rebuild, making prototyping more
convenient.

A basic example is shown below (and also in the added test case):
```python
# create a module with IRDL dialects
module = Module.create()
with InsertionPoint(module.body):
  dialect = irdl.DialectOp("irdl_test")
  with InsertionPoint(dialect.body):
    op = irdl.OperationOp("test_op")
    with InsertionPoint(op.body):
      f32 = irdl.is_(TypeAttr.get(F32Type.get()))
      irdl.operands_([f32], ["input"], [irdl.Variadicity.single])

# load the module
irdl.load_dialects(module)

# use the op defined in IRDL
m = Module.parse("""
  module {
    %a = arith.constant 1.0 : f32
    "irdl_test.test_op"(%a) : (f32) -> ()
  }
""")
```
2025-09-19 10:10:39 +08:00
Maksim Levental
6a4f66476f
[MLIR][Python] restore liveModuleMap (#158506)
There are cases where the same module can have multiple references (via
`PyModule::forModule` via `PyModule::createFromCapsule`) and thus when
`PyModule`s get gc'd `mlirModuleDestroy` can get called multiple times
for the same actual underlying `mlir::Module` (i.e., double free). So we
do actually need a "liveness map" for modules.

Note, if `type_caster<MlirModule>::from_cpp` weren't a thing we could guarantree
this never happened except explicitly when users called `PyModule::createFromCapsule`.
2025-09-15 06:45:30 +02:00
Twice
7123463ef9
[MLIR][Python] Add the ability to signal pass failures in python-defined passes (#157613)
This is a follow-up PR for #156000.

In this PR we add the ability to signal pass failures
(`signal_pass_failure()`) in python-defined passes.

To achieve this, we expose `MlirExternalPass` via `nb::class_` with a
method `signal_pass_failure()`, and the callable passed to `pm.add(..)`
now accepts two arguments (`op: MlirOperation, pass_:
MlirExternalPass`).

For example:
```python
def custom_pass_that_fails(op, pass_):
    if some_condition:
        pass_.signal_pass_failure()
    # do something
```
2025-09-09 08:05:39 -07:00
Sergei Lebedev
80c2da6372
[MLIR] [Python] Added a context manager for enabling traceback-based locations (#157562)
Previously this functionality was not surfaced in the public API.
2025-09-09 06:29:06 -07:00
Twice
7d04e37904
[MLIR][Python] Support Python-defined passes in MLIR (#156000)
It closes #155996.

This PR added a method `add(callable, ..)` to
`mlir.passmanager.PassManager` to accept a callable object for defining
passes in the Python side.

This is a simple example of a Python-defined pass.
```python
from mlir.passmanager import PassManager

def demo_pass_1(op):
    # do something with op
    pass

class DemoPass:
    def __init__(self, ...):
        pass
    def __call__(op):
        # do something
        pass

demo_pass_2 = DemoPass(..)

pm = PassManager('any', ctx)
pm.add(demo_pass_1)
pm.add(demo_pass_2)
pm.add("registered-passes")
pm.run(..)
```

---------

Co-authored-by: cnb.bsD2OPwAgEA <QejD2DJ2eEahUVy6Zg0aZI+cnb.bsD2OPwAgEA@noreply.cnb.cool>
Co-authored-by: Maksim Levental <maksim.levental@gmail.com>
2025-09-08 18:01:23 -07:00
Maksim Levental
c4181e51d1
[MLIR][Python] remove unnecessary arg.none() = nb::none() pattern (#157519)
We have `arg.none() = nb::none()` in a lot of places but this is no
longer necessary (as of
~[2022](62a23bb87b)).
2025-09-08 12:16:35 -07:00
Twice
aac4eb5c3c
[MLIR][Python] Add a python function to apply patterns with MlirOperation (#157487)
In https://github.com/llvm/llvm-project/pull/94714, we add a python
function `apply_patterns_and_fold_greedily` which accepts an
`MlirModule` as the argument type. However, sometimes we want to apply
patterns with an `MlirOperation` argument, and there is currently no
python API to convert an `MlirOperation` to `MlirModule`.

So here we overload this function `apply_patterns_and_fold_greedily` to
do this (also a corresponding new C API
`mlirApplyPatternsAndFoldGreedilyWithOp`)
2025-09-08 16:05:45 +00:00
Maksim Levental
9a0ed70319
[MLIR][Python] bind InsertionPointAfter (#157156) 2025-09-05 14:30:40 -07:00
Maksim Levental
70a291f322
[MLIR][Python] fix operation hashing (#156514)
https://github.com/llvm/llvm-project/pull/155114 broke op hashing
(because the python objects ceased to be reference equivalent). This PR
fixes by binding `OperationEquivalence::computeHash`.
2025-09-02 15:12:25 -05:00