Skip to content

Commit f69d30f

Browse files
authored
Merge pull request #1129 from schweitzpgi/ch-char1
Fix some CHARACTER related bugs.
2 parents a958469 + 8bcb40c commit f69d30f

36 files changed

+2546
-1137
lines changed

flang/include/flang/Lower/EvExprDumper.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,11 @@ LLVM_DUMP_METHOD void dumpEvExpr(
201201
const Fortran::evaluate::Expr<
202202
Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer, 8>> &x);
203203
LLVM_DUMP_METHOD void dumpEvExpr(const Fortran::evaluate::ArrayRef &x);
204+
LLVM_DUMP_METHOD void dumpEvExpr(const Fortran::evaluate::DataRef &x);
205+
LLVM_DUMP_METHOD void dumpEvExpr(const Fortran::evaluate::Substring &x);
206+
LLVM_DUMP_METHOD void dumpEvExpr(
207+
const Fortran::evaluate::Designator<
208+
Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer, 4>> &x);
204209

205210
} // namespace Fortran::lower
206211

flang/include/flang/Optimizer/Builder/Character.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,15 @@ class CharacterExprHelper {
107107
/// Extract the kind of a character or array of character type.
108108
static fir::KindTy getCharacterOrSequenceKind(mlir::Type type);
109109

110-
/// Determine the base character type
110+
// TODO: Do we really need all these flavors of unwrapping to get the fir.char
111+
// type? Or can we merge these? It would be better to merge them and eliminate
112+
// the confusion.
113+
114+
/// Determine the inner character type. Unwraps references, boxes, and
115+
/// sequences to find the !fir.char element type.
116+
static fir::CharacterType getCharType(mlir::Type type);
117+
118+
/// Get fir.char<kind> type with the same kind as inside str.
111119
static fir::CharacterType getCharacterType(mlir::Type type);
112120
static fir::CharacterType getCharacterType(const fir::CharBoxValue &box);
113121
static fir::CharacterType getCharacterType(mlir::Value str);

flang/include/flang/Optimizer/Builder/FIRBuilder.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,8 +465,19 @@ void genRecordAssignment(fir::FirOpBuilder &builder, mlir::Location loc,
465465
const fir::ExtendedValue &lhs,
466466
const fir::ExtendedValue &rhs);
467467

468+
/// Builds and returns the type of a ragged array header used to cache mask
469+
/// evaluations.
468470
mlir::TupleType getRaggedArrayHeaderType(fir::FirOpBuilder &builder);
469471

472+
/// Generate the, possibly dynamic, LEN of a CHARACTER. \p arrLoad determines
473+
/// the base array. After applying \p path, the result must be a reference to a
474+
/// `!fir.char` type object. \p substring must have 0, 1, or 2 members. The
475+
/// first member is the starting offset. The second is the ending offset.
476+
mlir::Value genLenOfCharacter(fir::FirOpBuilder &builder, mlir::Location loc,
477+
fir::ArrayLoadOp arrLoad,
478+
llvm::ArrayRef<mlir::Value> path,
479+
llvm::ArrayRef<mlir::Value> substring);
480+
470481
} // namespace fir::factory
471482

472483
#endif // FORTRAN_OPTIMIZER_BUILDER_FIRBUILDER_H

flang/include/flang/Optimizer/Transforms/Factory.h renamed to flang/include/flang/Optimizer/Builder/Factory.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//===-- Optimizer/Transforms/Factory.h --------------------------*- C++ -*-===//
1+
//===-- Optimizer/Builder/Factory.h -----------------------------*- C++ -*-===//
22
//
33
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44
// See https://llvm.org/LICENSE.txt for license information.
@@ -12,8 +12,8 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15-
#ifndef FORTRAN_OPTIMIZER_TRANSFORMS_FACTORY_H
16-
#define FORTRAN_OPTIMIZER_TRANSFORMS_FACTORY_H
15+
#ifndef FORTRAN_OPTIMIZER_BUILDER_FACTORY_H
16+
#define FORTRAN_OPTIMIZER_BUILDER_FACTORY_H
1717

1818
#include "flang/Optimizer/Dialect/FIROps.h"
1919
#include "flang/Optimizer/Dialect/FIRType.h"
@@ -253,4 +253,4 @@ llvm::SmallVector<fir::DoLoopOp> createLoopNest(
253253

254254
} // namespace fir::factory
255255

