diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td index c1820904f2665..f6f3ab778e1ce 100644 --- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td +++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td @@ -471,6 +471,7 @@ def EmitC_ConstantOp } def EmitC_DereferenceOp : EmitC_Op<"dereference", [ + CExpressionInterface, TypesMatchWith<"input and result reference the same type", "pointer", "result", "emitc::LValueType::get(::llvm::cast($_self).getPointee())"> ]> { @@ -493,6 +494,15 @@ def EmitC_DereferenceOp : EmitC_Op<"dereference", [ $pointer `:` qualified(type($pointer)) attr-dict }]; let hasVerifier = 1; + + let extraClassDeclaration = [{ + bool hasSideEffects() { + return false; + } + bool alwaysInline() { + return true; // C doesn't support references. + } + }]; } def EmitC_DivOp : EmitC_BinaryOp<"div", []> { @@ -561,7 +571,7 @@ def EmitC_ExpressionOp let arguments = (ins Variadic>:$defs, UnitAttr:$do_not_inline); - let results = (outs EmitCType:$result); + let results = (outs AnyTypeOf<[EmitCType, EmitC_LValueType]>:$result); let regions = (region SizedRegion<1>:$region); let hasVerifier = 1; @@ -927,7 +937,7 @@ def EmitC_IncludeOp let hasCustomAssemblyFormat = 1; } -def EmitC_LiteralOp : EmitC_Op<"literal", [Pure]> { +def EmitC_LiteralOp : EmitC_Op<"literal", [Pure, CExpressionInterface]> { let summary = "Literal operation"; let description = [{ The `emitc.literal` operation produces an SSA value equal to some constant @@ -950,6 +960,15 @@ def EmitC_LiteralOp : EmitC_Op<"literal", [Pure]> { let hasVerifier = 1; let assemblyFormat = "$value attr-dict `:` type($result)"; + + let extraClassDeclaration = [{ + bool hasSideEffects() { + return false; + } + bool alwaysInline() { + return true; // Always inlined by design. + } + }]; } def EmitC_LogicalAndOp : EmitC_BinaryOp<"logical_and", []> { @@ -1116,7 +1135,7 @@ def EmitC_SubOp : EmitC_BinaryOp<"sub", []> { let hasVerifier = 1; } -def EmitC_MemberOp : EmitC_Op<"member"> { +def EmitC_MemberOp : EmitC_Op<"member", [CExpressionInterface]> { let summary = "Member operation"; let description = [{ With the `emitc.member` operation the member access operator `.` can be @@ -1137,9 +1156,18 @@ def EmitC_MemberOp : EmitC_Op<"member"> { EmitC_LValueOf<[EmitC_OpaqueType]>:$operand ); let results = (outs AnyTypeOf<[EmitC_ArrayType, EmitC_LValueType]>); + + let extraClassDeclaration = [{ + bool hasSideEffects() { + return false; + } + bool alwaysInline() { + return true; // C doesn't support references. + } + }]; } -def EmitC_MemberOfPtrOp : EmitC_Op<"member_of_ptr"> { +def EmitC_MemberOfPtrOp : EmitC_Op<"member_of_ptr", [CExpressionInterface]> { let summary = "Member of pointer operation"; let description = [{ With the `emitc.member_of_ptr` operation the member access operator `->` @@ -1162,6 +1190,15 @@ def EmitC_MemberOfPtrOp : EmitC_Op<"member_of_ptr"> { EmitC_LValueOf<[EmitC_OpaqueType,EmitC_PointerType]>:$operand ); let results = (outs AnyTypeOf<[EmitC_ArrayType, EmitC_LValueType]>); + + let extraClassDeclaration = [{ + bool hasSideEffects() { + return false; + } + bool alwaysInline() { + return true; // C doesn't support references. + } + }]; } def EmitC_ConditionalOp : EmitC_Op<"conditional", @@ -1331,8 +1368,10 @@ def EmitC_GlobalOp : EmitC_Op<"global", [Symbol]> { let hasVerifier = 1; } -def EmitC_GetGlobalOp : EmitC_Op<"get_global", - [Pure, DeclareOpInterfaceMethods]> { +def EmitC_GetGlobalOp + : EmitC_Op<"get_global", [Pure, + DeclareOpInterfaceMethods, + CExpressionInterface]> { let summary = "Obtain access to a global variable"; let description = [{ The `emitc.get_global` operation retrieves the lvalue of a @@ -1350,6 +1389,15 @@ def EmitC_GetGlobalOp : EmitC_Op<"get_global", let arguments = (ins FlatSymbolRefAttr:$name); let results = (outs AnyTypeOf<[EmitC_ArrayType, EmitC_LValueType]>:$result); let assemblyFormat = "$name `:` type($result) attr-dict"; + + let extraClassDeclaration = [{ + bool hasSideEffects() { + return false; + } + bool alwaysInline() { + return true; // C doesn't support references. + } + }]; } def EmitC_VerbatimOp : EmitC_Op<"verbatim"> { @@ -1460,7 +1508,8 @@ def EmitC_YieldOp : EmitC_Op<"yield", value is yielded. }]; - let arguments = (ins Optional:$result); + let arguments = + (ins Optional>:$result); let builders = [OpBuilder<(ins), [{ /* nothing to do */ }]>]; let hasVerifier = 1; @@ -1531,7 +1580,7 @@ def EmitC_IfOp : EmitC_Op<"if", let hasCustomAssemblyFormat = 1; } -def EmitC_SubscriptOp : EmitC_Op<"subscript", []> { +def EmitC_SubscriptOp : EmitC_Op<"subscript", [CExpressionInterface]> { let summary = "Subscript operation"; let description = [{ With the `emitc.subscript` operation the subscript operator `[]` can be applied @@ -1579,6 +1628,15 @@ def EmitC_SubscriptOp : EmitC_Op<"subscript", []> { let hasVerifier = 1; let assemblyFormat = "$value `[` $indices `]` attr-dict `:` functional-type(operands, results)"; + + let extraClassDeclaration = [{ + bool hasSideEffects() { + return false; + } + bool alwaysInline() { + return true; // C doesn't support references. + } + }]; } def EmitC_SwitchOp : EmitC_Op<"switch", [RecursiveMemoryEffects, @@ -1759,8 +1817,9 @@ def EmitC_FieldOp : EmitC_Op<"field", [Symbol]> { } def EmitC_GetFieldOp - : EmitC_Op<"get_field", [Pure, DeclareOpInterfaceMethods< - SymbolUserOpInterface>]> { + : EmitC_Op<"get_field", [Pure, + DeclareOpInterfaceMethods, + CExpressionInterface]> { let summary = "Obtain access to a field within a class instance"; let description = [{ The `emitc.get_field` operation retrieves the lvalue of a @@ -1777,6 +1836,15 @@ def EmitC_GetFieldOp let results = (outs EmitCType:$result); let assemblyFormat = "$field_name `:` type($result) attr-dict"; let hasVerifier = 1; + + let extraClassDeclaration = [{ + bool hasSideEffects() { + return false; + } + bool alwaysInline() { + return true; // C doesn't support references. + } + }]; } def EmitC_DoOp : EmitC_Op<"do", diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitCInterfaces.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitCInterfaces.td index 777784e56202a..5e02d72252a23 100644 --- a/mlir/include/mlir/Dialect/EmitC/IR/EmitCInterfaces.td +++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitCInterfaces.td @@ -42,6 +42,25 @@ def CExpressionInterface : OpInterface<"CExpressionInterface"> { /*defaultImplementation=*/[{ return true; }]>, + InterfaceMethod<[{ + Check whether operation must be inlined into all its users. + + By default operation is not marked as always inlined. + + ```c++ + class ConcreteOp ... { + public: + bool alwaysInline() { + // That way we can override the default implementation. + return true; + } + }; + ``` + }], + "bool", "alwaysInline", (ins), /*methodBody=*/[{}], + /*defaultImplementation=*/[{ + return false; + }]>, ]; } diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp index b0566dd10f490..bea1d13b5936e 100644 --- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp +++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp @@ -512,7 +512,8 @@ LogicalResult ExpressionOp::verify() { Operation *op = worklist.back(); worklist.pop_back(); if (visited.contains(op)) { - if (cast(op).hasSideEffects()) + auto cExpr = cast(op); + if (!cExpr.alwaysInline() && cExpr.hasSideEffects()) return emitOpError( "requires exactly one use for operations with side effects"); } @@ -523,6 +524,14 @@ LogicalResult ExpressionOp::verify() { } } + // It is illegal to forbid inlining of expressions whose root operation must + // be inlined. + if (getDoNotInline() && + cast(rootOp).alwaysInline()) { + return emitOpError("root operation must be inlined but expression is marked" + " do-not-inline"); + } + return success(); } @@ -1015,6 +1024,10 @@ LogicalResult emitc::YieldOp::verify() { if (!isa(containingOp) && !result && containingOp->getNumResults() != 0) return emitOpError() << "does not yield a value to be returned by parent"; + if (result && isa(result.getType()) && + !isa(containingOp)) + return emitOpError() << "yielding lvalues is not supported for this op"; + return success(); } diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp index 15c23c60d0b86..6fd7c65190702 100644 --- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp +++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp @@ -8,6 +8,7 @@ #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h" #include "mlir/Dialect/EmitC/IR/EmitC.h" +#include "mlir/Dialect/EmitC/IR/EmitCInterfaces.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/BuiltinTypes.h" @@ -21,6 +22,7 @@ #include "llvm/ADT/ScopedHashTable.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/TypeSwitch.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FormatVariadic.h" #include @@ -99,14 +101,20 @@ static FailureOr getOperatorPrecedence(Operation *operation) { }) .Case([&](auto op) { return 2; }) .Case([&](auto op) { return 17; }) + .Case([&](auto op) { return 15; }) .Case([&](auto op) { return 13; }) + .Case([&](auto op) { return 18; }) + .Case([&](auto op) { return 18; }) .Case([&](auto op) { return 16; }) .Case([&](auto op) { return 4; }) .Case([&](auto op) { return 15; }) .Case([&](auto op) { return 3; }) + .Case([&](auto op) { return 17; }) + .Case([&](auto op) { return 17; }) .Case([&](auto op) { return 13; }) .Case([&](auto op) { return 13; }) .Case([&](auto op) { return 12; }) + .Case([&](auto op) { return 17; }) .Case([&](auto op) { return 15; }) .Case([&](auto op) { return 15; }) .Default([](auto op) { return op->emitError("unsupported operation"); }); @@ -183,10 +191,7 @@ struct CppEmitter { LogicalResult emitOperand(Value value, bool isInBrackets = false); /// Emit an expression as a C expression. - LogicalResult emitExpression(ExpressionOp expressionOp); - - /// Insert the expression representing the operation into the value cache. - void cacheDeferredOpResult(Value value, StringRef str); + LogicalResult emitExpression(Operation *op); /// Return the existing or a new name for a Value. StringRef getOrCreateName(Value val); @@ -331,30 +336,26 @@ struct CppEmitter { }; } // namespace -/// Determine whether expression \p op should be emitted in a deferred way. -static bool hasDeferredEmission(Operation *op) { - return isa_and_nonnull(op); -} - /// Determine whether operation \p op should be emitted inline, i.e. /// as part of its user. This function recommends inlining of any expressions /// that can be inlined unless it is used by another expression, under the /// assumption that any expression fusion/re-materialization was taken care of /// by transformations run by the backend. static bool shouldBeInlined(Operation *op) { - // CExpression operations are inlined if and only if they reside within an - // ExpressionOp. - if (isa(op)) - return isa(op->getParentOp()); + // CExpression operations are inlined if and only if they are marked as + // always-inline or reside in an ExpressionOp. + if (auto cExpression = dyn_cast(op)) + return cExpression.alwaysInline() || isa(op->getParentOp()); // Only other inlinable operation is ExpressionOp itself. ExpressionOp expressionOp = dyn_cast(op); if (!expressionOp) return false; + // Inline if the root operation is an always-inline CExpression. + if (cast(expressionOp.getRootOp()).alwaysInline()) + return true; + // Do not inline if expression is marked as such. if (expressionOp.getDoNotInline()) return false; @@ -366,11 +367,6 @@ static bool shouldBeInlined(Operation *op) { Operation *user = *result.getUsers().begin(); - // Do not inline expressions used by operations with deferred emission, since - // their translation requires the materialization of variables. - if (hasDeferredEmission(user)) - return false; - // Do not inline expressions used by other expressions or by ops with the // CExpressionInterface. If this was intended, the user could have been merged // into the expression op. @@ -406,61 +402,79 @@ static bool shouldBeInlined(Operation *op) { static LogicalResult printOperation(CppEmitter &emitter, emitc::DereferenceOp dereferenceOp) { - std::string out; - llvm::raw_string_ostream ss(out); - ss << "*" << emitter.getOrCreateName(dereferenceOp.getPointer()); - emitter.cacheDeferredOpResult(dereferenceOp.getResult(), out); - return success(); + raw_ostream &os = emitter.ostream(); + Operation &op = *dereferenceOp.getOperation(); + + if (failed(emitter.emitAssignPrefix(op))) + return failure(); + os << "*"; + return emitter.emitOperand(dereferenceOp.getPointer()); } static LogicalResult printOperation(CppEmitter &emitter, emitc::GetFieldOp getFieldOp) { - emitter.cacheDeferredOpResult(getFieldOp.getResult(), - getFieldOp.getFieldName()); + if (!emitter.isPartOfCurrentExpression(getFieldOp.getOperation())) + return success(); + + emitter.ostream() << getFieldOp.getFieldName(); return success(); } static LogicalResult printOperation(CppEmitter &emitter, emitc::GetGlobalOp getGlobalOp) { - emitter.cacheDeferredOpResult(getGlobalOp.getResult(), getGlobalOp.getName()); + if (!emitter.isPartOfCurrentExpression(getGlobalOp.getOperation())) + return success(); + + emitter.ostream() << getGlobalOp.getName(); return success(); } static LogicalResult printOperation(CppEmitter &emitter, emitc::LiteralOp literalOp) { - emitter.cacheDeferredOpResult(literalOp.getResult(), literalOp.getValue()); + if (!emitter.isPartOfCurrentExpression(literalOp.getOperation())) + return success(); + + emitter.ostream() << literalOp.getValue(); return success(); } static LogicalResult printOperation(CppEmitter &emitter, emitc::MemberOp memberOp) { - std::string out; - llvm::raw_string_ostream ss(out); - ss << emitter.getOrCreateName(memberOp.getOperand()); - ss << "." << memberOp.getMember(); - emitter.cacheDeferredOpResult(memberOp.getResult(), out); + if (!emitter.isPartOfCurrentExpression(memberOp.getOperation())) + return success(); + + if (failed(emitter.emitOperand(memberOp.getOperand()))) + return failure(); + emitter.ostream() << "." << memberOp.getMember(); return success(); } static LogicalResult printOperation(CppEmitter &emitter, emitc::MemberOfPtrOp memberOfPtrOp) { - std::string out; - llvm::raw_string_ostream ss(out); - ss << emitter.getOrCreateName(memberOfPtrOp.getOperand()); - ss << "->" << memberOfPtrOp.getMember(); - emitter.cacheDeferredOpResult(memberOfPtrOp.getResult(), out); + if (!emitter.isPartOfCurrentExpression(memberOfPtrOp.getOperation())) + return success(); + + if (failed(emitter.emitOperand(memberOfPtrOp.getOperand()))) + return failure(); + emitter.ostream() << "->" << memberOfPtrOp.getMember(); return success(); } static LogicalResult printOperation(CppEmitter &emitter, emitc::SubscriptOp subscriptOp) { - std::string out; - llvm::raw_string_ostream ss(out); - ss << emitter.getOrCreateName(subscriptOp.getValue()); + if (!emitter.isPartOfCurrentExpression(subscriptOp.getOperation())) { + return success(); + } + + raw_ostream &os = emitter.ostream(); + if (failed(emitter.emitOperand(subscriptOp.getValue()))) + return failure(); for (auto index : subscriptOp.getIndices()) { - ss << "[" << emitter.getOrCreateName(index) << "]"; + os << "["; + if (failed(emitter.emitOperand(index, /*isInBrackets=*/true))) + return failure(); + os << "]"; } - emitter.cacheDeferredOpResult(subscriptOp.getResult(), out); return success(); } @@ -534,11 +548,11 @@ static LogicalResult printOperation(CppEmitter &emitter, static LogicalResult printOperation(CppEmitter &emitter, emitc::AssignOp assignOp) { - OpResult result = assignOp.getVar().getDefiningOp()->getResult(0); - - if (failed(emitter.emitVariableAssignment(result))) + if (failed(emitter.emitOperand(assignOp.getVar()))) return failure(); + emitter.ostream() << " = "; + return emitter.emitOperand(assignOp.getValue()); } @@ -1409,18 +1423,9 @@ CppEmitter::CppEmitter(raw_ostream &os, bool declareVariablesAtTop, labelInScopeCount.push(0); } -void CppEmitter::cacheDeferredOpResult(Value value, StringRef str) { - if (!valueMapper.count(value)) - valueMapper.insert(value, str.str()); -} - /// Return the existing or a new name for a Value. StringRef CppEmitter::getOrCreateName(Value val) { if (!valueMapper.count(val)) { - assert(!hasDeferredEmission(val.getDefiningOp()) && - "cacheDeferredOpResult should have been called on this value, " - "update the emitOperation function."); - valueMapper.insert(val, formatv("v{0}", ++valueCount)); } return *valueMapper.begin(val); @@ -1587,11 +1592,20 @@ LogicalResult CppEmitter::emitAttribute(Location loc, Attribute attr) { return emitError(loc, "cannot emit attribute: ") << attr; } -LogicalResult CppEmitter::emitExpression(ExpressionOp expressionOp) { +LogicalResult CppEmitter::emitExpression(Operation *op) { assert(emittedExpressionPrecedence.empty() && "Expected precedence stack to be empty"); - Operation *rootOp = expressionOp.getRootOp(); + Operation *rootOp = nullptr; + if (auto expressionOp = dyn_cast(op)) { + rootOp = expressionOp.getRootOp(); + } else { + assert(cast(op).alwaysInline() && + "Expected an always-inline operation"); + assert(!isa(op->getParentOp()) && + "Expected operation to have no containing expression"); + rootOp = op; + } FailureOr precedence = getOperatorPrecedence(rootOp); if (failed(precedence)) return failure(); @@ -1611,6 +1625,8 @@ LogicalResult CppEmitter::emitOperand(Value value, bool isInBrackets) { if (isPartOfCurrentExpression(value)) { Operation *def = value.getDefiningOp(); assert(def && "Expected operand to be defined by an operation"); + if (auto expressionOp = dyn_cast(def)) + def = expressionOp.getRootOp(); FailureOr precedence = getOperatorPrecedence(def); if (failed(precedence)) return failure(); @@ -1639,6 +1655,12 @@ LogicalResult CppEmitter::emitOperand(Value value, bool isInBrackets) { if (expressionOp && shouldBeInlined(expressionOp)) return emitExpression(expressionOp); + if (auto cExpression = + dyn_cast_if_present(value.getDefiningOp())) { + if (cExpression.alwaysInline()) + return emitExpression(value.getDefiningOp()); + } + if (BlockArgument arg = dyn_cast(value)) { // If this operand is a block argument of an expression, emit instead the // matching expression parameter. @@ -1696,8 +1718,11 @@ LogicalResult CppEmitter::emitVariableAssignment(OpResult result) { LogicalResult CppEmitter::emitVariableDeclaration(OpResult result, bool trailingSemicolon) { - if (hasDeferredEmission(result.getDefiningOp())) - return success(); + if (auto cExpression = + dyn_cast(result.getDefiningOp())) { + if (cExpression.alwaysInline()) + return success(); + } if (hasValueInScope(result)) { return result.getDefiningOp()->emitError( "result variable for the operation already declared"); @@ -1816,8 +1841,10 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) { if (failed(status)) return failure(); - if (hasDeferredEmission(&op)) - return success(); + if (auto cExpression = dyn_cast(op)) { + if (cExpression.alwaysInline()) + return success(); + } if (isEmittingExpression() || (isa(op) && diff --git a/mlir/test/Dialect/EmitC/form-expressions.mlir b/mlir/test/Dialect/EmitC/form-expressions.mlir index 7b6723989e260..291f8e24693b7 100644 --- a/mlir/test/Dialect/EmitC/form-expressions.mlir +++ b/mlir/test/Dialect/EmitC/form-expressions.mlir @@ -131,17 +131,14 @@ func.func @single_result_requirement() -> (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.expression : () -> i64 { -// CHECK: %[[VAL_C:.*]] = "emitc.constant"() <{value = 0 : i64}> : () -> i64 -// CHECK: yield %[[VAL_C]] : i64 -// CHECK: } // CHECK: %[[VAL_3:.*]] = "emitc.variable"() <{value = #emitc.opaque<"42">}> : () -> !emitc.lvalue // CHECK: %[[VAL_4:.*]] = emitc.expression %[[VAL_3]] : (!emitc.lvalue) -> 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 %[[VAL_6]] : (!emitc.lvalue) -> i32 { +// CHECK: %[[VAL_7:.*]] = emitc.expression %[[VAL_1]] : (!emitc.ptr) -> i32 { +// CHECK: %[[VAL_C:.*]] = "emitc.constant"() <{value = 0 : i64}> : () -> i64 +// CHECK: %[[VAL_6:.*]] = subscript %[[VAL_1]]{{\[}}%[[VAL_C]]] : (!emitc.ptr, i64) -> !emitc.lvalue // CHECK: %[[VAL_8:.*]] = load %[[VAL_6]] : // CHECK: yield %[[VAL_8]] : i32 // CHECK: } @@ -208,3 +205,157 @@ func.func @expression_with_constant(%arg0: i32) -> i32 { %a = emitc.mul %arg0, %c42 : (i32, i32) -> i32 return %a : i32 } + +// CHECK-LABEL: func.func @expression_with_subscript( +// CHECK-SAME: %[[ARG0:.*]]: !emitc.array<4x8xi32>, +// CHECK-SAME: %[[ARG1:.*]]: i32, +// CHECK-SAME: %[[ARG2:.*]]: i32) -> i32 { +// CHECK: %[[VAL_0:.*]] = emitc.expression %[[ARG0]], %[[ARG2]], %[[ARG1]] : (!emitc.array<4x8xi32>, i32, i32) -> i32 { +// CHECK: %[[VAL_1:.*]] = add %[[ARG1]], %[[ARG2]] : (i32, i32) -> i32 +// CHECK: %[[VAL_2:.*]] = mul %[[VAL_1]], %[[ARG2]] : (i32, i32) -> i32 +// CHECK: %[[VAL_3:.*]] = subscript %[[ARG0]]{{\[}}%[[VAL_1]], %[[VAL_2]]] : (!emitc.array<4x8xi32>, i32, i32) -> !emitc.lvalue +// CHECK: %[[VAL_4:.*]] = load %[[VAL_3]] : +// CHECK: yield %[[VAL_4]] : i32 +// CHECK: } +// CHECK: return %[[VAL_0]] : i32 +// CHECK: } + +func.func @expression_with_subscript(%arg0: !emitc.array<4x8xi32>, %arg1: i32, %arg2: i32) -> i32 { + %0 = emitc.add %arg1, %arg2 : (i32, i32) -> i32 + %1 = emitc.mul %0, %arg2 : (i32, i32) -> i32 + %2 = emitc.subscript %arg0[%0, %1] : (!emitc.array<4x8xi32>, i32, i32) -> !emitc.lvalue + %3 = emitc.load %2 : !emitc.lvalue + return %3 : i32 +} + +// CHECK-LABEL: func.func @member( +// CHECK-SAME: %[[ARG0:.*]]: !emitc.opaque<"mystruct">, +// CHECK-SAME: %[[ARG1:.*]]: i32, +// CHECK-SAME: %[[ARG2:.*]]: index) { +// CHECK: %[[VAL_0:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue> +// CHECK: emitc.assign %[[ARG0]] : !emitc.opaque<"mystruct"> to %[[VAL_0]] : > +// CHECK: %[[VAL_1:.*]] = emitc.expression %[[VAL_0]] : (!emitc.lvalue>) -> !emitc.lvalue { +// CHECK: %[[VAL_2:.*]] = "emitc.member"(%[[VAL_0]]) <{member = "a"}> : (!emitc.lvalue>) -> !emitc.lvalue +// CHECK: yield %[[VAL_2]] : !emitc.lvalue +// CHECK: } +// CHECK: emitc.assign %[[ARG1]] : i32 to %[[VAL_1]] : +// CHECK: %[[VAL_3:.*]] = emitc.expression %[[VAL_0]] : (!emitc.lvalue>) -> i32 { +// CHECK: %[[VAL_4:.*]] = "emitc.member"(%[[VAL_0]]) <{member = "b"}> : (!emitc.lvalue>) -> !emitc.lvalue +// CHECK: %[[VAL_5:.*]] = load %[[VAL_4]] : +// CHECK: yield %[[VAL_5]] : i32 +// CHECK: } +// CHECK: %[[VAL_6:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue +// CHECK: emitc.assign %[[VAL_3]] : i32 to %[[VAL_6]] : +// CHECK: %[[VAL_7:.*]] = emitc.expression %[[ARG2]], %[[VAL_0]] : (index, !emitc.lvalue>) -> i32 { +// CHECK: %[[VAL_8:.*]] = "emitc.member"(%[[VAL_0]]) <{member = "c"}> : (!emitc.lvalue>) -> !emitc.array<2xi32> +// CHECK: %[[VAL_9:.*]] = subscript %[[VAL_8]]{{\[}}%[[ARG2]]] : (!emitc.array<2xi32>, index) -> !emitc.lvalue +// CHECK: %[[VAL_10:.*]] = load %[[VAL_9]] : +// CHECK: yield %[[VAL_10]] : i32 +// CHECK: } +// CHECK: emitc.assign %[[VAL_7]] : i32 to %[[VAL_6]] : +// CHECK: %[[VAL_11:.*]] = emitc.expression %[[ARG2]], %[[VAL_0]] : (index, !emitc.lvalue>) -> !emitc.lvalue { +// CHECK: %[[VAL_12:.*]] = "emitc.member"(%[[VAL_0]]) <{member = "d"}> : (!emitc.lvalue>) -> !emitc.array<2xi32> +// CHECK: %[[VAL_13:.*]] = subscript %[[VAL_12]]{{\[}}%[[ARG2]]] : (!emitc.array<2xi32>, index) -> !emitc.lvalue +// CHECK: yield %[[VAL_13]] : !emitc.lvalue +// CHECK: } +// CHECK: emitc.assign %[[ARG1]] : i32 to %[[VAL_11]] : +// CHECK: return +// CHECK: } + +func.func @member(%arg0: !emitc.opaque<"mystruct">, %arg1: i32, %arg2: index) { + %var0 = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue> + emitc.assign %arg0 : !emitc.opaque<"mystruct"> to %var0 : !emitc.lvalue> + + %0 = "emitc.member" (%var0) {member = "a"} : (!emitc.lvalue>) -> !emitc.lvalue + emitc.assign %arg1 : i32 to %0 : !emitc.lvalue + + %1 = "emitc.member" (%var0) {member = "b"} : (!emitc.lvalue>) -> !emitc.lvalue + %2 = emitc.load %1 : !emitc.lvalue + %3 = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue + emitc.assign %2 : i32 to %3 : !emitc.lvalue + + %4 = "emitc.member" (%var0) {member = "c"} : (!emitc.lvalue>) -> !emitc.array<2xi32> + %5 = emitc.subscript %4[%arg2] : (!emitc.array<2xi32>, index) -> !emitc.lvalue + %6 = emitc.load %5 : + emitc.assign %6 : i32 to %3 : !emitc.lvalue + + %7 = "emitc.member" (%var0) {member = "d"} : (!emitc.lvalue>) -> !emitc.array<2xi32> + %8 = emitc.subscript %7[%arg2] : (!emitc.array<2xi32>, index) -> !emitc.lvalue + emitc.assign %arg1 : i32 to %8 : !emitc.lvalue + + return +} + +// CHECK-LABEL: func.func @member_of_pointer( +// CHECK-SAME: %[[ARG0:.*]]: !emitc.ptr>, +// CHECK-SAME: %[[ARG1:.*]]: i32, +// CHECK-SAME: %[[ARG2:.*]]: index) { +// CHECK: %[[VAL_0:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue>> +// CHECK: emitc.assign %[[ARG0]] : !emitc.ptr> to %[[VAL_0]] : >> +// CHECK: %[[VAL_1:.*]] = emitc.expression %[[VAL_0]] : (!emitc.lvalue>>) -> !emitc.lvalue { +// CHECK: %[[VAL_2:.*]] = "emitc.member_of_ptr"(%[[VAL_0]]) <{member = "a"}> : (!emitc.lvalue>>) -> !emitc.lvalue +// CHECK: yield %[[VAL_2]] : !emitc.lvalue +// CHECK: } +// CHECK: emitc.assign %[[ARG1]] : i32 to %[[VAL_1]] : +// CHECK: %[[VAL_3:.*]] = emitc.expression %[[VAL_0]] : (!emitc.lvalue>>) -> i32 { +// CHECK: %[[VAL_4:.*]] = "emitc.member_of_ptr"(%[[VAL_0]]) <{member = "b"}> : (!emitc.lvalue>>) -> !emitc.lvalue +// CHECK: %[[VAL_5:.*]] = load %[[VAL_4]] : +// CHECK: yield %[[VAL_5]] : i32 +// CHECK: } +// CHECK: %[[VAL_6:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue +// CHECK: emitc.assign %[[VAL_3]] : i32 to %[[VAL_6]] : +// CHECK: %[[VAL_7:.*]] = emitc.expression %[[ARG2]], %[[VAL_0]] : (index, !emitc.lvalue>>) -> i32 { +// CHECK: %[[VAL_8:.*]] = "emitc.member_of_ptr"(%[[VAL_0]]) <{member = "c"}> : (!emitc.lvalue>>) -> !emitc.array<2xi32> +// CHECK: %[[VAL_9:.*]] = subscript %[[VAL_8]]{{\[}}%[[ARG2]]] : (!emitc.array<2xi32>, index) -> !emitc.lvalue +// CHECK: %[[VAL_10:.*]] = load %[[VAL_9]] : +// CHECK: yield %[[VAL_10]] : i32 +// CHECK: } +// CHECK: emitc.assign %[[VAL_7]] : i32 to %[[VAL_6]] : +// CHECK: %[[VAL_11:.*]] = emitc.expression %[[ARG2]], %[[VAL_0]] : (index, !emitc.lvalue>>) -> !emitc.lvalue { +// CHECK: %[[VAL_12:.*]] = "emitc.member_of_ptr"(%[[VAL_0]]) <{member = "d"}> : (!emitc.lvalue>>) -> !emitc.array<2xi32> +// CHECK: %[[VAL_13:.*]] = subscript %[[VAL_12]]{{\[}}%[[ARG2]]] : (!emitc.array<2xi32>, index) -> !emitc.lvalue +// CHECK: yield %[[VAL_13]] : !emitc.lvalue +// CHECK: } +// CHECK: emitc.assign %[[ARG1]] : i32 to %[[VAL_11]] : +// CHECK: return +// CHECK: } + +func.func @member_of_pointer(%arg0: !emitc.ptr>, %arg1: i32, %arg2: index) { + %var0 = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue>> + emitc.assign %arg0 : !emitc.ptr> to %var0 : !emitc.lvalue>> + + %0 = "emitc.member_of_ptr" (%var0) {member = "a"} : (!emitc.lvalue>>) -> !emitc.lvalue + emitc.assign %arg1 : i32 to %0 : !emitc.lvalue + + %1 = "emitc.member_of_ptr" (%var0) {member = "b"} : (!emitc.lvalue>>) -> !emitc.lvalue + %2 = emitc.load %1 : !emitc.lvalue + %3 = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue + emitc.assign %2 : i32 to %3 : !emitc.lvalue + + %4 = "emitc.member_of_ptr" (%var0) {member = "c"} : (!emitc.lvalue>>) -> !emitc.array<2xi32> + %5 = emitc.subscript %4[%arg2] : (!emitc.array<2xi32>, index) -> !emitc.lvalue + %6 = emitc.load %5 : + emitc.assign %6 : i32 to %3 : !emitc.lvalue + + %7 = "emitc.member_of_ptr" (%var0) {member = "d"} : (!emitc.lvalue>>) -> !emitc.array<2xi32> + %8 = emitc.subscript %7[%arg2] : (!emitc.array<2xi32>, index) -> !emitc.lvalue + emitc.assign %arg1 : i32 to %8 : !emitc.lvalue + + return +} + +// CHECK-LABEL: func.func @expression_with_literal( +// CHECK-SAME: %[[ARG0:.*]]: f32) -> f32 { +// CHECK: %[[VAL_0:.*]] = emitc.expression %[[ARG0]] : (f32) -> f32 { +// CHECK: %[[VAL_1:.*]] = literal "M_PI" : f32 +// CHECK: %[[VAL_2:.*]] = add %[[ARG0]], %[[VAL_1]] : (f32, f32) -> f32 +// CHECK: yield %[[VAL_2]] : f32 +// CHECK: } +// CHECK: return %[[VAL_0]] : f32 +// CHECK: } + +func.func @expression_with_literal(%arg0: f32) -> f32 { + %p0 = emitc.literal "M_PI" : f32 + %1 = "emitc.add" (%arg0, %p0) : (f32, f32) -> f32 + return %1 : f32 +} diff --git a/mlir/test/Target/Cpp/expressions.mlir b/mlir/test/Target/Cpp/expressions.mlir index 2de94d0b11fc8..7280377990cfc 100644 --- a/mlir/test/Target/Cpp/expressions.mlir +++ b/mlir/test/Target/Cpp/expressions.mlir @@ -374,6 +374,26 @@ func.func @expression_with_address_taken(%arg0: i32, %arg1: i32, %arg2: !emitc.p return %c : i1 } +// CPP-DEFAULT: int32_t expression_with_subscript(int32_t [[VAL_1:v.+]][4][8], int32_t [[VAL_2:v.+]], int32_t [[VAL_3:v.+]]) +// CPP-DEFAULT-NEXT: int32_t [[VAL_4:v.+]] = [[VAL_1]][[[VAL_2]] + [[VAL_3]]][([[VAL_2]] + [[VAL_3]]) * [[VAL_3]]]; +// CPP-DEFAULT-NEXT: return [[VAL_4]]; + +// CPP-DECLTOP: int32_t expression_with_subscript(int32_t [[VAL_1:v.+]][4][8], int32_t [[VAL_2:v.+]], int32_t [[VAL_3:v.+]]) +// CPP-DECLTOP-NEXT: int32_t [[VAL_4:v.+]]; +// CPP-DECLTOP-NEXT: [[VAL_4]] = [[VAL_1]][[[VAL_2]] + [[VAL_3]]][([[VAL_2]] + [[VAL_3]]) * [[VAL_3]]]; +// CPP-DECLTOP-NEXT: return [[VAL_4]]; + +func.func @expression_with_subscript(%arg0: !emitc.array<4x8xi32>, %arg1: i32, %arg2: i32) -> i32 { + %res = emitc.expression %arg0, %arg1, %arg2 : (!emitc.array<4x8xi32>, i32, i32) -> i32 { + %0 = add %arg1, %arg2 : (i32, i32) -> i32 + %1 = mul %0, %arg2 : (i32, i32) -> i32 + %2 = subscript %arg0[%0, %1] : (!emitc.array<4x8xi32>, i32, i32) -> !emitc.lvalue + %3 = emitc.load %2 : !emitc.lvalue + yield %3 : i32 + } + return %res : i32 +} + // CPP-DEFAULT: int32_t expression_with_subscript_user(void* [[VAL_1:v.+]]) // CPP-DEFAULT-NEXT: int64_t [[VAL_2:v.+]] = 0; // CPP-DEFAULT-NEXT: int32_t* [[VAL_3:v.+]] = (int32_t*) [[VAL_1]]; @@ -672,3 +692,163 @@ func.func @inline_side_effects_into_switch(%arg0: i32, %arg1: i32, %arg2: i32) { } return } + +// CPP-DEFAULT: void member(mystruct [[VAL_1:v[0-9]+]], int32_t [[VAL_2:v[0-9]+]], size_t [[VAL_3:v[0-9]+]]) { +// CPP-DEFAULT-NEXT: mystruct [[VAL_4:v[0-9]+]]; +// CPP-DEFAULT-NEXT: [[VAL_4]] = [[VAL_1]]; +// CPP-DEFAULT-NEXT: [[VAL_4]].a = [[VAL_2]]; +// CPP-DEFAULT-NEXT: int32_t [[VAL_5:v[0-9]+]] = [[VAL_4]].b; +// CPP-DEFAULT-NEXT: int32_t [[VAL_6:v[0-9]+]]; +// CPP-DEFAULT-NEXT: [[VAL_6]] = [[VAL_5]]; +// CPP-DEFAULT-NEXT: [[VAL_6]] = ([[VAL_4]].c)[[[VAL_3]]]; +// CPP-DEFAULT-NEXT: ([[VAL_4]].d)[[[VAL_3]]] = [[VAL_2]]; +// CPP-DEFAULT-NEXT: return; +// CPP-DEFAULT-NEXT: } + +// CPP-DECLTOP: void member(mystruct [[VAL_1:v[0-9]+]], int32_t [[VAL_2:v[0-9]+]], size_t [[VAL_3:v[0-9]+]]) { +// CPP-DECLTOP-NEXT: mystruct [[VAL_4:v[0-9]+]]; +// CPP-DECLTOP-NEXT: int32_t [[VAL_5:v[0-9]+]]; +// CPP-DECLTOP-NEXT: int32_t [[VAL_6:v[0-9]+]]; +// CPP-DECLTOP-NEXT: ; +// CPP-DECLTOP-NEXT: [[VAL_4]] = [[VAL_1]]; +// CPP-DECLTOP-NEXT: [[VAL_4]].a = [[VAL_2]]; +// CPP-DECLTOP-NEXT: [[VAL_5]] = [[VAL_4]].b; +// CPP-DECLTOP-NEXT: ; +// CPP-DECLTOP-NEXT: [[VAL_6]] = [[VAL_5]]; +// CPP-DECLTOP-NEXT: [[VAL_6]] = ([[VAL_4]].c)[[[VAL_3]]]; +// CPP-DECLTOP-NEXT: ([[VAL_4]].d)[[[VAL_3]]] = [[VAL_2]]; +// CPP-DECLTOP-NEXT: return; +// CPP-DECLTOP-NEXT: } + +func.func @member(%arg0: !emitc.opaque<"mystruct">, %arg1: i32, %arg2: index) { + %0 = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue> + emitc.assign %arg0 : !emitc.opaque<"mystruct"> to %0 : > + %1 = emitc.expression %0 : (!emitc.lvalue>) -> !emitc.lvalue { + %6 = "emitc.member"(%0) <{member = "a"}> : (!emitc.lvalue>) -> !emitc.lvalue + yield %6 : !emitc.lvalue + } + emitc.assign %arg1 : i32 to %1 : + %2 = emitc.expression %0 : (!emitc.lvalue>) -> i32 { + %6 = "emitc.member"(%0) <{member = "b"}> : (!emitc.lvalue>) -> !emitc.lvalue + %7 = load %6 : + yield %7 : i32 + } + %3 = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue + emitc.assign %2 : i32 to %3 : + %4 = emitc.expression %arg2, %0 : (index, !emitc.lvalue>) -> i32 { + %6 = "emitc.member"(%0) <{member = "c"}> : (!emitc.lvalue>) -> !emitc.array<2xi32> + %7 = subscript %6[%arg2] : (!emitc.array<2xi32>, index) -> !emitc.lvalue + %8 = load %7 : + yield %8 : i32 + } + emitc.assign %4 : i32 to %3 : + %5 = emitc.expression %arg2, %0 : (index, !emitc.lvalue>) -> !emitc.lvalue { + %6 = "emitc.member"(%0) <{member = "d"}> : (!emitc.lvalue>) -> !emitc.array<2xi32> + %7 = subscript %6[%arg2] : (!emitc.array<2xi32>, index) -> !emitc.lvalue + yield %7 : !emitc.lvalue + } + emitc.assign %arg1 : i32 to %5 : + return +} + +// CPP-DEFAULT: void member_of_pointer(mystruct* [[VAL_1:v[0-9]+]], int32_t [[VAL_2:v[0-9]+]], size_t [[VAL_3:v[0-9]+]]) { +// CPP-DEFAULT-NEXT: mystruct* [[VAL_4:v[0-9]+]]; +// CPP-DEFAULT-NEXT: [[VAL_4]] = [[VAL_1]]; +// CPP-DEFAULT-NEXT: [[VAL_4]]->a = [[VAL_2]]; +// CPP-DEFAULT-NEXT: int32_t [[VAL_5:v[0-9]+]] = [[VAL_4]]->b; +// CPP-DEFAULT-NEXT: int32_t [[VAL_6:v[0-9]+]]; +// CPP-DEFAULT-NEXT: [[VAL_6]] = [[VAL_5]]; +// CPP-DEFAULT-NEXT: [[VAL_6]] = ([[VAL_4]]->c)[[[VAL_3]]]; +// CPP-DEFAULT-NEXT: ([[VAL_4]]->d)[[[VAL_3]]] = [[VAL_2]]; +// CPP-DEFAULT-NEXT: return; +// CPP-DEFAULT-NEXT: } + +// CPP-DECLTOP: void member_of_pointer(mystruct* [[VAL_1:v[0-9]+]], int32_t [[VAL_2:v[0-9]+]], size_t [[VAL_3:v[0-9]+]]) { +// CPP-DECLTOP-NEXT: mystruct* [[VAL_4:v[0-9]+]]; +// CPP-DECLTOP-NEXT: int32_t [[VAL_5:v[0-9]+]]; +// CPP-DECLTOP-NEXT: int32_t [[VAL_6:v[0-9]+]]; +// CPP-DECLTOP-NEXT: ; +// CPP-DECLTOP-NEXT: [[VAL_4]] = [[VAL_1]]; +// CPP-DECLTOP-NEXT: [[VAL_4]]->a = [[VAL_2]]; +// CPP-DECLTOP-NEXT: [[VAL_5]] = [[VAL_4]]->b; +// CPP-DECLTOP-NEXT: ; +// CPP-DECLTOP-NEXT: [[VAL_6]] = [[VAL_5]]; +// CPP-DECLTOP-NEXT: [[VAL_6]] = ([[VAL_4]]->c)[[[VAL_3]]]; +// CPP-DECLTOP-NEXT: ([[VAL_4]]->d)[[[VAL_3]]] = [[VAL_2]]; +// CPP-DECLTOP-NEXT: return; +// CPP-DECLTOP-NEXT: } + +func.func @member_of_pointer(%arg0: !emitc.ptr>, %arg1: i32, %arg2: index) { + %0 = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue>> + emitc.assign %arg0 : !emitc.ptr> to %0 : >> + %1 = emitc.expression %0 : (!emitc.lvalue>>) -> !emitc.lvalue { + %6 = "emitc.member_of_ptr"(%0) <{member = "a"}> : (!emitc.lvalue>>) -> !emitc.lvalue + yield %6 : !emitc.lvalue + } + emitc.assign %arg1 : i32 to %1 : + %2 = emitc.expression %0 : (!emitc.lvalue>>) -> i32 { + %6 = "emitc.member_of_ptr"(%0) <{member = "b"}> : (!emitc.lvalue>>) -> !emitc.lvalue + %7 = load %6 : + yield %7 : i32 + } + %3 = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue + emitc.assign %2 : i32 to %3 : + %4 = emitc.expression %arg2, %0 : (index, !emitc.lvalue>>) -> i32 { + %6 = "emitc.member_of_ptr"(%0) <{member = "c"}> : (!emitc.lvalue>>) -> !emitc.array<2xi32> + %7 = subscript %6[%arg2] : (!emitc.array<2xi32>, index) -> !emitc.lvalue + %8 = load %7 : + yield %8 : i32 + } + emitc.assign %4 : i32 to %3 : + %5 = emitc.expression %arg2, %0 : (index, !emitc.lvalue>>) -> !emitc.lvalue { + %6 = "emitc.member_of_ptr"(%0) <{member = "d"}> : (!emitc.lvalue>>) -> !emitc.array<2xi32> + %7 = subscript %6[%arg2] : (!emitc.array<2xi32>, index) -> !emitc.lvalue + yield %7 : !emitc.lvalue + } + emitc.assign %arg1 : i32 to %5 : + return +} + +// CPP-DEFAULT: float expression_with_literal(float [[VAL_1:v[0-9]+]]) { +// CPP-DEFAULT-NEXT: return [[VAL_1]] + M_PI; +// CPP-DEFAULT-NEXT: } + +// CPP-DECLTOP: float expression_with_literal(float [[VAL_1:v[0-9]+]]) { +// CPP-DECLTOP-NEXT: return [[VAL_1]] + M_PI; +// CPP-DECLTOP-NEXT: } + +func.func @expression_with_literal(%arg0: f32) -> f32 { + %0 = emitc.expression %arg0 : (f32) -> f32 { + %1 = literal "M_PI" : f32 + %2 = add %arg0, %1 : (f32, f32) -> f32 + yield %2 : f32 + } + return %0 : f32 +} + +// CPP-DEFAULT: bool expression_tree(int32_t [[VAL_1:v[0-9]+]][2000], int32_t [[VAL_2:v[0-9]+]], int32_t [[VAL_3:v[0-9]+]], size_t [[VAL_4:v[0-9]+]]) { +// CPP-DEFAULT-NEXT: bool [[VAL_5:v[0-9]+]] = [[VAL_1]][[[VAL_4]] * 42] - [[VAL_2]] < [[VAL_3]]; +// CPP-DEFAULT-NEXT: return [[VAL_5]]; +// CPP-DEFAULT-NEXT: } + +// CPP-DECLTOP: bool expression_tree(int32_t [[VAL_1:v[0-9]+]][2000], int32_t [[VAL_2:v[0-9]+]], int32_t [[VAL_3:v[0-9]+]], size_t [[VAL_4:v[0-9]+]]) { +// CPP-DECLTOP-NEXT: bool [[VAL_5:v[0-9]+]]; +// CPP-DECLTOP-NEXT: [[VAL_5]] = [[VAL_1]][[[VAL_4]] * 42] - [[VAL_2]] < [[VAL_3]]; +// CPP-DECLTOP-NEXT: return [[VAL_5]]; +// CPP-DECLTOP-NEXT: } + +func.func @expression_tree(%arg0: !emitc.array<2000xi32>, %arg1: i32, %arg2: i32, %arg4: index) -> i1 { + %e1 = emitc.expression %arg0, %arg4 : (!emitc.array<2000xi32>, index) -> !emitc.lvalue { + %c42 = "emitc.constant"(){value = 42 : index} : () -> index + %i = mul %arg4, %c42 : (index, index) -> index + %k = subscript %arg0[%i] : (!emitc.array<2000xi32>, index) -> !emitc.lvalue + yield %k : !emitc.lvalue + } + %e2 = emitc.expression %e1, %arg1, %arg2 : (!emitc.lvalue, i32, i32) -> i1 { + %a = load %e1: !emitc.lvalue + %b = sub %a, %arg1 : (i32, i32) -> i32 + %c = cmp lt, %b, %arg2 :(i32, i32) -> i1 + yield %c : i1 + } + return %e2 : i1 +} diff --git a/mlir/test/Target/Cpp/member.mlir b/mlir/test/Target/Cpp/member.mlir index 6e0395250afbd..45b6336f63ce0 100644 --- a/mlir/test/Target/Cpp/member.mlir +++ b/mlir/test/Target/Cpp/member.mlir @@ -31,9 +31,9 @@ func.func @member(%arg0: !emitc.opaque<"mystruct">, %arg1: i32, %arg2: index) { // CPP-DEFAULT-NEXT: int32_t [[V3:[^ ]*]] = [[V2]].b; // CPP-DEFAULT-NEXT: int32_t [[V4:[^ ]*]]; // CPP-DEFAULT-NEXT: [[V4]] = [[V3]]; -// CPP-DEFAULT-NEXT: int32_t [[V5:[^ ]*]] = [[V2]].c[[[Index]]]; +// CPP-DEFAULT-NEXT: int32_t [[V5:[^ ]*]] = ([[V2]].c)[[[Index]]]; // CPP-DEFAULT-NEXT: [[V4]] = [[V5]]; -// CPP-DEFAULT-NEXT: [[V2]].d[[[Index]]] = [[V1]]; +// CPP-DEFAULT-NEXT: ([[V2]].d)[[[Index]]] = [[V1]]; func.func @member_of_pointer(%arg0: !emitc.ptr>, %arg1: i32, %arg2: index) { @@ -67,6 +67,6 @@ func.func @member_of_pointer(%arg0: !emitc.ptr>, %arg1 // CPP-DEFAULT-NEXT: int32_t [[V3:[^ ]*]] = [[V2]]->b; // CPP-DEFAULT-NEXT: int32_t [[V4:[^ ]*]]; // CPP-DEFAULT-NEXT: [[V4]] = [[V3]]; -// CPP-DEFAULT-NEXT: int32_t [[V5:[^ ]*]] = [[V2]]->c[[[Index]]]; +// CPP-DEFAULT-NEXT: int32_t [[V5:[^ ]*]] = ([[V2]]->c)[[[Index]]]; // CPP-DEFAULT-NEXT: [[V4]] = [[V5]]; -// CPP-DEFAULT-NEXT: [[V2]]->d[[[Index]]] = [[V1]]; +// CPP-DEFAULT-NEXT: ([[V2]]->d)[[[Index]]] = [[V1]];