Skip to content

Commit ff79411

Browse files
committed
[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 #58579 Differential Revision: https://reviews.llvm.org/D142677
1 parent 7d626e7 commit ff79411

File tree

7 files changed

+288
-49
lines changed

7 files changed

+288
-49
lines changed

flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "flang/Common/Fortran.h"
2121
#include "flang/Common/uint128.h"
2222
#include "flang/Optimizer/Builder/FIRBuilder.h"
23+
#include "flang/Optimizer/Dialect/FIRDialect.h"
2324
#include "flang/Optimizer/Dialect/FIRType.h"
2425
#include "mlir/IR/BuiltinTypes.h"
2526
#include "mlir/IR/MLIRContext.h"
@@ -418,7 +419,7 @@ static mlir::func::FuncOp getRuntimeFunc(mlir::Location loc,
418419
return func;
419420
auto funTy = RuntimeEntry::getTypeModel()(builder.getContext());
420421
func = builder.createFunction(loc, name, funTy);
421-
func->setAttr("fir.runtime", builder.getUnitAttr());
422+
func->setAttr(FIROpsDialect::getFirRuntimeAttrName(), builder.getUnitAttr());
422423
return func;
423424
}
424425

flang/include/flang/Optimizer/Dialect/FIRDialect.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ class FIROpsDialect final : public mlir::Dialect {
3737
void printAttribute(mlir::Attribute attr,
3838
mlir::DialectAsmPrinter &p) const override;
3939

40+
/// Return string name of fir.runtime attribute.
41+
static constexpr llvm::StringRef getFirRuntimeAttrName() {
42+
return "fir.runtime";
43+
}
44+
4045
private:
4146
// Register the Attributes of this dialect.
4247
void registerAttributes();

flang/lib/Lower/IO.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "flang/Optimizer/Builder/FIRBuilder.h"
2929
#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
3030
#include "flang/Optimizer/Builder/Todo.h"
31+
#include "flang/Optimizer/Dialect/FIRDialect.h"
3132
#include "flang/Optimizer/Support/FIRContext.h"
3233
#include "flang/Parser/parse-tree.h"
3334
#include "flang/Runtime/io-api.h"
@@ -169,7 +170,8 @@ static mlir::func::FuncOp getIORuntimeFunc(mlir::Location loc,
169170
return func;
170171
auto funTy = getTypeModel<E>()(builder.getContext());
171172
func = builder.createFunction(loc, name, funTy);
172-
func->setAttr("fir.runtime", builder.getUnitAttr());
173+
func->setAttr(fir::FIROpsDialect::getFirRuntimeAttrName(),
174+
builder.getUnitAttr());
173175
func->setAttr("fir.io", builder.getUnitAttr());
174176
return func;
175177
}

flang/lib/Optimizer/CodeGen/Target.cpp

Lines changed: 92 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,19 @@
2222

2323
using namespace fir;
2424

25+
namespace fir::details {
26+
llvm::StringRef Attributes::getIntExtensionAttrName() const {
27+
// The attribute names are available via LLVM dialect interfaces
28+
// like getZExtAttrName(), getByValAttrName(), etc., so we'd better
29+
// use them than literals.
30+
if (isZeroExt())
31+
return "llvm.zeroext";
32+
else if (isSignExt())
33+
return "llvm.signext";
34+
return {};
35+
}
36+
} // namespace fir::details
37+
2538
// Reduce a REAL/float type to the floating point semantics.
2639
static const llvm::fltSemantics &floatToSemantics(const KindMapping &kindMap,
2740
mlir::Type type) {
@@ -41,17 +54,17 @@ struct GenericTarget : public CodeGenSpecifics {
4154
assert(fir::isa_real(eleTy));
4255
// Use a type that will be translated into LLVM as:
4356
// { t, t } struct of 2 eleTy
44-
mlir::TypeRange range = {eleTy, eleTy};
45-
return mlir::TupleType::get(eleTy.getContext(), range);
57+
return mlir::TupleType::get(eleTy.getContext(),
58+
mlir::TypeRange{eleTy, eleTy});
4659
}
4760

4861
mlir::Type boxcharMemoryType(mlir::Type eleTy) const override {
4962
auto idxTy = mlir::IntegerType::get(eleTy.getContext(), S::defaultWidth);
5063
auto ptrTy = fir::ReferenceType::get(eleTy);
5164
// Use a type that will be translated into LLVM as:
5265
// { t*, index }
53-
mlir::TypeRange range = {ptrTy, idxTy};
54-
return mlir::TupleType::get(eleTy.getContext(), range);
66+
return mlir::TupleType::get(eleTy.getContext(),
67+
mlir::TypeRange{ptrTy, idxTy});
5568
}
5669

5770
Marshalling boxcharArgumentType(mlir::Type eleTy, bool sret) const override {
@@ -67,6 +80,46 @@ struct GenericTarget : public CodeGenSpecifics {
6780
/*sret=*/sret, /*append=*/!sret});
6881
return marshal;
6982
}
83+
84+
CodeGenSpecifics::Marshalling
85+
integerArgumentType(mlir::Location loc,
86+
mlir::IntegerType argTy) const override {
87+
CodeGenSpecifics::Marshalling marshal;
88+
AT::IntegerExtension intExt = AT::IntegerExtension::None;
89+
if (argTy.getWidth() < getCIntTypeWidth()) {
90+
// isSigned() and isUnsigned() branches below are dead code currently.
91+
// If needed, we can generate calls with signed/unsigned argument types
92+
// to more precisely match C side (e.g. for Fortran runtime functions
93+
// with 'unsigned short' arguments).
94+
if (argTy.isSigned())
95+
intExt = AT::IntegerExtension::Sign;
96+
else if (argTy.isUnsigned())
97+
intExt = AT::IntegerExtension::Zero;
98+
else if (argTy.isSignless()) {
99+
// Zero extend for 'i1' and sign extend for other types.
100+
if (argTy.getWidth() == 1)
101+
intExt = AT::IntegerExtension::Zero;
102+
else
103+
intExt = AT::IntegerExtension::Sign;
104+
}
105+
}
106+
107+
marshal.emplace_back(argTy, AT{/*alignment=*/0, /*byval=*/false,
108+
/*sret=*/false, /*append=*/false,
109+
/*intExt=*/intExt});
110+
return marshal;
111+
}
112+
113+
CodeGenSpecifics::Marshalling
114+
integerReturnType(mlir::Location loc,
115+
mlir::IntegerType argTy) const override {
116+
return integerArgumentType(loc, argTy);
117+
}
118+
119+
// Width of 'int' type is 32-bits for almost all targets, except
120+
// for AVR and MSP430 (see TargetInfo initializations
121+
// in clang/lib/Basic/Targets).
122+
unsigned char getCIntTypeWidth() const override { return 32; }
70123
};
71124
} // namespace
72125

