Skip to content

Commit e9e0167

Browse files
authored
[flang][HLFIR] Relax verifiers of intrinsic operations (#80132)
The verifiers are currently very strict: requiring intrinsic operations to be used only in cases where the Fortran standard permits the intrinsic to be used. There have now been a lot of cases where these verifiers have caused bugs in corner cases. In a recent ticket, @jeanPerier pointed out that it could be useful for future optimizations if somewhat invalid uses of these operations could be allowed in dead code. See this comment: #79995 (comment) In response to all of this, I have decided to relax the intrinsic operation verifiers. The intention is now to only disallow operation uses that are likely to crash the compiler. Other checks are still available under `-strict-intrinsic-verifier`. The disadvantage of this approach is that IR can now represent intrinsic invocations which are incorrect. The lowering and implementation of these intrinsic functions is unlikely to do the right thing in all of these cases, and as they should mostly be impossible to generate using normal Fortran code, these edge cases will see very little testing, before some new optimization causes them to become more common. Fixes #79995
1 parent ca7fd25 commit e9e0167

File tree

3 files changed

+90
-29
lines changed

3 files changed

+90
-29
lines changed

flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,16 @@
2323
#include "mlir/IR/OpImplementation.h"
2424
#include "llvm/ADT/APInt.h"
2525
#include "llvm/ADT/TypeSwitch.h"
26+
#include "llvm/Support/CommandLine.h"
2627
#include <iterator>
2728
#include <mlir/Interfaces/SideEffectInterfaces.h>
2829
#include <optional>
2930
#include <tuple>
3031

32+
static llvm::cl::opt<bool> useStrictIntrinsicVerifier(
33+
"strict-intrinsic-verifier", llvm::cl::init(false),
34+
llvm::cl::desc("use stricter verifier for HLFIR intrinsic operations"));
35+
3136
/// generic implementation of the memory side effects interface for hlfir
3237
/// transformational intrinsic operations
3338
static void
@@ -498,7 +503,7 @@ verifyLogicalReductionOp(LogicalReductionOp reductionOp) {
498503
mlir::Type resultType = results[0];
499504
if (mlir::isa<fir::LogicalType>(resultType)) {
500505
// Result is of the same type as MASK
501-
if (resultType != logicalTy)
506+
if ((resultType != logicalTy) && useStrictIntrinsicVerifier)
502507
return reductionOp->emitOpError(
503508
"result must have the same element type as MASK argument");
504509

@@ -509,7 +514,7 @@ verifyLogicalReductionOp(LogicalReductionOp reductionOp) {
509514
if (!resultExpr.isArray())
510515
return reductionOp->emitOpError("result must be an array");
511516

512-
if (resultExpr.getEleTy() != logicalTy)
517+
if ((resultExpr.getEleTy() != logicalTy) && useStrictIntrinsicVerifier)
513518
return reductionOp->emitOpError(
514519
"result must have the same element type as MASK argument");
515520

@@ -585,7 +590,7 @@ mlir::LogicalResult hlfir::CountOp::verify() {
585590
if (resultShape.size() != (maskShape.size() - 1))
586591
return emitOpError("result rank must be one less than MASK");
587592
} else {
588-
return emitOpError("result must be of numerical scalar type");
593+
return emitOpError("result must be of numerical array type");
589594
}
590595
} else if (!hlfir::isFortranScalarNumericalType(resultType)) {
591596
return emitOpError("result must be of numerical scalar type");
@@ -682,15 +687,18 @@ verifyArrayAndMaskForReductionOp(NumericalReductionOp reductionOp) {
682687
if (!maskShape.empty()) {
683688
if (maskShape.size() != arrayShape.size())
684689
return reductionOp->emitWarning("MASK must be conformable to ARRAY");
685-
static_assert(fir::SequenceType::getUnknownExtent() ==
686-
hlfir::ExprType::getUnknownExtent());
687-
constexpr int64_t unknownExtent = fir::SequenceType::getUnknownExtent();
688-
for (std::size_t i = 0; i < arrayShape.size(); ++i) {
689-
int64_t arrayExtent = arrayShape[i];
690-
int64_t maskExtent = maskShape[i];
691-
if ((arrayExtent != maskExtent) && (arrayExtent != unknownExtent) &&
692-
(maskExtent != unknownExtent))
693-
return reductionOp->emitWarning("MASK must be conformable to ARRAY");
690+
if (useStrictIntrinsicVerifier) {
691+
static_assert(fir::SequenceType::getUnknownExtent() ==
692+
hlfir::ExprType::getUnknownExtent());
693+
constexpr int64_t unknownExtent = fir::SequenceType::getUnknownExtent();
694+
for (std::size_t i = 0; i < arrayShape.size(); ++i) {
695+
int64_t arrayExtent = arrayShape[i];
696+
int64_t maskExtent = maskShape[i];
697+
if ((arrayExtent != maskExtent) && (arrayExtent != unknownExtent) &&
698+
(maskExtent != unknownExtent))
699+
return reductionOp->emitWarning(
700+
"MASK must be conformable to ARRAY");
701+
}
694702
}
695703
}
696704
}
@@ -719,7 +727,7 @@ verifyNumericalReductionOp(NumericalReductionOp reductionOp) {
719727
mlir::Type resultType = results[0];
720728
if (hlfir::isFortranScalarNumericalType(resultType)) {
721729
// Result is of the same type as ARRAY
722-
if (resultType != numTy)
730+
if ((resultType != numTy) && useStrictIntrinsicVerifier)
723731
return reductionOp->emitOpError(
724732
"result must have the same element type as ARRAY argument");
725733

@@ -729,7 +737,7 @@ verifyNumericalReductionOp(NumericalReductionOp reductionOp) {
729737
if (!resultExpr.isArray())
730738
return reductionOp->emitOpError("result must be an array");
731739

732-
if (resultExpr.getEleTy() != numTy)
740+
if ((resultExpr.getEleTy() != numTy) && useStrictIntrinsicVerifier)
733741
return reductionOp->emitOpError(
734742
"result must have the same element type as ARRAY argument");
735743

@@ -792,7 +800,7 @@ verifyCharacterReductionOp(CharacterReductionOp reductionOp) {
792800
"result must be character");
793801

794802
// Result is of the same type as ARRAY
795-
if (resultType != numTy)
803+
if ((resultType != numTy) && useStrictIntrinsicVerifier)
796804
return reductionOp->emitOpError(
797805
"result must have the same element type as ARRAY argument");
798806

@@ -823,9 +831,8 @@ mlir::LogicalResult hlfir::MaxvalOp::verify() {
823831
auto resultExpr = mlir::dyn_cast<hlfir::ExprType>(results[0]);
824832
if (resultExpr && mlir::isa<fir::CharacterType>(resultExpr.getEleTy())) {
825833
return verifyCharacterReductionOp<hlfir::MaxvalOp *>(this);
826-
} else {
827-
return verifyNumericalReductionOp<hlfir::MaxvalOp *>(this);
828834
}
835+
return verifyNumericalReductionOp<hlfir::MaxvalOp *>(this);
829836
}
830837

831838
void hlfir::MaxvalOp::getEffects(
@@ -848,9 +855,8 @@ mlir::LogicalResult hlfir::MinvalOp::verify() {
848855
auto resultExpr = mlir::dyn_cast<hlfir::ExprType>(results[0]);
849856
if (resultExpr && mlir::isa<fir::CharacterType>(resultExpr.getEleTy())) {
850857
return verifyCharacterReductionOp<hlfir::MinvalOp *>(this);
851-
} else {
852-
return verifyNumericalReductionOp<hlfir::MinvalOp *>(this);
853858
}
859+
return verifyNumericalReductionOp<hlfir::MinvalOp *>(this);
854860
}
855861

856862
void hlfir::MinvalOp::getEffects(
@@ -1007,17 +1013,19 @@ mlir::LogicalResult hlfir::DotProductOp::verify() {
10071013

10081014
constexpr int64_t unknownExtent = fir::SequenceType::getUnknownExtent();
10091015
if ((lhsSize != unknownExtent) && (rhsSize != unknownExtent) &&
1010-
(lhsSize != rhsSize))
1016+
(lhsSize != rhsSize) && useStrictIntrinsicVerifier)
10111017
return emitOpError("both arrays must have the same size");
10121018

1013-
if (mlir::isa<fir::LogicalType>(lhsEleTy) !=
1014-
mlir::isa<fir::LogicalType>(rhsEleTy))
1015-
return emitOpError("if one array is logical, so should the other be");
1019+
if (useStrictIntrinsicVerifier) {
1020+
if (mlir::isa<fir::LogicalType>(lhsEleTy) !=
1021+
mlir::isa<fir::LogicalType>(rhsEleTy))
1022+
return emitOpError("if one array is logical, so should the other be");
10161023

1017-
if (mlir::isa<fir::LogicalType>(lhsEleTy) !=
1018-
mlir::isa<fir::LogicalType>(resultTy))
1019-
return emitOpError("the result type should be a logical only if the "
1020-
"argument types are logical");
1024+
if (mlir::isa<fir::LogicalType>(lhsEleTy) !=
1025+
mlir::isa<fir::LogicalType>(resultTy))
1026+
return emitOpError("the result type should be a logical only if the "
1027+
"argument types are logical");
1028+
}
10211029

10221030
if (!hlfir::isFortranScalarNumericalType(resultTy) &&
10231031
!mlir::isa<fir::LogicalType>(resultTy))
@@ -1067,6 +1075,9 @@ mlir::LogicalResult hlfir::MatmulOp::verify() {
10671075
mlir::isa<fir::LogicalType>(rhsEleTy))
10681076
return emitOpError("if one array is logical, so should the other be");
10691077

1078+
if (!useStrictIntrinsicVerifier)
1079+
return mlir::success();
1080+
10701081
int64_t lastLhsDim = lhsShape[lhsRank - 1];
10711082
int64_t firstRhsDim = rhsShape[0];
10721083
constexpr int64_t unknownExtent = fir::SequenceType::getUnknownExtent();
@@ -1179,6 +1190,9 @@ mlir::LogicalResult hlfir::TransposeOp::verify() {
11791190
if (rank != 2 || resultRank != 2)
11801191
return emitOpError("input and output arrays should have rank 2");
11811192

1193+
if (!useStrictIntrinsicVerifier)
1194+
return mlir::success();
1195+
11821196
constexpr int64_t unknownExtent = fir::SequenceType::getUnknownExtent();
11831197
if ((inShape[0] != resultShape[1]) && (inShape[0] != unknownExtent))
11841198
return emitOpError("output shape does not match input array");
@@ -1226,6 +1240,9 @@ mlir::LogicalResult hlfir::MatmulTransposeOp::verify() {
12261240
if ((lhsRank != 2) || ((rhsRank != 1) && (rhsRank != 2)))
12271241
return emitOpError("array must have either rank 1 or rank 2");
12281242

1243+
if (!useStrictIntrinsicVerifier)
1244+
return mlir::success();
1245+
12291246
if (mlir::isa<fir::LogicalType>(lhsEleTy) !=
12301247
mlir::isa<fir::LogicalType>(rhsEleTy))
12311248
return emitOpError("if one array is logical, so should the other be");

flang/test/HLFIR/invalid.fir

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// HLFIR ops diagnotic tests
22

3-
// RUN: fir-opt -split-input-file -verify-diagnostics %s
3+
// RUN: fir-opt -strict-intrinsic-verifier -split-input-file -verify-diagnostics %s
44

55
func.func @bad_declare(%arg0: !fir.ref<f32>) {
66
// expected-error@+1 {{'hlfir.declare' op first result type is inconsistent with variable properties: expected '!fir.ref<f32>'}}
@@ -382,7 +382,7 @@ func.func @bad_count2(%arg0: !hlfir.expr<?x?x!fir.logical<4>>, %arg1: i32){
382382

383383
// -----
384384
func.func @bad_count3(%arg0: !hlfir.expr<?x!fir.logical<4>>, %arg1: i32) {
385-
// expected-error@+1 {{'hlfir.count' op result must be of numerical scalar type}}
385+
// expected-error@+1 {{'hlfir.count' op result must be of numerical array type}}
386386
%0 = hlfir.count %arg0 dim %arg1 : (!hlfir.expr<?x!fir.logical<4>>, i32) -> !hlfir.expr<i32>
387387
}
388388

flang/test/Lower/HLFIR/minval.f90

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,47 @@ end subroutine test_unknown_char_len_result
260260
! CHECK-NEXT: hlfir.destroy %[[EXPR]]
261261
! CHECK-NEXT: return
262262
! CHECK-NEXT: }
263+
264+
! Test edge case with missmatch between argument type !fir.char<1,?> and result
265+
! type !fir.char<1,4>
266+
function test_type_mismatch
267+
character(:), allocatable :: test_type_mismatch(:)
268+
character(3) :: char(3,4)
269+
test_type_mismatch = minval(char//' ', dim=1)
270+
end function
271+
! CHECK-LABEL: func.func @_QPtest_type_mismatch() -> !fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>> {
272+
! CHECK: %[[VAL_0:.*]] = arith.constant 3 : index
273+
! CHECK: %[[VAL_1:.*]] = arith.constant 3 : index
274+
! CHECK: %[[VAL_2:.*]] = arith.constant 4 : index
275+
! CHECK: %[[VAL_3:.*]] = fir.alloca !fir.array<3x4x!fir.char<1,3>> {bindc_name = "char", uniq_name = "_QFtest_type_mismatchEchar"}
276+
! CHECK: %[[VAL_4:.*]] = fir.shape %[[VAL_1]], %[[VAL_2]] : (index, index) -> !fir.shape<2>
277+
! CHECK: %[[VAL_5:.*]]:2 = hlfir.declare %[[VAL_3]](%[[VAL_4]]) typeparams %[[VAL_0]] {uniq_name = "_QFtest_type_mismatchEchar"} : (!fir.ref<!fir.array<3x4x!fir.char<1,3>>>, !fir.shape<2>, index) -> (!fir.ref<!fir.array<3x4x!fir.char<1,3>>>, !fir.ref<!fir.array<3x4x!fir.char<1,3>>>)
278+
! CHECK: %[[VAL_6:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>> {bindc_name = "test_type_mismatch", uniq_name = "_QFtest_type_mismatchEtest_type_mismatch"}
279+
! CHECK: %[[VAL_7:.*]] = fir.zero_bits !fir.heap<!fir.array<?x!fir.char<1,?>>>
280+
! CHECK: %[[VAL_8:.*]] = arith.constant 0 : index
281+
! CHECK: %[[VAL_9:.*]] = fir.shape %[[VAL_8]] : (index) -> !fir.shape<1>
282+
! CHECK: %[[VAL_10:.*]] = arith.constant 0 : index
283+
! CHECK: %[[VAL_11:.*]] = fir.embox %[[VAL_7]](%[[VAL_9]]) typeparams %[[VAL_10]] : (!fir.heap<!fir.array<?x!fir.char<1,?>>>, !fir.shape<1>, index) -> !fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>
284+
! CHECK: fir.store %[[VAL_11]] to %[[VAL_6]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>
285+
! CHECK: %[[VAL_12:.*]]:2 = hlfir.declare %[[VAL_6]] {fortran_attrs = #{{.*}}, uniq_name = "_QFtest_type_mismatchEtest_type_mismatch"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>)
286+
! CHECK: %[[VAL_13:.*]] = fir.address_of(@_QQclX20) : !fir.ref<!fir.char<1>>
287+
! CHECK: %[[VAL_14:.*]] = arith.constant 1 : index
288+
! CHECK: %[[VAL_15:.*]]:2 = hlfir.declare %[[VAL_13]] typeparams %[[VAL_14]] {fortran_attrs = {{.*}}, uniq_name = "_QQclX20"} : (!fir.ref<!fir.char<1>>, index) -> (!fir.ref<!fir.char<1>>, !fir.ref<!fir.char<1>>)
289+
! CHECK: %[[VAL_16:.*]] = arith.addi %[[VAL_0]], %[[VAL_14]] : index
290+
! CHECK: %[[VAL_17:.*]] = hlfir.elemental %[[VAL_4]] typeparams %[[VAL_16]] unordered : (!fir.shape<2>, index) -> !hlfir.expr<3x4x!fir.char<1,?>> {
291+
! CHECK: ^bb0(%[[VAL_18:.*]]: index, %[[VAL_19:.*]]: index):
292+
! CHECK: %[[VAL_20:.*]] = hlfir.designate %[[VAL_5]]#0 (%[[VAL_18]], %[[VAL_19]]) typeparams %[[VAL_0]] : (!fir.ref<!fir.array<3x4x!fir.char<1,3>>>, index, index, index) -> !fir.ref<!fir.char<1,3>>
293+
! CHECK: %[[VAL_21:.*]] = hlfir.concat %[[VAL_20]], %[[VAL_15]]#0 len %[[VAL_16]] : (!fir.ref<!fir.char<1,3>>, !fir.ref<!fir.char<1>>, index) -> !hlfir.expr<!fir.char<1,4>>
294+
! CHECK: hlfir.yield_element %[[VAL_21]] : !hlfir.expr<!fir.char<1,4>>
295+
! CHECK: }
296+
! CHECK: %[[VAL_22:.*]] = arith.constant 1 : i32
297+
! CHECK: %[[VAL_23:.*]] = hlfir.minval %[[VAL_17]] dim %[[VAL_22]] {fastmath = {{.*}}} : (!hlfir.expr<3x4x!fir.char<1,?>>, i32) -> !hlfir.expr<4x!fir.char<1,4>>
298+
! CHECK: hlfir.assign %[[VAL_23]] to %[[VAL_12]]#0 realloc : !hlfir.expr<4x!fir.char<1,4>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>
299+
! CHECK: hlfir.destroy %[[VAL_23]] : !hlfir.expr<4x!fir.char<1,4>>
300+
! CHECK: hlfir.destroy %[[VAL_17]] : !hlfir.expr<3x4x!fir.char<1,?>>
301+
! CHECK: %[[VAL_24:.*]] = fir.load %[[VAL_12]]#1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>
302+
! CHECK: %[[VAL_25:.*]] = arith.constant 1 : index
303+
! CHECK: %[[VAL_26:.*]] = fir.shift %[[VAL_25]] : (index) -> !fir.shift<1>
304+
! CHECK: %[[VAL_27:.*]] = fir.rebox %[[VAL_24]](%[[VAL_26]]) : (!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>, !fir.shift<1>) -> !fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>
305+
! CHECK: return %[[VAL_27]] : !fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>
306+
! CHECK: }

0 commit comments

Comments
 (0)