[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@nvidia.com> Co-authored-by: Jean Perier <jperier@nvidia.com>
This commit is contained in:
parent
bb2018261e
commit
1e6d9c06a5
|
|
@ -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 @@ protected:
|
|||
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 @@ public:
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -93,6 +93,9 @@ public:
|
|||
// 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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue