Skip to content

Commit c9fb37c

Browse files
authored
[flang][FIR] add fir.assumed_size_extent to abstract assumed-size extent encoding (#164452)
The purpose of this patch is to allow converting FIR array representation to memref when possible without hitting memref verifier issue. The issue was that FIR arrays may be assumed size, in which case the last dimension will not be known at runtime. Flang uses -1 to encode this to fulfill Fortran 2023 standard requirements in 18.5.3 point 5 about CFI_desc_t. When arrays are converted to memeref, if this `-1` reaches memeref operations, it triggers verifier errors (even if the conversion happened in code that guards the code to be entered at runtime if the array is assumed-size because folders/verifiers do not take into account reachability). This follows-up on discussions in #163505 merge requests
1 parent 104b783 commit c9fb37c

File tree

16 files changed

+165
-18
lines changed

16 files changed

+165
-18
lines changed

flang/include/flang/Optimizer/Dialect/FIROps.td

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,41 @@ def fir_IsAssumedSizeOp : fir_SimpleOp<"is_assumed_size", [NoMemoryEffect]> {
12491249
let results = (outs BoolLike);
12501250
}
12511251

1252+
def fir_AssumedSizeExtentOp : fir_SimpleOneResultOp<"assumed_size_extent", [NoMemoryEffect]> {
1253+
let summary = "get the assumed-size last extent sentinel";
1254+
1255+
let description = [{
1256+
Returns the special extent value representing the last dimension of an
1257+
assumed-size array. This is used to model the semantics in FIR without
1258+
directly materializing the sentinel value. The concrete encoding is
1259+
introduced during FIR to LLVM lowering.
1260+
1261+
```
1262+
%e = fir.assumed_size_extent : index
1263+
```
1264+
}];
1265+
1266+
let results = (outs Index);
1267+
let assemblyFormat = "attr-dict `:` type(results)";
1268+
}
1269+
1270+
def fir_IsAssumedSizeExtentOp : fir_SimpleOp<"is_assumed_size_extent", [NoMemoryEffect]> {
1271+
let summary = "is value the assumed-size last extent sentinel";
1272+
1273+
let description = [{
1274+
Returns true iff the given integer equals the assumed-size extent sentinel.
1275+
1276+
```
1277+
%t = fir.is_assumed_size_extent %v : (index) -> i1
1278+
%c = fir.is_assumed_size_extent %x : (i32) -> i1
1279+
```
1280+
}];
1281+
1282+
let arguments = (ins AnyIntegerLike:$val);
1283+
let results = (outs BoolLike);
1284+
let hasCanonicalizer = 1;
1285+
}
1286+
12521287
def fir_BoxIsPtrOp : fir_SimpleOp<"box_isptr", [NoMemoryEffect]> {
12531288
let summary = "is the boxed value a POINTER?";
12541289

flang/lib/Lower/Bridge.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4010,8 +4010,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
40104010
// parameters and dynamic type. The selector cannot be a
40114011
// POINTER/ALLOCATBLE as per F'2023 C1160.
40124012
fir::ExtendedValue newExv;
4013-
llvm::SmallVector assumeSizeExtents{
4014-
builder->createMinusOneInteger(loc, builder->getIndexType())};
4013+
llvm::SmallVector<mlir::Value> assumeSizeExtents{
4014+
fir::AssumedSizeExtentOp::create(*builder, loc)};
40154015
mlir::Value baseAddr =
40164016
hlfir::genVariableRawAddress(loc, *builder, selector);
40174017
const bool isVolatile = fir::isa_volatile_type(selector.getType());

flang/lib/Lower/ConvertVariable.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1711,7 +1711,7 @@ static void lowerExplicitLowerBounds(
17111711
/// CFI_desc_t requirements in 18.5.3 point 5.).
17121712
static mlir::Value getAssumedSizeExtent(mlir::Location loc,
17131713
fir::FirOpBuilder &builder) {
1714-
return builder.createMinusOneInteger(loc, builder.getIndexType());
1714+
return fir::AssumedSizeExtentOp::create(builder, loc);
17151715
}
17161716

17171717
/// Lower explicit extents into \p result if this is an explicit-shape or

flang/lib/Optimizer/CodeGen/CodeGen.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,44 @@ struct VolatileCastOpConversion
749749
}
750750
};
751751

752+
/// Lower `fir.assumed_size_extent` to constant -1 of index type.
753+
struct AssumedSizeExtentOpConversion
754+
: public fir::FIROpConversion<fir::AssumedSizeExtentOp> {
755+
using FIROpConversion::FIROpConversion;
756+
757+
llvm::LogicalResult
758+
matchAndRewrite(fir::AssumedSizeExtentOp op, OpAdaptor,
759+
mlir::ConversionPatternRewriter &rewriter) const override {
760+
mlir::Location loc = op.getLoc();
761+
mlir::Type ity = lowerTy().indexType();
762+
auto cst = fir::genConstantIndex(loc, ity, rewriter, -1);
763+
rewriter.replaceOp(op, cst.getResult());
764+
return mlir::success();
765+
}
766+
};
767+
768+
/// Lower `fir.is_assumed_size_extent` to integer equality with -1.
769+
struct IsAssumedSizeExtentOpConversion
770+
: public fir::FIROpConversion<fir::IsAssumedSizeExtentOp> {
771+
using FIROpConversion::FIROpConversion;
772+
773+
llvm::LogicalResult
774+
matchAndRewrite(fir::IsAssumedSizeExtentOp op, OpAdaptor adaptor,
775+
mlir::ConversionPatternRewriter &rewriter) const override {
776+
mlir::Location loc = op.getLoc();
777+
mlir::Value val = adaptor.getVal();
778+
mlir::Type valTy = val.getType();
779+
// Create constant -1 of the operand type.
780+
auto negOneAttr = rewriter.getIntegerAttr(valTy, -1);
781+
auto negOne =
782+
mlir::LLVM::ConstantOp::create(rewriter, loc, valTy, negOneAttr);
783+
auto cmp = mlir::LLVM::ICmpOp::create(
784+
rewriter, loc, mlir::LLVM::ICmpPredicate::eq, val, negOne);
785+
rewriter.replaceOp(op, cmp.getResult());
786+
return mlir::success();
787+
}
788+
};
789+
752790
/// convert value of from-type to value of to-type
753791
struct ConvertOpConversion : public fir::FIROpConversion<fir::ConvertOp> {
754792
using FIROpConversion::FIROpConversion;
@@ -4360,6 +4398,7 @@ void fir::populateFIRToLLVMConversionPatterns(
43604398
AllocaOpConversion, AllocMemOpConversion, BoxAddrOpConversion,
43614399
BoxCharLenOpConversion, BoxDimsOpConversion, BoxEleSizeOpConversion,
43624400
BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion,
4401+
AssumedSizeExtentOpConversion, IsAssumedSizeExtentOpConversion,
43634402
BoxOffsetOpConversion, BoxProcHostOpConversion, BoxRankOpConversion,
43644403
BoxTypeCodeOpConversion, BoxTypeDescOpConversion, CallOpConversion,
43654404
CmpcOpConversion, VolatileCastOpConversion, ConvertOpConversion,

flang/lib/Optimizer/Dialect/FIROps.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5142,6 +5142,34 @@ void fir::BoxTotalElementsOp::getCanonicalizationPatterns(
51425142
patterns.add<SimplifyBoxTotalElementsOp>(context);
51435143
}
51445144

5145+
//===----------------------------------------------------------------------===//
5146+
// IsAssumedSizeExtentOp and AssumedSizeExtentOp
5147+
//===----------------------------------------------------------------------===//
5148+
5149+
namespace {
5150+
struct FoldIsAssumedSizeExtentOnCtor
5151+
: public mlir::OpRewritePattern<fir::IsAssumedSizeExtentOp> {
5152+
using mlir::OpRewritePattern<fir::IsAssumedSizeExtentOp>::OpRewritePattern;
5153+
mlir::LogicalResult
5154+
matchAndRewrite(fir::IsAssumedSizeExtentOp op,
5155+
mlir::PatternRewriter &rewriter) const override {
5156+
if (llvm::isa_and_nonnull<fir::AssumedSizeExtentOp>(
5157+
op.getVal().getDefiningOp())) {
5158+
mlir::Type i1 = rewriter.getI1Type();
5159+
rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
5160+
op, i1, rewriter.getIntegerAttr(i1, 1));
5161+
return mlir::success();
5162+
}
5163+
return mlir::failure();
5164+
}
5165+
};
5166+
} // namespace
5167+
5168+
void fir::IsAssumedSizeExtentOp::getCanonicalizationPatterns(
5169+
mlir::RewritePatternSet &patterns, mlir::MLIRContext *context) {
5170+
patterns.add<FoldIsAssumedSizeExtentOnCtor>(context);
5171+
}
5172+
51455173
//===----------------------------------------------------------------------===//
51465174
// LocalitySpecifierOp
51475175
//===----------------------------------------------------------------------===//

flang/lib/Optimizer/Transforms/ArrayValueCopy.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -832,8 +832,8 @@ static mlir::Type getEleTy(mlir::Type ty) {
832832
static bool isAssumedSize(llvm::SmallVectorImpl<mlir::Value> &extents) {
833833
if (extents.empty())
834834
return false;
835-
auto cstLen = fir::getIntIfConstant(extents.back());
836-
return cstLen.has_value() && *cstLen == -1;
835+
return llvm::isa_and_nonnull<fir::AssumedSizeExtentOp>(
836+
extents.back().getDefiningOp());
837837
}
838838

839839
// Extract extents from the ShapeOp/ShapeShiftOp into the result vector.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: fir-opt --fir-to-llvm-ir="target=x86_64-unknown-linux-gnu" %s | FileCheck %s
2+
3+
// CHECK-LABEL: @assumed_size_extent(
4+
// CHECK: %[[CNEG1:.*]] = llvm.mlir.constant(-1 : i64)
5+
// CHECK: llvm.return %[[CNEG1]] : i64
6+
func.func @assumed_size_extent() -> index {
7+
%e = fir.assumed_size_extent : index
8+
return %e : index
9+
}
10+
11+
// CHECK-LABEL: @is_assumed_size_extent(
12+
// CHECK: %[[NEG1:.*]] = llvm.mlir.constant(-1 : i64)
13+
// CHECK: %[[CMP:.*]] = llvm.icmp "eq"
14+
// CHECK: llvm.return %[[CMP]] : i1
15+
func.func @is_assumed_size_extent(%x: index) -> i1 {
16+
%c = fir.is_assumed_size_extent %x : (index) -> i1
17+
return %c : i1
18+
}
19+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: fir-opt --canonicalize %s | FileCheck %s
2+
3+
// Verify: fir.is_assumed_size_extent(fir.assumed_size_extent) folds to i1 true.
4+
5+
// CHECK-LABEL: func.func @fold(
6+
func.func @fold() -> i1 {
7+
%e = fir.assumed_size_extent : index
8+
// CHECK: %[[C:.*]] = arith.constant true
9+
%t = fir.is_assumed_size_extent %e : (index) -> i1
10+
return %t : i1
11+
}
12+
13+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: fir-opt %s | fir-opt | FileCheck %s
2+
3+
func.func @roundtrip() {
4+
// CHECK: %[[E:.*]] = fir.assumed_size_extent : index
5+
%e = fir.assumed_size_extent : index
6+
7+
// CHECK: %[[T:.*]] = fir.is_assumed_size_extent %[[E]] : (index) -> i1
8+
%t = fir.is_assumed_size_extent %e : (index) -> i1
9+
10+
return
11+
}
12+
13+

flang/test/HLFIR/assumed-type-actual-args.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ subroutine s5b(x)
113113
! CHECK-LABEL: func.func @_QPtest2(
114114
! CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.array<?xnone>> {fir.bindc_name = "x"}) {
115115
! CHECK: %[[DSCOPE:.*]] = fir.dummy_scope : !fir.dscope
116-
! CHECK: %[[VAL_1:.*]] = arith.constant -1 : index
116+
! CHECK: %[[VAL_1:.*]] = fir.assumed_size_extent : index
117117
! CHECK: %[[VAL_2:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1>
118118
! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_0]](%[[VAL_2]]) dummy_scope %[[DSCOPE]] {uniq_name = "_QFtest2Ex"} : (!fir.ref<!fir.array<?xnone>>, !fir.shape<1>, !fir.dscope) -> (!fir.box<!fir.array<?xnone>>, !fir.ref<!fir.array<?xnone>>)
119119
! CHECK: fir.call @_QPs2(%[[VAL_3]]#1) fastmath<contract> : (!fir.ref<!fir.array<?xnone>>) -> ()

0 commit comments

Comments
 (0)