Revert "[flang] Use proper attributes for runtime calls with 'i1' arguments/returns."

This reverts commit d5b0de35bd.
This commit is contained in:
Slava Zakharin 2022-12-01 09:35:59 -08:00
parent 8ef4632681
commit 68755f223f
8 changed files with 14 additions and 252 deletions

View File

@ -20,7 +20,6 @@
#include "flang/Common/Fortran.h"
#include "flang/Common/uint128.h"
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Dialect/FIRType.h"
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/MLIRContext.h"
@ -412,7 +411,7 @@ static mlir::func::FuncOp getRuntimeFunc(mlir::Location loc,
return func;
auto funTy = RuntimeEntry::getTypeModel()(builder.getContext());
func = builder.createFunction(loc, name, funTy);
func->setAttr(FIROpsDialect::getFirRuntimeAttrName(), builder.getUnitAttr());
func->setAttr("fir.runtime", builder.getUnitAttr());
return func;
}

View File

@ -37,11 +37,6 @@ public:
void printAttribute(mlir::Attribute attr,
mlir::DialectAsmPrinter &p) const override;
/// Return string name of fir.runtime attribute.
static constexpr llvm::StringRef getFirRuntimeAttrName() {
return "fir.runtime";
}
private:
// Register the Attributes of this dialect.
void registerAttributes();

View File

@ -27,7 +27,6 @@
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
#include "flang/Optimizer/Builder/Todo.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Support/FIRContext.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Runtime/io-api.h"
@ -168,8 +167,7 @@ static mlir::func::FuncOp getIORuntimeFunc(mlir::Location loc,
return func;
auto funTy = getTypeModel<E>()(builder.getContext());
func = builder.createFunction(loc, name, funTy);
func->setAttr(fir::FIROpsDialect::getFirRuntimeAttrName(),
builder.getUnitAttr());
func->setAttr("fir.runtime", builder.getUnitAttr());
func->setAttr("fir.io", builder.getUnitAttr());
return func;
}

View File

@ -32,7 +32,6 @@
#include "flang/Optimizer/Builder/Runtime/Stop.h"
#include "flang/Optimizer/Builder/Runtime/Transformational.h"
#include "flang/Optimizer/Builder/Todo.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Dialect/FIROpsSupport.h"
#include "flang/Optimizer/Support/FatalError.h"
#include "flang/Runtime/entry-names.h"
@ -1705,8 +1704,7 @@ static mlir::func::FuncOp getFuncOp(mlir::Location loc,
const RuntimeFunction &runtime) {
mlir::func::FuncOp function = builder.addNamedFunction(
loc, runtime.symbol, runtime.typeGenerator(builder.getContext()));
function->setAttr(fir::FIROpsDialect::getFirRuntimeAttrName(),
builder.getUnitAttr());
function->setAttr("fir.runtime", builder.getUnitAttr());
return function;
}

View File

@ -22,19 +22,6 @@
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.
static const llvm::fltSemantics &floatToSemantics(const KindMapping &kindMap,
mlir::Type type) {
@ -80,46 +67,6 @@ struct GenericTarget : public CodeGenSpecifics {
/*sret=*/sret, /*append=*/!sret});
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

View File

