diff options
| author | gysit <gysit@google.com> | 2022-02-11 08:20:37 +0000 |
|---|---|---|
| committer | gysit <gysit@google.com> | 2022-02-11 08:27:49 +0000 |
| commit | a3655de2c81fc959590c109d81a010fc8e09c48e (patch) | |
| tree | 89200935cbcf800f2adfab6833fa081d812cefd0 /mlir/test/python/integration | |
| parent | b21f497a782e155b19f41ec48da7ef75e8120195 (diff) | |
[mlir][OpDSL] Add support for basic rank polymorphism.
Previously, OpDSL did not support rank polymorphism, which required a separate implementation of linalg.fill. This revision extends OpDSL to support rank polymorphism for a limited class of operations that access only scalars and tensors of rank zero. At operation instantiation time, it scales these scalar computations to multi-dimensional pointwise computations by replacing the empty indexing maps with identity index maps. The revision does not change the DSL itself, instead it adapts the Python emitter and the YAML generator to generate different indexing maps and and iterators depending on the rank of the first output.
Additionally, the revision introduces a `linalg.fill_tensor` operation that in a future revision shall replace the current handwritten `linalg.fill` operation. `linalg.fill_tensor` is thus only temporarily available and will be renamed to `linalg.fill`.
Reviewed By: nicolasvasilache, stellaraccident
Differential Revision: https://reviews.llvm.org/D119003
Diffstat (limited to 'mlir/test/python/integration')
| -rw-r--r-- | mlir/test/python/integration/dialects/linalg/opsrun.py | 121 |
1 files changed, 110 insertions, 11 deletions
diff --git a/mlir/test/python/integration/dialects/linalg/opsrun.py b/mlir/test/python/integration/dialects/linalg/opsrun.py index b75de1208550..5be00f8df333 100644 --- a/mlir/test/python/integration/dialects/linalg/opsrun.py +++ b/mlir/test/python/integration/dialects/linalg/opsrun.py @@ -43,12 +43,41 @@ func @main() -> f32 attributes {llvm.emit_c_interface} { fill_boiler = """ func @main() -> i32 attributes {llvm.emit_c_interface} { + %O0 = memref.alloc() : memref<i32> + %O1 = memref.alloc() : memref<16xi32> + %O2 = memref.alloc() : memref<4x16xi32> + + %val0 = arith.constant 1.0 : f32 + %val1 = arith.constant 2.0 : f32 + %val2 = arith.constant 3.0 : f32 + + call @fill_0d_on_buffers(%val0, %O0) : (f32, memref<i32>) -> () + call @fill_1d_on_buffers(%val1, %O1) : (f32, memref<16xi32>) -> () + call @fill_2d_on_buffers(%val2, %O2) : (f32, memref<4x16xi32>) -> () + + %c0 = arith.constant 0 : index + %res0 = memref.load %O0[] : memref<i32> + %c8 = arith.constant 8 : index + %res1 = memref.load %O1[%c8] : memref<16xi32> + %c2 = arith.constant 2 : index + %res2 = memref.load %O2[%c2, %c8] : memref<4x16xi32> + + %0 = arith.addi %res0, %res1 : i32 + %1 = arith.addi %0, %res2 : i32 + + // TODO: FFI-based solution to allow testing and printing with python code. + return %1 : i32 +} +""" + +fill_rng_boiler = """ +func @main() -> i32 attributes {llvm.emit_c_interface} { %O = memref.alloc() : memref<4x16xi32> %min = arith.constant -1000.0 : f64 %max = arith.constant 1000.0 : f64 %seed = arith.constant 42 : i32 - call @fill_on_buffers(%min, %max, %seed, %O) : + call @fill_rng_on_buffers(%min, %max, %seed, %O) : (f64, f64, i32, memref<4x16xi32>) -> () %c0 = arith.constant 0 : index @@ -123,9 +152,9 @@ def transform(module, boilerplate): # TODO: Allow cloning functions from one module to another. # Atm we have to resort to string concatenation. - mod = Module.parse( - str(module.operation.regions[0].blocks[0].operations[0].operation) + - boilerplate) + ops = module.operation.regions[0].blocks[0].operations + mod = Module.parse("\n".join([str(op) for op in ops]) + boilerplate) + pm = PassManager.parse( "builtin.func(convert-linalg-to-loops, lower-affine, " + "convert-scf-to-cf, arith-expand, memref-expand), convert-vector-to-llvm," + @@ -194,15 +223,85 @@ test_matmul_generic() def test_fill_builtin(): with Context() as ctx, Location.unknown(): module = Module.create() + f32 = F32Type.get() + i32 = IntegerType.get_signless(32) + with InsertionPoint(module.body): + + @builtin.FuncOp.from_py_func(f32, MemRefType.get([], i32)) + def fill_0d_on_buffers(value, out): + linalg.fill_tensor(value, outs=[out]) + + @builtin.FuncOp.from_py_func(f32, MemRefType.get([16], i32)) + def fill_1d_on_buffers(value, out): + linalg.fill_tensor(value, outs=[out]) + + @builtin.FuncOp.from_py_func(f32, MemRefType.get([4, 16], i32)) + def fill_2d_on_buffers(value, out): + linalg.fill_tensor(value, outs=[out]) + + execution_engine = ExecutionEngine(transform(module, fill_boiler)) + + # TODO: FFI-based solution to allow testing and printing with python code. + # Prepare arguments: one result i32. + # Arguments must be passed as pointers. + c_int_p = ctypes.c_int * 1 + res = c_int_p(-1) + execution_engine.invoke("main", res) + + log("RESULT: ", res[0]) + # CHECK: RESULT: 6 + + +test_fill_builtin() + + +def test_fill_generic(): + with Context() as ctx, Location.unknown(): + module = Module.create() + f32 = F32Type.get() + i32 = IntegerType.get_signless(32) + with InsertionPoint(module.body): + + @builtin.FuncOp.from_py_func(f32, MemRefType.get([], i32)) + def fill_0d_on_buffers(value, out): + linalg.fill_tensor(value, outs=[out], emit_generic=True) + + @builtin.FuncOp.from_py_func(f32, MemRefType.get([16], i32)) + def fill_1d_on_buffers(value, out): + linalg.fill_tensor(value, outs=[out], emit_generic=True) + + @builtin.FuncOp.from_py_func(f32, MemRefType.get([4, 16], i32)) + def fill_2d_on_buffers(value, out): + linalg.fill_tensor(value, outs=[out], emit_generic=True) + + execution_engine = ExecutionEngine(transform(module, fill_boiler)) + + # TODO: FFI-based solution to allow testing and printing with python code. + # Prepare arguments: one result i32. + # Arguments must be passed as pointers. + c_int_p = ctypes.c_int * 1 + res = c_int_p(-1) + execution_engine.invoke("main", res) + + log("RESULT: ", res[0]) + # CHECK: RESULT: 6 + + +test_fill_generic() + + +def test_fill_rng_builtin(): + with Context() as ctx, Location.unknown(): + module = Module.create() f64 = F64Type.get() i32 = IntegerType.get_signless(32) with InsertionPoint(module.body): @builtin.FuncOp.from_py_func(f64, f64, i32, MemRefType.get((4, 16), i32)) - def fill_on_buffers(min, max, seed, out): + def fill_rng_on_buffers(min, max, seed, out): linalg.fill_rng_2d(min, max, seed, outs=[out]) - execution_engine = ExecutionEngine(transform(module, fill_boiler)) + execution_engine = ExecutionEngine(transform(module, fill_rng_boiler)) # TODO: FFI-based solution to allow testing and printing with python code. # Prepare arguments: one result i32. @@ -215,10 +314,10 @@ def test_fill_builtin(): # CHECK: RESULT: -480 -test_fill_builtin() +test_fill_rng_builtin() -def test_fill_generic(): +def test_fill_rng_generic(): with Context() as ctx, Location.unknown(): module = Module.create() f64 = F64Type.get() @@ -226,10 +325,10 @@ def test_fill_generic(): with InsertionPoint(module.body): @builtin.FuncOp.from_py_func(f64, f64, i32, MemRefType.get((4, 16), i32)) - def fill_on_buffers(min, max, seed, out): + def fill_rng_on_buffers(min, max, seed, out): linalg.fill_rng_2d(min, max, seed, outs=[out], emit_generic=True) - execution_engine = ExecutionEngine(transform(module, fill_boiler)) + execution_engine = ExecutionEngine(transform(module, fill_rng_boiler)) # TODO: FFI-based solution to allow testing and printing with python code. # Prepare arguments: one result i32. @@ -242,7 +341,7 @@ def test_fill_generic(): # CHECK: RESULT: -480 -test_fill_generic() +test_fill_rng_generic() def test_max_pooling_builtin(): |
