Skip to content

Commit 95d4362

Browse files
authored
[flang] Added hlfir.eoshift operation definition. (#153105)
This is a basic definition of the operation corresponding to the Fortran's EOSHIFT transformational intrinsic.
1 parent c84a43f commit 95d4362

File tree

4 files changed

+215
-47
lines changed

4 files changed

+215
-47
lines changed

flang/include/flang/Optimizer/HLFIR/HLFIROpBase.td

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@ def IsFortranValuePred : CPred<"::hlfir::isFortranValueType($_self)">;
9595
def AnyFortranValue
9696
: TypeConstraint<IsFortranValuePred, "any Fortran value type">;
9797

98-
99-
def AnyFortranEntity : TypeConstraint<Or<[AnyFortranVariable.predicate,
100-
AnyFortranValue.predicate]>, "any Fortran value or variable type">;
98+
def AnyFortranEntity
99+
: Type<Or<[AnyFortranVariable.predicate, AnyFortranValue.predicate]>,
100+
"any Fortran value or variable type">;
101101

102102
def IsFortranScalarCharacterPred
103103
: CPred<"::hlfir::isFortranScalarCharacterType($_self)">;

flang/include/flang/Optimizer/HLFIR/HLFIROps.td

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,28 @@ def hlfir_CShiftOp
721721
let hasVerifier = 1;
722722
}
723723

