Skip to content

Commit

Permalink
[fir] Add complex operations conversion from FIR LLVM IR
Browse files Browse the repository at this point in the history
This patch add conversion for primitive operations on complex types.
- fir.addc
- fir.subc
- fir.mulc
- fir.divc
- fir.negc

This adds also the type conversion for !fir.complex<KIND> type.

This patch is part of the upstreaming effort from fir-dev branch.

Reviewed By: rovka

Differential Revision: https://reviews.llvm.org/D113434

Co-authored-by: Jean Perier <jperier@nvidia.com>
Co-authored-by: Eric Schweitz <eschweitz@nvidia.com>
  • Loading branch information
3 people committed Nov 9, 2021
1 parent 2ca1cd7 commit b9bc64b
Show file tree
Hide file tree
Showing 6 changed files with 401 additions and 7 deletions.
183 changes: 177 additions & 6 deletions flang/lib/Optimizer/CodeGen/CodeGen.cpp
Expand Up @@ -14,6 +14,7 @@
#include "PassDetail.h"
#include "flang/Optimizer/Dialect/FIROps.h"
#include "flang/Optimizer/Dialect/FIRType.h"
#include "flang/Optimizer/Support/FIRContext.h"
#include "mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h"
#include "mlir/Conversion/LLVMCommon/Pattern.h"
#include "mlir/Conversion/LLVMCommon/TypeConverter.h"
Expand Down Expand Up @@ -487,6 +488,175 @@ struct InsertOnRangeOpConversion
return success();
}
};

static mlir::Type getComplexEleTy(mlir::Type complex) {
if (auto cc = complex.dyn_cast<mlir::ComplexType>())
return cc.getElementType();
return complex.cast<fir::ComplexType>().getElementType();
}

//
// Primitive operations on Complex types
//

/// Generate inline code for complex addition/subtraction
template <typename LLVMOP, typename OPTY>
mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds,
mlir::ConversionPatternRewriter &rewriter,
fir::LLVMTypeConverter &lowering) {
mlir::Value a = opnds[0];
mlir::Value b = opnds[1];
auto loc = sumop.getLoc();
auto ctx = sumop.getContext();
auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
mlir::Type ty = lowering.convertType(sumop.getType());
auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
}

struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
using FIROpConversion::FIROpConversion;

mlir::LogicalResult
matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
// given: (x + iy) + (x' + iy')
// result: (x + x') + i(y + y')
auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
rewriter, lowerTy());
rewriter.replaceOp(addc, r.getResult());
return success();
}
};

struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
using FIROpConversion::FIROpConversion;

mlir::LogicalResult
matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
// given: (x + iy) - (x' + iy')
// result: (x - x') + i(y - y')
auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
rewriter, lowerTy());
rewriter.replaceOp(subc, r.getResult());
return success();
}
};

/// Inlined complex multiply
struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
using FIROpConversion::FIROpConversion;

mlir::LogicalResult
matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
// TODO: Can we use a call to __muldc3 ?
// given: (x + iy) * (x' + iy')
// result: (xx'-yy')+i(xy'+yx')
mlir::Value a = adaptor.getOperands()[0];
mlir::Value b = adaptor.getOperands()[1];
auto loc = mulc.getLoc();
auto *ctx = mulc.getContext();
auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
mlir::Type ty = convertType(mulc.getType());
auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
rewriter.replaceOp(mulc, r0.getResult());
return success();
}
};

/// Inlined complex division
struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
using FIROpConversion::FIROpConversion;

mlir::LogicalResult
matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
// TODO: Can we use a call to __divdc3 instead?
// Just generate inline code for now.
// given: (x + iy) / (x' + iy')
// result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
mlir::Value a = adaptor.getOperands()[0];
mlir::Value b = adaptor.getOperands()[1];
auto loc = divc.getLoc();
auto *ctx = divc.getContext();
auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
mlir::Type ty = convertType(divc.getType());
auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
rewriter.replaceOp(divc, r0.getResult());
return success();
}
};

/// Inlined complex negation
struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
using FIROpConversion::FIROpConversion;

mlir::LogicalResult
matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
// given: -(x + iy)
// result: -x - iy
auto *ctxt = neg.getContext();
auto eleTy = convertType(getComplexEleTy(neg.getType()));
auto ty = convertType(neg.getType());
auto loc = neg.getLoc();
mlir::Value o0 = adaptor.getOperands()[0];
auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
return success();
}
};

} // namespace

