Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions flang/include/flang/Optimizer/HLFIR/HLFIROpBase.td
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ def IsFortranValuePred : CPred<"::hlfir::isFortranValueType($_self)">;
def AnyFortranValue
: TypeConstraint<IsFortranValuePred, "any Fortran value type">;


def AnyFortranEntity : TypeConstraint<Or<[AnyFortranVariable.predicate,
AnyFortranValue.predicate]>, "any Fortran value or variable type">;
def AnyFortranEntity
: Type<Or<[AnyFortranVariable.predicate, AnyFortranValue.predicate]>,
"any Fortran value or variable type">;

def IsFortranScalarCharacterPred
: CPred<"::hlfir::isFortranScalarCharacterType($_self)">;
Expand Down
22 changes: 22 additions & 0 deletions flang/include/flang/Optimizer/HLFIR/HLFIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,28 @@ def hlfir_CShiftOp
let hasVerifier = 1;
}

def hlfir_EOShiftOp
: hlfir_Op<
"eoshift", [AttrSizedOperandSegments,
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
let summary = "EOSHIFT transformational intrinsic";
let description = [{
End-off shift of an array
}];

let arguments = (ins AnyFortranArrayObject:$array,
AnyFortranIntegerScalarOrArrayObject:$shift,
Optional<AnyFortranEntity>:$boundary, Optional<AnyIntegerType>:$dim);

let results = (outs hlfir_ExprType);

let assemblyFormat = [{
$array $shift (`boundary` $boundary^)? (`dim` $dim^)? attr-dict `:` functional-type(operands, results)
}];

let hasVerifier = 1;
}

