[flang] Use proper attributes for runtime calls with 'i1' arguments/returns.
Clang uses signext/zeroext attributes for integer arguments shorter than the default 'int' type on a target. So Flang has to match this for functions from Fortran runtime and also for BIND(C) routines. This patch implements ABI adjustments only for Fortran runtime calls. BIND(C) part will be done separately. This resolves https://github.com/llvm/llvm-project/issues/58579 Differential Revision: https://reviews.llvm.org/D142677
This commit is contained in:
parent
7d626e7cbb
commit
ff794116f9
@ -20,6 +20,7 @@
|
|||||||
#include "flang/Common/Fortran.h"
|
#include "flang/Common/Fortran.h"
|
||||||
#include "flang/Common/uint128.h"
|
#include "flang/Common/uint128.h"
|
||||||
#include "flang/Optimizer/Builder/FIRBuilder.h"
|
#include "flang/Optimizer/Builder/FIRBuilder.h"
|
||||||
|
#include "flang/Optimizer/Dialect/FIRDialect.h"
|
||||||
#include "flang/Optimizer/Dialect/FIRType.h"
|
#include "flang/Optimizer/Dialect/FIRType.h"
|
||||||
#include "mlir/IR/BuiltinTypes.h"
|
#include "mlir/IR/BuiltinTypes.h"
|
||||||
#include "mlir/IR/MLIRContext.h"
|
#include "mlir/IR/MLIRContext.h"
|
||||||
@ -418,7 +419,7 @@ static mlir::func::FuncOp getRuntimeFunc(mlir::Location loc,
|
|||||||
return func;
|
return func;
|
||||||
auto funTy = RuntimeEntry::getTypeModel()(builder.getContext());
|
auto funTy = RuntimeEntry::getTypeModel()(builder.getContext());
|
||||||
func = builder.createFunction(loc, name, funTy);
|
func = builder.createFunction(loc, name, funTy);
|
||||||
func->setAttr("fir.runtime", builder.getUnitAttr());
|
func->setAttr(FIROpsDialect::getFirRuntimeAttrName(), builder.getUnitAttr());
|
||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,11 @@ public:
|
|||||||
void printAttribute(mlir::Attribute attr,
|
void printAttribute(mlir::Attribute attr,
|
||||||
mlir::DialectAsmPrinter &p) const override;
|
mlir::DialectAsmPrinter &p) const override;
|
||||||
|
|
||||||
|
/// Return string name of fir.runtime attribute.
|
||||||
|
static constexpr llvm::StringRef getFirRuntimeAttrName() {
|
||||||
|
return "fir.runtime";
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Register the Attributes of this dialect.
|
// Register the Attributes of this dialect.
|
||||||
void registerAttributes();
|
void registerAttributes();
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "flang/Optimizer/Builder/FIRBuilder.h"
|
#include "flang/Optimizer/Builder/FIRBuilder.h"
|
||||||
#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
|
#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
|
||||||
#include "flang/Optimizer/Builder/Todo.h"
|
#include "flang/Optimizer/Builder/Todo.h"
|
||||||
|
#include "flang/Optimizer/Dialect/FIRDialect.h"
|
||||||
#include "flang/Optimizer/Support/FIRContext.h"
|
#include "flang/Optimizer/Support/FIRContext.h"
|
||||||
#include "flang/Parser/parse-tree.h"
|
#include "flang/Parser/parse-tree.h"
|
||||||
#include "flang/Runtime/io-api.h"
|
#include "flang/Runtime/io-api.h"
|
||||||
@ -169,7 +170,8 @@ static mlir::func::FuncOp getIORuntimeFunc(mlir::Location loc,
|
|||||||
return func;
|
return func;
|
||||||
auto funTy = getTypeModel<E>()(builder.getContext());
|
auto funTy = getTypeModel<E>()(builder.getContext());
|
||||||
func = builder.createFunction(loc, name, funTy);
|
func = builder.createFunction(loc, name, funTy);
|
||||||
func->setAttr("fir.runtime", builder.getUnitAttr());
|
func->setAttr(fir::FIROpsDialect::getFirRuntimeAttrName(),
|
||||||
|
builder.getUnitAttr());
|
||||||
func->setAttr("fir.io", builder.getUnitAttr());
|
func->setAttr("fir.io", builder.getUnitAttr());
|
||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,19 @@
|
|||||||
|
|
||||||
using namespace fir;
|
using namespace fir;
|
||||||
|
|
||||||
|
namespace fir::details {
|
||||||
|
llvm::StringRef Attributes::getIntExtensionAttrName() const {
|
||||||
|
// The attribute names are available via LLVM dialect interfaces
|
||||||
|
// like getZExtAttrName(), getByValAttrName(), etc., so we'd better
|
||||||
|
// use them than literals.
|
||||||
|
if (isZeroExt())
|
||||||
|
return "llvm.zeroext";
|
||||||
|
else if (isSignExt())
|
||||||
|
return "llvm.signext";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
} // namespace fir::details
|
||||||
|
|
||||||
// Reduce a REAL/float type to the floating point semantics.
|
// Reduce a REAL/float type to the floating point semantics.
|
||||||
static const llvm::fltSemantics &floatToSemantics(const KindMapping &kindMap,
|
static const llvm::fltSemantics &floatToSemantics(const KindMapping &kindMap,
|
||||||
mlir::Type type) {
|
mlir::Type type) {
|
||||||
@ -41,8 +54,8 @@ struct GenericTarget : public CodeGenSpecifics {
|
|||||||
assert(fir::isa_real(eleTy));
|
assert(fir::isa_real(eleTy));
|
||||||
// Use a type that will be translated into LLVM as:
|
// Use a type that will be translated into LLVM as:
|
||||||
// { t, t } struct of 2 eleTy
|
// { t, t } struct of 2 eleTy
|
||||||
mlir::TypeRange range = {eleTy, eleTy};
|
return mlir::TupleType::get(eleTy.getContext(),
|
||||||
return mlir::TupleType::get(eleTy.getContext(), range);
|
mlir::TypeRange{eleTy, eleTy});
|
||||||
}
|
}
|
||||||
|
|
||||||
mlir::Type boxcharMemoryType(mlir::Type eleTy) const override {
|
mlir::Type boxcharMemoryType(mlir::Type eleTy) const override {
|
||||||
@ -50,8 +63,8 @@ struct GenericTarget : public CodeGenSpecifics {
|
|||||||
auto ptrTy = fir::ReferenceType::get(eleTy);
|
auto ptrTy = fir::ReferenceType::get(eleTy);
|
||||||
// Use a type that will be translated into LLVM as:
|
// Use a type that will be translated into LLVM as:
|
||||||
// { t*, index }
|
// { t*, index }
|
||||||
mlir::TypeRange range = {ptrTy, idxTy};
|
return mlir::TupleType::get(eleTy.getContext(),
|
||||||
return mlir::TupleType::get(eleTy.getContext(), range);
|
mlir::TypeRange{ptrTy, idxTy});
|
||||||
}
|
}
|
||||||
|
|
||||||
Marshalling boxcharArgumentType(mlir::Type eleTy, bool sret) const override {
|
Marshalling boxcharArgumentType(mlir::Type eleTy, bool sret) const override {
|
||||||
@ -67,6 +80,46 @@ struct GenericTarget : public CodeGenSpecifics {
|
|||||||
/*sret=*/sret, /*append=*/!sret});
|
/*sret=*/sret, /*append=*/!sret});
|
||||||
return marshal;
|
return marshal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CodeGenSpecifics::Marshalling
|
||||||
|
integerArgumentType(mlir::Location loc,
|
||||||
|
mlir::IntegerType argTy) const override {
|
||||||
|
CodeGenSpecifics::Marshalling marshal;
|
||||||
|
AT::IntegerExtension intExt = AT::IntegerExtension::None;
|
||||||
|
if (argTy.getWidth() < getCIntTypeWidth()) {
|
||||||
|
// isSigned() and isUnsigned() branches below are dead code currently.
|
||||||
|
// If needed, we can generate calls with signed/unsigned argument types
|
||||||
|
// to more precisely match C side (e.g. for Fortran runtime functions
|
||||||
|
// with 'unsigned short' arguments).
|
||||||
|
if (argTy.isSigned())
|
||||||
|
intExt = AT::IntegerExtension::Sign;
|
||||||
|
else if (argTy.isUnsigned())
|
||||||
|
intExt = AT::IntegerExtension::Zero;
|
||||||
|
else if (argTy.isSignless()) {
|
||||||
|
// Zero extend for 'i1' and sign extend for other types.
|
||||||
|
if (argTy.getWidth() == 1)
|
||||||
|
intExt = AT::IntegerExtension::Zero;
|
||||||
|
else
|
||||||
|
intExt = AT::IntegerExtension::Sign;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false,
|
||||||
|
/*sret=*/false, /*append=*/false,
|
||||||
|
/*intExt=*/intExt});
|
||||||
|
return marshal;
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeGenSpecifics::Marshalling
|
||||||
|
integerReturnType(mlir::Location loc,
|
||||||
|
mlir::IntegerType argTy) const override {
|
||||||
|
return integerArgumentType(loc, argTy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Width of 'int' type is 32-bits for almost all targets, except
|
||||||
|
// for AVR and MSP430 (see TargetInfo initializations
|
||||||
|
// in clang/lib/Basic/Targets).
|
||||||
|
unsigned char getCIntTypeWidth() const override { return 32; }
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -86,8 +139,8 @@ struct TargetI386 : public GenericTarget<TargetI386> {
|
|||||||
CodeGenSpecifics::Marshalling marshal;
|
CodeGenSpecifics::Marshalling marshal;
|
||||||
// Use a type that will be translated into LLVM as:
|
// Use a type that will be translated into LLVM as:
|
||||||
// { t, t } struct of 2 eleTy, byval, align 4
|
// { t, t } struct of 2 eleTy, byval, align 4
|
||||||
mlir::TypeRange range = {eleTy, eleTy};
|
auto structTy =
|
||||||
auto structTy = mlir::TupleType::get(eleTy.getContext(), range);
|
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
|
||||||
marshal.emplace_back(fir::ReferenceType::get(structTy),
|
marshal.emplace_back(fir::ReferenceType::get(structTy),
|
||||||
AT{/*alignment=*/4, /*byval=*/true});
|
AT{/*alignment=*/4, /*byval=*/true});
|
||||||
return marshal;
|
return marshal;
|
||||||
@ -105,8 +158,8 @@ struct TargetI386 : public GenericTarget<TargetI386> {
|
|||||||
} else if (sem == &llvm::APFloat::IEEEdouble()) {
|
} else if (sem == &llvm::APFloat::IEEEdouble()) {
|
||||||
// Use a type that will be translated into LLVM as:
|
// Use a type that will be translated into LLVM as:
|
||||||
// { t, t } struct of 2 eleTy, sret, align 4
|
// { t, t } struct of 2 eleTy, sret, align 4
|
||||||
mlir::TypeRange range = {eleTy, eleTy};
|
auto structTy = mlir::TupleType::get(eleTy.getContext(),
|
||||||
auto structTy = mlir::TupleType::get(eleTy.getContext(), range);
|
mlir::TypeRange{eleTy, eleTy});
|
||||||
marshal.emplace_back(fir::ReferenceType::get(structTy),
|
marshal.emplace_back(fir::ReferenceType::get(structTy),
|
||||||
AT{/*alignment=*/4, /*byval=*/false, /*sret=*/true});
|
AT{/*alignment=*/4, /*byval=*/false, /*sret=*/true});
|
||||||
} else {
|
} else {
|
||||||
@ -141,10 +194,10 @@ struct TargetX86_64 : public GenericTarget<TargetX86_64> {
|
|||||||
} else if (sem == &llvm::APFloat::IEEEquad()) {
|
} else if (sem == &llvm::APFloat::IEEEquad()) {
|
||||||
// Use a type that will be translated into LLVM as:
|
// Use a type that will be translated into LLVM as:
|
||||||
// { fp128, fp128 } struct of 2 fp128, byval, align 16
|
// { fp128, fp128 } struct of 2 fp128, byval, align 16
|
||||||
mlir::TypeRange range = {eleTy, eleTy};
|
marshal.emplace_back(
|
||||||
marshal.emplace_back(fir::ReferenceType::get(
|
fir::ReferenceType::get(mlir::TupleType::get(
|
||||||
mlir::TupleType::get(eleTy.getContext(), range)),
|
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
|
||||||
AT{/*align=*/16, /*byval=*/true});
|
AT{/*align=*/16, /*byval=*/true});
|
||||||
} else {
|
} else {
|
||||||
TODO(loc, "complex for this precision");
|
TODO(loc, "complex for this precision");
|
||||||
}
|
}
|
||||||
@ -161,16 +214,16 @@ struct TargetX86_64 : public GenericTarget<TargetX86_64> {
|
|||||||
} else if (sem == &llvm::APFloat::IEEEdouble()) {
|
} else if (sem == &llvm::APFloat::IEEEdouble()) {
|
||||||
// Use a type that will be translated into LLVM as:
|
// Use a type that will be translated into LLVM as:
|
||||||
// { double, double } struct of 2 double
|
// { double, double } struct of 2 double
|
||||||
mlir::TypeRange range = {eleTy, eleTy};
|
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
|
||||||
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range),
|
mlir::TypeRange{eleTy, eleTy}),
|
||||||
AT{});
|
AT{});
|
||||||
} else if (sem == &llvm::APFloat::IEEEquad()) {
|
} else if (sem == &llvm::APFloat::IEEEquad()) {
|
||||||
// Use a type that will be translated into LLVM as:
|
// Use a type that will be translated into LLVM as:
|
||||||
// { fp128, fp128 } struct of 2 fp128, sret, align 16
|
// { fp128, fp128 } struct of 2 fp128, sret, align 16
|
||||||
mlir::TypeRange range = {eleTy, eleTy};
|
marshal.emplace_back(
|
||||||
marshal.emplace_back(fir::ReferenceType::get(
|
fir::ReferenceType::get(mlir::TupleType::get(
|
||||||
mlir::TupleType::get(eleTy.getContext(), range)),
|
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
|
||||||
AT{/*align=*/16, /*byval=*/false, /*sret=*/true});
|
AT{/*align=*/16, /*byval=*/false, /*sret=*/true});
|
||||||
} else {
|
} else {
|
||||||
TODO(loc, "complex for this precision");
|
TODO(loc, "complex for this precision");
|
||||||
}
|
}
|
||||||
@ -211,8 +264,8 @@ struct TargetAArch64 : public GenericTarget<TargetAArch64> {
|
|||||||
sem == &llvm::APFloat::IEEEdouble()) {
|
sem == &llvm::APFloat::IEEEdouble()) {
|
||||||
// Use a type that will be translated into LLVM as:
|
// Use a type that will be translated into LLVM as:
|
||||||
// { t, t } struct of 2 eleTy
|
// { t, t } struct of 2 eleTy
|
||||||
mlir::TypeRange range = {eleTy, eleTy};
|
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
|
||||||
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range),
|
mlir::TypeRange{eleTy, eleTy}),
|
||||||
AT{});
|
AT{});
|
||||||
} else {
|
} else {
|
||||||
TODO(loc, "complex for this precision");
|
TODO(loc, "complex for this precision");
|
||||||
@ -246,8 +299,9 @@ struct TargetPPC64 : public GenericTarget<TargetPPC64> {
|
|||||||
CodeGenSpecifics::Marshalling marshal;
|
CodeGenSpecifics::Marshalling marshal;
|
||||||
// Use a type that will be translated into LLVM as:
|
// Use a type that will be translated into LLVM as:
|
||||||
// { t, t } struct of 2 element type
|
// { t, t } struct of 2 element type
|
||||||
mlir::TypeRange range = {eleTy, eleTy};
|
marshal.emplace_back(
|
||||||
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{});
|
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
|
||||||
|
AT{});
|
||||||
return marshal;
|
return marshal;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -277,8 +331,9 @@ struct TargetPPC64le : public GenericTarget<TargetPPC64le> {
|
|||||||
CodeGenSpecifics::Marshalling marshal;
|
CodeGenSpecifics::Marshalling marshal;
|
||||||
// Use a type that will be translated into LLVM as:
|
// Use a type that will be translated into LLVM as:
|
||||||
// { t, t } struct of 2 element type
|
// { t, t } struct of 2 element type
|
||||||
mlir::TypeRange range = {eleTy, eleTy};
|
marshal.emplace_back(
|
||||||
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{});
|
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
|
||||||
|
AT{});
|
||||||
return marshal;
|
return marshal;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -300,8 +355,8 @@ struct TargetSparc : public GenericTarget<TargetSparc> {
|
|||||||
CodeGenSpecifics::Marshalling marshal;
|
CodeGenSpecifics::Marshalling marshal;
|
||||||
// Use a type that will be translated into LLVM as:
|
// Use a type that will be translated into LLVM as:
|
||||||
// { t, t } struct of 2 eleTy
|
// { t, t } struct of 2 eleTy
|
||||||
mlir::TypeRange range = {eleTy, eleTy};
|
auto structTy =
|
||||||
auto structTy = mlir::TupleType::get(eleTy.getContext(), range);
|
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
|
||||||
marshal.emplace_back(fir::ReferenceType::get(structTy), AT{});
|
marshal.emplace_back(fir::ReferenceType::get(structTy), AT{});
|
||||||
return marshal;
|
return marshal;
|
||||||
}
|
}
|
||||||
@ -312,8 +367,8 @@ struct TargetSparc : public GenericTarget<TargetSparc> {
|
|||||||
CodeGenSpecifics::Marshalling marshal;
|
CodeGenSpecifics::Marshalling marshal;
|
||||||
// Use a type that will be translated into LLVM as:
|
// Use a type that will be translated into LLVM as:
|
||||||
// { t, t } struct of 2 eleTy, byval
|
// { t, t } struct of 2 eleTy, byval
|
||||||
mlir::TypeRange range = {eleTy, eleTy};
|
auto structTy =
|
||||||
auto structTy = mlir::TupleType::get(eleTy.getContext(), range);
|
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
|
||||||
marshal.emplace_back(fir::ReferenceType::get(structTy),
|
marshal.emplace_back(fir::ReferenceType::get(structTy),
|
||||||
AT{/*alignment=*/0, /*byval=*/true});
|
AT{/*alignment=*/0, /*byval=*/true});
|
||||||
return marshal;
|
return marshal;
|
||||||
@ -343,10 +398,10 @@ struct TargetSparcV9 : public GenericTarget<TargetSparcV9> {
|
|||||||
} else if (sem == &llvm::APFloat::IEEEquad()) {
|
} else if (sem == &llvm::APFloat::IEEEquad()) {
|
||||||
// Use a type that will be translated into LLVM as:
|
// Use a type that will be translated into LLVM as:
|
||||||
// { fp128, fp128 } struct of 2 fp128, byval, align 16
|
// { fp128, fp128 } struct of 2 fp128, byval, align 16
|
||||||
mlir::TypeRange range = {eleTy, eleTy};
|
marshal.emplace_back(
|
||||||
marshal.emplace_back(fir::ReferenceType::get(
|
fir::ReferenceType::get(mlir::TupleType::get(
|
||||||
mlir::TupleType::get(eleTy.getContext(), range)),
|
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
|
||||||
AT{/*align=*/16, /*byval=*/true});
|
AT{/*align=*/16, /*byval=*/true});
|
||||||
} else {
|
} else {
|
||||||
TODO(loc, "complex for this precision");
|
TODO(loc, "complex for this precision");
|
||||||
}
|
}
|
||||||
@ -358,8 +413,9 @@ struct TargetSparcV9 : public GenericTarget<TargetSparcV9> {
|
|||||||
CodeGenSpecifics::Marshalling marshal;
|
CodeGenSpecifics::Marshalling marshal;
|
||||||
// Use a type that will be translated into LLVM as:
|
// Use a type that will be translated into LLVM as:
|
||||||
// { eleTy, eleTy } struct of 2 eleTy
|
// { eleTy, eleTy } struct of 2 eleTy
|
||||||
mlir::TypeRange range = {eleTy, eleTy};
|
marshal.emplace_back(
|
||||||
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{});
|
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
|
||||||
|
AT{});
|
||||||
return marshal;
|
return marshal;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -398,8 +454,8 @@ struct TargetRISCV64 : public GenericTarget<TargetRISCV64> {
|
|||||||
sem == &llvm::APFloat::IEEEdouble()) {
|
sem == &llvm::APFloat::IEEEdouble()) {
|
||||||
// Use a type that will be translated into LLVM as:
|
// Use a type that will be translated into LLVM as:
|
||||||
// { t, t } struct of 2 eleTy, byVal
|
// { t, t } struct of 2 eleTy, byVal
|
||||||
mlir::TypeRange range = {eleTy, eleTy};
|
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
|
||||||
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range),
|
mlir::TypeRange{eleTy, eleTy}),
|
||||||
AT{/*alignment=*/0, /*byval=*/true});
|
AT{/*alignment=*/0, /*byval=*/true});
|
||||||
} else {
|
} else {
|
||||||
TODO(loc, "complex for this precision");
|
TODO(loc, "complex for this precision");
|
||||||
|
@ -29,21 +29,29 @@ namespace details {
|
|||||||
/// LLVMContext.
|
/// LLVMContext.
|
||||||
class Attributes {
|
class Attributes {
|
||||||
public:
|
public:
|
||||||
|
enum class IntegerExtension { None, Zero, Sign };
|
||||||
|
|
||||||
Attributes(unsigned short alignment = 0, bool byval = false,
|
Attributes(unsigned short alignment = 0, bool byval = false,
|
||||||
bool sret = false, bool append = false)
|
bool sret = false, bool append = false,
|
||||||
: alignment{alignment}, byval{byval}, sret{sret}, append{append} {}
|
IntegerExtension intExt = IntegerExtension::None)
|
||||||
|
: alignment{alignment}, byval{byval}, sret{sret}, append{append},
|
||||||
|
intExt{intExt} {}
|
||||||
|
|
||||||
unsigned getAlignment() const { return alignment; }
|
unsigned getAlignment() const { return alignment; }
|
||||||
bool hasAlignment() const { return alignment != 0; }
|
bool hasAlignment() const { return alignment != 0; }
|
||||||
bool isByVal() const { return byval; }
|
bool isByVal() const { return byval; }
|
||||||
bool isSRet() const { return sret; }
|
bool isSRet() const { return sret; }
|
||||||
bool isAppend() const { return append; }
|
bool isAppend() const { return append; }
|
||||||
|
bool isZeroExt() const { return intExt == IntegerExtension::Zero; }
|
||||||
|
bool isSignExt() const { return intExt == IntegerExtension::Sign; }
|
||||||
|
llvm::StringRef getIntExtensionAttrName() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned short alignment{};
|
unsigned short alignment{};
|
||||||
bool byval : 1;
|
bool byval : 1;
|
||||||
bool sret : 1;
|
bool sret : 1;
|
||||||
bool append : 1;
|
bool append : 1;
|
||||||
|
IntegerExtension intExt;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace details
|
} // namespace details
|
||||||
@ -94,6 +102,47 @@ public:
|
|||||||
virtual Marshalling boxcharArgumentType(mlir::Type eleTy,
|
virtual Marshalling boxcharArgumentType(mlir::Type eleTy,
|
||||||
bool sret = false) const = 0;
|
bool sret = false) const = 0;
|
||||||
|
|
||||||
|
// Compute ABI rules for an integer argument of the given mlir::IntegerType
|
||||||
|
// \p argTy. Note that this methods is supposed to be called for
|
||||||
|
// arguments passed by value not via reference, e.g. the 'i1' argument here:
|
||||||
|
// declare i1 @_FortranAioOutputLogical(ptr, i1)
|
||||||
|
//
|
||||||
|
// \p loc is the location of the operation using/specifying the argument.
|
||||||
|
//
|
||||||
|
// Currently, the only supported marshalling is whether the argument
|
||||||
|
// should be zero or sign extended.
|
||||||
|
//
|
||||||
|
// The zero/sign extension is especially important to comply with the ABI
|
||||||
|
// used by C/C++ compiler that builds Fortran runtime. As in the above
|
||||||
|
// example the callee will expect the caller to zero extend the second
|
||||||
|
// argument up to the size of the C/C++'s 'int' type.
|
||||||
|
// The corresponding handling in clang is done in
|
||||||
|
// DefaultABIInfo::classifyArgumentType(), and the logic may brielfy
|
||||||
|
// be explained as some sort of extension is required if the integer
|
||||||
|
// type is shorter than the size of 'int' for the target.
|
||||||
|
// The related code is located in ASTContext::isPromotableIntegerType()
|
||||||
|
// and ABIInfo::isPromotableIntegerTypeForABI().
|
||||||
|
// In particular, the latter returns 'true' for 'bool', several kinds
|
||||||
|
// of 'char', 'short', 'wchar' and enumerated types.
|
||||||
|
// The type of the extensions (zero or sign) depends on the signedness
|
||||||
|
// of the original language type.
|
||||||
|
//
|
||||||
|
// It is not clear how to handle signless integer types.
|
||||||
|
// From the point of Fortran-C interface all supported integer types
|
||||||
|
// seem to be signed except for CFI_type_Bool/bool that is supported
|
||||||
|
// via signless 'i1', but that is treated as unsigned type by clang
|
||||||
|
// (e.g. 'bool' arguments are using 'zeroext' ABI).
|
||||||
|
virtual Marshalling integerArgumentType(mlir::Location loc,
|
||||||
|
mlir::IntegerType argTy) const = 0;
|
||||||
|
|
||||||
|
// By default, integer argument and return values use the same
|
||||||
|
// zero/sign extension rules.
|
||||||
|
virtual Marshalling integerReturnType(mlir::Location loc,
|
||||||
|
mlir::IntegerType argTy) const = 0;
|
||||||
|
|
||||||
|
// Returns width in bits of C/C++ 'int' type size.
|
||||||
|
virtual unsigned char getCIntTypeWidth() const = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
mlir::MLIRContext &context;
|
mlir::MLIRContext &context;
|
||||||
llvm::Triple triple;
|
llvm::Triple triple;
|
||||||
|
@ -101,14 +101,14 @@ public:
|
|||||||
// Convert ops in target-specific patterns.
|
// Convert ops in target-specific patterns.
|
||||||
mod.walk([&](mlir::Operation *op) {
|
mod.walk([&](mlir::Operation *op) {
|
||||||
if (auto call = mlir::dyn_cast<fir::CallOp>(op)) {
|
if (auto call = mlir::dyn_cast<fir::CallOp>(op)) {
|
||||||
if (!hasPortableSignature(call.getFunctionType()))
|
if (!hasPortableSignature(call.getFunctionType(), op))
|
||||||
convertCallOp(call);
|
convertCallOp(call);
|
||||||
} else if (auto dispatch = mlir::dyn_cast<fir::DispatchOp>(op)) {
|
} else if (auto dispatch = mlir::dyn_cast<fir::DispatchOp>(op)) {
|
||||||
if (!hasPortableSignature(dispatch.getFunctionType()))
|
if (!hasPortableSignature(dispatch.getFunctionType(), op))
|
||||||
convertCallOp(dispatch);
|
convertCallOp(dispatch);
|
||||||
} else if (auto addr = mlir::dyn_cast<fir::AddrOfOp>(op)) {
|
} else if (auto addr = mlir::dyn_cast<fir::AddrOfOp>(op)) {
|
||||||
if (addr.getType().isa<mlir::FunctionType>() &&
|
if (addr.getType().isa<mlir::FunctionType>() &&
|
||||||
!hasPortableSignature(addr.getType()))
|
!hasPortableSignature(addr.getType(), op))
|
||||||
convertAddrOp(addr);
|
convertAddrOp(addr);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -457,19 +457,23 @@ public:
|
|||||||
/// then it is considered portable for any target, and this function will
|
/// then it is considered portable for any target, and this function will
|
||||||
/// return `true`. Otherwise, the signature is not portable and `false` is
|
/// return `true`. Otherwise, the signature is not portable and `false` is
|
||||||
/// returned.
|
/// returned.
|
||||||
bool hasPortableSignature(mlir::Type signature) {
|
bool hasPortableSignature(mlir::Type signature, mlir::Operation *op) {
|
||||||
assert(signature.isa<mlir::FunctionType>());
|
assert(signature.isa<mlir::FunctionType>());
|
||||||
auto func = signature.dyn_cast<mlir::FunctionType>();
|
auto func = signature.dyn_cast<mlir::FunctionType>();
|
||||||
|
bool hasFirRuntime = op->hasAttrOfType<mlir::UnitAttr>(
|
||||||
|
fir::FIROpsDialect::getFirRuntimeAttrName());
|
||||||
for (auto ty : func.getResults())
|
for (auto ty : func.getResults())
|
||||||
if ((ty.isa<fir::BoxCharType>() && !noCharacterConversion) ||
|
if ((ty.isa<fir::BoxCharType>() && !noCharacterConversion) ||
|
||||||
(fir::isa_complex(ty) && !noComplexConversion)) {
|
(fir::isa_complex(ty) && !noComplexConversion) ||
|
||||||
|
(ty.isa<mlir::IntegerType>() && hasFirRuntime)) {
|
||||||
LLVM_DEBUG(llvm::dbgs() << "rewrite " << signature << " for target\n");
|
LLVM_DEBUG(llvm::dbgs() << "rewrite " << signature << " for target\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (auto ty : func.getInputs())
|
for (auto ty : func.getInputs())
|
||||||
if (((ty.isa<fir::BoxCharType>() || fir::isCharacterProcedureTuple(ty)) &&
|
if (((ty.isa<fir::BoxCharType>() || fir::isCharacterProcedureTuple(ty)) &&
|
||||||
!noCharacterConversion) ||
|
!noCharacterConversion) ||
|
||||||
(fir::isa_complex(ty) && !noComplexConversion)) {
|
(fir::isa_complex(ty) && !noComplexConversion) ||
|
||||||
|
(ty.isa<mlir::IntegerType>() && hasFirRuntime)) {
|
||||||
LLVM_DEBUG(llvm::dbgs() << "rewrite " << signature << " for target\n");
|
LLVM_DEBUG(llvm::dbgs() << "rewrite " << signature << " for target\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -490,13 +494,14 @@ public:
|
|||||||
/// the immediately subsequent target code gen.
|
/// the immediately subsequent target code gen.
|
||||||
void convertSignature(mlir::func::FuncOp func) {
|
void convertSignature(mlir::func::FuncOp func) {
|
||||||
auto funcTy = func.getFunctionType().cast<mlir::FunctionType>();
|
auto funcTy = func.getFunctionType().cast<mlir::FunctionType>();
|
||||||
if (hasPortableSignature(funcTy) && !hasHostAssociations(func))
|
if (hasPortableSignature(funcTy, func) && !hasHostAssociations(func))
|
||||||
return;
|
return;
|
||||||
llvm::SmallVector<mlir::Type> newResTys;
|
llvm::SmallVector<mlir::Type> newResTys;
|
||||||
llvm::SmallVector<mlir::Type> newInTys;
|
llvm::SmallVector<mlir::Type> newInTys;
|
||||||
llvm::SmallVector<std::pair<unsigned, mlir::NamedAttribute>> savedAttrs;
|
llvm::SmallVector<std::pair<unsigned, mlir::NamedAttribute>> savedAttrs;
|
||||||
llvm::SmallVector<std::pair<unsigned, mlir::NamedAttribute>> extraAttrs;
|
llvm::SmallVector<std::pair<unsigned, mlir::NamedAttribute>> extraAttrs;
|
||||||
llvm::SmallVector<FixupTy> fixups;
|
llvm::SmallVector<FixupTy> fixups;
|
||||||
|
llvm::SmallVector<std::pair<unsigned, mlir::NamedAttrList>, 1> resultAttrs;
|
||||||
|
|
||||||
// Save argument attributes in case there is a shift so we can replace them
|
// Save argument attributes in case there is a shift so we can replace them
|
||||||
// correctly.
|
// correctly.
|
||||||
@ -524,6 +529,22 @@ public:
|
|||||||
else
|
else
|
||||||
doComplexReturn(func, cmplx, newResTys, newInTys, fixups);
|
doComplexReturn(func, cmplx, newResTys, newInTys, fixups);
|
||||||
})
|
})
|
||||||
|
.Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {
|
||||||
|
auto m = specifics->integerArgumentType(func.getLoc(), intTy);
|
||||||
|
assert(m.size() == 1);
|
||||||
|
auto attr = std::get<fir::CodeGenSpecifics::Attributes>(m[0]);
|
||||||
|
auto retTy = std::get<mlir::Type>(m[0]);
|
||||||
|
std::size_t resId = newResTys.size();
|
||||||
|
llvm::StringRef extensionAttrName = attr.getIntExtensionAttrName();
|
||||||
|
if (!extensionAttrName.empty() &&
|
||||||
|
// TODO: we have to do the same for BIND(C) routines.
|
||||||
|
func->hasAttrOfType<mlir::UnitAttr>(
|
||||||
|
fir::FIROpsDialect::getFirRuntimeAttrName()))
|
||||||
|
resultAttrs.emplace_back(
|
||||||
|
resId, rewriter->getNamedAttr(extensionAttrName,
|
||||||
|
rewriter->getUnitAttr()));
|
||||||
|
newResTys.push_back(retTy);
|
||||||
|
})
|
||||||
.Default([&](mlir::Type ty) { newResTys.push_back(ty); });
|
.Default([&](mlir::Type ty) { newResTys.push_back(ty); });
|
||||||
|
|
||||||
// Saved potential shift in argument. Handling of result can add arguments
|
// Saved potential shift in argument. Handling of result can add arguments
|
||||||
@ -587,6 +608,26 @@ public:
|
|||||||
newInTys.push_back(ty);
|
newInTys.push_back(ty);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.Case<mlir::IntegerType>([&](mlir::IntegerType intTy) {
|
||||||
|
auto m = specifics->integerArgumentType(func.getLoc(), intTy);
|
||||||
|
assert(m.size() == 1);
|
||||||
|
auto attr = std::get<fir::CodeGenSpecifics::Attributes>(m[0]);
|
||||||
|
auto argTy = std::get<mlir::Type>(m[0]);
|
||||||
|
auto argNo = newInTys.size();
|
||||||
|
llvm::StringRef extensionAttrName = attr.getIntExtensionAttrName();
|
||||||
|
if (!extensionAttrName.empty() &&
|
||||||
|
// TODO: we have to do the same for BIND(C) routines.
|
||||||
|
func->hasAttrOfType<mlir::UnitAttr>(
|
||||||
|
fir::FIROpsDialect::getFirRuntimeAttrName())) {
|
||||||
|
fixups.emplace_back(FixupTy::Codes::ArgumentType, argNo,
|
||||||
|
[=](mlir::func::FuncOp func) {
|
||||||
|
func.setArgAttr(
|
||||||
|
argNo, extensionAttrName,
|
||||||
|
mlir::UnitAttr::get(func.getContext()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
newInTys.push_back(argTy);
|
||||||
|
})
|
||||||
.Default([&](mlir::Type ty) { newInTys.push_back(ty); });
|
.Default([&](mlir::Type ty) { newInTys.push_back(ty); });
|
||||||
|
|
||||||
if (func.getArgAttrOfType<mlir::UnitAttr>(index,
|
if (func.getArgAttrOfType<mlir::UnitAttr>(index,
|
||||||
@ -623,14 +664,18 @@ public:
|
|||||||
case FixupTy::Codes::ArgumentType: {
|
case FixupTy::Codes::ArgumentType: {
|
||||||
// Argument is pass-by-value, but its type has likely been modified to
|
// Argument is pass-by-value, but its type has likely been modified to
|
||||||
// suit the target ABI convention.
|
// suit the target ABI convention.
|
||||||
|
auto oldArgTy =
|
||||||
|
fir::ReferenceType::get(oldArgTys[fixup.index - offset]);
|
||||||
|
// If type did not change, keep the original argument.
|
||||||
|
if (newInTys[fixup.index] == oldArgTy)
|
||||||
|
break;
|
||||||
|
|
||||||
auto newArg = func.front().insertArgument(fixup.index,
|
auto newArg = func.front().insertArgument(fixup.index,
|
||||||
newInTys[fixup.index], loc);
|
newInTys[fixup.index], loc);
|
||||||
rewriter->setInsertionPointToStart(&func.front());
|
rewriter->setInsertionPointToStart(&func.front());
|
||||||
auto mem =
|
auto mem =
|
||||||
rewriter->create<fir::AllocaOp>(loc, newInTys[fixup.index]);
|
rewriter->create<fir::AllocaOp>(loc, newInTys[fixup.index]);
|
||||||
rewriter->create<fir::StoreOp>(loc, newArg, mem);
|
rewriter->create<fir::StoreOp>(loc, newArg, mem);
|
||||||
auto oldArgTy =
|
|
||||||
fir::ReferenceType::get(oldArgTys[fixup.index - offset]);
|
|
||||||
auto cast = rewriter->create<fir::ConvertOp>(loc, oldArgTy, mem);
|
auto cast = rewriter->create<fir::ConvertOp>(loc, oldArgTy, mem);
|
||||||
mlir::Value load = rewriter->create<fir::LoadOp>(loc, cast);
|
mlir::Value load = rewriter->create<fir::LoadOp>(loc, cast);
|
||||||
func.getArgument(fixup.index + 1).replaceAllUsesWith(load);
|
func.getArgument(fixup.index + 1).replaceAllUsesWith(load);
|
||||||
@ -759,6 +804,10 @@ public:
|
|||||||
func.setArgAttr(extraAttr.first, extraAttr.second.getName(),
|
func.setArgAttr(extraAttr.first, extraAttr.second.getName(),
|
||||||
extraAttr.second.getValue());
|
extraAttr.second.getValue());
|
||||||
|
|
||||||
|
for (auto [resId, resAttrList] : resultAttrs)
|
||||||
|
for (mlir::NamedAttribute resAttr : resAttrList)
|
||||||
|
func.setResultAttr(resId, resAttr.getName(), resAttr.getValue());
|
||||||
|
|
||||||
// Replace attributes to the correct argument if there was an argument shift
|
// Replace attributes to the correct argument if there was an argument shift
|
||||||
// to the right.
|
// to the right.
|
||||||
if (argumentShift > 0) {
|
if (argumentShift > 0) {
|
||||||
|
77
flang/test/Fir/target-rewrite-integer.fir
Normal file
77
flang/test/Fir/target-rewrite-integer.fir
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// RUN: fir-opt --split-input-file --target-rewrite="target=i386-unknown-linux-gnu" %s | FileCheck %s --check-prefixes=I32,ALL
|
||||||
|
// RUN: fir-opt --split-input-file --target-rewrite="target=x86_64-unknown-linux-gnu" %s | FileCheck %s --check-prefixes=X64,ALL
|
||||||
|
// RUN: fir-opt --split-input-file --target-rewrite="target=aarch64-unknown-linux-gnu" %s | FileCheck %s --check-prefixes=AARCH64,ALL
|
||||||
|
// RUN: fir-opt --split-input-file --target-rewrite="target=powerpc64le-unknown-linux-gnu" %s | FileCheck %s --check-prefixes=PPC,ALL
|
||||||
|
// RUN: fir-opt --split-input-file --target-rewrite="target=sparc64-unknown-linux-gnu" %s | FileCheck %s --check-prefixes=SPARCV9,ALL
|
||||||
|
// RUN: fir-opt --split-input-file --target-rewrite="target=sparcv9-sun-solaris2.11" %s | FileCheck %s --check-prefixes=SPARCV9,ALL
|
||||||
|
|
||||||
|
// -----
|
||||||
|
|
||||||
|
// subroutine test_i1(x)
|
||||||
|
// logical x
|
||||||
|
// print *, x
|
||||||
|
// end subroutine test_i1
|
||||||
|
|
||||||
|
// ALL-LABEL: @_QPtest_i1
|
||||||
|
// I32: func.func{{.*}}@_FortranAioOutputLogical({{.*}}i1 {llvm.zeroext}) -> (i1 {llvm.zeroext})
|
||||||
|
// X64: func.func{{.*}}@_FortranAioOutputLogical({{.*}}i1 {llvm.zeroext}) -> (i1 {llvm.zeroext})
|
||||||
|
// AARCH64: func.func{{.*}}@_FortranAioOutputLogical({{.*}}i1 {llvm.zeroext}) -> (i1 {llvm.zeroext})
|
||||||
|
// PPC: func.func{{.*}}@_FortranAioOutputLogical({{.*}}i1 {llvm.zeroext}) -> (i1 {llvm.zeroext})
|
||||||
|
// SPARCV9: func.func{{.*}}@_FortranAioOutputLogical({{.*}}i1 {llvm.zeroext}) -> (i1 {llvm.zeroext})
|
||||||
|
func.func @_QPtest_i1(%arg0: !fir.ref<!fir.logical<4>> {fir.bindc_name = "x"}) {
|
||||||
|
%c3_i32 = arith.constant 3 : i32
|
||||||
|
%c-1_i32 = arith.constant -1 : i32
|
||||||
|
%0 = fir.address_of(@_QQcl.2E2F746573742E66393000) : !fir.ref<!fir.char<1,11>>
|
||||||
|
%1 = fir.convert %0 : (!fir.ref<!fir.char<1,11>>) -> !fir.ref<i8>
|
||||||
|
%2 = fir.call @_FortranAioBeginExternalListOutput(%c-1_i32, %1, %c3_i32) : (i32, !fir.ref<i8>, i32) -> !fir.ref<i8>
|
||||||
|
%3 = fir.load %arg0 : !fir.ref<!fir.logical<4>>
|
||||||
|
%4 = fir.convert %3 : (!fir.logical<4>) -> i1
|
||||||
|
%5 = fir.call @_FortranAioOutputLogical(%2, %4) : (!fir.ref<i8>, i1) -> i1
|
||||||
|
%6 = fir.call @_FortranAioEndIoStatement(%2) : (!fir.ref<i8>) -> i32
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func.func private @_FortranAioBeginExternalListOutput(i32, !fir.ref<i8>, i32) -> !fir.ref<i8> attributes {fir.io, fir.runtime}
|
||||||
|
fir.global linkonce @_QQcl.2E2F746573742E66393000 constant : !fir.char<1,11> {
|
||||||
|
%0 = fir.string_lit "./test.f90\00"(11) : !fir.char<1,11>
|
||||||
|
fir.has_value %0 : !fir.char<1,11>
|
||||||
|
}
|
||||||
|
func.func private @_FortranAioOutputLogical(!fir.ref<i8>, i1) -> i1 attributes {fir.io, fir.runtime}
|
||||||
|
func.func private @_FortranAioEndIoStatement(!fir.ref<i8>) -> i32 attributes {fir.io, fir.runtime}
|
||||||
|
|
||||||
|
// -----
|
||||||
|
|
||||||
|
// Manually created test with 'si1' argument/return type.
|
||||||
|
// Flang does not use 'si1' type currently.
|
||||||
|
|
||||||
|
// ALL-LABEL: @_QPtest_si1
|
||||||
|
// I32: func.func{{.*}}@_SomeFunc_si1(si1 {llvm.signext}) -> (si1 {llvm.signext})
|
||||||
|
// X64: func.func{{.*}}@_SomeFunc_si1(si1 {llvm.signext}) -> (si1 {llvm.signext})
|
||||||
|
// AARCH64: func.func{{.*}}@_SomeFunc_si1(si1 {llvm.signext}) -> (si1 {llvm.signext})
|
||||||
|
// PPC: func.func{{.*}}@_SomeFunc_si1(si1 {llvm.signext}) -> (si1 {llvm.signext})
|
||||||
|
// SPARCV9: func.func{{.*}}@_SomeFunc_si1(si1 {llvm.signext}) -> (si1 {llvm.signext})
|
||||||
|
func.func @_QPtest_si1(%arg0: !fir.ref<!fir.logical<4>> {fir.bindc_name = "x"}) {
|
||||||
|
%0 = fir.load %arg0 : !fir.ref<!fir.logical<4>>
|
||||||
|
%1 = fir.convert %0 : (!fir.logical<4>) -> si1
|
||||||
|
%2 = fir.call @_SomeFunc_si1(%1) : (si1) -> si1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func.func private @_SomeFunc_si1(si1) -> si1 attributes {fir.runtime}
|
||||||
|
|
||||||
|
// -----
|
||||||
|
|
||||||
|
// Manually created test with 'ui1' argument/return type.
|
||||||
|
// Flang does not use 'ui1' type currently.
|
||||||
|
|
||||||
|
// ALL-LABEL: @_QPtest_ui1
|
||||||
|
// I32: func.func{{.*}}@_SomeFunc_ui1(ui1 {llvm.zeroext}) -> (ui1 {llvm.zeroext})
|
||||||
|
// X64: func.func{{.*}}@_SomeFunc_ui1(ui1 {llvm.zeroext}) -> (ui1 {llvm.zeroext})
|
||||||
|
// AARCH64: func.func{{.*}}@_SomeFunc_ui1(ui1 {llvm.zeroext}) -> (ui1 {llvm.zeroext})
|
||||||
|
// PPC: func.func{{.*}}@_SomeFunc_ui1(ui1 {llvm.zeroext}) -> (ui1 {llvm.zeroext})
|
||||||
|
// SPARCV9: func.func{{.*}}@_SomeFunc_ui1(ui1 {llvm.zeroext}) -> (ui1 {llvm.zeroext})
|
||||||
|
func.func @_QPtest_ui1(%arg0: !fir.ref<!fir.logical<4>> {fir.bindc_name = "x"}) {
|
||||||
|
%0 = fir.load %arg0 : !fir.ref<!fir.logical<4>>
|
||||||
|
%1 = fir.convert %0 : (!fir.logical<4>) -> ui1
|
||||||
|
%2 = fir.call @_SomeFunc_ui1(%1) : (ui1) -> ui1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func.func private @_SomeFunc_ui1(ui1) -> ui1 attributes {fir.runtime}
|
Loading…
x
Reference in New Issue
Block a user