[flang-commits] [flang] 1e6d9c0 - [fir] Add !fir.alloca conversion
Diana Picus via flang-commits
flang-commits at lists.llvm.org
Thu Nov 11 03:28:03 PST 2021
Author: Diana Picus
Date: 2021-11-11T11:26:33Z
New Revision: 1e6d9c06a5d20b65499b74a891a0e79cada3803e
URL: https://github.com/llvm/llvm-project/commit/1e6d9c06a5d20b65499b74a891a0e79cada3803e
DIFF: https://github.com/llvm/llvm-project/commit/1e6d9c06a5d20b65499b74a891a0e79cada3803e.diff
LOG: [fir] Add !fir.alloca conversion
Convert !fir.alloca into !llvm.alloca.
This also contains a fix for verifyInType in FIROps.cpp, to make sure we
can handle a !fir.ptr<!fir.array<?xN>>.
Differential Revision: https://reviews.llvm.org/D113563
Co-authored-by: Eric Schweitz <eschweitz at nvidia.com>
Co-authored-by: Jean Perier <jperier at nvidia.com>
Added:
Modified:
flang/lib/Optimizer/CodeGen/CodeGen.cpp
flang/lib/Optimizer/CodeGen/TypeConverter.h
flang/lib/Optimizer/Dialect/FIROps.cpp
flang/test/Fir/convert-to-llvm.fir
Removed:
################################################################################
diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index 6018429ac51c..a95306983ae1 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -32,6 +32,14 @@
static constexpr unsigned kAttrPointer = CFI_attribute_pointer;
static constexpr unsigned kAttrAllocatable = CFI_attribute_allocatable;
+static mlir::LLVM::ConstantOp
+genConstantIndex(mlir::Location loc, mlir::Type ity,
+ mlir::ConversionPatternRewriter &rewriter,
+ std::int64_t offset) {
+ auto cattr = rewriter.getI64IntegerAttr(offset);
+ return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr);
+}
+
namespace {
/// FIR conversion pattern template
template <typename FromOp>
@@ -145,6 +153,27 @@ class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> {
return rewriter.create<mlir::LLVM::GEPOp>(loc, ty, base, cv);
}
+ /// Perform an extension or truncation as needed on an integer value. Lowering
+ /// to the specific target may involve some sign-extending or truncation of
+ /// values, particularly to fit them from abstract box types to the
+ /// appropriate reified structures.
+ mlir::Value integerCast(mlir::Location loc,
+ mlir::ConversionPatternRewriter &rewriter,
+ mlir::Type ty, mlir::Value val) const {
+ auto valTy = val.getType();
+ // If the value was not yet lowered, lower its type so that it can
+ // be used in getPrimitiveTypeSizeInBits.
+ if (!valTy.isa<mlir::IntegerType>())
+ valTy = convertType(valTy);
+ auto toSize = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
+ auto fromSize = mlir::LLVM::getPrimitiveTypeSizeInBits(valTy);
+ if (toSize < fromSize)
+ return rewriter.create<mlir::LLVM::TruncOp>(loc, ty, val);
+ if (toSize > fromSize)
+ return rewriter.create<mlir::LLVM::SExtOp>(loc, ty, val);
+ return val;
+ }
+
fir::LLVMTypeConverter &lowerTy() const {
return *static_cast<fir::LLVMTypeConverter *>(this->getTypeConverter());
}
@@ -182,6 +211,92 @@ struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> {
return success();
}
};
+} // namespace
+
+/// Lookup the function to compute the memory size of this parametric derived
+/// type. The size of the object may depend on the LEN type parameters of the
+/// derived type.
+static mlir::LLVM::LLVMFuncOp
+getDependentTypeMemSizeFn(fir::RecordType recTy, fir::AllocaOp op,
+ mlir::ConversionPatternRewriter &rewriter) {
+ auto module = op->getParentOfType<mlir::ModuleOp>();
+ std::string name = recTy.getName().str() + "P.mem.size";
+ return module.lookupSymbol<mlir::LLVM::LLVMFuncOp>(name);
+}
+
+namespace {
+/// convert to LLVM IR dialect `alloca`
+struct AllocaOpConversion : public FIROpConversion<fir::AllocaOp> {
+ using FIROpConversion::FIROpConversion;
+
+ mlir::LogicalResult
+ matchAndRewrite(fir::AllocaOp alloc, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const override {
+ mlir::ValueRange operands = adaptor.getOperands();
+ auto loc = alloc.getLoc();
+ mlir::Type ity = lowerTy().indexType();
+ unsigned i = 0;
+ mlir::Value size = genConstantIndex(loc, ity, rewriter, 1).getResult();
+ mlir::Type ty = convertType(alloc.getType());
+ mlir::Type resultTy = ty;
+ if (alloc.hasLenParams()) {
+ unsigned end = alloc.numLenParams();
+ llvm::SmallVector<mlir::Value> lenParams;
+ for (; i < end; ++i)
+ lenParams.push_back(operands[i]);
+ mlir::Type scalarType = fir::unwrapSequenceType(alloc.getInType());
+ if (auto chrTy = scalarType.dyn_cast<fir::CharacterType>()) {
+ fir::CharacterType rawCharTy = fir::CharacterType::getUnknownLen(
+ chrTy.getContext(), chrTy.getFKind());
+ ty = mlir::LLVM::LLVMPointerType::get(convertType(rawCharTy));
+ assert(end == 1);
+ size = integerCast(loc, rewriter, ity, lenParams[0]);
+ } else if (auto recTy = scalarType.dyn_cast<fir::RecordType>()) {
+ mlir::LLVM::LLVMFuncOp memSizeFn =
+ getDependentTypeMemSizeFn(recTy, alloc, rewriter);
+ if (!memSizeFn)
+ emitError(loc, "did not find allocation function");
+ mlir::NamedAttribute attr = rewriter.getNamedAttr(
+ "callee", mlir::SymbolRefAttr::get(memSizeFn));
+ auto call = rewriter.create<mlir::LLVM::CallOp>(
+ loc, ity, lenParams, llvm::ArrayRef<mlir::NamedAttribute>{attr});
+ size = call.getResult(0);
+ ty = mlir::LLVM::LLVMPointerType::get(
+ mlir::IntegerType::get(alloc.getContext(), 8));
+ } else {
+ return emitError(loc, "unexpected type ")
+ << scalarType << " with type parameters";
+ }
+ }
+ if (alloc.hasShapeOperands()) {
+ mlir::Type allocEleTy = fir::unwrapRefType(alloc.getType());
+ // Scale the size by constant factors encoded in the array type.
+ if (auto seqTy = allocEleTy.dyn_cast<fir::SequenceType>()) {
+ fir::SequenceType::Extent constSize = 1;
+ for (auto extent : seqTy.getShape())
+ if (extent != fir::SequenceType::getUnknownExtent())
+ constSize *= extent;
+ mlir::Value constVal{
+ genConstantIndex(loc, ity, rewriter, constSize).getResult()};
+ size = rewriter.create<mlir::LLVM::MulOp>(loc, ity, size, constVal);
+ }
+ unsigned end = operands.size();
+ for (; i < end; ++i)
+ size = rewriter.create<mlir::LLVM::MulOp>(
+ loc, ity, size, integerCast(loc, rewriter, ity, operands[i]));
+ }
+ if (ty == resultTy) {
+ // Do not emit the bitcast if ty and resultTy are the same.
+ rewriter.replaceOpWithNewOp<mlir::LLVM::AllocaOp>(alloc, ty, size,
+ alloc->getAttrs());
+ } else {
+ auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, ty, size,
+ alloc->getAttrs());
+ rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(alloc, resultTy, al);
+ }
+ return success();
+ }
+};
/// Lower `fir.box_addr` to the sequence of operations to extract the first
/// element of the box.
@@ -1070,16 +1185,16 @@ class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
fir::LLVMTypeConverter typeConverter{getModule()};
mlir::OwningRewritePatternList pattern(context);
pattern.insert<
- AddcOpConversion, AddrOfOpConversion, BoxAddrOpConversion,
- BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion,
- BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxRankOpConversion,
- CallOpConversion, ConvertOpConversion, DivcOpConversion,
- ExtractValueOpConversion, HasValueOpConversion, GlobalOpConversion,
- InsertOnRangeOpConversion, InsertValueOpConversion, LoadOpConversion,
- NegcOpConversion, MulcOpConversion, SelectOpConversion,
- SelectRankOpConversion, StoreOpConversion, SubcOpConversion,
- UndefOpConversion, UnreachableOpConversion, ZeroOpConversion>(
- typeConverter);
+ AddcOpConversion, AddrOfOpConversion, AllocaOpConversion,
+ BoxAddrOpConversion, BoxDimsOpConversion, BoxEleSizeOpConversion,
+ BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion,
+ BoxRankOpConversion, CallOpConversion, ConvertOpConversion,
+ DivcOpConversion, ExtractValueOpConversion, HasValueOpConversion,
+ GlobalOpConversion, InsertOnRangeOpConversion, InsertValueOpConversion,
+ LoadOpConversion, NegcOpConversion, MulcOpConversion,
+ SelectOpConversion, SelectRankOpConversion, StoreOpConversion,
+ SubcOpConversion, UndefOpConversion, UnreachableOpConversion,
+ ZeroOpConversion>(typeConverter);
mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
pattern);
diff --git a/flang/lib/Optimizer/CodeGen/TypeConverter.h b/flang/lib/Optimizer/CodeGen/TypeConverter.h
index 21e65a2f289e..8595513c84b9 100644
--- a/flang/lib/Optimizer/CodeGen/TypeConverter.h
+++ b/flang/lib/Optimizer/CodeGen/TypeConverter.h
@@ -93,6 +93,9 @@ class LLVMTypeConverter : public mlir::LLVMTypeConverter {
// types. Indexing into other aggregate types is more flexible.
mlir::Type offsetType() { return mlir::IntegerType::get(&getContext(), 32); }
+ // i64 can be used to index into aggregates like arrays
+ mlir::Type indexType() { return mlir::IntegerType::get(&getContext(), 64); }
+
// fir.type<name(p : TY'...){f : TY...}> --> llvm<"%name = { ty... }">
mlir::Type convertRecordType(fir::RecordType derived) {
auto name = derived.getName();
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 62d04b30c694..dab873809bcf 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -57,8 +57,6 @@ static bool verifyInType(mlir::Type inType,
if (verifyInType(field.second, visited))
return true;
visited.pop_back();
- } else if (auto rt = inType.dyn_cast<fir::PointerType>()) {
- return verifyInType(rt.getEleTy(), visited);
}
return false;
}
diff --git a/flang/test/Fir/convert-to-llvm.fir b/flang/test/Fir/convert-to-llvm.fir
index 4b0fac6ad49c..08621052051e 100644
--- a/flang/test/Fir/convert-to-llvm.fir
+++ b/flang/test/Fir/convert-to-llvm.fir
@@ -329,6 +329,7 @@ func @insert_tuple(%a : tuple<i32, f32>) {
// CHECK: llvm.return
// -----
+
// Test `fir.call` -> `llvm.call` conversion for functions that take no arguments
// and return nothing
@@ -829,3 +830,134 @@ func @box_isptr(%arg0: !fir.box<!fir.array<*:f64>>) -> i1 {
// CHECK: %[[CMP_C0:.*]] = llvm.mlir.constant(0 : i32) : i32
// CHECK: %[[IS_ALLOC:.*]] = llvm.icmp "ne" %[[AND]], %[[CMP_C0]] : i32
// CHECK: llvm.return %[[IS_ALLOC]] : i1
+
+// -----
+
+// Test fir.alloca of one element
+
+func @alloca_one() -> !fir.ref<i32> {
+ %1 = fir.alloca i32
+ return %1 : !fir.ref<i32>
+}
+
+// CHECK-LABEL: llvm.func @alloca_one() -> !llvm.ptr<i32>
+// CHECK: [[N:%.*]] = llvm.mlir.constant(1 : i64) : i64
+// CHECK: [[A:%.*]] = llvm.alloca [[N]] x i32
+// CHECK: llvm.return [[A]] : !llvm.ptr<i32>
+
+// -----
+
+// Test fir.alloca of several elements
+
+func @alloca_several() -> !fir.ref<i32> {
+ %0 = arith.constant 100 : index
+ %1 = fir.alloca i32, %0
+ return %1 : !fir.ref<i32>
+}
+
+// CHECK-LABEL: llvm.func @alloca_several() -> !llvm.ptr<i32>
+// CHECK: [[N:%.*]] = llvm.mlir.constant(100 : index) : i64
+// CHECK: [[ONE:%.*]] = llvm.mlir.constant(1 : i64) : i64
+// CHECK: [[TOTAL:%.*]] = llvm.mul [[ONE]], [[N]] : i64
+// CHECK: [[A:%.*]] = llvm.alloca [[TOTAL]] x i32
+// CHECK: llvm.return [[A]] : !llvm.ptr<i32>
+
+// -----
+
+// Test fir.alloca of pointer to array
+
+func @alloca_ptr_to_array() -> !fir.ref<!fir.ptr<!fir.array<?xi32>>> {
+ %1 = fir.alloca !fir.ptr<!fir.array<?xi32>>
+ return %1 : !fir.ref<!fir.ptr<!fir.array<?xi32>>>
+}
+
+// CHECK-LABEL: llvm.func @alloca_ptr_to_array() -> !llvm.ptr<ptr<i32>>
+// CHECK: [[ONE:%.*]] = llvm.mlir.constant(1 : i64) : i64
+// CHECK: [[A:%.*]] = llvm.alloca [[ONE]] x !llvm.ptr<i32>
+// CHECK: llvm.return [[A]] : !llvm.ptr<ptr<i32>>
+
+// -----
+
+// Test fir.alloca of char array
+
+func @alloca_char_array(%l: i32, %e : index) -> !fir.ref<!fir.array<?x?x!fir.char<1,?>>> {
+ %a = fir.alloca !fir.array<?x?x!fir.char<1,?>>(%l : i32), %e, %e
+ return %a : !fir.ref<!fir.array<?x?x!fir.char<1,?>>>
+}
+
+// CHECK-LABEL: llvm.func @alloca_char_array
+// CHECK-SAME: ([[L:%.*]]: i32, [[E:%.*]]: i64) -> !llvm.ptr<i8>
+// CHECK-DAG: [[UNUSEDONE:%.*]] = llvm.mlir.constant(1 : i64) : i64
+// CHECK-DAG: [[LCAST:%.*]] = llvm.sext [[L]] : i32 to i64
+// CHECK-DAG: [[ONE:%.*]] = llvm.mlir.constant(1 : i64) : i64
+// CHECK: [[PROD1:%.*]] = llvm.mul [[LCAST]], [[ONE]] : i64
+// CHECK: [[PROD2:%.*]] = llvm.mul [[PROD1]], [[E]] : i64
+// CHECK: [[PROD3:%.*]] = llvm.mul [[PROD2]], [[E]] : i64
+// CHECK: [[A:%.*]] = llvm.alloca [[PROD3]] x i8 {in_type = !fir.array<?x?x!fir.char<1,?>>
+// CHECK: return [[A]] : !llvm.ptr<i8>
+
+// -----
+
+// Test fir.alloca of record type with LEN parameters
+// type t(p1,p2)
+// integer, len :: p1
+// integer(kind=2), len :: p2
+// integer f1
+// real f2
+// end type t
+
+func private @_QTtP.mem.size(%0 : i32, %1 : i16) -> index
+
+func @alloca_record(%arg0 : i32, %arg1 : i16) -> !fir.ref<!fir.type<_QTt(p1:i32,p2:i16){f1:i32,f2:f32}>> {
+ %0 = fir.alloca !fir.type<_QTt(p1:i32,p2:i16){f1:i32,f2:f32}>(%arg0, %arg1 : i32, i16) {name = "_QEvar"}
+ return %0 : !fir.ref<!fir.type<_QTt(p1:i32,p2:i16){f1:i32,f2:f32}>>
+}
+
+// CHECK-LABEL: llvm.func @alloca_record
+// CHECK-SAME: ([[ARG0:%.*]]: i32, [[ARG1:%.*]]: i16)
+// CHECK-SAME: -> !llvm.ptr<struct<"_QTt", (i32, f32)>>
+// CHECK: [[SIZE:%.*]] = llvm.call @_QTtP.mem.size([[ARG0]], [[ARG1]]) : (i32, i16) -> i64
+// CHECK: [[ALLOC:%.*]] = llvm.alloca [[SIZE]] x i8
+// CHECK: [[A:%.*]] = llvm.bitcast [[ALLOC]] : !llvm.ptr<i8> to !llvm.ptr<struct<"_QTt", (i32, f32)>>
+// CHECK: llvm.return [[A]] : !llvm.ptr<struct<"_QTt", (i32, f32)>>
+
+// -----
+
+// Test fir.alloca of a multidimensional array, with operands
+
+func @alloca_multidim_array(%0 : index) -> !fir.ref<!fir.array<8x16x32xf32>> {
+ %1 = arith.constant 24 : index
+ %2 = fir.alloca !fir.array<8x16x32xf32>, %0, %1
+ return %2 : !fir.ref<!fir.array<8x16x32xf32>>
+}
+
+// CHECK-LABEL: llvm.func @alloca_multidim_array
+// CHECK-SAME: ([[OP1:%.*]]: i64) -> !llvm.ptr<array<32 x array<16 x array<8 x f32>
+// CHECK: [[OP2:%.*]] = llvm.mlir.constant(24 : index) : i64
+// CHECK: [[ONE:%.*]] = llvm.mlir.constant(1 : i64) : i64
+// CHECK: [[ALL:%.*]] = llvm.mlir.constant(4096 : i64) : i64
+// CHECK: [[MUL1:%.*]] = llvm.mul [[ONE]], [[ALL]] : i64
+// CHECK: [[MUL2:%.*]] = llvm.mul [[MUL1]], [[OP1]] : i64
+// CHECK: [[TOTAL:%.*]] = llvm.mul [[MUL2]], [[OP2]] : i64
+// CHECK: [[A:%.*]] = llvm.alloca [[TOTAL]] x !llvm.array<32 x array<16 x array<8 x f32>
+// CHECK: llvm.return [[A]] : !llvm.ptr<array<32 x array<16 x array<8 x f32>
+
+// -----
+
+// Test alloca with an array with holes.
+// Constant factor of 60 (4*3*5) must be included.
+
+func @alloca_array_with_holes(%0 : index, %1 : index) -> !fir.ref<!fir.array<4x?x3x?x5xi32>> {
+ %a = fir.alloca !fir.array<4x?x3x?x5xi32>, %0, %1
+ return %a : !fir.ref<!fir.array<4x?x3x?x5xi32>>
+}
+
+// CHECK-LABEL: llvm.func @alloca_array_with_holes
+// CHECK-SAME: ([[A:%.*]]: i64, [[B:%.*]]: i64) -> !llvm.ptr<i32>
+// CHECK-DAG: [[ONE:%.*]] = llvm.mlir.constant(1 : i64) : i64
+// CHECK-DAG: [[FIXED:%.*]] = llvm.mlir.constant(60 : i64) : i64
+// CHECK: [[PROD1:%.*]] = llvm.mul [[ONE]], [[FIXED]] : i64
+// CHECK: [[PROD2:%.*]] = llvm.mul [[PROD1]], [[A]] : i64
+// CHECK: [[PROD3:%.*]] = llvm.mul [[PROD2]], [[B]] : i64
+// CHECK: [[RES:%.*]] = llvm.alloca [[PROD3]] x i32 {in_type = !fir.array<4x?x3x?x5xi32>
+// CHECK: llvm.return [[RES]] : !llvm.ptr<i32>
More information about the flang-commits
mailing list