LLVM Discussion Forums

Ch5 Toy tutorial Extending toy dialect

Have added extra operations to the existing toy dialect, the MLIR is being generated successfully but not able to reduce it to affine affine, (also I have changed he namespace to use sim instead of toy). What are the steps to be taken to convert to affine after generating MLIR .

1 Like

I’d guess that currently your sim.max, sim.min, and sim.sum are being lowered to operations marked as having no side effects. MLIR also folds operations when executing rewrites as a low cost cleanup, so in this case, anything being generated is being deleted at some point because it’s unused.

To see what is actually being generated, you would want to return the 3 values being created from the function, so they aren’t dead code eliminated.

2 Likes

The tutorial is intended as a minimal example one can use to understand the workings of MLIR, not as a reusable component.

In this specific case, the tutorial has conversions to affine for binary operations and transpose here - https://github.com/llvm/llvm-project/blob/master/mlir/examples/toy/Ch5/mlir/LowerToAffineLoops.cpp#L98. You will have to write similar conversions for your operations and add them to conversion pass.

1 Like

Thank you that helped in resolving the errors

I was trying to add a conversion from the max function which i defined in the language
to an affine.max in the LowerToAffineLoops.cpp but was having trouble figuring out the parameters which affine.max takes.

code

affine.max is very unlikely the operation you want to lower to, it is intended for index computations with affine maps https://mlir.llvm.org/docs/Dialects/Affine/#affinemax-affinemaxop.

Any .create ultimately calls .build from the respective op after passing it an opbuilder and result info, here’s the relevant build interface https://github.com/llvm/llvm-project/blob/master/mlir/include/mlir/Dialect/Affine/IR/AffineOps.td#L445.

1 Like

Oh yes thank you affine.max isnt the operation I wanted, but is it possible to do the same (to find the max value in the memRefOperands) using an affine for, affine if and some operations in the std dialect (like std.cmpf) . If so how is it done since each operation is converted to one equivalent operation in another dialect using rewrite, how can I convert the max operation to a sequence of operations from the affine and std dialect.

min/max are implementable as a combination of std.cmpf/cmpi and std.select (https://mlir.llvm.org/docs/Rationale/Rationale/#select-operation-to-implement-minmax).

If you want to stay within affine loops, you need to write the result of min/max to memory (e.g., use a single-element memref). Something along the lines of:

%min = alloca() : memref<1xf32>
%infty = constant 0x7f800000 : f32  // float inf
%c0 = constant 0 : index
store %infty, %min[%c0] : memref<1xf32>
affine.for ... {
  %ld = load %min[%c0]
  %val = ...
  %cond = cmpf "olt" %ld, %val
  %res = select %cond, %ld, %val
  store %res, %min[%c0]
}

If you don’t need affine loops specifically, the SCF dialect has loops and conditionals that can yield values so you don’t have to pass through memory. But those loops cannot be handled by affine analyses.

The CMPFOp in the standard dialect doesn’t take any parameters for comparison, What is the way to compare 2 floating-point values. Any dialect having such operations in hand?


Getting “Bye World” as a result, Help needed in figuring out the right way to use CmpFOp, as according to documentation it has to take 2 floating points and a predicate as parameters?
Also, can we build standard dialect operations using the rewriter class defined in the toy dialect tutorial?

1 Like

What do you mean it “doesn’t take any parameters for comparison”? Here is the doc by the way: https://mlir.llvm.org/docs/Dialects/Standard/#stdcmpf-cmpfop

Alex showed above how to compare two floats and combine this with a select to implement a “max”:

  %cond = cmpf "olt" %ld, %val
  %res = select %cond, %ld, %val

To create the IR, you need to do like the AffineLoadOp in your screenshot and use the rewriter to create it:

CmpFOp cmpOp = rewriter.create<CmpFOp>(loc, ....);

Mehdi thank you that was helpful, Now I have the input of the from mlir::value type is it possible to have an iterate over this and pass values to CmpFOp to conduct the comparison operation ?. Also I need to include scf for loop for this how do i use this to iterate over that input and then do the comparison operation ?.

@AkhilJ99 have you been through all the chapters of the Toy tutorial? I would have expected it to cover such topic, if not it may be useful for us to understand how to improve it.

1 Like

Thank you Mehdi, I just went through the tutorial again it was helpful. This resolved some of the issues but had another question with respect to the index given to the memref object when storing. As Alex has mentioned above (Ch5 Toy tutorial Extending toy dialect) The index given while storing is an affine loop variable like %c0, is it possible to set it as a constant like %min[0] instead of %min[%c0] . If so how ?.

If you use affine.store, you can write affine.store %value, %min[0] directly. This is shorthand for "affine.store"(%value, %min) {map = affine_map<[]() -> (0)>} so the literal goes into the affine map, that’s why it is allowed.

There is no such shorthand for std.store because it does not take affine maps, and representing constants as values produced by a dedicated operation is an explicit design choice in MLIR.

As Alex previously hinted on using an affine map and an affine store respectively, we could create the affine map successfully but we couldn’t use it in the affine store as there was type mismatch.
We wanted to know how to create a temporary variable and assign a constant value
for ex :
temp = 0;
using std::StoreOp
the syntactical representation for the same given in the dialect is :
StoreOp(loc, temp, alloc, ValueRange)

temp: how to initialize temp as a constant value like 0
ValueRange: is it possible to store a single value like index to store the temp value onto the alloc[index].
alloc: in the given code snippet alloc is %0 and we want to initialize this before we proceed into the loops.
We have worked around this issue and have created a sum function which isn’t optimally sound

1 Like

(Please consider copy-pasting the IR into the message instead of inserting screenshots. People are sometimes reading these as emails on small screens.)

I’m not sure what is your question here. Answering by guess.

It is insufficient to have a variable in C++ to make a value magically appear in the IR. You must create the constant, just like any other instruction. Constants are defined by ConstantOp, I’m pretty sure it is mentioned in the tutorial. That would give you a Value for the constant. ValueRange is essentially a list of mlir::Value (or a range if you are familiar with C++20 terminology). Normally, ValueRange is implicitly constructible form a Value, but due to the combination of variadic templates and overload resolution, you may sometimes need to explicitly construct ValueRange from Value when calling .create.

1 Like

Thank you Alex and Mehdi that was helpful.