diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td --- a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td +++ b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td @@ -319,13 +319,15 @@ let hasVerifier = 1; } -def hlfir_ConcatOp : hlfir_Op<"concat", []> { +// Allocation effect is because this creates a hlfir.expr, which must be +// destroyed (only once) +def hlfir_ConcatOp : hlfir_Op<"concat", [MemoryEffects<[MemAlloc]>]> { let summary = "concatenate characters"; let description = [{ Concatenate two or more character strings of a same character kind. }]; - let arguments = (ins Variadic:$strings, + let arguments = (ins Arg, "", [MemRead]>:$strings, AnyIntegerType:$length); let results = (outs AnyScalarCharacterExpr); @@ -340,7 +342,9 @@ let hasVerifier = 1; } -def hlfir_AllOp : hlfir_Op<"all", []> { +// Allocation effect is because this might create a hlfir.expr, which must be +// destroyed (only once) +def hlfir_AllOp : hlfir_Op<"all", [MemoryEffects<[MemAlloc]>]> { let summary = "ALL transformational intrinsic"; let description = [{ Takes a logical array MASK as argument, optionally along a particular dimension, @@ -348,7 +352,7 @@ }]; let arguments = (ins - AnyFortranLogicalArrayObject:$mask, + Arg:$mask, Optional:$dim ); @@ -361,7 +365,9 @@ let hasVerifier = 1; } -def hlfir_AnyOp : hlfir_Op<"any", []> { +// Allocation effect is because this might create a hlfir.expr, which must be +// destroyed (only once) +def hlfir_AnyOp : hlfir_Op<"any", [MemoryEffects<[MemAlloc]>]> { let summary = "ANY transformational intrinsic"; let description = [{ Takes a logical array MASK as argument, optionally along a particular dimension, @@ -369,7 +375,7 @@ }]; let arguments = (ins - AnyFortranLogicalArrayObject:$mask, + Arg:$mask, Optional:$dim ); @@ -382,14 +388,16 @@ let hasVerifier = 1; } -def hlfir_CountOp : hlfir_Op<"count", [AttrSizedOperandSegments]> { +// Allocation effect is because this might create a hlfir.expr, which must be +// destroyed (only once) +def hlfir_CountOp : hlfir_Op<"count", [AttrSizedOperandSegments, MemoryEffects<[MemAlloc]>]> { let summary = "COUNT transformational intrinsic"; let description = [{ Takes a logical and counts the number of true values. }]; let arguments = (ins - AnyFortranLogicalArrayObject:$mask, + Arg:$mask, Optional:$dim, Optional:$kind ); @@ -403,9 +411,11 @@ let hasVerifier = 1; } - +// Allocation effect is because this might create a hlfir.expr, which must be +// destroyed (only once) def hlfir_ProductOp : hlfir_Op<"product", [AttrSizedOperandSegments, - DeclareOpInterfaceMethods]> { + DeclareOpInterfaceMethods, + MemoryEffects<[MemAlloc]>]> { let summary = "PRODUCT transformational intrinsic"; let description = [{ Multiplies the elements of an array, optionally along a particular dimension, @@ -413,9 +423,9 @@ }]; let arguments = (ins - AnyFortranNumericalArrayObject:$array, + Arg:$array, Optional:$dim, - Optional:$mask, + Arg, "", [MemRead]>:$mask, DefaultValuedAttr:$fastmath ); @@ -429,14 +439,16 @@ let hasVerifier = 1; } -def hlfir_SetLengthOp : hlfir_Op<"set_length", []> { +// Allocation effect is because this might create a hlfir.expr, which must be +// destroyed (only once) +def hlfir_SetLengthOp : hlfir_Op<"set_length", [MemoryEffects<[MemAlloc]>]> { let summary = "change the length of a character entity"; let description = [{ Change the length of character entity. This trims or pads the character argument according to the new length. }]; - let arguments = (ins AnyScalarCharacterEntity:$string, + let arguments = (ins Arg:$string, AnyIntegerType:$length); let results = (outs AnyScalarCharacterExpr); @@ -467,8 +479,11 @@ let hasCanonicalizeMethod = 1; } +// Allocation effect is because this might create a hlfir.expr, which must be +// destroyed (only once) def hlfir_SumOp : hlfir_Op<"sum", [AttrSizedOperandSegments, - DeclareOpInterfaceMethods]> { + DeclareOpInterfaceMethods, + MemoryEffects<[MemAlloc]>]> { let summary = "SUM transformational intrinsic"; let description = [{ Sums the elements of an array, optionally along a particular dimension, @@ -476,7 +491,7 @@ }]; let arguments = (ins - AnyFortranNumericalArrayObject:$array, + Arg:$array, Optional:$dim, Optional:$mask, DefaultValuedAttr]> { + [DeclareOpInterfaceMethods, + MemoryEffects<[MemAlloc]>]> { let summary = "DOT_PRODUCT transformational intrinsic"; let description = [{ Dot product of two vectors }]; let arguments = (ins - AnyFortranNumericalOrLogicalArrayObject:$lhs, - AnyFortranNumericalOrLogicalArrayObject:$rhs, + Arg:$lhs, + Arg:$rhs, DefaultValuedAttr:$fastmath ); @@ -515,16 +533,18 @@ let hasVerifier = 1; } +// Allocation effect is because this creates a hlfir.expr, which must be +// destroyed (only once) def hlfir_MatmulOp : hlfir_Op<"matmul", - [DeclareOpInterfaceMethods]> { + [DeclareOpInterfaceMethods, MemoryEffects<[MemAlloc]>]> { let summary = "MATMUL transformational intrinsic"; let description = [{ Matrix multiplication }]; let arguments = (ins - AnyFortranNumericalOrLogicalArrayObject:$lhs, - AnyFortranNumericalOrLogicalArrayObject:$rhs, + Arg:$lhs, + Arg:$rhs, DefaultValuedAttr:$fastmath ); @@ -541,13 +561,15 @@ let hasVerifier = 1; } -def hlfir_TransposeOp : hlfir_Op<"transpose", []> { +// Allocation effect is because this creates a hlfir.expr, which must be +// destroyed (only once) +def hlfir_TransposeOp : hlfir_Op<"transpose", [MemoryEffects<[MemAlloc]>]> { let summary = "TRANSPOSE transformational intrinsic"; let description = [{ Transpose a rank 2 array }]; - let arguments = (ins AnyFortranArrayObject:$array); + let arguments = (ins Arg:$array); let results = (outs hlfir_ExprType); @@ -558,16 +580,19 @@ let hasVerifier = 1; } +// Allocation effect is because this creates a hlfir.expr, which must be +// destroyed (only once) def hlfir_MatmulTransposeOp : hlfir_Op<"matmul_transpose", - [DeclareOpInterfaceMethods]> { + [DeclareOpInterfaceMethods, + MemoryEffects<[MemAlloc]>]> { let summary = "Optimized MATMUL(TRANSPOSE(...), ...)"; let description = [{ Matrix multiplication where the left hand side is transposed }]; let arguments = (ins - AnyFortranNumericalOrLogicalArrayObject:$lhs, - AnyFortranNumericalOrLogicalArrayObject:$rhs, + Arg:$lhs, + Arg:$rhs, DefaultValuedAttr:$fastmath ); @@ -581,8 +606,14 @@ let hasVerifier = 1; } +// this needs a read effect on the source argument because it can generate +// a fir.load when the source is polymorphic +// An allocation effect is needed because the value produced by the associate +// is "deallocated" by hlfir.end_associate (the end_associate must not be +// removed, and there must be only one hlfir.end_associate). def hlfir_AssociateOp : hlfir_Op<"associate", [AttrSizedOperandSegments, - DeclareOpInterfaceMethods]> { + DeclareOpInterfaceMethods, + MemoryEffects<[MemAlloc]>]> { let summary = "Create a variable from an expression value"; let description = [{ Create a variable from an expression value. @@ -592,7 +623,7 @@ }]; let arguments = (ins - AnyFortranValue:$source, + Arg:$source, Optional:$shape, Variadic:$typeparams, Builtin_StringAttr:$uniq_name, @@ -635,7 +666,7 @@ }]; } -def hlfir_EndAssociateOp : hlfir_Op<"end_associate", []> { +def hlfir_EndAssociateOp : hlfir_Op<"end_associate", [MemoryEffects<[MemFree]>]> { let summary = "Mark the end of life of a variable associated to an expression"; let description = [{ @@ -652,7 +683,9 @@ let builders = [OpBuilder<(ins "hlfir::AssociateOp":$associate)>]; } -def hlfir_AsExprOp : hlfir_Op<"as_expr", []> { +// Allocation effect is because this creates a hlfir.expr, which must be +// destroyed (only once) +def hlfir_AsExprOp : hlfir_Op<"as_expr", [MemoryEffects<[MemAlloc]>]> { let summary = "Take the value of an array, character or derived variable"; let description = [{ @@ -669,7 +702,7 @@ hlfir.expr value without paying the price of introducing a copy. }]; - let arguments = (ins AnyFortranVariable:$var, + let arguments = (ins Arg:$var, Optional:$must_free); let results = (outs hlfir_ExprType); @@ -928,7 +961,7 @@ let assemblyFormat = "$expr attr-dict `:` qualified(type($expr))"; } -def hlfir_CopyInOp : hlfir_Op<"copy_in", []> { +def hlfir_CopyInOp : hlfir_Op<"copy_in", [MemoryEffects<[MemAlloc]>]> { let summary = "copy a variable into a contiguous temporary if it is not contiguous"; let description = [{ Copy a variable into a contiguous temporary if the variable is not @@ -948,7 +981,7 @@ is true and, when it is false, the original value will be returned instead. }]; - let arguments = (ins fir_BaseBoxType:$var, + let arguments = (ins Arg:$var, Optional:$var_is_present); let results = (outs fir_BaseBoxType, I1); @@ -975,7 +1008,7 @@ }]; } -def hlfir_CopyOutOp : hlfir_Op<"copy_out", []> { +def hlfir_CopyOutOp : hlfir_Op<"copy_out", [MemoryEffects<[MemFree]>]> { let summary = "copy out a variable after a copy in"; let description = [{ If the variable was copied in a temporary in the related hlfir.copy_in, @@ -988,7 +1021,7 @@ let arguments = (ins fir_BaseBoxType:$temp, I1:$was_copied, - Optional:$var); + Arg, "", [MemWrite]>:$var); let assemblyFormat = [{ $temp `,` $was_copied (`to` $var^)? @@ -1540,7 +1573,9 @@ let hasCanonicalizeMethod = 1; } -def hlfir_CharExtremumOp : hlfir_Op<"char_extremum", []> { +// Allocation effect because this creates a hlfir.expr, which must be destroyed +// (only once) +def hlfir_CharExtremumOp : hlfir_Op<"char_extremum", [MemoryEffects<[MemAlloc]>]> { let summary = "Find max/min from given character strings"; let description = [{ Find the lexicographical minimum or maximum of two or more character @@ -1551,7 +1586,7 @@ }]; let arguments = (ins hlfir_CharExtremumPredicateAttr:$predicate, - Variadic:$strings + Arg, "", [MemRead]>:$strings ); let results = (outs AnyScalarCharacterExpr); diff --git a/flang/test/HLFIR/memory-effects.fir b/flang/test/HLFIR/memory-effects.fir new file mode 100644 --- /dev/null +++ b/flang/test/HLFIR/memory-effects.fir @@ -0,0 +1,13 @@ +// RUN: fir-opt %s --test-side-effects --verify-diagnostics + +func.func @concat(%arg0: !fir.ref>, %arg1: !fir.ref>) { +// expected-remark@+1 {{operation has no memory effects}} + %c30 = arith.constant 30 : index +// expected-remark@+2 {{found an instance of 'allocate' on resource ''}} +// expected-remark@+1 {{found an instance of 'read' on a value, on resource ''}} + %0 = hlfir.concat %arg0, %arg1 len %c30 : (!fir.ref>, !fir.ref>, index) -> (!hlfir.expr>) +// expected-remark@+1 {{operation has no memory effects}} + return +} + +// TODO: testing for other operations diff --git a/flang/test/HLFIR/order_assignments/where-scheduling.f90 b/flang/test/HLFIR/order_assignments/where-scheduling.f90 --- a/flang/test/HLFIR/order_assignments/where-scheduling.f90 +++ b/flang/test/HLFIR/order_assignments/where-scheduling.f90 @@ -139,12 +139,9 @@ !CHECK-NEXT: run 2 evaluate: where/region_assign1 !CHECK-NEXT: run 3 evaluate: where/region_assign2 !CHECK-LABEL: ------------ scheduling where in _QPrhs_lhs_conflict ------------ -!CHECK-NEXT: unknown effect: %{{.*}} = hlfir.transpose %{{.*}} : (!fir.box>) -> !hlfir.expr -!CHECK-NEXT: conflict: R/W: %6 = hlfir.designate %{{.*}} (%{{.*}}, %{{.*}}) : (!fir.box>, index, index) -> !fir.ref W: -!CHECK-NEXT: run 1 save : where/mask -!CHECK-NEXT: unknown effect: %{{.*}} = hlfir.transpose %{{.*}} : (!fir.box>) -> !hlfir.expr -!CHECK-NEXT: run 2 save (w): where/region_assign1/rhs -!CHECK-NEXT: run 3 evaluate: where/region_assign1 +!CHECK-NEXT: conflict: R/W: of type '!fir.box>' at index: 0 W: of type '!fir.box>' at index: 0 +!CHECK-NEXT: run 1 save : where/region_assign1/rhs +!CHECK-NEXT: run 2 evaluate: where/region_assign1 !CHECK-LABEL: ------------ scheduling where in _QPwhere_construct_no_conflict ------------ !CHECK-NEXT: run 1 evaluate: where/region_assign1 !CHECK-NEXT: run 2 evaluate: where/elsewhere1/region_assign1 diff --git a/flang/tools/fir-opt/CMakeLists.txt b/flang/tools/fir-opt/CMakeLists.txt --- a/flang/tools/fir-opt/CMakeLists.txt +++ b/flang/tools/fir-opt/CMakeLists.txt @@ -6,6 +6,7 @@ if(FLANG_INCLUDE_TESTS) set(test_libs FIRTestAnalysis + MLIRTestIR ) endif() @@ -35,5 +36,4 @@ MLIRSupport MLIRVectorToLLVM MLIROptLib - ) diff --git a/flang/tools/fir-opt/fir-opt.cpp b/flang/tools/fir-opt/fir-opt.cpp --- a/flang/tools/fir-opt/fir-opt.cpp +++ b/flang/tools/fir-opt/fir-opt.cpp @@ -24,6 +24,11 @@ } // namespace test } // namespace fir +// Defined in mlir/test, no pulic header. +namespace mlir { +void registerSideEffectTestPasses(); +} + int main(int argc, char **argv) { fir::support::registerMLIRPassesForFortranTools(); fir::registerOptCodeGenPasses(); @@ -31,6 +36,7 @@ hlfir::registerHLFIRPasses(); #ifdef FLANG_INCLUDE_TESTS fir::test::registerTestFIRAliasAnalysisPass(); + mlir::registerSideEffectTestPasses(); #endif DialectRegistry registry; fir::support::registerDialects(registry);