@ -29,29 +29,21 @@ namespace details {
/// LLVMContext.
class Attributes {
public:
enum class IntegerExtension { None, Zero, Sign };
Attributes(unsigned short alignment = 0, bool byval = false,
bool sret = false, bool append = false,
IntegerExtension intExt = IntegerExtension::None)
: alignment{alignment}, byval{byval}, sret{sret}, append{append},
intExt{intExt} {}
bool sret = false, bool append = false)
: alignment{alignment}, byval{byval}, sret{sret}, append{append} {}
unsigned getAlignment() const { return alignment; }
bool hasAlignment() const { return alignment != 0; }
bool isByVal() const { return byval; }
bool isSRet() const { return sret; }
bool isAppend() const { return append; }
bool isZeroExt() const { return intExt == IntegerExtension::Zero; }
bool isSignExt() const { return intExt == IntegerExtension::Sign; }
llvm::StringRef getIntExtensionAttrName() const;
private:
unsigned short alignment{};
bool byval : 1;
bool sret : 1;
bool append : 1;
IntegerExtension intExt;
};
} // namespace details
@ -102,47 +94,6 @@ public:
virtual Marshalling boxcharArgumentType(mlir::Type eleTy,
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:
mlir::MLIRContext &context;
llvm::Triple triple;

View File

@ -100,14 +100,14 @@ public:
// Convert ops in target-specific patterns.
mod.walk([&](mlir::Operation *op) {
if (auto call = mlir::dyn_cast<fir::CallOp>(op)) {
if (!hasPortableSignature(call.getFunctionType(), op))
if (!hasPortableSignature(call.getFunctionType()))
convertCallOp(call);
} else if (auto dispatch = mlir::dyn_cast<fir::DispatchOp>(op)) {
if (!hasPortableSignature(dispatch.getFunctionType(), op))
if (!hasPortableSignature(dispatch.getFunctionType()))
convertCallOp(dispatch);
} else if (auto addr = mlir::dyn_cast<fir::AddrOfOp>(op)) {
if (addr.getType().isa<mlir::FunctionType>() &&
!hasPortableSignature(addr.getType(), op))
!hasPortableSignature(addr.getType()))
convertAddrOp(addr);
}
});
@ -456,23 +456,19 @@ public:
/// then it is considered portable for any target, and this function will
/// return `true`. Otherwise, the signature is not portable and `false` is
/// returned.
bool hasPortableSignature(mlir::Type signature, mlir::Operation *op) {
bool hasPortableSignature(mlir::Type signature) {
assert(signature.isa<mlir::FunctionType>());
auto func = signature.dyn_cast<mlir::FunctionType>();
bool hasFirRuntime = op->hasAttrOfType<mlir::UnitAttr>(
fir::FIROpsDialect::getFirRuntimeAttrName());
for (auto ty : func.getResults())
if ((ty.isa<fir::BoxCharType>() && !noCharacterConversion) ||
(fir::isa_complex(ty) && !noComplexConversion) ||
(ty.isa<mlir::IntegerType>() && hasFirRuntime)) {
(fir::isa_complex(ty) && !noComplexConversion)) {
LLVM_DEBUG(llvm::dbgs() << "rewrite " << signature << " for target\n");
return false;
}
for (auto ty : func.getInputs())
if (((ty.isa<fir::BoxCharType>() || fir::isCharacterProcedureTuple(ty)) &&
!noCharacterConversion) ||
(fir::isa_complex(ty) && !noComplexConversion) ||
(ty.isa<mlir::IntegerType>() && hasFirRuntime)) {
(fir::isa_complex(ty) && !noComplexConversion)) {
LLVM_DEBUG(llvm::dbgs() << "rewrite " << signature << " for target\n");
return false;
}
@ -493,14 +489,13 @@ public:
/// the immediately subsequent target code gen.
void convertSignature(mlir::func::FuncOp func) {
auto funcTy = func.getFunctionType().cast<mlir::FunctionType>();
if (hasPortableSignature(funcTy, func) && !hasHostAssociations(func))
if (hasPortableSignature(funcTy) && !hasHostAssociations(func))
return;
llvm::SmallVector<mlir::Type> newResTys;
llvm::SmallVector<mlir::Type> newInTys;
llvm::SmallVector<std::pair<unsigned, mlir::NamedAttribute>> savedAttrs;
llvm::SmallVector<std::pair<unsigned, mlir::NamedAttribute>> extraAttrs;
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
// correctly.
@ -527,22 +522,6 @@ public:
else
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); });
// Saved potential shift in argument. Handling of result can add arguments
@ -606,26 +585,6 @@ public:
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); });
if (func.getArgAttrOfType<mlir::UnitAttr>(index,
@ -662,18 +621,14 @@ public:
case FixupTy::Codes::ArgumentType: {
// Argument is pass-by-value, but its type has likely been modified to
// 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,
newInTys[fixup.index], loc);
rewriter->setInsertionPointToStart(&func.front());
auto mem =
rewriter->create<fir::AllocaOp>(loc, newInTys[fixup.index]);
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);
mlir::Value load = rewriter->create<fir::LoadOp>(loc, cast);
func.getArgument(fixup.index + 1).replaceAllUsesWith(load);
@ -802,10 +757,6 @@ public:
func.setArgAttr(extraAttr.first, extraAttr.second.getName(),
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
// to the right.
if (argumentShift > 0) {

View File

@ -1,77 +0,0 @@
// 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}