Skip to content

Commit

Permalink
[flang][codegen] Add a conversion for fir.coordinate_of - part 2
Browse files Browse the repository at this point in the history
This patch extends the `FIRToLLVMLowering` pass in Flang by extending
the hook to transform `fir.coordinate_of` into a sequence of LLVM MLIR
instructions (i.e. `CoordinateOpConversion::doRewrite`). The following
case is added:
  3.1 the input object is inside `!fir.ref` (e.g. `!fir.ref<!fir.array>` or
      `!fir.ref<!fir.type>`).
  3.2 the input object is inside `!fir.ptr` (e.g. `!fir.ptr<!fir.array>` or
      `!fir.ptr<!fir.type>`).
From the point of view of the conversion, 3.1 and 3.2 are currently identical.

This is part of the upstreaming effort from the `fir-dev` branch in [1].

[1] https://github.com/flang-compiler/f18-llvm-project

Originally written by:
Co-authored-by: Jean Perier <jperier@nvidia.com>
Co-authored-by: Eric Schweitz <eschweitz@nvidia.com>
Co-authored-by: V Donaldson <vdonaldson@nvidia.com>

Depends on: D114159

Differential Revision: https://reviews.llvm.org/D115333
  • Loading branch information
banach-space committed Dec 17, 2021
1 parent 5675b61 commit 6d655ad
Show file tree
Hide file tree
Showing 6 changed files with 333 additions and 20 deletions.
195 changes: 179 additions & 16 deletions flang/lib/Optimizer/CodeGen/CodeGen.cpp
Expand Up @@ -2940,17 +2940,13 @@ struct CoordinateOpConversion
return success();
}

// Box type - get the base pointer from the box
if (auto boxTy = baseObjectTy.dyn_cast<fir::BoxType>()) {
doRewriteBox(coor, ty, operands, loc, rewriter);
return success();
}
// Boxed type - get the base pointer from the box
if (baseObjectTy.dyn_cast<fir::BoxType>())
return doRewriteBox(coor, ty, operands, loc, rewriter);

// Sequence type (e.g. fir.array)
if (auto arrTy = objectTy.dyn_cast<fir::SequenceType>()) {
doRewriteSequence(loc);
return success();
}
// Reference or pointer type
if (baseObjectTy.isa<fir::ReferenceType, fir::PointerType>())
return doRewriteRefOrPtr(coor, ty, operands, loc, rewriter);

return rewriter.notifyMatchFailure(
coor, "fir.coordinate_of base operand has unsupported type");
Expand All @@ -2976,10 +2972,70 @@ struct CoordinateOpConversion
fir::emitFatalError(val.getLoc(), "must be a constant");
}

bool hasSubDimensions(mlir::Type type) const {
return type.isa<fir::SequenceType, fir::RecordType, mlir::TupleType>();
}

/// Check whether this form of `!fir.coordinate_of` is supported. These
/// additional checks are required, because we are not yet able to convert
/// all valid forms of `!fir.coordinate_of`.
/// TODO: Either implement the unsupported cases or extend the verifier
/// in FIROps.cpp instead.
bool supportedCoordinate(mlir::Type type, mlir::ValueRange coors) const {
const std::size_t numOfCoors = coors.size();
std::size_t i = 0;
bool subEle = false;
bool ptrEle = false;
for (; i < numOfCoors; ++i) {
mlir::Value nxtOpnd = coors[i];
if (auto arrTy = type.dyn_cast<fir::SequenceType>()) {
subEle = true;
i += arrTy.getDimension() - 1;
type = arrTy.getEleTy();
} else if (auto recTy = type.dyn_cast<fir::RecordType>()) {
subEle = true;
type = recTy.getType(getFieldNumber(recTy, nxtOpnd));
} else if (auto tupTy = type.dyn_cast<mlir::TupleType>()) {
subEle = true;
type = tupTy.getType(getIntValue(nxtOpnd));
} else {
ptrEle = true;
}
}
if (ptrEle)
return (!subEle) && (numOfCoors == 1);
return subEle && (i >= numOfCoors);
}

