forked from OSchip/llvm-project
293 lines
10 KiB
C++
293 lines
10 KiB
C++
//===-- Target.cpp --------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Target.h"
|
|
#include "flang/Optimizer/Dialect/FIRType.h"
|
|
#include "flang/Optimizer/Support/KindMapping.h"
|
|
#include "mlir/IR/BuiltinTypes.h"
|
|
#include "mlir/IR/TypeRange.h"
|
|
|
|
#define DEBUG_TYPE "flang-codegen-target"
|
|
|
|
using namespace fir;
|
|
|
|
// Reduce a REAL/float type to the floating point semantics.
|
|
static const llvm::fltSemantics &floatToSemantics(const KindMapping &kindMap,
|
|
mlir::Type type) {
|
|
assert(isa_real(type));
|
|
if (auto ty = type.dyn_cast<fir::RealType>())
|
|
return kindMap.getFloatSemantics(ty.getFKind());
|
|
return type.cast<mlir::FloatType>().getFloatSemantics();
|
|
}
|
|
|
|
namespace {
|
|
template <typename S>
|
|
struct GenericTarget : public CodeGenSpecifics {
|
|
using CodeGenSpecifics::CodeGenSpecifics;
|
|
using AT = CodeGenSpecifics::Attributes;
|
|
|
|
mlir::Type complexMemoryType(mlir::Type eleTy) const override {
|
|
assert(fir::isa_real(eleTy));
|
|
// Use a type that will be translated into LLVM as:
|
|
// { t, t } struct of 2 eleTy
|
|
mlir::TypeRange range = {eleTy, eleTy};
|
|
return mlir::TupleType::get(eleTy.getContext(), range);
|
|
}
|
|
|
|
mlir::Type boxcharMemoryType(mlir::Type eleTy) const override {
|
|
auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth);
|
|
auto ptrTy = fir::ReferenceType::get(eleTy);
|
|
// Use a type that will be translated into LLVM as:
|
|
// { t*, index }
|
|
mlir::TypeRange range = {ptrTy, idxTy};
|
|
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);
|
|
auto ptrTy = fir::ReferenceType::get(eleTy);
|
|
marshal.emplace_back(ptrTy, AT{});
|
|
// Return value arguments are grouped as a pair. Others are passed in a
|
|
// split format with all pointers first (in the declared position) and all
|
|
// LEN arguments appended after all of the dummy arguments.
|
|
// NB: Other conventions/ABIs can/should be supported via options.
|
|
marshal.emplace_back(idxTy, AT{/*alignment=*/0, /*byval=*/false,
|
|
/*sret=*/sret, /*append=*/!sret});
|
|
return marshal;
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// i386 (x86 32 bit) linux target specifics.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
struct TargetI386 : public GenericTarget<TargetI386> {
|
|
using GenericTarget::GenericTarget;
|
|
|
|
static constexpr int defaultWidth = 32;
|
|
|
|
CodeGenSpecifics::Marshalling
|
|
complexArgumentType(mlir::Type eleTy) const override {
|
|
assert(fir::isa_real(eleTy));
|
|
CodeGenSpecifics::Marshalling marshal;
|
|
// Use a type that will be translated into LLVM as:
|
|
// { t, t } struct of 2 eleTy, byval, align 4
|
|
mlir::TypeRange range = {eleTy, eleTy};
|
|
auto structTy = mlir::TupleType::get(eleTy.getContext(), range);
|
|
marshal.emplace_back(fir::ReferenceType::get(structTy),
|
|
AT{/*alignment=*/4, /*byval=*/true});
|
|
return marshal;
|
|
}
|
|
|
|
CodeGenSpecifics::Marshalling
|
|
complexReturnType(mlir::Type eleTy) const override {
|
|
assert(fir::isa_real(eleTy));
|
|
CodeGenSpecifics::Marshalling marshal;
|
|
const auto *sem = &floatToSemantics(kindMap, eleTy);
|
|
if (sem == &llvm::APFloat::IEEEsingle()) {
|
|
// i64 pack both floats in a 64-bit GPR
|
|
marshal.emplace_back(mlir::IntegerType::get(eleTy.getContext(), 64),
|
|
AT{});
|
|
} else if (sem == &llvm::APFloat::IEEEdouble()) {
|
|
// Use a type that will be translated into LLVM as:
|
|
// { t, t } struct of 2 eleTy, sret, align 4
|
|
mlir::TypeRange range = {eleTy, eleTy};
|
|
auto structTy = mlir::TupleType::get(eleTy.getContext(), range);
|
|
marshal.emplace_back(fir::ReferenceType::get(structTy),
|
|
AT{/*alignment=*/4, /*byval=*/false, /*sret=*/true});
|
|
} else {
|
|
llvm::report_fatal_error("complex for this precision not implemented");
|
|
}
|
|
return marshal;
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// x86_64 (x86 64 bit) linux target specifics.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
struct TargetX86_64 : public GenericTarget<TargetX86_64> {
|
|
using GenericTarget::GenericTarget;
|
|
|
|
static constexpr int defaultWidth = 64;
|
|
|
|
CodeGenSpecifics::Marshalling
|
|
complexArgumentType(mlir::Type eleTy) const override {
|
|
CodeGenSpecifics::Marshalling marshal;
|
|
const auto *sem = &floatToSemantics(kindMap, eleTy);
|
|
if (sem == &llvm::APFloat::IEEEsingle()) {
|
|
// <2 x t> vector of 2 eleTy
|
|
marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{});
|
|
} else if (sem == &llvm::APFloat::IEEEdouble()) {
|
|
// two distinct double arguments
|
|
marshal.emplace_back(eleTy, AT{});
|
|
marshal.emplace_back(eleTy, AT{});
|
|
} else {
|
|
llvm::report_fatal_error("complex for this precision not implemented");
|
|
}
|
|
return marshal;
|
|
}
|
|
|
|
CodeGenSpecifics::Marshalling
|
|
complexReturnType(mlir::Type eleTy) const override {
|
|
CodeGenSpecifics::Marshalling marshal;
|
|
const auto *sem = &floatToSemantics(kindMap, eleTy);
|
|
if (sem == &llvm::APFloat::IEEEsingle()) {
|
|
// <2 x t> vector of 2 eleTy
|
|
marshal.emplace_back(fir::VectorType::get(2, eleTy), AT{});
|
|
} else if (sem == &llvm::APFloat::IEEEdouble()) {
|
|
// Use a type that will be translated into LLVM as:
|
|
// { double, double } struct of 2 double
|
|
mlir::TypeRange range = {eleTy, eleTy};
|
|
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range),
|
|
AT{});
|
|
} else {
|
|
llvm::report_fatal_error("complex for this precision not implemented");
|
|
}
|
|
return marshal;
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// AArch64 linux target specifics.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
struct TargetAArch64 : public GenericTarget<TargetAArch64> {
|
|
using GenericTarget::GenericTarget;
|
|
|
|
static constexpr int defaultWidth = 64;
|
|
|
|
CodeGenSpecifics::Marshalling
|
|
complexArgumentType(mlir::Type eleTy) const override {
|
|
CodeGenSpecifics::Marshalling marshal;
|
|
const auto *sem = &floatToSemantics(kindMap, eleTy);
|
|
if (sem == &llvm::APFloat::IEEEsingle() ||
|
|
sem == &llvm::APFloat::IEEEdouble()) {
|
|
// [2 x t] array of 2 eleTy
|
|
marshal.emplace_back(fir::SequenceType::get({2}, eleTy), AT{});
|
|
} else {
|
|
llvm::report_fatal_error("complex for this precision not implemented");
|
|
}
|
|
return marshal;
|
|
}
|
|
|
|
CodeGenSpecifics::Marshalling
|
|
complexReturnType(mlir::Type eleTy) const override {
|
|
CodeGenSpecifics::Marshalling marshal;
|
|
const auto *sem = &floatToSemantics(kindMap, eleTy);
|
|
if (sem == &llvm::APFloat::IEEEsingle() ||
|
|
sem == &llvm::APFloat::IEEEdouble()) {
|
|
// Use a type that will be translated into LLVM as:
|
|
// { t, t } struct of 2 eleTy
|
|
mlir::TypeRange range = {eleTy, eleTy};
|
|
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range),
|
|
AT{});
|
|
} else {
|
|
llvm::report_fatal_error("complex for this precision not implemented");
|
|
}
|
|
return marshal;
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// PPC64le linux target specifics.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
struct TargetPPC64le : public GenericTarget<TargetPPC64le> {
|
|
using GenericTarget::GenericTarget;
|
|
|
|
static constexpr int defaultWidth = 64;
|
|
|
|
CodeGenSpecifics::Marshalling
|
|
complexArgumentType(mlir::Type eleTy) const override {
|
|
CodeGenSpecifics::Marshalling marshal;
|
|
// two distinct element type arguments (re, im)
|
|
marshal.emplace_back(eleTy, AT{});
|
|
marshal.emplace_back(eleTy, AT{});
|
|
return marshal;
|
|
}
|
|
|
|
CodeGenSpecifics::Marshalling
|
|
complexReturnType(mlir::Type eleTy) const override {
|
|
CodeGenSpecifics::Marshalling marshal;
|
|
// Use a type that will be translated into LLVM as:
|
|
// { t, t } struct of 2 element type
|
|
mlir::TypeRange range = {eleTy, eleTy};
|
|
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{});
|
|
return marshal;
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
// Instantiate the overloaded target instance based on the triple value.
|
|
// TODO: Add other targets to this file as needed.
|
|
std::unique_ptr<fir::CodeGenSpecifics>
|
|
fir::CodeGenSpecifics::get(mlir::MLIRContext *ctx, llvm::Triple &&trp,
|
|
KindMapping &&kindMap) {
|
|
switch (trp.getArch()) {
|
|
default:
|
|
break;
|
|
case llvm::Triple::ArchType::x86:
|
|
switch (trp.getOS()) {
|
|
default:
|
|
break;
|
|
case llvm::Triple::OSType::Linux:
|
|
case llvm::Triple::OSType::Darwin:
|
|
case llvm::Triple::OSType::Win32:
|
|
return std::make_unique<TargetI386>(ctx, std::move(trp),
|
|
std::move(kindMap));
|
|
}
|
|
break;
|
|
case llvm::Triple::ArchType::x86_64:
|
|
switch (trp.getOS()) {
|
|
default:
|
|
break;
|
|
case llvm::Triple::OSType::Linux:
|
|
case llvm::Triple::OSType::Darwin:
|
|
case llvm::Triple::OSType::Win32:
|
|
return std::make_unique<TargetX86_64>(ctx, std::move(trp),
|
|
std::move(kindMap));
|
|
}
|
|
break;
|
|
case llvm::Triple::ArchType::aarch64:
|
|
switch (trp.getOS()) {
|
|
default:
|
|
break;
|
|
case llvm::Triple::OSType::Linux:
|
|
case llvm::Triple::OSType::Darwin:
|
|
case llvm::Triple::OSType::Win32:
|
|
return std::make_unique<TargetAArch64>(ctx, std::move(trp),
|
|
std::move(kindMap));
|
|
}
|
|
break;
|
|
case llvm::Triple::ArchType::ppc64le:
|
|
switch (trp.getOS()) {
|
|
default:
|
|
break;
|
|
case llvm::Triple::OSType::Linux:
|
|
return std::make_unique<TargetPPC64le>(ctx, std::move(trp),
|
|
std::move(kindMap));
|
|
}
|
|
break;
|
|
}
|
|
llvm::report_fatal_error("target not implemented");
|
|
}
|