724+
def hlfir_EOShiftOp
725+
: hlfir_Op<
726+
"eoshift", [AttrSizedOperandSegments,
727+
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
728+
let summary = "EOSHIFT transformational intrinsic";
729+
let description = [{
730+
End-off shift of an array
731+
}];
732+
733+
let arguments = (ins AnyFortranArrayObject:$array,
734+
AnyFortranIntegerScalarOrArrayObject:$shift,
735+
Optional<AnyFortranEntity>:$boundary, Optional<AnyIntegerType>:$dim);
736+
737+
let results = (outs hlfir_ExprType);
738+
739+
let assemblyFormat = [{
740+
$array $shift (`boundary` $boundary^)? (`dim` $dim^)? attr-dict `:` functional-type(operands, results)
741+
}];
742+
743+
let hasVerifier = 1;
744+
}
745+
724746
def hlfir_ReshapeOp
725747
: hlfir_Op<
726748
"reshape", [AttrSizedOperandSegments,

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

Lines changed: 97 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,96 +1440,148 @@ void hlfir::MatmulTransposeOp::getEffects(
14401440
}
14411441

14421442
//===----------------------------------------------------------------------===//
1443-
// CShiftOp
1443+
// Array shifts: CShiftOp/EOShiftOp
14441444
//===----------------------------------------------------------------------===//
14451445

1446-
llvm::LogicalResult hlfir::CShiftOp::verify() {
1447-
mlir::Value array = getArray();
1446+
template <typename Op>
1447+
static llvm::LogicalResult verifyArrayShift(Op op) {
1448+
mlir::Value array = op.getArray();
14481449
fir::SequenceType arrayTy = mlir::cast<fir::SequenceType>(
14491450
hlfir::getFortranElementOrSequenceType(array.getType()));
14501451
llvm::ArrayRef<int64_t> inShape = arrayTy.getShape();
14511452
std::size_t arrayRank = inShape.size();
14521453
mlir::Type eleTy = arrayTy.getEleTy();
1453-
hlfir::ExprType resultTy = mlir::cast<hlfir::ExprType>(getResult().getType());
1454+
hlfir::ExprType resultTy =
1455+
mlir::cast<hlfir::ExprType>(op.getResult().getType());
14541456
llvm::ArrayRef<int64_t> resultShape = resultTy.getShape();
14551457
std::size_t resultRank = resultShape.size();
14561458
mlir::Type resultEleTy = resultTy.getEleTy();
1457-
mlir::Value shift = getShift();
1459+
mlir::Value shift = op.getShift();
14581460
mlir::Type shiftTy = hlfir::getFortranElementOrSequenceType(shift.getType());
14591461

1460-
// TODO: turn allowCharacterLenMismatch into true.
1461-
if (auto match = areMatchingTypes(*this, eleTy, resultEleTy,
1462-
/*allowCharacterLenMismatch=*/false);
1462+
if (auto match = areMatchingTypes(
1463+
op, eleTy, resultEleTy,
1464+
/*allowCharacterLenMismatch=*/!useStrictIntrinsicVerifier);
14631465
match.failed())
1464-
return emitOpError(
1466+
return op.emitOpError(
14651467
"input and output arrays should have the same element type");
14661468

14671469
if (arrayRank != resultRank)
1468-
return emitOpError("input and output arrays should have the same rank");
1470+
return op.emitOpError("input and output arrays should have the same rank");
14691471

14701472
constexpr int64_t unknownExtent = fir::SequenceType::getUnknownExtent();
14711473
for (auto [inDim, resultDim] : llvm::zip(inShape, resultShape))
14721474
if (inDim != unknownExtent && resultDim != unknownExtent &&
14731475
inDim != resultDim)
1474-
return emitOpError(
1476+
return op.emitOpError(
14751477
"output array's shape conflicts with the input array's shape");
14761478

14771479
int64_t dimVal = -1;
1478-
if (!getDim())
1480+
if (!op.getDim())
14791481
dimVal = 1;
1480-
else if (auto dim = fir::getIntIfConstant(getDim()))
1482+
else if (auto dim = fir::getIntIfConstant(op.getDim()))
14811483
dimVal = *dim;
14821484

14831485
// The DIM argument may be statically invalid (e.g. exceed the
14841486
// input array rank) in dead code after constant propagation,
14851487
// so avoid some checks unless useStrictIntrinsicVerifier is true.
14861488
if (useStrictIntrinsicVerifier && dimVal != -1) {
14871489
if (dimVal < 1)
1488-
return emitOpError("DIM must be >= 1");
1490+
return op.emitOpError("DIM must be >= 1");
14891491
if (dimVal > static_cast<int64_t>(arrayRank))
1490-
return emitOpError("DIM must be <= input array's rank");
1492+
return op.emitOpError("DIM must be <= input array's rank");
14911493
}
14921494

1493-
if (auto shiftSeqTy = mlir::dyn_cast<fir::SequenceType>(shiftTy)) {
1494-
// SHIFT is an array. Verify the rank and the shape (if DIM is constant).
1495-
llvm::ArrayRef<int64_t> shiftShape = shiftSeqTy.getShape();
1496-
std::size_t shiftRank = shiftShape.size();
1497-
if (shiftRank != arrayRank - 1)
1498-
return emitOpError(
1499-
"SHIFT's rank must be 1 less than the input array's rank");
1500-
1501-
if (useStrictIntrinsicVerifier && dimVal != -1) {
1502-
// SHIFT's shape must be [d(1), d(2), ..., d(DIM-1), d(DIM+1), ..., d(n)],
1503-
// where [d(1), d(2), ..., d(n)] is the shape of the ARRAY.
1504-
int64_t arrayDimIdx = 0;
1505-
int64_t shiftDimIdx = 0;
1506-
for (auto shiftDim : shiftShape) {
1507-
if (arrayDimIdx == dimVal - 1)
1495+
// A helper lambda to verify the shape of the array types of
1496+
// certain operands of the array shift (e.g. the SHIFT and BOUNDARY operands).
1497+
auto verifyOperandTypeShape = [&](mlir::Type type,
1498+
llvm::Twine name) -> llvm::LogicalResult {
1499+
if (auto opndSeqTy = mlir::dyn_cast<fir::SequenceType>(type)) {
1500+
// The operand is an array. Verify the rank and the shape (if DIM is
1501+
// constant).
1502+
llvm::ArrayRef<int64_t> opndShape = opndSeqTy.getShape();
1503+
std::size_t opndRank = opndShape.size();
1504+
if (opndRank != arrayRank - 1)
1505+
return op.emitOpError(
1506+
name + "'s rank must be 1 less than the input array's rank");
1507+
1508+
if (useStrictIntrinsicVerifier && dimVal != -1) {
1509+
// The operand's shape must be
1510+
// [d(1), d(2), ..., d(DIM-1), d(DIM+1), ..., d(n)],
1511+
// where [d(1), d(2), ..., d(n)] is the shape of the ARRAY.
1512+
int64_t arrayDimIdx = 0;
1513+
int64_t opndDimIdx = 0;
1514+
for (auto opndDim : opndShape) {
1515+
if (arrayDimIdx == dimVal - 1)
1516+
++arrayDimIdx;
1517+
1518+
if (inShape[arrayDimIdx] != unknownExtent &&
1519+
opndDim != unknownExtent && inShape[arrayDimIdx] != opndDim)
1520+
return op.emitOpError("SHAPE(ARRAY)(" +
1521+
llvm::Twine(arrayDimIdx + 1) +
1522+
") must be equal to SHAPE(" + name + ")(" +
1523+
llvm::Twine(opndDimIdx + 1) +
1524+
"): " + llvm::Twine(inShape[arrayDimIdx]) +
1525+
" != " + llvm::Twine(opndDim));
15081526
++arrayDimIdx;
1509-
1510-
if (inShape[arrayDimIdx] != unknownExtent &&
1511-
shiftDim != unknownExtent && inShape[arrayDimIdx] != shiftDim)
1512-
return emitOpError("SHAPE(ARRAY)(" + llvm::Twine(arrayDimIdx + 1) +
1513-
") must be equal to SHAPE(SHIFT)(" +
1514-
llvm::Twine(shiftDimIdx + 1) +
1515-
"): " + llvm::Twine(inShape[arrayDimIdx]) +
1516-
" != " + llvm::Twine(shiftDim));
1517-
++arrayDimIdx;
1518-
++shiftDimIdx;
1527+
++opndDimIdx;
1528+
}
15191529
}
15201530
}
1531+
return mlir::success();
1532+
};
1533+
1534+
if (failed(verifyOperandTypeShape(shiftTy, "SHIFT")))
1535+
return mlir::failure();
1536+
1537+
if constexpr (std::is_same_v<Op, hlfir::EOShiftOp>) {
1538+
if (mlir::Value boundary = op.getBoundary()) {
1539+
mlir::Type boundaryTy =
1540+
hlfir::getFortranElementOrSequenceType(boundary.getType());
1541+
if (auto match = areMatchingTypes(
1542+
op, eleTy, hlfir::getFortranElementType(boundaryTy),
1543+
/*allowCharacterLenMismatch=*/!useStrictIntrinsicVerifier);
1544+
match.failed())
1545+
return op.emitOpError(
1546+
"ARRAY and BOUNDARY operands must have the same element type");
1547+
if (failed(verifyOperandTypeShape(boundaryTy, "BOUNDARY")))
1548+
return mlir::failure();
1549+
}
15211550
}
15221551

15231552
return mlir::success();
15241553
}
15251554

1555+
//===----------------------------------------------------------------------===//
1556+
// CShiftOp
1557+
//===----------------------------------------------------------------------===//
1558+
1559+
llvm::LogicalResult hlfir::CShiftOp::verify() {
1560+
return verifyArrayShift(*this);
1561+
}
1562+
15261563
void hlfir::CShiftOp::getEffects(
15271564
llvm::SmallVectorImpl<
15281565
mlir::SideEffects::EffectInstance<mlir::MemoryEffects::Effect>>
15291566
&effects) {
15301567
getIntrinsicEffects(getOperation(), effects);
15311568
}
15321569

1570+
//===----------------------------------------------------------------------===//
1571+
// EOShiftOp
1572+
//===----------------------------------------------------------------------===//
1573+
1574+
llvm::LogicalResult hlfir::EOShiftOp::verify() {
1575+
return verifyArrayShift(*this);
1576+
}
1577+
1578+
void hlfir::EOShiftOp::getEffects(
1579+
llvm::SmallVectorImpl<
1580+
mlir::SideEffects::EffectInstance<mlir::MemoryEffects::Effect>>
1581+
&effects) {
1582+
getIntrinsicEffects(getOperation(), effects);
1583+
}
1584+
15331585
//===----------------------------------------------------------------------===//
15341586
// ReshapeOp
15351587
//===----------------------------------------------------------------------===//
@@ -1543,7 +1595,8 @@ llvm::LogicalResult hlfir::ReshapeOp::verify() {
15431595
hlfir::getFortranElementOrSequenceType(array.getType()));
15441596
if (auto match = areMatchingTypes(
15451597
*this, hlfir::getFortranElementType(resultType),
1546-
arrayType.getElementType(), /*allowCharacterLenMismatch=*/true);
1598+
arrayType.getElementType(),
1599+
/*allowCharacterLenMismatch=*/!useStrictIntrinsicVerifier);
15471600
match.failed())
15481601
return emitOpError("ARRAY and the result must have the same element type");
15491602
if (hlfir::isPolymorphicType(resultType) !=
@@ -1565,9 +1618,9 @@ llvm::LogicalResult hlfir::ReshapeOp::verify() {
15651618
if (mlir::Value pad = getPad()) {
15661619
auto padArrayType = mlir::cast<fir::SequenceType>(
15671620
hlfir::getFortranElementOrSequenceType(pad.getType()));
1568-
if (auto match = areMatchingTypes(*this, arrayType.getElementType(),
1569-
padArrayType.getElementType(),
1570-
/*allowCharacterLenMismatch=*/true);
1621+
if (auto match = areMatchingTypes(
1622+
*this, arrayType.getElementType(), padArrayType.getElementType(),
1623+
/*allowCharacterLenMismatch=*/!useStrictIntrinsicVerifier);
15711624
match.failed())
15721625
return emitOpError("ARRAY and PAD must be of the same type");
15731626
}

flang/test/HLFIR/invalid.fir

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1555,3 +1555,96 @@ func.func @bad_reshape(%arg0: !hlfir.expr<1x!fir.char<1,2>>, %arg1: !hlfir.expr<
15551555
%0 = hlfir.reshape %arg0 %arg1 pad %arg2 : (!hlfir.expr<1x!fir.char<1,2>>, !hlfir.expr<1xi32>, !hlfir.expr<1x!fir.char<2,?>>) -> !hlfir.expr<?x!fir.char<1,?>>
15561556
return
15571557
}
1558+
1559+
// -----
1560+
1561+
func.func @bad_eoshift1(%arg0: !hlfir.expr<?x?xi32>, %arg1: i32) {
1562+
// expected-error@+1 {{'hlfir.eoshift' op input and output arrays should have the same element type}}
1563+
%0 = hlfir.eoshift %arg0 %arg1 : (!hlfir.expr<?x?xi32>, i32) -> !hlfir.expr<?x?xf32>
1564+
return
1565+
}
1566+
1567+
// -----
1568+
1569+
func.func @bad_eoshift2(%arg0: !hlfir.expr<?x?xi32>, %arg1: i32) {
1570+
// expected-error@+1 {{'hlfir.eoshift' op input and output arrays should have the same rank}}
1571+
%0 = hlfir.eoshift %arg0 %arg1 : (!hlfir.expr<?x?xi32>, i32) -> !hlfir.expr<?xi32>
1572+
return
1573+
}
1574+
1575+
// -----
1576+
1577+
func.func @bad_eoshift3(%arg0: !hlfir.expr<2x2xi32>, %arg1: i32) {
1578+
// expected-error@+1 {{'hlfir.eoshift' op output array's shape conflicts with the input array's shape}}
1579+
%0 = hlfir.eoshift %arg0 %arg1 : (!hlfir.expr<2x2xi32>, i32) -> !hlfir.expr<2x3xi32>
1580+
return
1581+
}
1582+
1583+
// -----
1584+
1585+
func.func @bad_eoshift4(%arg0: !hlfir.expr<2x2xi32>, %arg1: i32) {
1586+
%c0 = arith.constant 0 : index
1587+
// expected-error@+1 {{'hlfir.eoshift' op DIM must be >= 1}}
1588+
%0 = hlfir.eoshift %arg0 %arg1 dim %c0 : (!hlfir.expr<2x2xi32>, i32, index) -> !hlfir.expr<2x2xi32>
1589+
return
1590+
}
1591+
1592+
// -----
1593+
1594+
func.func @bad_eoshift5(%arg0: !hlfir.expr<2x2xi32>, %arg1: i32) {
1595+
%c10 = arith.constant 10 : index
1596+
// expected-error@+1 {{'hlfir.eoshift' op DIM must be <= input array's rank}}
1597+
%0 = hlfir.eoshift %arg0 %arg1 dim %c10 : (!hlfir.expr<2x2xi32>, i32, index) -> !hlfir.expr<2x2xi32>
1598+
return
1599+
}
1600+
1601+
// -----
1602+
1603+
func.func @bad_eoshift6(%arg0: !hlfir.expr<2x2xi32>, %arg1: !hlfir.expr<2x2xi32>) {
1604+
// expected-error@+1 {{'hlfir.eoshift' op SHIFT's rank must be 1 less than the input array's rank}}
1605+
%0 = hlfir.eoshift %arg0 %arg1 : (!hlfir.expr<2x2xi32>, !hlfir.expr<2x2xi32>) -> !hlfir.expr<2x2xi32>
1606+
return
1607+
}
1608+
1609+
// -----
1610+
1611+
func.func @bad_eoshift7(%arg0: !hlfir.expr<?x2xi32>, %arg1: !hlfir.expr<3xi32>) {
1612+
%c1 = arith.constant 1 : index
1613+
// expected-error@+1 {{'hlfir.eoshift' op SHAPE(ARRAY)(2) must be equal to SHAPE(SHIFT)(1): 2 != 3}}
1614+
%0 = hlfir.eoshift %arg0 %arg1 dim %c1 : (!hlfir.expr<?x2xi32>, !hlfir.expr<3xi32>, index) -> !hlfir.expr<2x2xi32>
1615+
return
1616+
}
1617+
1618+
// -----
1619+
1620+
func.func @bad_eoshift8(%arg0: !hlfir.expr<?x!fir.char<1,?>>, %arg1: i32) {
1621+
// expected-error@+2 {{'hlfir.eoshift' op character KIND mismatch}}
1622+
// expected-error@+1 {{'hlfir.eoshift' op input and output arrays should have the same element type}}
1623+
%0 = hlfir.eoshift %arg0 %arg1 : (!hlfir.expr<?x!fir.char<1,?>>, i32) -> !hlfir.expr<?x!fir.char<2,?>>
1624+
return
1625+
}
1626+
1627+
// -----
1628+
1629+
func.func @bad_eoshift9(%arg0: !hlfir.expr<?x!fir.char<1,1>>, %arg1: i32) {
1630+
// expected-error@+2 {{'hlfir.eoshift' op character LEN mismatch}}
1631+
// expected-error@+1 {{'hlfir.eoshift' op input and output arrays should have the same element type}}
1632+
%0 = hlfir.eoshift %arg0 %arg1 : (!hlfir.expr<?x!fir.char<1,1>>, i32) -> !hlfir.expr<?x!fir.char<1,2>>
1633+
return
1634+
}
1635+
1636+
// -----
1637+
1638+
func.func @bad_eoshift10(%arg0: !hlfir.expr<2x2xi32>, %arg1: i32, %arg2: f32) {
1639+
// expected-error@+1 {{'hlfir.eoshift' op ARRAY and BOUNDARY operands must have the same element type}}
1640+
%0 = hlfir.eoshift %arg0 %arg1 boundary %arg2 : (!hlfir.expr<2x2xi32>, i32, f32) -> !hlfir.expr<2x2xi32>
1641+
return
1642+
}
1643+
1644+
// -----
1645+
1646+
func.func @bad_eoshift11(%arg0: !hlfir.expr<2x2xi32>, %arg1: i32, %arg2: !hlfir.expr<2x2xi32>) {
1647+
// expected-error@+1 {{'hlfir.eoshift' op BOUNDARY's rank must be 1 less than the input array's rank}}
1648+
%0 = hlfir.eoshift %arg0 %arg1 boundary %arg2 : (!hlfir.expr<2x2xi32>, i32, !hlfir.expr<2x2xi32>) -> !hlfir.expr<2x2xi32>
1649+
return
1650+
}

0 commit comments

Comments
 (0)