/// Walk the abstract memory layout and determine if the path traverses any
/// array types with unknown shape. Return true iff all the array types have a
/// constant shape along the path.
bool arraysHaveKnownShape(mlir::Type type, mlir::ValueRange coors) const {
const std::size_t sz = coors.size();
std::size_t i = 0;
for (; i < sz; ++i) {
mlir::Value nxtOpnd = coors[i];
if (auto arrTy = type.dyn_cast<fir::SequenceType>()) {
if (fir::sequenceWithNonConstantShape(arrTy))
return false;
i += arrTy.getDimension() - 1;
type = arrTy.getEleTy();
} else if (auto strTy = type.dyn_cast<fir::RecordType>()) {
type = strTy.getType(getFieldNumber(strTy, nxtOpnd));
} else if (auto strTy = type.dyn_cast<mlir::TupleType>()) {
type = strTy.getType(getIntValue(nxtOpnd));
} else {
return true;
}
}
return true;
}

private:
void doRewriteBox(fir::CoordinateOp coor, mlir::Type ty,
mlir::ValueRange operands, mlir::Location loc,
mlir::ConversionPatternRewriter &rewriter) const {
mlir::LogicalResult
doRewriteBox(fir::CoordinateOp coor, mlir::Type ty, mlir::ValueRange operands,
mlir::Location loc,
mlir::ConversionPatternRewriter &rewriter) const {
mlir::Type boxObjTy = coor.getBaseType();
assert(boxObjTy.dyn_cast<fir::BoxType>() && "This is not a `fir.box`");

Expand Down Expand Up @@ -3064,11 +3120,118 @@ struct CoordinateOpConversion
}

rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(coor, ty, resultAddr);
return;
return success();
}

void doRewriteSequence(mlir::Location loc) const {
TODO(loc, "fir.coordinate_of codegen for sequence types");
mlir::LogicalResult
doRewriteRefOrPtr(fir::CoordinateOp coor, mlir::Type ty,
mlir::ValueRange operands, mlir::Location loc,
mlir::ConversionPatternRewriter &rewriter) const {
mlir::Type baseObjectTy = coor.getBaseType();

mlir::Type currentObjTy = fir::dyn_cast_ptrOrBoxEleTy(baseObjectTy);
bool hasSubdimension = hasSubDimensions(currentObjTy);
bool columnIsDeferred = !hasSubdimension;

if (!supportedCoordinate(currentObjTy, operands.drop_front(1))) {
TODO(loc, "unsupported combination of coordinate operands");
}

const bool hasKnownShape =
arraysHaveKnownShape(currentObjTy, operands.drop_front(1));

// If only the column is `?`, then we can simply place the column value in
// the 0-th GEP position.
if (auto arrTy = currentObjTy.dyn_cast<fir::SequenceType>()) {
if (!hasKnownShape) {
const unsigned sz = arrTy.getDimension();
if (arraysHaveKnownShape(arrTy.getEleTy(),
operands.drop_front(1 + sz))) {
llvm::ArrayRef<int64_t> shape = arrTy.getShape();
bool allConst = true;
for (unsigned i = 0; i < sz - 1; ++i) {
if (shape[i] < 0) {
allConst = false;
break;
}
}
if (allConst)
columnIsDeferred = true;
}
}
}

if (fir::hasDynamicSize(fir::unwrapSequenceType(currentObjTy))) {
mlir::emitError(
loc, "fir.coordinate_of with a dynamic element size is unsupported");
return failure();
}

if (hasKnownShape || columnIsDeferred) {
SmallVector<mlir::Value> offs;
if (hasKnownShape && hasSubdimension) {
mlir::LLVM::ConstantOp c0 =
genConstantIndex(loc, lowerTy().indexType(), rewriter, 0);
offs.push_back(c0);
}
const std::size_t sz = operands.size();
Optional<int> dims;
SmallVector<mlir::Value> arrIdx;
for (std::size_t i = 1; i < sz; ++i) {
mlir::Value nxtOpnd = operands[i];

if (!currentObjTy) {
mlir::emitError(loc, "invalid coordinate/check failed");
return failure();
}

// check if the i-th coordinate relates to an array
if (dims.hasValue()) {
arrIdx.push_back(nxtOpnd);
int dimsLeft = *dims;
if (dimsLeft > 1) {
dims = dimsLeft - 1;
continue;
}
currentObjTy = currentObjTy.cast<fir::SequenceType>().getEleTy();
// append array range in reverse (FIR arrays are column-major)
offs.append(arrIdx.rbegin(), arrIdx.rend());
arrIdx.clear();
dims.reset();
continue;
}
if (auto arrTy = currentObjTy.dyn_cast<fir::SequenceType>()) {
int d = arrTy.getDimension() - 1;
if (d > 0) {
dims = d;
arrIdx.push_back(nxtOpnd);
continue;
}
currentObjTy = currentObjTy.cast<fir::SequenceType>().getEleTy();
offs.push_back(nxtOpnd);
continue;
}

// check if the i-th coordinate relates to a field
if (auto recTy = currentObjTy.dyn_cast<fir::RecordType>())
currentObjTy = recTy.getType(getFieldNumber(recTy, nxtOpnd));
else if (auto tupTy = currentObjTy.dyn_cast<mlir::TupleType>())
currentObjTy = tupTy.getType(getIntValue(nxtOpnd));
else
currentObjTy = nullptr;

offs.push_back(nxtOpnd);
}
if (dims.hasValue())
offs.append(arrIdx.rbegin(), arrIdx.rend());
mlir::Value base = operands[0];
mlir::Value retval = genGEP(loc, ty, rewriter, base, offs);
rewriter.replaceOp(coor, retval);
return success();
}

mlir::emitError(loc, "fir.coordinate_of base operand has unsupported type");
return failure();
}
};

