This adds an `emitc.lvalue` type which models assignable lvlaues in the type system. Operations modifying memory are restricted to this type accordingly. See also the discussion on [discourse](https://discourse.llvm.org/t/rfc-separate-variables-from-ssa-values-in-emitc/75224/9). The most notable changes are as follows. - `emitc.variable` and `emitc.global` ops are restricted to return `emitc.array` or `emitc.lvalue` types - Taking the address of a value is restricted to operands with lvalue type - Conversion from lvalues into SSA values is done with the new `emitc.load` op - The var operand of the `emitc.assign` op is restricted to lvalue type - The result of the `emitc.subscript` and `emitc.get_global` ops is a lvalue type - The operands and results of the `emitc.member` and `emitc.member_of_ptr` ops are restricted to lvalue types --------- Co-authored-by: Matthias Gehre <matthias.gehre@amd.com>
97 lines
4.7 KiB
MLIR
97 lines
4.7 KiB
MLIR
// RUN: mlir-opt -allow-unregistered-dialect -convert-scf-to-emitc %s | FileCheck %s
|
|
|
|
func.func @simple_std_for_loop(%arg0 : index, %arg1 : index, %arg2 : index) {
|
|
scf.for %i0 = %arg0 to %arg1 step %arg2 {
|
|
%c1 = arith.constant 1 : index
|
|
}
|
|
return
|
|
}
|
|
// CHECK-LABEL: func.func @simple_std_for_loop(
|
|
// CHECK-SAME: %[[VAL_0:.*]]: index, %[[VAL_1:.*]]: index, %[[VAL_2:.*]]: index) {
|
|
// CHECK-NEXT: emitc.for %[[VAL_3:.*]] = %[[VAL_0]] to %[[VAL_1]] step %[[VAL_2]] {
|
|
// CHECK-NEXT: %[[VAL_4:.*]] = arith.constant 1 : index
|
|
// CHECK-NEXT: }
|
|
// CHECK-NEXT: return
|
|
// CHECK-NEXT: }
|
|
|
|
func.func @simple_std_2_for_loops(%arg0 : index, %arg1 : index, %arg2 : index) {
|
|
scf.for %i0 = %arg0 to %arg1 step %arg2 {
|
|
%c1 = arith.constant 1 : index
|
|
scf.for %i1 = %arg0 to %arg1 step %arg2 {
|
|
%c1_0 = arith.constant 1 : index
|
|
}
|
|
}
|
|
return
|
|
}
|
|
// CHECK-LABEL: func.func @simple_std_2_for_loops(
|
|
// CHECK-SAME: %[[VAL_0:.*]]: index, %[[VAL_1:.*]]: index, %[[VAL_2:.*]]: index) {
|
|
// CHECK-NEXT: emitc.for %[[VAL_3:.*]] = %[[VAL_0]] to %[[VAL_1]] step %[[VAL_2]] {
|
|
// CHECK-NEXT: %[[VAL_4:.*]] = arith.constant 1 : index
|
|
// CHECK-NEXT: emitc.for %[[VAL_5:.*]] = %[[VAL_0]] to %[[VAL_1]] step %[[VAL_2]] {
|
|
// CHECK-NEXT: %[[VAL_6:.*]] = arith.constant 1 : index
|
|
// CHECK-NEXT: }
|
|
// CHECK-NEXT: }
|
|
// CHECK-NEXT: return
|
|
// CHECK-NEXT: }
|
|
|
|
func.func @for_yield(%arg0 : index, %arg1 : index, %arg2 : index) -> (f32, f32) {
|
|
%s0 = arith.constant 0.0 : f32
|
|
%s1 = arith.constant 1.0 : f32
|
|
%result:2 = scf.for %i0 = %arg0 to %arg1 step %arg2 iter_args(%si = %s0, %sj = %s1) -> (f32, f32) {
|
|
%sn = arith.addf %si, %sj : f32
|
|
scf.yield %sn, %sn : f32, f32
|
|
}
|
|
return %result#0, %result#1 : f32, f32
|
|
}
|
|
// CHECK-LABEL: func.func @for_yield(
|
|
// CHECK-SAME: %[[VAL_0:.*]]: index, %[[VAL_1:.*]]: index, %[[VAL_2:.*]]: index) -> (f32, f32) {
|
|
// CHECK-NEXT: %[[VAL_3:.*]] = arith.constant 0.000000e+00 : f32
|
|
// CHECK-NEXT: %[[VAL_4:.*]] = arith.constant 1.000000e+00 : f32
|
|
// CHECK-NEXT: %[[VAL_5:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<f32>
|
|
// CHECK-NEXT: %[[VAL_6:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<f32>
|
|
// CHECK-NEXT: emitc.assign %[[VAL_3]] : f32 to %[[VAL_5]] : <f32>
|
|
// CHECK-NEXT: emitc.assign %[[VAL_4]] : f32 to %[[VAL_6]] : <f32>
|
|
// CHECK-NEXT: emitc.for %[[VAL_7:.*]] = %[[VAL_0]] to %[[VAL_1]] step %[[VAL_2]] {
|
|
// CHECK-NEXT: %[[VAL_8:.*]] = emitc.load %[[VAL_5]] : <f32>
|
|
// CHECK-NEXT: %[[VAL_9:.*]] = emitc.load %[[VAL_6]] : <f32>
|
|
// CHECK-NEXT: %[[VAL_10:.*]] = arith.addf %[[VAL_8]], %[[VAL_9]] : f32
|
|
// CHECK-NEXT: emitc.assign %[[VAL_10]] : f32 to %[[VAL_5]] : <f32>
|
|
// CHECK-NEXT: emitc.assign %[[VAL_10]] : f32 to %[[VAL_6]] : <f32>
|
|
// CHECK-NEXT: }
|
|
// CHECK-NEXT: %[[VAL_11:.*]] = emitc.load %[[VAL_5]] : <f32>
|
|
// CHECK-NEXT: %[[VAL_12:.*]] = emitc.load %[[VAL_6]] : <f32>
|
|
// CHECK-NEXT: return %[[VAL_11]], %[[VAL_12]] : f32, f32
|
|
// CHECK-NEXT: }
|
|
|
|
func.func @nested_for_yield(%arg0 : index, %arg1 : index, %arg2 : index) -> f32 {
|
|
%s0 = arith.constant 1.0 : f32
|
|
%r = scf.for %i0 = %arg0 to %arg1 step %arg2 iter_args(%iter = %s0) -> (f32) {
|
|
%result = scf.for %i1 = %arg0 to %arg1 step %arg2 iter_args(%si = %iter) -> (f32) {
|
|
%sn = arith.addf %si, %si : f32
|
|
scf.yield %sn : f32
|
|
}
|
|
scf.yield %result : f32
|
|
}
|
|
return %r : f32
|
|
}
|
|
// CHECK-LABEL: func.func @nested_for_yield(
|
|
// CHECK-SAME: %[[VAL_0:.*]]: index, %[[VAL_1:.*]]: index, %[[VAL_2:.*]]: index) -> f32 {
|
|
// CHECK-NEXT: %[[VAL_3:.*]] = arith.constant 1.000000e+00 : f32
|
|
// CHECK-NEXT: %[[VAL_4:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<f32>
|
|
// CHECK-NEXT: emitc.assign %[[VAL_3]] : f32 to %[[VAL_4]] : <f32>
|
|
// CHECK-NEXT: emitc.for %[[VAL_5:.*]] = %[[VAL_0]] to %[[VAL_1]] step %[[VAL_2]] {
|
|
// CHECK-NEXT: %[[VAL_6:.*]] = emitc.load %[[VAL_4]] : <f32>
|
|
// CHECK-NEXT: %[[VAL_7:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<f32>
|
|
// CHECK-NEXT: emitc.assign %[[VAL_6]] : f32 to %[[VAL_7]] : <f32>
|
|
// CHECK-NEXT: emitc.for %[[VAL_8:.*]] = %[[VAL_0]] to %[[VAL_1]] step %[[VAL_2]] {
|
|
// CHECK-NEXT: %[[VAL_9:.*]] = emitc.load %[[VAL_7]] : <f32>
|
|
// CHECK-NEXT: %[[VAL_10:.*]] = arith.addf %[[VAL_9]], %[[VAL_9]] : f32
|
|
// CHECK-NEXT: emitc.assign %[[VAL_10]] : f32 to %[[VAL_7]] : <f32>
|
|
// CHECK-NEXT: }
|
|
// CHECK-NEXT: %[[VAL_11:.*]] = emitc.load %[[VAL_7]] : <f32>
|
|
// CHECK-NEXT: emitc.assign %[[VAL_11]] : f32 to %[[VAL_4]] : <f32>
|
|
// CHECK-NEXT: }
|
|
// CHECK-NEXT: %[[VAL_12:.*]] = emitc.load %[[VAL_4]] : <f32>
|
|
// CHECK-NEXT: return %[[VAL_12]] : f32
|
|
// CHECK-NEXT: }
|