namespace {
Expand All @@ -504,12 +674,13 @@ class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
auto *context = getModule().getContext();
fir::LLVMTypeConverter typeConverter{getModule()};
mlir::OwningRewritePatternList pattern(context);
pattern.insert<
AddrOfOpConversion, CallOpConversion, ExtractValueOpConversion,
HasValueOpConversion, GlobalOpConversion, InsertOnRangeOpConversion,
InsertValueOpConversion, SelectOpConversion, SelectRankOpConversion,
UndefOpConversion, UnreachableOpConversion, ZeroOpConversion>(
typeConverter);
pattern.insert<AddcOpConversion, AddrOfOpConversion, CallOpConversion,
DivcOpConversion, ExtractValueOpConversion,
HasValueOpConversion, GlobalOpConversion,
InsertOnRangeOpConversion, InsertValueOpConversion,
NegcOpConversion, MulcOpConversion, SelectOpConversion,
SelectRankOpConversion, SubcOpConversion, UndefOpConversion,
UnreachableOpConversion, ZeroOpConversion>(typeConverter);
mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
pattern);
Expand Down
7 changes: 7 additions & 0 deletions flang/lib/Optimizer/CodeGen/Target.cpp
Expand Up @@ -35,6 +35,13 @@ struct GenericTarget : public CodeGenSpecifics {
using CodeGenSpecifics::CodeGenSpecifics;
using AT = CodeGenSpecifics::Attributes;

mlir::Type complexMemoryType(mlir::Type eleTy) const override {
assert(fir::isa_real(eleTy));
// { t, t } struct of 2 eleTy
mlir::TypeRange range = {eleTy, eleTy};
return mlir::TupleType::get(eleTy.getContext(), range);
}

Marshalling boxcharArgumentType(mlir::Type eleTy, bool sret) const override {
CodeGenSpecifics::Marshalling marshal;
auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth);
Expand Down
3 changes: 3 additions & 0 deletions flang/lib/Optimizer/CodeGen/Target.h
Expand Up @@ -65,6 +65,9 @@ class CodeGenSpecifics {
CodeGenSpecifics() = delete;
virtual ~CodeGenSpecifics() {}

/// Type presentation of a `complex<ele>` type value in memory.
virtual mlir::Type complexMemoryType(mlir::Type eleTy) const = 0;

/// Type representation of a `complex<eleTy>` type argument when passed by
/// value. An argument value may need to be passed as a (safe) reference
/// argument.
Expand Down
53 changes: 52 additions & 1 deletion flang/lib/Optimizer/CodeGen/TypeConverter.h
Expand Up @@ -14,6 +14,7 @@
#define FORTRAN_OPTIMIZER_CODEGEN_TYPECONVERTER_H

#include "DescriptorModel.h"
#include "Target.h"
#include "flang/Lower/Todo.h" // remove when TODO's are done
#include "flang/Optimizer/Support/FIRContext.h"
#include "flang/Optimizer/Support/KindMapping.h"
Expand All @@ -28,7 +29,10 @@ class LLVMTypeConverter : public mlir::LLVMTypeConverter {
public:
LLVMTypeConverter(mlir::ModuleOp module)
: mlir::LLVMTypeConverter(module.getContext()),
kindMapping(getKindMapping(module)) {
kindMapping(getKindMapping(module)),
specifics(CodeGenSpecifics::get(module.getContext(),
getTargetTriple(module),
getKindMapping(module))) {
LLVM_DEBUG(llvm::dbgs() << "FIR type converter\n");

// Each conversion should return a value of type mlir::Type.
Expand All @@ -39,6 +43,10 @@ class LLVMTypeConverter : public mlir::LLVMTypeConverter {
});
addConversion(
[&](fir::RecordType derived) { return convertRecordType(derived); });
addConversion(
[&](fir::ComplexType cmplx) { return convertComplexType(cmplx); });
addConversion(
[&](fir::RealType real) { return convertRealType(real.getFKind()); });
addConversion(
[&](fir::ReferenceType ref) { return convertPointerLike(ref); });
addConversion(
Expand Down Expand Up @@ -140,6 +148,24 @@ class LLVMTypeConverter : public mlir::LLVMTypeConverter {
/*isPacked=*/false));
}

// Use the target specifics to figure out how to map complex to LLVM IR. The
// use of complex values in function signatures is handled before conversion
// to LLVM IR dialect here.
//
// fir.complex<T> | std.complex<T> --> llvm<"{t,t}">
template <typename C>
mlir::Type convertComplexType(C cmplx) {
LLVM_DEBUG(llvm::dbgs() << "type convert: " << cmplx << '\n');
auto eleTy = cmplx.getElementType();
return convertType(specifics->complexMemoryType(eleTy));
}

// convert a front-end kind value to either a std or LLVM IR dialect type
// fir.real<n> --> llvm.anyfloat where anyfloat is a kind mapping
mlir::Type convertRealType(fir::KindTy kind) {
return fromRealTypeID(kindMapping.getRealTypeID(kind), kind);
}

template <typename A>
mlir::Type convertPointerLike(A &ty) {
mlir::Type eleTy = ty.getEleTy();
Expand Down Expand Up @@ -187,8 +213,33 @@ class LLVMTypeConverter : public mlir::LLVMTypeConverter {
return mlir::LLVM::LLVMPointerType::get(baseTy);
}

/// Convert llvm::Type::TypeID to mlir::Type
mlir::Type fromRealTypeID(llvm::Type::TypeID typeID, fir::KindTy kind) {
switch (typeID) {
case llvm::Type::TypeID::HalfTyID:
return mlir::FloatType::getF16(&getContext());
case llvm::Type::TypeID::BFloatTyID:
return mlir::FloatType::getBF16(&getContext());
case llvm::Type::TypeID::FloatTyID:
return mlir::FloatType::getF32(&getContext());
case llvm::Type::TypeID::DoubleTyID:
return mlir::FloatType::getF64(&getContext());
case llvm::Type::TypeID::X86_FP80TyID:
return mlir::FloatType::getF80(&getContext());
case llvm::Type::TypeID::FP128TyID:
return mlir::FloatType::getF128(&getContext());
default:
emitError(UnknownLoc::get(&getContext()))
<< "unsupported type: !fir.real<" << kind << ">";
return {};
}
}

KindMapping &getKindMap() { return kindMapping; }

private:
KindMapping kindMapping;
std::unique_ptr<CodeGenSpecifics> specifics;
};

} // namespace fir
Expand Down

0 comments on commit b9bc64b

Please sign in to comment.