diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td index a518e7425956c..d4aea52a0d485 100644 --- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td +++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td @@ -489,12 +489,9 @@ def EmitC_ExpressionOp : EmitC_Op<"expression", auto applyOp = dyn_cast(op); if (applyOp) return applyOp.getApplicableOperator() == "*"; - // Any operation using variables is assumed to have a side effect of - // reading memory mutable by emitc::assign ops. - return llvm::any_of(op.getOperands(), [](Value operand) { - Operation *def = operand.getDefiningOp(); - return def && isa(def); - }); + // Any load operation is assumed to read from memory and thus perform + // a side effect. + return isa(op); }; return llvm::any_of(getRegion().front().without_terminator(), predicate); }; @@ -927,7 +924,7 @@ def EmitC_LogicalOrOp : EmitC_BinaryOp<"logical_or", [CExpression]> { let assemblyFormat = "operands attr-dict `:` type(operands)"; } -def EmitC_LoadOp : EmitC_Op<"load", [ +def EmitC_LoadOp : EmitC_Op<"load", [CExpression, TypesMatchWith<"result type matches value type of 'operand'", "operand", "result", "::llvm::cast($_self).getValueType()"> diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp index b00820ffc542b..a3761ead4adde 100644 --- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp +++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp @@ -100,6 +100,7 @@ static FailureOr getOperatorPrecedence(Operation *operation) { }) .Case([&](auto op) { return 2; }) .Case([&](auto op) { return 13; }) + .Case([&](auto op) { return 16; }) .Case([&](auto op) { return 4; }) .Case([&](auto op) { return 15; }) .Case([&](auto op) { return 3; }) diff --git a/mlir/test/Dialect/EmitC/transforms.mlir b/mlir/test/Dialect/EmitC/transforms.mlir index d204dec70d449..a38f396dad953 100644 --- a/mlir/test/Dialect/EmitC/transforms.mlir +++ b/mlir/test/Dialect/EmitC/transforms.mlir @@ -129,3 +129,37 @@ func.func @single_result_requirement() -> (i32, i32) { %0:2 = emitc.call_opaque "foo" () : () -> (i32, i32) return %0#0, %0#1 : i32, i32 } + +// CHECK-LABEL: func.func @expression_with_load( +// CHECK-SAME: %[[VAL_0:.*]]: i32, +// CHECK-SAME: %[[VAL_1:.*]]: !emitc.ptr) -> i1 { +// CHECK: %[[VAL_2:.*]] = "emitc.constant"() <{value = 0 : i64}> : () -> i64 +// CHECK: %[[VAL_3:.*]] = "emitc.variable"() <{value = #emitc.opaque<"42">}> : () -> !emitc.lvalue +// CHECK: %[[VAL_4:.*]] = emitc.expression : i32 { +// CHECK: %[[VAL_5:.*]] = load %[[VAL_3]] : +// CHECK: yield %[[VAL_5]] : i32 +// CHECK: } +// CHECK: %[[VAL_6:.*]] = emitc.subscript %[[VAL_1]]{{\[}}%[[VAL_2]]] : (!emitc.ptr, i64) -> !emitc.lvalue +// CHECK: %[[VAL_7:.*]] = emitc.expression : i32 { +// CHECK: %[[VAL_8:.*]] = load %[[VAL_6]] : +// CHECK: yield %[[VAL_8]] : i32 +// CHECK: } +// CHECK: %[[VAL_9:.*]] = emitc.expression : i1 { +// CHECK: %[[VAL_10:.*]] = add %[[VAL_4]], %[[VAL_7]] : (i32, i32) -> i32 +// CHECK: %[[VAL_11:.*]] = cmp lt, %[[VAL_10]], %[[VAL_0]] : (i32, i32) -> i1 +// CHECK: yield %[[VAL_11]] : i1 +// CHECK: } +// CHECK: return %[[VAL_9]] : i1 +// CHECK: } + + +func.func @expression_with_load(%arg0: i32, %arg1: !emitc.ptr) -> i1 { + %c0 = "emitc.constant"() {value = 0 : i64} : () -> i64 + %0 = "emitc.variable"() <{value = #emitc.opaque<"42">}> : () -> !emitc.lvalue + %a = emitc.load %0 : !emitc.lvalue + %ptr = emitc.subscript %arg1[%c0] : (!emitc.ptr, i64) -> !emitc.lvalue + %ptr_load = emitc.load %ptr : !emitc.lvalue + %b = emitc.add %a, %ptr_load : (i32, i32) -> i32 + %c = emitc.cmp lt, %b, %arg0 :(i32, i32) -> i1 + return %c : i1 +} diff --git a/mlir/test/Target/Cpp/expressions.mlir b/mlir/test/Target/Cpp/expressions.mlir index 3a1694e7d15dc..9316d7b77619b 100644 --- a/mlir/test/Target/Cpp/expressions.mlir +++ b/mlir/test/Target/Cpp/expressions.mlir @@ -342,3 +342,60 @@ func.func @expression_with_subscript_user(%arg0: !emitc.ptr return %res_load : i32 } + +// CPP-DEFAULT: bool expression_with_load(int32_t [[VAL_1:v.+]], int32_t [[VAL_2:v.+]], int32_t* [[VAL_3:v.+]]) { +// CPP-DEFAULT-NEXT: int64_t [[VAL_4:v.+]] = 0; +// CPP-DEFAULT-NEXT: int32_t [[VAL_5:v.+]] = 42; +// CPP-DEFAULT-NEXT: bool [[VAL_6:v.+]] = [[VAL_5]] + [[VAL_2]] < [[VAL_3]][[[VAL_4]]] + [[VAL_1]]; +// CPP-DEFAULT-NEXT: return [[VAL_6]]; + +// CPP-DECLTOP: bool expression_with_load(int32_t [[VAL_1:v.+]], int32_t [[VAL_2:v.+]], int32_t* [[VAL_3:v.+]]) { +// CPP-DECLTOP-NEXT: int64_t [[VAL_4:v.+]]; +// CPP-DECLTOP-NEXT: int32_t [[VAL_5:v.+]]; +// CPP-DECLTOP-NEXT: bool [[VAL_6:v.+]]; +// CPP-DECLTOP-NEXT: [[VAL_4]] = 0; +// CPP-DECLTOP-NEXT: [[VAL_5]] = 42; +// CPP-DECLTOP-NEXT: [[VAL_6]] = [[VAL_5]] + [[VAL_2]] < [[VAL_3]][[[VAL_4]]] + [[VAL_1]]; +// CPP-DECLTOP-NEXT: return [[VAL_6]]; + +func.func @expression_with_load(%arg0: i32, %arg1: i32, %arg2: !emitc.ptr) -> i1 { + %c0 = "emitc.constant"() {value = 0 : i64} : () -> i64 + %0 = "emitc.variable"() <{value = #emitc.opaque<"42">}> : () -> !emitc.lvalue + %ptr = emitc.subscript %arg2[%c0] : (!emitc.ptr, i64) -> !emitc.lvalue + %result = emitc.expression : i1 { + %a = emitc.load %0 : !emitc.lvalue + %b = emitc.add %a, %arg1 : (i32, i32) -> i32 + %c = emitc.load %ptr : !emitc.lvalue + %d = emitc.add %c, %arg0 : (i32, i32) -> i32 + %e = emitc.cmp lt, %b, %d :(i32, i32) -> i1 + yield %e : i1 + } + return %result : i1 +} + +// CPP-DEFAULT: bool expression_with_load_and_call(int32_t* [[VAL_1:v.+]]) { +// CPP-DEFAULT-NEXT: int64_t [[VAL_2:v.+]] = 0; +// CPP-DEFAULT-NEXT: bool [[VAL_3:v.+]] = [[VAL_1]][[[VAL_2]]] + bar([[VAL_1]][[[VAL_2]]]) < [[VAL_1]][[[VAL_2]]]; +// CPP-DEFAULT-NEXT: return [[VAL_3]]; + +// CPP-DECLTOP: bool expression_with_load_and_call(int32_t* [[VAL_1:v.+]]) { +// CPP-DECLTOP-NEXT: int64_t [[VAL_2:v.+]]; +// CPP-DECLTOP-NEXT: bool [[VAL_3:v.+]]; +// CPP-DECLTOP-NEXT: [[VAL_2]] = 0; +// CPP-DECLTOP-NEXT: [[VAL_3]] = [[VAL_1]][[[VAL_2]]] + bar([[VAL_1]][[[VAL_2]]]) < [[VAL_1]][[[VAL_2]]]; +// CPP-DECLTOP-NEXT: return [[VAL_3]]; + +func.func @expression_with_load_and_call(%arg0: !emitc.ptr) -> i1 { + %c0 = "emitc.constant"() {value = 0 : i64} : () -> i64 + %ptr = emitc.subscript %arg0[%c0] : (!emitc.ptr, i64) -> !emitc.lvalue + %result = emitc.expression : i1 { + %a = emitc.load %ptr : !emitc.lvalue + %b = emitc.load %ptr : !emitc.lvalue + %c = emitc.load %ptr : !emitc.lvalue + %d = emitc.call_opaque "bar" (%a) : (i32) -> (i32) + %e = add %c, %d : (i32, i32) -> i32 + %f = emitc.cmp lt, %e, %b :(i32, i32) -> i1 + yield %f : i1 + } + return %result : i1 +}