Files
clang-p2996/mlir/test/Dialect/EmitC/transforms.mlir
Simon Camphausen e47b507562 [mlir][EmitC] Model lvalues as a type in EmitC (#91475)
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>
2024-08-20 11:52:16 +02:00

132 lines
6.2 KiB
MLIR

// RUN: mlir-opt %s --form-expressions --verify-diagnostics --split-input-file | FileCheck %s
// CHECK-LABEL: func.func @single_expression(
// CHECK-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32, %[[VAL_2:.*]]: i32, %[[VAL_3:.*]]: i32) -> i1 {
// CHECK: %[[VAL_4:.*]] = "emitc.constant"() <{value = 42 : i32}> : () -> i32
// CHECK: %[[VAL_5:.*]] = emitc.expression : i1 {
// CHECK: %[[VAL_6:.*]] = emitc.mul %[[VAL_0]], %[[VAL_4]] : (i32, i32) -> i32
// CHECK: %[[VAL_7:.*]] = emitc.sub %[[VAL_6]], %[[VAL_2]] : (i32, i32) -> i32
// CHECK: %[[VAL_8:.*]] = emitc.cmp lt, %[[VAL_7]], %[[VAL_3]] : (i32, i32) -> i1
// CHECK: emitc.yield %[[VAL_8]] : i1
// CHECK: }
// CHECK: return %[[VAL_5]] : i1
// CHECK: }
func.func @single_expression(%arg0: i32, %arg1: i32, %arg2: i32, %arg3: i32) -> i1 {
%c42 = "emitc.constant"(){value = 42 : i32} : () -> i32
%a = emitc.mul %arg0, %c42 : (i32, i32) -> i32
%b = emitc.sub %a, %arg2 : (i32, i32) -> i32
%c = emitc.cmp lt, %b, %arg3 :(i32, i32) -> i1
return %c : i1
}
// CHECK-LABEL: func.func @multiple_expressions(
// CHECK-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32, %[[VAL_2:.*]]: i32, %[[VAL_3:.*]]: i32) -> (i32, i32) {
// CHECK: %[[VAL_4:.*]] = emitc.expression : i32 {
// CHECK: %[[VAL_5:.*]] = emitc.mul %[[VAL_0]], %[[VAL_1]] : (i32, i32) -> i32
// CHECK: %[[VAL_6:.*]] = emitc.sub %[[VAL_5]], %[[VAL_2]] : (i32, i32) -> i32
// CHECK: emitc.yield %[[VAL_6]] : i32
// CHECK: }
// CHECK: %[[VAL_7:.*]] = emitc.expression : i32 {
// CHECK: %[[VAL_8:.*]] = emitc.add %[[VAL_1]], %[[VAL_3]] : (i32, i32) -> i32
// CHECK: %[[VAL_9:.*]] = emitc.div %[[VAL_8]], %[[VAL_2]] : (i32, i32) -> i32
// CHECK: emitc.yield %[[VAL_9]] : i32
// CHECK: }
// CHECK: return %[[VAL_4]], %[[VAL_7]] : i32, i32
// CHECK: }
func.func @multiple_expressions(%arg0: i32, %arg1: i32, %arg2: i32, %arg3: i32) -> (i32, i32) {
%a = emitc.mul %arg0, %arg1 : (i32, i32) -> i32
%b = emitc.sub %a, %arg2 : (i32, i32) -> i32
%c = emitc.add %arg1, %arg3 : (i32, i32) -> i32
%d = emitc.div %c, %arg2 : (i32, i32) -> i32
return %b, %d : i32, i32
}
// CHECK-LABEL: func.func @expression_with_call(
// CHECK-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32, %[[VAL_2:.*]]: i32, %[[VAL_3:.*]]: i32) -> i1 {
// CHECK: %[[VAL_4:.*]] = emitc.expression : i32 {
// CHECK: %[[VAL_5:.*]] = emitc.mul %[[VAL_0]], %[[VAL_1]] : (i32, i32) -> i32
// CHECK: %[[VAL_6:.*]] = emitc.call_opaque "foo"(%[[VAL_5]], %[[VAL_2]]) : (i32, i32) -> i32
// CHECK: emitc.yield %[[VAL_6]] : i32
// CHECK: }
// CHECK: %[[VAL_7:.*]] = emitc.expression : i1 {
// CHECK: %[[VAL_8:.*]] = emitc.cmp lt, %[[VAL_4]], %[[VAL_1]] : (i32, i32) -> i1
// CHECK: emitc.yield %[[VAL_8]] : i1
// CHECK: }
// CHECK: return %[[VAL_7]] : i1
// CHECK: }
func.func @expression_with_call(%arg0: i32, %arg1: i32, %arg2: i32, %arg3: i32) -> i1 {
%a = emitc.mul %arg0, %arg1 : (i32, i32) -> i32
%b = emitc.call_opaque "foo" (%a, %arg2) : (i32, i32) -> (i32)
%c = emitc.cmp lt, %b, %arg1 :(i32, i32) -> i1
return %c : i1
}
// CHECK-LABEL: func.func @expression_with_dereference(
// CHECK-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32, %[[VAL_2:.*]]: !emitc.ptr<i32>) -> i1 {
// CHECK: %[[VAL_3:.*]] = emitc.expression : i32 {
// CHECK: %[[VAL_4:.*]] = emitc.apply "*"(%[[VAL_2]]) : (!emitc.ptr<i32>) -> i32
// CHECK: emitc.yield %[[VAL_4]] : i32
// CHECK: }
// CHECK: %[[VAL_5:.*]] = emitc.expression : i1 {
// CHECK: %[[VAL_6:.*]] = emitc.mul %[[VAL_0]], %[[VAL_1]] : (i32, i32) -> i32
// CHECK: %[[VAL_7:.*]] = emitc.cmp lt, %[[VAL_6]], %[[VAL_3]] : (i32, i32) -> i1
// CHECK: return %[[VAL_5]] : i1
// CHECK: }
func.func @expression_with_dereference(%arg0: i32, %arg1: i32, %arg2: !emitc.ptr<i32>) -> i1 {
%a = emitc.mul %arg0, %arg1 : (i32, i32) -> i32
%b = emitc.apply "*"(%arg2) : (!emitc.ptr<i32>) -> (i32)
%c = emitc.cmp lt, %a, %b :(i32, i32) -> i1
return %c : i1
}
// CHECK-LABEL: func.func @expression_with_address_taken(
// CHECK-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32, %[[VAL_2:.*]]: !emitc.ptr<i32>) -> i1 {
// CHECK: %[[VAL_3:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
// CHECK: %[[VAL_4:.*]] = emitc.expression : i1 {
// CHECK: %[[VAL_5:.*]] = emitc.apply "&"(%[[VAL_3]]) : (!emitc.lvalue<i32>) -> !emitc.ptr<i32>
// CHECK: %[[VAL_6:.*]] = emitc.add %[[VAL_5]], %[[VAL_1]] : (!emitc.ptr<i32>, i32) -> !emitc.ptr<i32>
// CHECK: %[[VAL_7:.*]] = emitc.cmp lt, %[[VAL_6]], %[[VAL_2]] : (!emitc.ptr<i32>, !emitc.ptr<i32>) -> i1
// CHECK: emitc.yield %[[VAL_7]] : i1
// CHECK: }
// CHECK: return %[[VAL_4]] : i1
// CHECK: }
func.func @expression_with_address_taken(%arg0: i32, %arg1: i32, %arg2: !emitc.ptr<i32>) -> i1 {
%0 = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<i32>
%a = emitc.apply "&"(%0) : (!emitc.lvalue<i32>) -> !emitc.ptr<i32>
%b = emitc.add %a, %arg1 : (!emitc.ptr<i32>, i32) -> !emitc.ptr<i32>
%c = emitc.cmp lt, %b, %arg2 :(!emitc.ptr<i32>, !emitc.ptr<i32>) -> i1
return %c : i1
}
// CHECK-LABEL: func.func @no_nested_expression(
// CHECK-SAME: %[[VAL_0:.*]]: i32, %[[VAL_1:.*]]: i32) -> i1 {
// CHECK: %[[VAL_2:.*]] = emitc.expression : i1 {
// CHECK: %[[VAL_3:.*]] = emitc.cmp lt, %[[VAL_0]], %[[VAL_1]] : (i32, i32) -> i1
// CHECK: emitc.yield %[[VAL_3]] : i1
// CHECK: }
// CHECK: return %[[VAL_2]] : i1
// CHECK: }
func.func @no_nested_expression(%arg0: i32, %arg1: i32) -> i1 {
%a = emitc.expression : i1 {
%b = emitc.cmp lt, %arg0, %arg1 :(i32, i32) -> i1
emitc.yield %b : i1
}
return %a : i1
}
// CHECK-LABEL: func.func @single_result_requirement
// CHECK-NOT: emitc.expression
func.func @single_result_requirement() -> (i32, i32) {
%0:2 = emitc.call_opaque "foo" () : () -> (i32, i32)
return %0#0, %0#1 : i32, i32
}