256-
#endif // FORTRAN_OPTIMIZER_TRANSFORMS_FACTORY_H
256+
#endif // FORTRAN_OPTIMIZER_BUILDER_FACTORY_H

flang/include/flang/Optimizer/CodeGen/CGOps.td

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def fircg_XEmboxOp : fircg_Op<"ext_embox", [AttrSizedOperandSegments]> {
4242
The default is a vector of the value 1.
4343
- slice: A vector of triples that describe an array slice.
4444
- subcomponent: A vector of indices for subobject slicing.
45+
- substring: A substring operator (offset, length) for CHARACTER.
4546
- LEN type parameters: A vector of runtime LEN type parameters that
4647
describe an correspond to the elemental derived type.
4748

@@ -54,14 +55,15 @@ def fircg_XEmboxOp : fircg_Op<"ext_embox", [AttrSizedOperandSegments]> {
5455
Variadic<AnyIntegerType>:$shift,
5556
Variadic<AnyIntegerType>:$slice,
5657
Variadic<AnyCoordinateType>:$subcomponent,
58+
Variadic<AnyIntegerType>:$substr,
5759
Variadic<AnyIntegerType>:$lenParams
5860
);
5961
let results = (outs fir_BoxType);
6062

6163
let assemblyFormat = [{
6264
$memref (`(`$shape^`)`)? (`origin` $shift^)? (`[`$slice^`]`)?
63-
(`path` $subcomponent^)? (`typeparams` $lenParams^)? attr-dict
64-
`:` functional-type(operands, results)
65+
(`path` $subcomponent^)? (`substr` $substr^)? (`typeparams` $lenParams^)?
66+
attr-dict `:` functional-type(operands, results)
6567
}];
6668

6769
let extraClassDeclaration = [{
@@ -76,9 +78,10 @@ def fircg_XEmboxOp : fircg_Op<"ext_embox", [AttrSizedOperandSegments]> {
7678
unsigned shiftOffset() { return shapeOffset() + shape().size(); }
7779
unsigned sliceOffset() { return shiftOffset() + shift().size(); }
7880
unsigned subcomponentOffset() { return sliceOffset() + slice().size(); }
79-
unsigned lenParamOffset() {
81+
unsigned substrOffset() {
8082
return subcomponentOffset() + subcomponent().size();
8183
}
84+
unsigned lenParamOffset() { return substrOffset() + substr().size(); }
8285
}];
8386
}
8487

@@ -97,6 +100,7 @@ def fircg_XReboxOp : fircg_Op<"ext_rebox", [AttrSizedOperandSegments]> {
97100
The default is a vector of the value 1.
98101
- slice: A vector of triples that describe an array slice.
99102
- subcomponent: A vector of indices for subobject slicing.
103+
- substring: A substring operator (offset, length) for CHARACTER.
100104

101105
The box argument is mandatory, the other arguments are optional.
102106
There must not both be a shape and slice/subcomponent arguments
@@ -107,14 +111,15 @@ def fircg_XReboxOp : fircg_Op<"ext_rebox", [AttrSizedOperandSegments]> {
107111
Variadic<AnyIntegerType>:$shape,
108112
Variadic<AnyIntegerType>:$shift,
109113
Variadic<AnyIntegerType>:$slice,
110-
Variadic<AnyCoordinateType>:$subcomponent
114+
Variadic<AnyCoordinateType>:$subcomponent,
115+
Variadic<AnyIntegerType>:$substr
111116
);
112117
let results = (outs fir_BoxType);
113118

114119
let assemblyFormat = [{
115120
$box (`(`$shape^`)`)? (`origin` $shift^)? (`[`$slice^`]`)?
116-
(`path` $subcomponent^) ? attr-dict
117-
`:` functional-type(operands, results)
121+
(`path` $subcomponent^)? (`substr` $substr^)? attr-dict `:`
122+
functional-type(operands, results)
118123
}];
119124

120125
let extraClassDeclaration = [{
@@ -127,6 +132,9 @@ def fircg_XReboxOp : fircg_Op<"ext_rebox", [AttrSizedOperandSegments]> {
127132
unsigned shiftOffset() { return shapeOffset() + shape().size(); }
128133
unsigned sliceOffset() { return shiftOffset() + shift().size(); }
129134
unsigned subcomponentOffset() { return sliceOffset() + slice().size(); }
135+
unsigned substrOffset() {
136+
return subcomponentOffset() + subcomponent().size();
137+
}
130138
}];
131139
}
132140

flang/include/flang/Optimizer/Dialect/FIROps.td

Lines changed: 118 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -826,9 +826,9 @@ def fir_HasValueOp : fir_Op<"has_value", [Terminator, HasParent<"GlobalOp">]> {
826826
let assemblyFormat = "$resval attr-dict `:` type($resval)";
827827
}
828828

829-
//===------------------------------------------------------------------------===//
829+
//===----------------------------------------------------------------------===//
830830
// Operations on !fir.box<T> type objects
831-
//===------------------------------------------------------------------------===//
831+
//===----------------------------------------------------------------------===//
832832

833833
def fir_EmboxOp : fir_Op<"embox", [NoSideEffect, AttrSizedOperandSegments]> {
834834
let summary = "boxes a given reference and (optional) dimension information";
@@ -891,26 +891,27 @@ def fir_EmboxOp : fir_Op<"embox", [NoSideEffect, AttrSizedOperandSegments]> {
891891
}
892892

893893
def fir_ReboxOp : fir_Op<"rebox", [NoSideEffect, AttrSizedOperandSegments]> {
894-
let summary = "create a box given another box and (optional) dimension information";
894+
let summary =
895+
"create a box given another box and (optional) dimension information";
895896

896897
let description = [{
897-
Create a new boxed reference value from another box. This is meant to be used
898-
when the taking a reference to part of a boxed value, or to an entire boxed value with
899-
new shape or type information.
898+
Create a new boxed reference value from another box. This is meant to be
899+
used when the taking a reference to part of a boxed value, or to an entire
900+
boxed value with new shape or type information.
900901

901902
The new extra information can be:
902903
- new shape information (new lower bounds, new rank, or new extents.
903904
New rank/extents can only be provided if the original fir.box is
904-
contiguous in all dimension but maybe the first one). The shape
905+
contiguous in all dimension but maybe the first row). The shape
905906
operand must be provided to set new shape information.
906-
- new type (only for derived types). It is possible to set the dynamic type
907-
of the new box to one of the parent types of the input box dynamic type.
908-
Type parameters cannot be changed. This change is reflected in the requested
909-
result type of the new box.
907+
- new type (only for derived types). It is possible to set the dynamic
908+
type of the new box to one of the parent types of the input box dynamic
909+
type. Type parameters cannot be changed. This change is reflected in
910+
the requested result type of the new box.
910911

911-
A slice argument can be provided to build a reference to part of a boxed value.
912-
In this case, the shape operand must be absent or be a fir.shift that can be
913-
used to provide a non default origin for the slice.
912+
A slice argument can be provided to build a reference to part of a boxed
913+
value. In this case, the shape operand must be absent or be a fir.shift
914+
that can be used to provide a non default origin for the slice.
914915

915916
The following example illustrates creating a fir.box for x(10:33:2)
916917
where x is described by a fir.box and has non default lower bounds,
@@ -1342,6 +1343,16 @@ def fir_ArrayLoadOp : fir_Op<"array_load", [AttrSizedOperandSegments]> {
13421343
let summary = "Load an array as a value.";
13431344

13441345
let description = [{
1346+
This operation taken with array_merge_store captures Fortran's
1347+
copy-in/copy-out semantics. One way to think of this is that array_load
1348+
creates a snapshot copy of the entire array. This copy can then be used
1349+
as the "original value" of the array while the array's new value is
1350+
computed. The array_merge_store operation is the copy-out semantics, which
1351+
merge the updates with the original array value to produce the final array
1352+
result. This abstracts the copy operations as opposed to always creating
1353+
copies or requiring dependence analysis be performed on the syntax trees
1354+
and before lowering to the IR.
1355+
13451356
Load an entire array as a single SSA value.
13461357

13471358
```fortran
@@ -1540,6 +1551,81 @@ def fir_ArrayModifyOp : fir_Op<"array_modify", [AttrSizedOperandSegments,
15401551
let verifier = [{ return ::verify(*this); }];
15411552
}
15421553

1554+
def fir_ArrayAccessOp : fir_Op<"array_access", [AttrSizedOperandSegments,
1555+
NoSideEffect]> {
1556+
let summary = "Fetch the reference of an element of an array value";
1557+
1558+
let description = [{
1559+
Fetch the memory reference of an element in an array value.
1560+
1561+
```fortran
1562+
real :: a(n,m)
1563+
...
1564+
... a ...
1565+
... a(r,s+1) ...
1566+
```
1567+
1568+
One can use `fir.array_access` to recover the implied memory reference to
1569+
the element `a(i,j)` in an array expression `a` as shown above. It can also
1570+
be used to recover the reference element `a(r,s+1)` in the second
1571+
expression.
1572+
1573+
```mlir
1574+
%s = fir.shape %n, %m : (index, index) -> !fir.shape<2>
1575+
// load the entire array 'a'
1576+
%v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.array<?x?xf32>
1577+
// fetch the value of one of the array value's elements
1578+
%1 = fir.array_access %v, %i, %j : (!fir.array<?x?xf32>, index, index) -> !fir.ref<f32>
1579+
```
1580+
1581+
It is only possible to use `array_access` on an `array_load` result value.
1582+
}];
1583+
1584+
let arguments = (ins
1585+
fir_SequenceType:$sequence,
1586+
Variadic<AnyCoordinateType>:$indices,
1587+
Variadic<AnyIntegerType>:$typeparams
1588+
);
1589+
1590+
let results = (outs fir_ReferenceType:$element);
1591+
1592+
let assemblyFormat = [{
1593+
$sequence `,` $indices (`typeparams` $typeparams^)? attr-dict `:`
1594+
functional-type(operands, results)
1595+
}];
1596+
1597+
let verifier = "return ::verify(*this);";
1598+
}
1599+
1600+
def fir_ArrayAmendOp : fir_Op<"array_amend", [NoSideEffect]> {
1601+
let summary = "Mark an array value as having been changed by reference.";
1602+
1603+
let description = [{
1604+
Marks an array value as having been changed via a reference. The reference
1605+
into the array value is obtained via an fir.array_access op.
1606+
1607+
```mlir
1608+
// fetch the value of one of the array value's elements
1609+
%1 = fir.array_access %v, %i, %j : (!fir.array<?x?xT>, index, index) -> !fir.ref<T>
1610+
// modify the element by storing data using %1 as a reference
1611+
%2 = ... %1 ...
1612+
// mark the array value
1613+
%new_v = fir.array_amend %v, %2 : (!fir.array<?x?xT>, !fir.ref<T>) -> !fir.array<?x?xT>
1614+
```
1615+
}];
1616+
1617+
let arguments = (ins
1618+
fir_SequenceType:$sequence,
1619+
fir_ReferenceType:$memref
1620+
);
1621+
1622+
let results = (outs fir_SequenceType);
1623+
1624+
let assemblyFormat = [{
1625+
$sequence `,` $memref attr-dict `:` functional-type(operands, results)
1626+
}];
1627+
}
1628+
15431629
def fir_ArrayMergeStoreOp : fir_Op<"array_merge_store",
15441630
[AttrSizedOperandSegments]> {
15451631
let summary = "Store merged array value to memory.";
@@ -1893,19 +1979,34 @@ def fir_SliceOp : fir_Op<"slice", [NoSideEffect, AttrSizedOperandSegments]> {
18931979
%fld = fir.field_index component, !fir.type<t{...component:ct...}>
18941980
%d = fir.slice %lo, %hi, %step path %fld : (index, index, index, !fir.field) -> !fir.slice<1>
18951981
```
1982+
1983+
Projections of `!fir.char` type can be further narrowed to invariant
1984+
substrings.
1985+
1986+
```mlir
1987+
%d = fir.slice %lo, %hi, %step substr %offset, %width : (index, index, index, index, index) -> !fir.slice<1>
1988+
```
18961989
}];
18971990

18981991
let arguments = (ins
1899-
Variadic<AnyCoordinateType>:$triples,
1900-
Variadic<AnyComponentType>:$fields
1992+
Variadic<AnyIntegerType>:$triples,
1993+
Variadic<AnyComponentType>:$fields,
1994+
Variadic<AnyIntegerType>:$substr
19011995
);
19021996

19031997
let results = (outs fir_SliceType);
19041998

19051999
let assemblyFormat = [{
1906-
$triples (`path` $fields^)? attr-dict `:` functional-type(operands, results)
2000+
$triples (`path` $fields^)? (`substr` $substr^)? attr-dict `:`
2001+
functional-type(operands, results)
19072002
}];
19082003

2004+
let builders = [
2005+
OpBuilder<(ins "mlir::ValueRange":$triples,
2006+
CArg<"mlir::ValueRange", "llvm::None">:$fields,
2007+
CArg<"mlir::ValueRange", "llvm::None">:$substr)>
2008+
];
2009+
19092010
let verifier = "return ::verify(*this);";
19102011

19112012
let extraClassDeclaration = [{

flang/include/flang/Optimizer/Dialect/FIROpsSupport.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,33 @@
1515

1616
namespace fir {
1717

18-
/// return true iff the Operation is a non-volatile LoadOp
18+
/// Return true iff the Operation is a non-volatile LoadOp or ArrayLoadOp.
1919
inline bool nonVolatileLoad(mlir::Operation *op) {
2020
if (auto load = dyn_cast<fir::LoadOp>(op))
2121
return !load->getAttr("volatile");
22+
if (auto arrLoad = dyn_cast<fir::ArrayLoadOp>(op))
23+
return !arrLoad->getAttr("volatile");
2224
return false;
2325
}
2426

25-
/// return true iff the Operation is a call
27+
/// Return true iff the Operation is a call.
2628
inline bool isaCall(mlir::Operation *op) {
2729
return isa<fir::CallOp>(op) || isa<fir::DispatchOp>(op) ||
2830
isa<mlir::CallOp>(op) || isa<mlir::CallIndirectOp>(op);
2931
}
3032

31-
/// return true iff the Operation is a fir::CallOp, fir::DispatchOp,
33+
/// Return true iff the Operation is a fir::CallOp, fir::DispatchOp,
3234
/// mlir::CallOp, or mlir::CallIndirectOp and not pure
33-
/// NB: this is not the same as `!pureCall(op)`
35+
/// NB: This is not the same as `!pureCall(op)`.
3436
inline bool impureCall(mlir::Operation *op) {
3537
// Should we also auto-detect that the called function is pure if its
3638
// arguments are not references? For now, rely on a "pure" attribute.
3739
return op && isaCall(op) && !op->getAttr("pure");
3840
}
3941

40-
/// return true iff the Operation is a fir::CallOp, fir::DispatchOp,
42+
/// Return true iff the Operation is a fir::CallOp, fir::DispatchOp,
4143
/// mlir::CallOp, or mlir::CallIndirectOp and is also pure.
42-
/// NB: this is not the same as `!impureCall(op)`
44+
/// NB: This is not the same as `!impureCall(op)`.
4345
inline bool pureCall(mlir::Operation *op) {
4446
// Should we also auto-detect that the called function is pure if its
4547
// arguments are not references? For now, rely on a "pure" attribute.

flang/include/flang/Optimizer/Dialect/FIRType.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,22 @@ inline mlir::Type unwrapPassByRefType(mlir::Type t) {
193193
return t;
194194
}
195195

196+
/// Unwrap all pointer and box types and return the element type if it is a
197+
/// sequence type, otherwise return null.
198+
inline fir::SequenceType unwrapUntilSeqType(mlir::Type t) {
199+
while (true) {
200+
if (!t)
201+
return {};
202+
if (auto ty = dyn_cast_ptrOrBoxEleTy(t)) {
203+
t = ty;
204+
continue;
205+
}
206+
if (auto seqTy = t.dyn_cast<fir::SequenceType>())
207+
return seqTy;
208+
return {};
209+
}
210+
}
211+
196212
#ifndef NDEBUG
197213
// !fir.ptr<X> and !fir.heap<X> where X is !fir.ptr, !fir.heap, or !fir.ref
198214
// is undefined and disallowed.

0 commit comments

Comments
 (0)