Index: flang/lib/Optimizer/CodeGen/CodeGen.cpp =================================================================== --- flang/lib/Optimizer/CodeGen/CodeGen.cpp +++ flang/lib/Optimizer/CodeGen/CodeGen.cpp @@ -29,6 +29,14 @@ // fir::LLVMTypeConverter for converting to LLVM IR dialect types. #include "TypeConverter.h" +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(loc, ity, cattr); +} + namespace { /// FIR conversion pattern template template @@ -42,6 +50,27 @@ return lowerTy().convertType(ty); } + /// 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()) + valTy = convertType(valTy); + auto toSize = mlir::LLVM::getPrimitiveTypeSizeInBits(ty); + auto fromSize = mlir::LLVM::getPrimitiveTypeSizeInBits(valTy); + if (toSize < fromSize) + return rewriter.create(loc, ty, val); + if (toSize > fromSize) + return rewriter.create(loc, ty, val); + return val; + } + fir::LLVMTypeConverter &lowerTy() const { return *static_cast(this->getTypeConverter()); } @@ -80,6 +109,92 @@ } }; +/// 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(); + std::string name = recTy.getName().str() + "P.mem.size"; + return module.lookupSymbol(name); +} + +namespace { +/// convert to LLVM IR dialect `alloca` +struct AllocaOpConversion : public FIROpConversion { + 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 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 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()) { + 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( + loc, ity, lenParams, llvm::ArrayRef{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::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(loc, ity, size, constVal); + } + unsigned end = operands.size(); + for (; i < end; ++i) + size = rewriter.create( + 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(alloc, ty, size, + alloc->getAttrs()); + } else { + auto al = rewriter.create(loc, ty, size, + alloc->getAttrs()); + rewriter.replaceOpWithNewOp(alloc, resultTy, al); + } + return success(); + } +}; +} // namespace + // `fir.call` -> `llvm.call` struct CallOpConversion : public FIROpConversion { using FIROpConversion::FIROpConversion; @@ -835,8 +950,8 @@ auto *context = getModule().getContext(); fir::LLVMTypeConverter typeConverter{getModule()}; mlir::OwningRewritePatternList pattern(context); - pattern.insert --> llvm<"%name = { ty... }"> mlir::Type convertRecordType(fir::RecordType derived) { auto name = derived.getName(); Index: flang/lib/Optimizer/Dialect/FIROps.cpp =================================================================== --- flang/lib/Optimizer/Dialect/FIROps.cpp +++ flang/lib/Optimizer/Dialect/FIROps.cpp @@ -57,8 +57,6 @@ if (verifyInType(field.second, visited)) return true; visited.pop_back(); - } else if (auto rt = inType.dyn_cast()) { - return verifyInType(rt.getEleTy(), visited); } return false; } Index: flang/test/Fir/convert-to-llvm.fir =================================================================== --- flang/test/Fir/convert-to-llvm.fir +++ flang/test/Fir/convert-to-llvm.fir @@ -329,6 +329,7 @@ // CHECK: llvm.return // ----- + // Test `fir.call` -> `llvm.call` conversion for functions that take no arguments // and return nothing @@ -686,3 +687,134 @@ // CHECK-SAME: (%{{.*}}: !llvm.ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<1 x array<3 x i{{.*}}>>)>>) { // CHECK-NEXT: llvm.return // CHECK-NEXT: } + +// ----- + +// Test fir.alloca of one element + +func @alloca_one() -> !fir.ref { + %1 = fir.alloca i32 + return %1 : !fir.ref +} + +// CHECK-LABEL: llvm.func @alloca_one() -> !llvm.ptr +// CHECK: [[N:%.*]] = llvm.mlir.constant(1 : i64) : i64 +// CHECK: [[A:%.*]] = llvm.alloca [[N]] x i32 +// CHECK: llvm.return [[A]] : !llvm.ptr + +// ----- + +// Test fir.alloca of several elements + +func @alloca_several() -> !fir.ref { + %0 = arith.constant 100 : index + %1 = fir.alloca i32, %0 + return %1 : !fir.ref +} + +// CHECK-LABEL: llvm.func @alloca_several() -> !llvm.ptr +// 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 + +// ----- + +// Test fir.alloca of pointer to array + +func @alloca_ptr_to_array() -> !fir.ref>> { + %1 = fir.alloca !fir.ptr> + return %1 : !fir.ref>> +} + +// CHECK-LABEL: llvm.func @alloca_ptr_to_array() -> !llvm.ptr> +// CHECK: [[ONE:%.*]] = llvm.mlir.constant(1 : i64) : i64 +// CHECK: [[A:%.*]] = llvm.alloca [[ONE]] x !llvm.ptr +// CHECK: llvm.return [[A]] : !llvm.ptr> + +// ----- + +// Test fir.alloca of char array + +func @alloca_char_array(%l: i32, %e : index) -> !fir.ref>> { + %a = fir.alloca !fir.array>(%l : i32), %e, %e + return %a : !fir.ref>> +} + +// CHECK-LABEL: llvm.func @alloca_char_array +// CHECK-SAME: ([[L:%.*]]: i32, [[E:%.*]]: i64) -> !llvm.ptr +// 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> +// CHECK: return [[A]] : !llvm.ptr + +// ----- + +// 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> { + %0 = fir.alloca !fir.type<_QTt(p1:i32,p2:i16){f1:i32,f2:f32}>(%arg0, %arg1 : i32, i16) {name = "_QEvar"} + return %0 : !fir.ref> +} + +// CHECK-LABEL: llvm.func @alloca_record +// CHECK-SAME: ([[ARG0:%.*]]: i32, [[ARG1:%.*]]: i16) +// CHECK-SAME: -> !llvm.ptr> +// 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 to !llvm.ptr> +// CHECK: llvm.return [[A]] : !llvm.ptr> + +// ----- + +// Test fir.alloca of a multidimensional array, with operands + +func @alloca_multidim_array(%0 : index) -> !fir.ref> { + %1 = arith.constant 24 : index + %2 = fir.alloca !fir.array<8x16x32xf32>, %0, %1 + return %2 : !fir.ref> +} + +// CHECK-LABEL: llvm.func @alloca_multidim_array +// CHECK-SAME: ([[OP1:%.*]]: i64) -> !llvm.ptr +// 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 + +// ----- + +// 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> { + %a = fir.alloca !fir.array<4x?x3x?x5xi32>, %0, %1 + return %a : !fir.ref> +} + +// CHECK-LABEL: llvm.func @alloca_array_with_holes +// CHECK-SAME: ([[A:%.*]]: i64, [[B:%.*]]: i64) -> !llvm.ptr +// 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