@@ -86,8 +139,8 @@ struct TargetI386 : public GenericTarget<TargetI386> {
86139
CodeGenSpecifics::Marshalling marshal;
87140
// Use a type that will be translated into LLVM as:
88141
// { t, t } struct of 2 eleTy, byval, align 4
89-
mlir::TypeRange range = {eleTy, eleTy};
90-
auto structTy = mlir::TupleType::get(eleTy.getContext(), range);
142+
auto structTy =
143+
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
91144
marshal.emplace_back(fir::ReferenceType::get(structTy),
92145
AT{/*alignment=*/4, /*byval=*/true});
93146
return marshal;
@@ -105,8 +158,8 @@ struct TargetI386 : public GenericTarget<TargetI386> {
105158
} else if (sem == &llvm::APFloat::IEEEdouble()) {
106159
// Use a type that will be translated into LLVM as:
107160
// { t, t } struct of 2 eleTy, sret, align 4
108-
mlir::TypeRange range = {eleTy, eleTy};
109-
auto structTy = mlir::TupleType::get(eleTy.getContext(), range);
161+
auto structTy = mlir::TupleType::get(eleTy.getContext(),
162+
mlir::TypeRange{eleTy, eleTy});
110163
marshal.emplace_back(fir::ReferenceType::get(structTy),
111164
AT{/*alignment=*/4, /*byval=*/false, /*sret=*/true});
112165
} else {
@@ -141,10 +194,10 @@ struct TargetX86_64 : public GenericTarget<TargetX86_64> {
141194
} else if (sem == &llvm::APFloat::IEEEquad()) {
142195
// Use a type that will be translated into LLVM as:
143196
// { fp128, fp128 } struct of 2 fp128, byval, align 16
144-
mlir::TypeRange range = {eleTy, eleTy};
145-
marshal.emplace_back(fir::ReferenceType::get(
146-
mlir::TupleType::get(eleTy.getContext(), range)),
147-
AT{/*align=*/16, /*byval=*/true});
197+
marshal.emplace_back(
198+
fir::ReferenceType::get(mlir::TupleType::get(
199+
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
200+
AT{/*align=*/16, /*byval=*/true});
148201
} else {
149202
TODO(loc, "complex for this precision");
150203
}
@@ -161,16 +214,16 @@ struct TargetX86_64 : public GenericTarget<TargetX86_64> {
161214
} else if (sem == &llvm::APFloat::IEEEdouble()) {
162215
// Use a type that will be translated into LLVM as:
163216
// { double, double } struct of 2 double
164-
mlir::TypeRange range = {eleTy, eleTy};
165-
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range),
217+
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
218+
mlir::TypeRange{eleTy, eleTy}),
166219
AT{});
167220
} else if (sem == &llvm::APFloat::IEEEquad()) {
168221
// Use a type that will be translated into LLVM as:
169222
// { fp128, fp128 } struct of 2 fp128, sret, align 16
170-
mlir::TypeRange range = {eleTy, eleTy};
171-
marshal.emplace_back(fir::ReferenceType::get(
172-
mlir::TupleType::get(eleTy.getContext(), range)),
173-
AT{/*align=*/16, /*byval=*/false, /*sret=*/true});
223+
marshal.emplace_back(
224+
fir::ReferenceType::get(mlir::TupleType::get(
225+
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
226+
AT{/*align=*/16, /*byval=*/false, /*sret=*/true});
174227
} else {
175228
TODO(loc, "complex for this precision");
176229
}
@@ -211,8 +264,8 @@ struct TargetAArch64 : public GenericTarget<TargetAArch64> {
211264
sem == &llvm::APFloat::IEEEdouble()) {
212265
// Use a type that will be translated into LLVM as:
213266
// { t, t } struct of 2 eleTy
214-
mlir::TypeRange range = {eleTy, eleTy};
215-
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range),
267+
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
268+
mlir::TypeRange{eleTy, eleTy}),
216269
AT{});
217270
} else {
218271
TODO(loc, "complex for this precision");
@@ -246,8 +299,9 @@ struct TargetPPC64 : public GenericTarget<TargetPPC64> {
246299
CodeGenSpecifics::Marshalling marshal;
247300
// Use a type that will be translated into LLVM as:
248301
// { t, t } struct of 2 element type
249-
mlir::TypeRange range = {eleTy, eleTy};
250-
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{});
302+
marshal.emplace_back(
303+
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
304+
AT{});
251305
return marshal;
252306
}
253307
};
@@ -277,8 +331,9 @@ struct TargetPPC64le : public GenericTarget<TargetPPC64le> {
277331
CodeGenSpecifics::Marshalling marshal;
278332
// Use a type that will be translated into LLVM as:
279333
// { t, t } struct of 2 element type
280-
mlir::TypeRange range = {eleTy, eleTy};
281-
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{});
334+
marshal.emplace_back(
335+
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
336+
AT{});
282337
return marshal;
283338
}
284339
};
@@ -300,8 +355,8 @@ struct TargetSparc : public GenericTarget<TargetSparc> {
300355
CodeGenSpecifics::Marshalling marshal;
301356
// Use a type that will be translated into LLVM as:
302357
// { t, t } struct of 2 eleTy
303-
mlir::TypeRange range = {eleTy, eleTy};
304-
auto structTy = mlir::TupleType::get(eleTy.getContext(), range);
358+
auto structTy =
359+
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
305360
marshal.emplace_back(fir::ReferenceType::get(structTy), AT{});
306361
return marshal;
307362
}
@@ -312,8 +367,8 @@ struct TargetSparc : public GenericTarget<TargetSparc> {
312367
CodeGenSpecifics::Marshalling marshal;
313368
// Use a type that will be translated into LLVM as:
314369
// { t, t } struct of 2 eleTy, byval
315-
mlir::TypeRange range = {eleTy, eleTy};
316-
auto structTy = mlir::TupleType::get(eleTy.getContext(), range);
370+
auto structTy =
371+
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy});
317372
marshal.emplace_back(fir::ReferenceType::get(structTy),
318373
AT{/*alignment=*/0, /*byval=*/true});
319374
return marshal;
@@ -343,10 +398,10 @@ struct TargetSparcV9 : public GenericTarget<TargetSparcV9> {
343398
} else if (sem == &llvm::APFloat::IEEEquad()) {
344399
// Use a type that will be translated into LLVM as:
345400
// { fp128, fp128 } struct of 2 fp128, byval, align 16
346-
mlir::TypeRange range = {eleTy, eleTy};
347-
marshal.emplace_back(fir::ReferenceType::get(
348-
mlir::TupleType::get(eleTy.getContext(), range)),
349-
AT{/*align=*/16, /*byval=*/true});
401+
marshal.emplace_back(
402+
fir::ReferenceType::get(mlir::TupleType::get(
403+
eleTy.getContext(), mlir::TypeRange{eleTy, eleTy})),
404+
AT{/*align=*/16, /*byval=*/true});
350405
} else {
351406
TODO(loc, "complex for this precision");
352407
}
@@ -358,8 +413,9 @@ struct TargetSparcV9 : public GenericTarget<TargetSparcV9> {
358413
CodeGenSpecifics::Marshalling marshal;
359414
// Use a type that will be translated into LLVM as:
360415
// { eleTy, eleTy } struct of 2 eleTy
361-
mlir::TypeRange range = {eleTy, eleTy};
362-
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range), AT{});
416+
marshal.emplace_back(
417+
mlir::TupleType::get(eleTy.getContext(), mlir::TypeRange{eleTy, eleTy}),
418+
AT{});
363419
return marshal;
364420
}
365421
};
@@ -398,8 +454,8 @@ struct TargetRISCV64 : public GenericTarget<TargetRISCV64> {
398454
sem == &llvm::APFloat::IEEEdouble()) {
399455
// Use a type that will be translated into LLVM as:
400456
// { t, t } struct of 2 eleTy, byVal
401-
mlir::TypeRange range = {eleTy, eleTy};
402-
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(), range),
457+
marshal.emplace_back(mlir::TupleType::get(eleTy.getContext(),
458+
mlir::TypeRange{eleTy, eleTy}),
403459
AT{/*alignment=*/0, /*byval=*/true});
404460
} else {
405461
TODO(loc, "complex for this precision");

flang/lib/Optimizer/CodeGen/Target.h

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,29 @@ namespace details {
2929
/// LLVMContext.
3030
class Attributes {
3131
public:
32+
enum class IntegerExtension { None, Zero, Sign };
33+
3234
Attributes(unsigned short alignment = 0, bool byval = false,
33-
bool sret = false, bool append = false)
34-
: alignment{alignment}, byval{byval}, sret{sret}, append{append} {}
35+
bool sret = false, bool append = false,
36+
IntegerExtension intExt = IntegerExtension::None)
37+
: alignment{alignment}, byval{byval}, sret{sret}, append{append},
38+
intExt{intExt} {}
3539

3640
unsigned getAlignment() const { return alignment; }
3741
bool hasAlignment() const { return alignment != 0; }
3842
bool isByVal() const { return byval; }
3943
bool isSRet() const { return sret; }
4044
bool isAppend() const { return append; }
45+
bool isZeroExt() const { return intExt == IntegerExtension::Zero; }
46+
bool isSignExt() const { return intExt == IntegerExtension::Sign; }
47+
llvm::StringRef getIntExtensionAttrName() const;
4148

4249
private:
4350
unsigned short alignment{};
4451
bool byval : 1;
4552
bool sret : 1;
4653
bool append : 1;
54+
IntegerExtension intExt;
4755
};
4856

4957
} // namespace details
@@ -94,6 +102,47 @@ class CodeGenSpecifics {
94102
virtual Marshalling boxcharArgumentType(mlir::Type eleTy,
95103
bool sret = false) const = 0;
96104

105+
// Compute ABI rules for an integer argument of the given mlir::IntegerType
106+
// \p argTy. Note that this methods is supposed to be called for
107+
// arguments passed by value not via reference, e.g. the 'i1' argument here:
108+
// declare i1 @_FortranAioOutputLogical(ptr, i1)
109+
//
110+
// \p loc is the location of the operation using/specifying the argument.
111+
//
112+
// Currently, the only supported marshalling is whether the argument
113+
// should be zero or sign extended.
114+
//
115+
// The zero/sign extension is especially important to comply with the ABI
116+
// used by C/C++ compiler that builds Fortran runtime. As in the above
117+
// example the callee will expect the caller to zero extend the second
118+
// argument up to the size of the C/C++'s 'int' type.
119+
// The corresponding handling in clang is done in
120+
// DefaultABIInfo::classifyArgumentType(), and the logic may brielfy
121+
// be explained as some sort of extension is required if the integer
122+
// type is shorter than the size of 'int' for the target.
123+
// The related code is located in ASTContext::isPromotableIntegerType()
124+
// and ABIInfo::isPromotableIntegerTypeForABI().
125+
// In particular, the latter returns 'true' for 'bool', several kinds
126+
// of 'char', 'short', 'wchar' and enumerated types.
127+
// The type of the extensions (zero or sign) depends on the signedness
128+
// of the original language type.
129+
//
130+
// It is not clear how to handle signless integer types.
131+
// From the point of Fortran-C interface all supported integer types
132+
// seem to be signed except for CFI_type_Bool/bool that is supported
133+
// via signless 'i1', but that is treated as unsigned type by clang
134+
// (e.g. 'bool' arguments are using 'zeroext' ABI).
135+
virtual Marshalling integerArgumentType(mlir::Location loc,
136+
mlir::IntegerType argTy) const = 0;
137+
138+
// By default, integer argument and return values use the same
139+
// zero/sign extension rules.
140+
virtual Marshalling integerReturnType(mlir::Location loc,
141+
mlir::IntegerType argTy) const = 0;
142+
143+
// Returns width in bits of C/C++ 'int' type size.
144+
virtual unsigned char getCIntTypeWidth() const = 0;
145+
97146
protected:
98147
mlir::MLIRContext &context;
99148
llvm::Triple triple;

0 commit comments

Comments
 (0)