def hlfir_ReshapeOp
: hlfir_Op<
"reshape", [AttrSizedOperandSegments,
Expand Down
141 changes: 97 additions & 44 deletions flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1440,96 +1440,148 @@ void hlfir::MatmulTransposeOp::getEffects(
}

//===----------------------------------------------------------------------===//
// CShiftOp
// Array shifts: CShiftOp/EOShiftOp
//===----------------------------------------------------------------------===//

llvm::LogicalResult hlfir::CShiftOp::verify() {
mlir::Value array = getArray();
template <typename Op>
static llvm::LogicalResult verifyArrayShift(Op op) {
mlir::Value array = op.getArray();
fir::SequenceType arrayTy = mlir::cast<fir::SequenceType>(
hlfir::getFortranElementOrSequenceType(array.getType()));
llvm::ArrayRef<int64_t> inShape = arrayTy.getShape();
std::size_t arrayRank = inShape.size();
mlir::Type eleTy = arrayTy.getEleTy();
hlfir::ExprType resultTy = mlir::cast<hlfir::ExprType>(getResult().getType());
hlfir::ExprType resultTy =
mlir::cast<hlfir::ExprType>(op.getResult().getType());
llvm::ArrayRef<int64_t> resultShape = resultTy.getShape();
std::size_t resultRank = resultShape.size();
mlir::Type resultEleTy = resultTy.getEleTy();
mlir::Value shift = getShift();
mlir::Value shift = op.getShift();
mlir::Type shiftTy = hlfir::getFortranElementOrSequenceType(shift.getType());

// TODO: turn allowCharacterLenMismatch into true.
if (auto match = areMatchingTypes(*this, eleTy, resultEleTy,
/*allowCharacterLenMismatch=*/false);
if (auto match = areMatchingTypes(
op, eleTy, resultEleTy,
/*allowCharacterLenMismatch=*/!useStrictIntrinsicVerifier);
match.failed())
return emitOpError(
return op.emitOpError(
"input and output arrays should have the same element type");

if (arrayRank != resultRank)
return emitOpError("input and output arrays should have the same rank");
return op.emitOpError("input and output arrays should have the same rank");

constexpr int64_t unknownExtent = fir::SequenceType::getUnknownExtent();
for (auto [inDim, resultDim] : llvm::zip(inShape, resultShape))
if (inDim != unknownExtent && resultDim != unknownExtent &&
inDim != resultDim)
return emitOpError(
return op.emitOpError(
"output array's shape conflicts with the input array's shape");

int64_t dimVal = -1;
if (!getDim())
if (!op.getDim())
dimVal = 1;
else if (auto dim = fir::getIntIfConstant(getDim()))
else if (auto dim = fir::getIntIfConstant(op.getDim()))
dimVal = *dim;

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

if (auto shiftSeqTy = mlir::dyn_cast<fir::SequenceType>(shiftTy)) {
// SHIFT is an array. Verify the rank and the shape (if DIM is constant).
llvm::ArrayRef<int64_t> shiftShape = shiftSeqTy.getShape();
std::size_t shiftRank = shiftShape.size();
if (shiftRank != arrayRank - 1)
return emitOpError(
"SHIFT's rank must be 1 less than the input array's rank");

if (useStrictIntrinsicVerifier && dimVal != -1) {
// SHIFT's shape must be [d(1), d(2), ..., d(DIM-1), d(DIM+1), ..., d(n)],
// where [d(1), d(2), ..., d(n)] is the shape of the ARRAY.
int64_t arrayDimIdx = 0;
int64_t shiftDimIdx = 0;
for (auto shiftDim : shiftShape) {
if (arrayDimIdx == dimVal - 1)
// A helper lambda to verify the shape of the array types of
// certain operands of the array shift (e.g. the SHIFT and BOUNDARY operands).
auto verifyOperandTypeShape = [&](mlir::Type type,
llvm::Twine name) -> llvm::LogicalResult {
if (auto opndSeqTy = mlir::dyn_cast<fir::SequenceType>(type)) {
// The operand is an array. Verify the rank and the shape (if DIM is
// constant).
llvm::ArrayRef<int64_t> opndShape = opndSeqTy.getShape();
std::size_t opndRank = opndShape.size();
if (opndRank != arrayRank - 1)
return op.emitOpError(
name + "'s rank must be 1 less than the input array's rank");

if (useStrictIntrinsicVerifier && dimVal != -1) {
// The operand's shape must be
// [d(1), d(2), ..., d(DIM-1), d(DIM+1), ..., d(n)],
// where [d(1), d(2), ..., d(n)] is the shape of the ARRAY.
int64_t arrayDimIdx = 0;
int64_t opndDimIdx = 0;
for (auto opndDim : opndShape) {
if (arrayDimIdx == dimVal - 1)
++arrayDimIdx;

if (inShape[arrayDimIdx] != unknownExtent &&
opndDim != unknownExtent && inShape[arrayDimIdx] != opndDim)
return op.emitOpError("SHAPE(ARRAY)(" +
llvm::Twine(arrayDimIdx + 1) +
") must be equal to SHAPE(" + name + ")(" +
llvm::Twine(opndDimIdx + 1) +
"): " + llvm::Twine(inShape[arrayDimIdx]) +
" != " + llvm::Twine(opndDim));
++arrayDimIdx;

if (inShape[arrayDimIdx] != unknownExtent &&
shiftDim != unknownExtent && inShape[arrayDimIdx] != shiftDim)
return emitOpError("SHAPE(ARRAY)(" + llvm::Twine(arrayDimIdx + 1) +
") must be equal to SHAPE(SHIFT)(" +
llvm::Twine(shiftDimIdx + 1) +
"): " + llvm::Twine(inShape[arrayDimIdx]) +
" != " + llvm::Twine(shiftDim));
++arrayDimIdx;
++shiftDimIdx;
++opndDimIdx;
}
}
}
return mlir::success();
};

if (failed(verifyOperandTypeShape(shiftTy, "SHIFT")))
return mlir::failure();

if constexpr (std::is_same_v<Op, hlfir::EOShiftOp>) {
if (mlir::Value boundary = op.getBoundary()) {
mlir::Type boundaryTy =
hlfir::getFortranElementOrSequenceType(boundary.getType());
if (auto match = areMatchingTypes(
op, eleTy, hlfir::getFortranElementType(boundaryTy),
/*allowCharacterLenMismatch=*/!useStrictIntrinsicVerifier);
match.failed())
return op.emitOpError(
"ARRAY and BOUNDARY operands must have the same element type");
if (failed(verifyOperandTypeShape(boundaryTy, "BOUNDARY")))
return mlir::failure();
}
}

return mlir::success();
}

//===----------------------------------------------------------------------===//
// CShiftOp
//===----------------------------------------------------------------------===//

llvm::LogicalResult hlfir::CShiftOp::verify() {
return verifyArrayShift(*this);
}

void hlfir::CShiftOp::getEffects(
llvm::SmallVectorImpl<
mlir::SideEffects::EffectInstance<mlir::MemoryEffects::Effect>>
&effects) {
getIntrinsicEffects(getOperation(), effects);
}

//===----------------------------------------------------------------------===//
// EOShiftOp
//===----------------------------------------------------------------------===//

llvm::LogicalResult hlfir::EOShiftOp::verify() {
return verifyArrayShift(*this);
}

void hlfir::EOShiftOp::getEffects(
llvm::SmallVectorImpl<
mlir::SideEffects::EffectInstance<mlir::MemoryEffects::Effect>>
&effects) {
getIntrinsicEffects(getOperation(), effects);
}

//===----------------------------------------------------------------------===//
// ReshapeOp
//===----------------------------------------------------------------------===//
Expand All @@ -1543,7 +1595,8 @@ llvm::LogicalResult hlfir::ReshapeOp::verify() {
hlfir::getFortranElementOrSequenceType(array.getType()));
if (auto match = areMatchingTypes(
*this, hlfir::getFortranElementType(resultType),
arrayType.getElementType(), /*allowCharacterLenMismatch=*/true);
arrayType.getElementType(),
/*allowCharacterLenMismatch=*/!useStrictIntrinsicVerifier);
match.failed())
return emitOpError("ARRAY and the result must have the same element type");
if (hlfir::isPolymorphicType(resultType) !=
Expand All @@ -1565,9 +1618,9 @@ llvm::LogicalResult hlfir::ReshapeOp::verify() {
if (mlir::Value pad = getPad()) {
auto padArrayType = mlir::cast<fir::SequenceType>(
hlfir::getFortranElementOrSequenceType(pad.getType()));
if (auto match = areMatchingTypes(*this, arrayType.getElementType(),
padArrayType.getElementType(),
/*allowCharacterLenMismatch=*/true);
if (auto match = areMatchingTypes(
*this, arrayType.getElementType(), padArrayType.getElementType(),
/*allowCharacterLenMismatch=*/!useStrictIntrinsicVerifier);
match.failed())
return emitOpError("ARRAY and PAD must be of the same type");
}
Expand Down
93 changes: 93 additions & 0 deletions flang/test/HLFIR/invalid.fir
Original file line number Diff line number Diff line change
Expand Up @@ -1555,3 +1555,96 @@ func.func @bad_reshape(%arg0: !hlfir.expr<1x!fir.char<1,2>>, %arg1: !hlfir.expr<
%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,?>>
return
}

// -----

func.func @bad_eoshift1(%arg0: !hlfir.expr<?x?xi32>, %arg1: i32) {
// expected-error@+1 {{'hlfir.eoshift' op input and output arrays should have the same element type}}
%0 = hlfir.eoshift %arg0 %arg1 : (!hlfir.expr<?x?xi32>, i32) -> !hlfir.expr<?x?xf32>
return
}

// -----

func.func @bad_eoshift2(%arg0: !hlfir.expr<?x?xi32>, %arg1: i32) {
// expected-error@+1 {{'hlfir.eoshift' op input and output arrays should have the same rank}}
%0 = hlfir.eoshift %arg0 %arg1 : (!hlfir.expr<?x?xi32>, i32) -> !hlfir.expr<?xi32>
return
}

// -----

func.func @bad_eoshift3(%arg0: !hlfir.expr<2x2xi32>, %arg1: i32) {
// expected-error@+1 {{'hlfir.eoshift' op output array's shape conflicts with the input array's shape}}
%0 = hlfir.eoshift %arg0 %arg1 : (!hlfir.expr<2x2xi32>, i32) -> !hlfir.expr<2x3xi32>
return
}

// -----

func.func @bad_eoshift4(%arg0: !hlfir.expr<2x2xi32>, %arg1: i32) {
%c0 = arith.constant 0 : index
// expected-error@+1 {{'hlfir.eoshift' op DIM must be >= 1}}
%0 = hlfir.eoshift %arg0 %arg1 dim %c0 : (!hlfir.expr<2x2xi32>, i32, index) -> !hlfir.expr<2x2xi32>
return
}

// -----

func.func @bad_eoshift5(%arg0: !hlfir.expr<2x2xi32>, %arg1: i32) {
%c10 = arith.constant 10 : index
// expected-error@+1 {{'hlfir.eoshift' op DIM must be <= input array's rank}}
%0 = hlfir.eoshift %arg0 %arg1 dim %c10 : (!hlfir.expr<2x2xi32>, i32, index) -> !hlfir.expr<2x2xi32>
return
}

// -----

func.func @bad_eoshift6(%arg0: !hlfir.expr<2x2xi32>, %arg1: !hlfir.expr<2x2xi32>) {
// expected-error@+1 {{'hlfir.eoshift' op SHIFT's rank must be 1 less than the input array's rank}}
%0 = hlfir.eoshift %arg0 %arg1 : (!hlfir.expr<2x2xi32>, !hlfir.expr<2x2xi32>) -> !hlfir.expr<2x2xi32>
return
}

// -----

func.func @bad_eoshift7(%arg0: !hlfir.expr<?x2xi32>, %arg1: !hlfir.expr<3xi32>) {
%c1 = arith.constant 1 : index
// expected-error@+1 {{'hlfir.eoshift' op SHAPE(ARRAY)(2) must be equal to SHAPE(SHIFT)(1): 2 != 3}}
%0 = hlfir.eoshift %arg0 %arg1 dim %c1 : (!hlfir.expr<?x2xi32>, !hlfir.expr<3xi32>, index) -> !hlfir.expr<2x2xi32>
return
}

// -----

func.func @bad_eoshift8(%arg0: !hlfir.expr<?x!fir.char<1,?>>, %arg1: i32) {
// expected-error@+2 {{'hlfir.eoshift' op character KIND mismatch}}
// expected-error@+1 {{'hlfir.eoshift' op input and output arrays should have the same element type}}
%0 = hlfir.eoshift %arg0 %arg1 : (!hlfir.expr<?x!fir.char<1,?>>, i32) -> !hlfir.expr<?x!fir.char<2,?>>
return
}

// -----

func.func @bad_eoshift9(%arg0: !hlfir.expr<?x!fir.char<1,1>>, %arg1: i32) {
// expected-error@+2 {{'hlfir.eoshift' op character LEN mismatch}}
// expected-error@+1 {{'hlfir.eoshift' op input and output arrays should have the same element type}}
%0 = hlfir.eoshift %arg0 %arg1 : (!hlfir.expr<?x!fir.char<1,1>>, i32) -> !hlfir.expr<?x!fir.char<1,2>>
return
}

// -----

func.func @bad_eoshift10(%arg0: !hlfir.expr<2x2xi32>, %arg1: i32, %arg2: f32) {
// expected-error@+1 {{'hlfir.eoshift' op ARRAY and BOUNDARY operands must have the same element type}}
%0 = hlfir.eoshift %arg0 %arg1 boundary %arg2 : (!hlfir.expr<2x2xi32>, i32, f32) -> !hlfir.expr<2x2xi32>
return
}

// -----

func.func @bad_eoshift11(%arg0: !hlfir.expr<2x2xi32>, %arg1: i32, %arg2: !hlfir.expr<2x2xi32>) {
// expected-error@+1 {{'hlfir.eoshift' op BOUNDARY's rank must be 1 less than the input array's rank}}
%0 = hlfir.eoshift %arg0 %arg1 boundary %arg2 : (!hlfir.expr<2x2xi32>, i32, !hlfir.expr<2x2xi32>) -> !hlfir.expr<2x2xi32>
return
}