How to create an .mlir file using the mlir python package?

I am trying to set myself up with test files I can work on, so I figured I write a quick python program to create an .mlir file with well-defined graphs. When prompted for an mlir script, Gemini produces this:

import mlir

# Create a context
context = mlir.Context()

# Create a module
module = mlir.Module.create(context)

# Create a location (optional)
loc = mlir.Location.unknown(context)

# Define the input and output types
input_type = mlir.RankedTensorType.get([1, 10], mlir.F32Type())
weight_type = mlir.RankedTensorType.get([10, 20], mlir.F32Type())
bias_type = mlir.RankedTensorType.get([20], mlir.F32Type())
output_type = mlir.RankedTensorType.get([1, 20], mlir.F32Type())

# Create the dense layer operation
dense_op = mlir.Operation.create(
    "my_dialect.dense",
    [input_type, weight_type, bias_type],
    [output_type],
    loc
)

# Create the activation operation (e.g., ReLU)
relu_op = mlir.Operation.create(
    "my_dialect.relu",
    [output_type],
    [output_type],
    loc
)

# Add operations to the module
module.body.append(dense_op)
module.body.append(relu_op)

# Print the MLIR code
print(module)

which looks reasonable, short of the dialect, but this code is making nobody happy. I got mlir v0.0.1 from PyPi and it contains none of the functions referenced.

Is there documentation I can follow to accomplish my task of creating well-defined mlir test graphs?

Reading MLIR Python Bindings - MLIR

The doc guidance is that there is a python_packages directory created

But that does not appear to have occurred :

That’s not an official release (I have no clue whose release that is…). You need to build the package from source by doing -DMLIR_ENABLE_BINDINGS_PYTHON=ON. If you want a prebuilt package you can do

pip install mlir-python-bindings -f https://makslevental.github.io/wheels

(also not an official release but at least I know that person…).

2 Likes

Thank you for the guidance.

When I try to build the python bindings, I get many warnings of this kind:

warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc

That looks like CMake didn’t configure the build correctly.

Showstopper or innocuous?

I am on Windows 11, using the MSVC compiler

@makslevental I need a little more guidance as neither method is getting me unstuck.

When I try to point to the directory tools/mlir/python_packages/mlir_core via a PYTHONPATH, my environment complains that it isn’t a python package.

and when I installed the package from your repo:

it completed successfully, but it contains no symbols:

F:\Users\tomtz\dev\clones\esim\venv\Scripts\python.exe F:\Users\tomtz\dev\clones\esim\scripts\mlir\one_layer_test.py
Content of mlir package
[‘doc’, ‘file’, ‘loader’, ‘name’, ‘package’, ‘path’, ‘spec’]

Doesn’t make sense - PYTHONPATH sets a location where python packages themselves reside rather than a path to a particular package; mlir_core is correct for where you should be pointing that env variable.

You’re configuring CMake somehow such that our own /EHsc flags aren’t being passed here. Not innocuous but not a “showstopper” - I’m guessing you just won’t get the stacktrace if/when an exception is thrown.

There’s not enough information here to debug. Please post a complete stack trace somewhere like http://gist.github.com.

FYI your python is probably just “malformed” - here’s a short “hello world” in colab.

1 Like

You were correct, so many things weren’t lining up that ‘malformed’ was an understatement.

But enough of it started to work that I was able to produce this working example of a fully connected layer using the TOSA dialect:

import numpy as np
from mlir.ir import *
from mlir.dialects.func import *
from mlir.dialects.tosa import *
import mlir.execution_engine
from mlir.passmanager import PassManager

def create_fc_layer_ir(input_size=10, output_size=5):
    context = Context()
    
    # Create random weights and biases
    weights = np.random.randn(input_size, output_size).astype(np.float32)
    biases = np.random.randn(output_size).astype(np.float32)
    
    with context, Location.unknown():
        module = Module.create()
        
        # Create function type
        input_type = RankedTensorType.get([1, input_size], F32Type.get())
        output_type = RankedTensorType.get([1, output_size], F32Type.get())
        fn_type = FunctionType.get([input_type], [output_type])
        
        with InsertionPoint(module.body):
            @FuncOp.from_py_func(input_type)
            def fc_relu(arg):
                # Create constant for weights
                weight_type = RankedTensorType.get([input_size, output_size], F32Type.get())
                weight_attr = DenseElementsAttr.get(weights, type=weight_type)
                weight_const = Operation.create("tosa.const", 
                                             results=[weight_type],
                                             attributes={"value": weight_attr}).results[0]
                
                # Create constant for biases
                bias_type = RankedTensorType.get([output_size], F32Type.get())
                bias_attr = DenseElementsAttr.get(biases, type=bias_type)
                bias_const = Operation.create("tosa.const",
                                           results=[bias_type],
                                           attributes={"value": bias_attr}).results[0]
                
                # Create zero constant for ReLU
                zero_type = RankedTensorType.get([1, output_size], F32Type.get())
                zero_attr = DenseElementsAttr.get_splat(zero_type, FloatAttr.get(F32Type.get(), 0.0))
                zero_const = Operation.create("tosa.const",
                                           results=[zero_type],
                                           attributes={"value": zero_attr}).results[0]
                
                # Reshape bias for broadcasting
                reshape_out_type = RankedTensorType.get([1, output_size], F32Type.get())
                bias_shape = np.array([1, output_size], dtype=np.int64)
                bias_shape_attr = DenseElementsAttr.get(bias_shape)
                reshaped_bias = Operation.create(
                    "tosa.reshape",
                    results=[reshape_out_type],
                    operands=[bias_const],
                    attributes={"new_shape": bias_shape_attr}).results[0]
                
                # Matmul operation
                matmul_out_type = RankedTensorType.get([1, output_size], F32Type.get())
                matmul = Operation.create(
                    "tosa.matmul",
                    results=[matmul_out_type],
                    operands=[arg, weight_const]).results[0]
                
                # Add biases
                add_out_type = RankedTensorType.get([1, output_size], F32Type.get())
                add = Operation.create(
                    "tosa.add",
                    results=[add_out_type],
                    operands=[matmul, reshaped_bias]).results[0]
                
                # ReLU activation using maximum
                relu_out_type = RankedTensorType.get([1, output_size], F32Type.get())
                relu = Operation.create(
                    "tosa.maximum",
                    results=[relu_out_type],
                    operands=[add, zero_const]).results[0]
                
                return relu

    return module

def save_mlir_to_file(module, filename="fc_layer.mlir"):
    with open(filename, "w") as f:
        f.write(str(module))
    print(f"MLIR module has been written to {filename}")

if __name__ == "__main__":
    # Create the MLIR module
    module = create_fc_layer_ir(10, 5)
    
    # Save to file
    save_mlir_to_file(module)

Yay, thank you for your time and guidance, that got me motivated and unstuck. Thank you, thank you.

1 Like

No prob. Glad it worked. FYI the LLVM Discord is a way to get realtime help in the future (feel free to DM me under this same username).

1 Like