Expand Down
8 changes: 8 additions & 0 deletions flang/test/Fir/Todo/cordinate_of_5.fir
@@ -0,0 +1,8 @@
// RUN: %not_todo_cmd fir-opt --fir-to-llvm-ir="target=x86_64-unknown-linux-gnu" %s 2>&1 | FileCheck %s

// CHECK: unsupported combination of coordinate operands
func @test_coordinate_of(%arr : !fir.ref<!fir.array<2 x !fir.char<10, 2>>>, %arg1: index) {
%1 = arith.constant 10 : i32
%2 = fir.coordinate_of %arr, %arg1, %1 : (!fir.ref<!fir.array<2 x !fir.char<10, 2>>>, index, i32) -> !fir.ref<!fir.char<1,10>>
return
}
8 changes: 8 additions & 0 deletions flang/test/Fir/Todo/cordinate_of_6.fir
@@ -0,0 +1,8 @@
// RUN: %not_todo_cmd fir-opt --fir-to-llvm-ir="target=x86_64-unknown-linux-gnu" %s 2>&1 | FileCheck %s

// CHECK: unsupported combination of coordinate operands

func @test_coordinate_of(%arr : !fir.ref<!fir.array<2 x i32>>, %arg1: index) {
%2 = fir.coordinate_of %arr, %arg1, %arg1 : (!fir.ref<!fir.array<2 x i32>>, index, index) -> !fir.ref<i32>
return
}
10 changes: 10 additions & 0 deletions flang/test/Fir/convert-to-llvm-invalid.fir
Expand Up @@ -88,3 +88,13 @@ func @bar_select_type(%arg : !fir.box<!fir.ref<f32>>) -> i32 {
%zero = arith.constant 0 : i32
return %zero : i32
}

// -----

// `fir.coordinate_of` - dynamically sized arrays are not supported
func @coordinate_of_dynamic_array(%arg0: !fir.ref<!fir.array<1x!fir.char<4,?>>>, %arg1: index) {
// expected-error@+2{{fir.coordinate_of with a dynamic element size is unsupported}}
// expected-error@+1{{failed to legalize operation 'fir.coordinate_of'}}
%p = fir.coordinate_of %arg0, %arg1 : (!fir.ref<!fir.array<1x!fir.char<4,?>>>, index) -> !fir.ref<f32>
return
}

0 comments on commit 6d655ad

Please sign in to comment.