Skip to content

Commit 5aaf384

Browse files
authored
[flang][NFC] use llvm.intr.stacksave/restore instead of opaque calls (#108562)
The new LLVM stack save/restore intrinsic operations are more convenient than function calls because they do not add function declarations to the module and therefore do not block the parallelisation of passes. Furthermore they could be much more easily marked with memory effects than function calls if that ever proved useful. This builds on top of #107879. Resolves #108016
1 parent 9548dbe commit 5aaf384

25 files changed

+124
-155
lines changed

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <utility>
3030

3131
namespace mlir {
32+
class DataLayout;
3233
class SymbolTable;
3334
}
3435

@@ -253,6 +254,15 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
253254
mlir::ValueRange lenParams = {},
254255
llvm::ArrayRef<mlir::NamedAttribute> attrs = {});
255256

257+
/// Create an LLVM stack save intrinsic op. Returns the saved stack pointer.
258+
/// The stack address space is fetched from the data layout of the current
259+
/// module.
260+
mlir::Value genStackSave(mlir::Location loc);
261+
262+
/// Create an LLVM stack restore intrinsic op. stackPointer should be a value
263+
/// previously returned from genStackSave.
264+
void genStackRestore(mlir::Location loc, mlir::Value stackPointer);
265+
256266
/// Create a global value.
257267
fir::GlobalOp createGlobal(mlir::Location loc, mlir::Type type,
258268
llvm::StringRef name,
@@ -523,6 +533,9 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
523533
setCommonAttributes(op);
524534
}
525535

536+
/// Construct a data layout on demand and return it
537+
mlir::DataLayout &getDataLayout();
538+
526539
private:
527540
/// Set attributes (e.g. FastMathAttr) to \p op operation
528541
/// based on the current attributes setting.
@@ -537,6 +550,11 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
537550
/// fir::GlobalOp and func::FuncOp symbol table to speed-up
538551
/// lookups.
539552
mlir::SymbolTable *symbolTable = nullptr;
553+
554+
/// DataLayout constructed on demand. Access via getDataLayout().
555+
/// Stored via a unique_ptr rather than an optional so as not to bloat this
556+
/// class when most instances won't ever need a data layout.
557+
std::unique_ptr<mlir::DataLayout> dataLayout = nullptr;
540558
};
541559

542560
} // namespace fir
@@ -729,6 +747,9 @@ elideExtentsAlreadyInType(mlir::Type type, mlir::ValueRange shape);
729747
llvm::SmallVector<mlir::Value>
730748
elideLengthsAlreadyInType(mlir::Type type, mlir::ValueRange lenParams);
731749

750+
/// Get the address space which should be used for allocas
751+
uint64_t getAllocaAddressSpace(mlir::DataLayout *dataLayout);
752+
732753
} // namespace fir::factory
733754

734755
#endif // FORTRAN_OPTIMIZER_BUILDER_FIRBUILDER_H

flang/include/flang/Optimizer/Builder/LowLevelIntrinsics.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,6 @@ mlir::func::FuncOp getLlvmGetRounding(FirOpBuilder &builder);
4242
/// Get the `llvm.set.rounding` intrinsic.
4343
mlir::func::FuncOp getLlvmSetRounding(FirOpBuilder &builder);
4444

45-
/// Get the `llvm.stacksave` intrinsic.
46-
mlir::func::FuncOp getLlvmStackSave(FirOpBuilder &builder);
47-
48-
/// Get the `llvm.stackrestore` intrinsic.
49-
mlir::func::FuncOp getLlvmStackRestore(FirOpBuilder &builder);
50-
5145
/// Get the `llvm.init.trampoline` intrinsic.
5246
mlir::func::FuncOp getLlvmInitTrampoline(FirOpBuilder &builder);
5347

flang/include/flang/Optimizer/Support/DataLayout.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ void setMLIRDataLayoutFromAttributes(mlir::ModuleOp mlirModule,
4545
/// std::nullopt.
4646
std::optional<mlir::DataLayout>
4747
getOrSetDataLayout(mlir::ModuleOp mlirModule, bool allowDefaultLayout = false);
48-
4948
} // namespace fir::support
5049

5150
#endif // FORTRAN_OPTIMIZER_SUPPORT_DATALAYOUT_H

flang/lib/Lower/Bridge.cpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3257,15 +3257,10 @@ class FirConverter : public Fortran::lower::AbstractConverter {
32573257
const Fortran::parser::CharBlock &endPosition =
32583258
eval.getLastNestedEvaluation().position;
32593259
localSymbols.pushScope();
3260-
mlir::func::FuncOp stackSave = fir::factory::getLlvmStackSave(*builder);
3261-
mlir::func::FuncOp stackRestore =
3262-
fir::factory::getLlvmStackRestore(*builder);
3263-
mlir::Value stackPtr =
3264-
builder->create<fir::CallOp>(toLocation(), stackSave).getResult(0);
3260+
mlir::Value stackPtr = builder->genStackSave(toLocation());
32653261
mlir::Location endLoc = genLocation(endPosition);
3266-
stmtCtx.attachCleanup([=]() {
3267-
builder->create<fir::CallOp>(endLoc, stackRestore, stackPtr);
3268-
});
3262+
stmtCtx.attachCleanup(
3263+
[=]() { builder->genStackRestore(endLoc, stackPtr); });
32693264
Fortran::semantics::Scope &scope =
32703265
bridge.getSemanticsContext().FindScope(endPosition);
32713266
scopeBlockIdMap.try_emplace(&scope, ++blockId);

flang/lib/Lower/ConvertCall.cpp

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -368,22 +368,9 @@ std::pair<fir::ExtendedValue, bool> Fortran::lower::genCallOpAndResult(
368368

369369
if (!extents.empty() || !lengths.empty()) {
370370
auto *bldr = &converter.getFirOpBuilder();
371-
auto stackSaveFn = fir::factory::getLlvmStackSave(builder);
372-
auto stackSaveSymbol = bldr->getSymbolRefAttr(stackSaveFn.getName());
373-
mlir::Value sp;
374-
fir::CallOp call = bldr->create<fir::CallOp>(
375-
loc, stackSaveSymbol, stackSaveFn.getFunctionType().getResults(),
376-
mlir::ValueRange{});
377-
if (call.getNumResults() != 0)
378-
sp = call.getResult(0);
379-
stmtCtx.attachCleanup([bldr, loc, sp]() {
380-
auto stackRestoreFn = fir::factory::getLlvmStackRestore(*bldr);
381-
auto stackRestoreSymbol =
382-
bldr->getSymbolRefAttr(stackRestoreFn.getName());
383-
bldr->create<fir::CallOp>(loc, stackRestoreSymbol,
384-
stackRestoreFn.getFunctionType().getResults(),
385-
mlir::ValueRange{sp});
386-
});
371+
mlir::Value sp = bldr->genStackSave(loc);
372+
stmtCtx.attachCleanup(
373+
[bldr, loc, sp]() { bldr->genStackRestore(loc, sp); });
387374
}
388375
mlir::Value temp =
389376
builder.createTemporary(loc, type, ".result", extents, resultLengths);

flang/lib/Optimizer/Builder/FIRBuilder.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "flang/Optimizer/Dialect/FIRAttr.h"
1919
#include "flang/Optimizer/Dialect/FIROpsSupport.h"
2020
#include "flang/Optimizer/Dialect/FIRType.h"
21+
#include "flang/Optimizer/Support/DataLayout.h"
2122
#include "flang/Optimizer/Support/FatalError.h"
2223
#include "flang/Optimizer/Support/InternalNames.h"
2324
#include "flang/Optimizer/Support/Utils.h"
@@ -328,6 +329,17 @@ mlir::Value fir::FirOpBuilder::createHeapTemporary(
328329
name, dynamicLength, dynamicShape, attrs);
329330
}
330331

332+
mlir::Value fir::FirOpBuilder::genStackSave(mlir::Location loc) {
333+
mlir::Type voidPtr = mlir::LLVM::LLVMPointerType::get(
334+
getContext(), fir::factory::getAllocaAddressSpace(&getDataLayout()));
335+
return create<mlir::LLVM::StackSaveOp>(loc, voidPtr);
336+
}
337+
338+
void fir::FirOpBuilder::genStackRestore(mlir::Location loc,
339+
mlir::Value stackPointer) {
340+
create<mlir::LLVM::StackRestoreOp>(loc, stackPointer);
341+
}
342+
331343
/// Create a global variable in the (read-only) data section. A global variable
332344
/// must have a unique name to identify and reference it.
333345
fir::GlobalOp fir::FirOpBuilder::createGlobal(
@@ -791,6 +803,15 @@ void fir::FirOpBuilder::setFastMathFlags(
791803
setFastMathFlags(arithFMF);
792804
}
793805

806+
// Construction of an mlir::DataLayout is expensive so only do it on demand and
807+
// memoise it in the builder instance
808+
mlir::DataLayout &fir::FirOpBuilder::getDataLayout() {
809+
if (dataLayout)
810+
return *dataLayout;
811+
dataLayout = std::make_unique<mlir::DataLayout>(getModule());
812+
return *dataLayout;
813+
}
814+
794815
//===--------------------------------------------------------------------===//
795816
// ExtendedValue inquiry helper implementation
796817
//===--------------------------------------------------------------------===//
@@ -1664,3 +1685,10 @@ void fir::factory::setInternalLinkage(mlir::func::FuncOp func) {
16641685
mlir::LLVM::LinkageAttr::get(func->getContext(), internalLinkage);
16651686
func->setAttr("llvm.linkage", linkage);
16661687
}
1688+
1689+
uint64_t fir::factory::getAllocaAddressSpace(mlir::DataLayout *dataLayout) {
1690+
if (dataLayout)
1691+
if (mlir::Attribute addrSpace = dataLayout->getAllocaMemorySpace())
1692+
return mlir::cast<mlir::IntegerAttr>(addrSpace).getUInt();
1693+
return 0;
1694+
}

flang/lib/Optimizer/Builder/LowLevelIntrinsics.cpp

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -76,25 +76,6 @@ fir::factory::getLlvmSetRounding(fir::FirOpBuilder &builder) {
7676
funcTy);
7777
}
7878

79-
mlir::func::FuncOp fir::factory::getLlvmStackSave(fir::FirOpBuilder &builder) {
80-
// FIXME: This should query the target alloca address space
81-
auto ptrTy = builder.getRefType(builder.getIntegerType(8));
82-
auto funcTy =
83-
mlir::FunctionType::get(builder.getContext(), std::nullopt, {ptrTy});
84-
return builder.createFunction(builder.getUnknownLoc(), "llvm.stacksave.p0",
85-
funcTy);
86-
}
87-
88-
mlir::func::FuncOp
89-
fir::factory::getLlvmStackRestore(fir::FirOpBuilder &builder) {
90-
// FIXME: This should query the target alloca address space
91-
auto ptrTy = builder.getRefType(builder.getIntegerType(8));
92-
auto funcTy =
93-
mlir::FunctionType::get(builder.getContext(), {ptrTy}, std::nullopt);
94-
return builder.createFunction(builder.getUnknownLoc(), "llvm.stackrestore.p0",
95-
funcTy);
96-
}
97-
9879
mlir::func::FuncOp
9980
fir::factory::getLlvmInitTrampoline(fir::FirOpBuilder &builder) {
10081
auto ptrTy = builder.getRefType(builder.getIntegerType(8));

flang/lib/Optimizer/CodeGen/TargetRewrite.cpp

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,25 +1236,18 @@ class TargetRewrite : public fir::impl::TargetRewritePassBase<TargetRewrite> {
12361236

12371237
inline void clearMembers() { setMembers(nullptr, nullptr, nullptr); }
12381238

1239-
uint64_t getAllocaAddressSpace() const {
1240-
if (dataLayout)
1241-
if (mlir::Attribute addrSpace = dataLayout->getAllocaMemorySpace())
1242-
return llvm::cast<mlir::IntegerAttr>(addrSpace).getUInt();
1243-
return 0;
1244-
}
1245-
12461239
// Inserts a call to llvm.stacksave at the current insertion
12471240
// point and the given location. Returns the call's result Value.
12481241
inline mlir::Value genStackSave(mlir::Location loc) {
1249-
mlir::Type voidPtr = mlir::LLVM::LLVMPointerType::get(
1250-
rewriter->getContext(), getAllocaAddressSpace());
1251-
return rewriter->create<mlir::LLVM::StackSaveOp>(loc, voidPtr);
1242+
fir::FirOpBuilder builder(*rewriter, getModule());
1243+
return builder.genStackSave(loc);
12521244
}
12531245

12541246
// Inserts a call to llvm.stackrestore at the current insertion
12551247
// point and the given location and argument.
12561248
inline void genStackRestore(mlir::Location loc, mlir::Value sp) {
1257-
rewriter->create<mlir::LLVM::StackRestoreOp>(loc, sp);
1249+
fir::FirOpBuilder builder(*rewriter, getModule());
1250+
return builder.genStackRestore(loc, sp);
12581251
}
12591252

12601253
fir::CodeGenSpecifics *specifics = nullptr;

flang/lib/Optimizer/Transforms/StackArrays.cpp

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -734,28 +734,12 @@ void AllocMemConversion::insertStackSaveRestore(
734734
auto mod = oldAlloc->getParentOfType<mlir::ModuleOp>();
735735
fir::FirOpBuilder builder{rewriter, mod};
736736

737-
mlir::func::FuncOp stackSaveFn = fir::factory::getLlvmStackSave(builder);
738-
mlir::SymbolRefAttr stackSaveSym =
739-
builder.getSymbolRefAttr(stackSaveFn.getName());
740-
741737
builder.setInsertionPoint(oldAlloc);
742-
mlir::Value sp =
743-
builder
744-
.create<fir::CallOp>(oldAlloc.getLoc(), stackSaveSym,
745-
stackSaveFn.getFunctionType().getResults(),
746-
mlir::ValueRange{})
747-
.getResult(0);
748-
749-
mlir::func::FuncOp stackRestoreFn =
750-
fir::factory::getLlvmStackRestore(builder);
751-
mlir::SymbolRefAttr stackRestoreSym =
752-
builder.getSymbolRefAttr(stackRestoreFn.getName());
738+
mlir::Value sp = builder.genStackSave(oldAlloc.getLoc());
753739

754740
auto createStackRestoreCall = [&](mlir::Operation *user) {
755741
builder.setInsertionPoint(user);
756-
builder.create<fir::CallOp>(user->getLoc(), stackRestoreSym,
757-
stackRestoreFn.getFunctionType().getResults(),
758-
mlir::ValueRange{sp});
742+
builder.genStackRestore(user->getLoc(), sp);
759743
};
760744

761745
for (mlir::Operation *user : oldAlloc->getUsers()) {

flang/lib/Optimizer/Transforms/StackReclaim.cpp

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "flang/Common/Fortran.h"
10+
#include "flang/Optimizer/Builder/FIRBuilder.h"
1011
#include "flang/Optimizer/Dialect/FIRDialect.h"
1112
#include "flang/Optimizer/Dialect/FIROps.h"
1213
#include "flang/Optimizer/Transforms/Passes.h"
@@ -31,34 +32,20 @@ class StackReclaimPass : public fir::impl::StackReclaimBase<StackReclaimPass> {
3132
};
3233
} // namespace
3334

34-
uint64_t getAllocaAddressSpace(Operation *op) {
35-
mlir::ModuleOp module = mlir::dyn_cast_or_null<mlir::ModuleOp>(op);
36-
if (!module)
37-
module = op->getParentOfType<mlir::ModuleOp>();
38-
39-
if (mlir::Attribute addrSpace =
40-
mlir::DataLayout(module).getAllocaMemorySpace())
41-
return llvm::cast<mlir::IntegerAttr>(addrSpace).getUInt();
42-
return 0;
43-
}
44-
4535
void StackReclaimPass::runOnOperation() {
4636
auto *op = getOperation();
47-
auto *context = &getContext();
48-
mlir::OpBuilder builder(context);
49-
mlir::Type voidPtr =
50-
mlir::LLVM::LLVMPointerType::get(context, getAllocaAddressSpace(op));
37+
fir::FirOpBuilder builder(op, fir::getKindMapping(op));
5138

5239
op->walk([&](fir::DoLoopOp loopOp) {
5340
mlir::Location loc = loopOp.getLoc();
5441

5542
if (!loopOp.getRegion().getOps<fir::AllocaOp>().empty()) {
5643
builder.setInsertionPointToStart(&loopOp.getRegion().front());
57-
auto stackSaveOp = builder.create<LLVM::StackSaveOp>(loc, voidPtr);
44+
mlir::Value sp = builder.genStackSave(loc);
5845

5946
auto *terminator = loopOp.getRegion().back().getTerminator();
6047
builder.setInsertionPoint(terminator);
61-
builder.create<LLVM::StackRestoreOp>(loc, stackSaveOp);
48+
builder.genStackRestore(loc, sp);
6249
}
6350
});
6451
}

flang/test/HLFIR/order_assignments/where-scheduling.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ end function f
134134
!CHECK-NEXT: run 1 save : where/mask
135135
!CHECK-NEXT: run 2 evaluate: where/region_assign1
136136
!CHECK-LABEL: ------------ scheduling where in _QPonly_once ------------
137-
!CHECK-NEXT: unknown effect: %{{[0-9]+}} = fir.call @llvm.stacksave.p0() fastmath<contract> : () -> !fir.ref<i8>
137+
!CHECK-NEXT: unknown effect: %{{[0-9]+}} = llvm.intr.stacksave : !llvm.ptr
138138
!CHECK-NEXT: run 1 save (w): where/mask
139139
!CHECK-NEXT: run 2 evaluate: where/region_assign1
140140
!CHECK-NEXT: run 3 evaluate: where/region_assign2

flang/test/Lower/HLFIR/block_bindc_pocs.f90

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ subroutine test_proc() bind(C)
88
end subroutine test_proc
99
end interface
1010
end module m
11-
!CHECK-DAG: %[[S0:.*]] = fir.call @llvm.stacksave.p0() fastmath<contract> : () -> !fir.ref<i8>
11+
!CHECK-DAG: %[[S0:.*]] = llvm.intr.stacksave : !llvm.ptr
1212
!CHECK-DAG: fir.call @test_proc() proc_attrs<bind_c> fastmath<contract> : () -> ()
13-
!CHECK-DAG: fir.call @llvm.stackrestore.p0(%[[S0]]) fastmath<contract> : (!fir.ref<i8>) -> ()
13+
!CHECK-DAG: llvm.intr.stackrestore %[[S0]] : !llvm.ptr
1414
!CHECK-DAG: func.func private @test_proc() attributes {fir.bindc_name = "test_proc"}
1515
subroutine test
1616
BLOCK

flang/test/Lower/HLFIR/elemental-array-ops.f90

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,12 @@ end subroutine char_return
182182
! CHECK: %[[VAL_23:.*]] = arith.constant 0 : index
183183
! CHECK: %[[VAL_24:.*]] = arith.cmpi sgt, %[[VAL_22]], %[[VAL_23]] : index
184184
! CHECK: %[[VAL_25:.*]] = arith.select %[[VAL_24]], %[[VAL_22]], %[[VAL_23]] : index
185-
! CHECK: %[[VAL_26:.*]] = fir.call @llvm.stacksave.p0() fastmath<contract> : () -> !fir.ref<i8>
185+
! CHECK: %[[VAL_26:.*]] = llvm.intr.stacksave : !llvm.ptr
186186
! CHECK: %[[VAL_27:.*]] = fir.call @_QPcallee(%[[VAL_2]], %[[VAL_25]], %[[VAL_20]]) fastmath<contract> : (!fir.ref<!fir.char<1,3>>, index, !fir.boxchar<1>) -> !fir.boxchar<1>
187187
! CHECK: %[[VAL_28:.*]]:2 = hlfir.declare %[[VAL_2]] typeparams %[[VAL_25]] {uniq_name = ".tmp.func_result"} : (!fir.ref<!fir.char<1,3>>, index) -> (!fir.ref<!fir.char<1,3>>, !fir.ref<!fir.char<1,3>>)
188188
! CHECK: %[[MustFree:.*]] = arith.constant false
189189
! CHECK: %[[ResultTemp:.*]] = hlfir.as_expr %[[VAL_28]]#0 move %[[MustFree]] : (!fir.ref<!fir.char<1,3>>, i1) -> !hlfir.expr<!fir.char<1,3>>
190-
! CHECK: fir.call @llvm.stackrestore.p0(%[[VAL_26]]) fastmath<contract> : (!fir.ref<i8>) -> ()
190+
! CHECK: llvm.intr.stackrestore %[[VAL_26]] : !llvm.ptr
191191
! CHECK: hlfir.yield_element %[[ResultTemp]] : !hlfir.expr<!fir.char<1,3>>
192192
! CHECK: }
193193
! CHECK: %[[VAL_29:.*]] = arith.constant 0 : index

flang/test/Lower/HLFIR/proc-pointer-comp-pass.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,6 @@ subroutine test5(x)
105105
! CHECK: %[[VAL_7:.*]] = arith.constant 0 : index
106106
! CHECK: %[[VAL_8:.*]] = arith.cmpi sgt, %[[VAL_6]], %[[VAL_7]] : index
107107
! CHECK: %[[VAL_9:.*]] = arith.select %[[VAL_8]], %[[VAL_6]], %[[VAL_7]] : index
108-
! CHECK: %[[VAL_10:.*]] = fir.call @llvm.stacksave.p0() fastmath<contract> : () -> !fir.ref<i8>
108+
! CHECK: %[[VAL_10:.*]] = llvm.intr.stacksave : !llvm.ptr
109109
! CHECK: %[[VAL_11:.*]] = fir.box_addr %[[VAL_4]] : (!fir.boxproc<(!fir.ref<!fir.char<1,4>>, index, !fir.ref<!fir.type<_QMmTt3{c:!fir.char<1,4>,p:!fir.boxproc<(!fir.ref<!fir.char<1,4>>, index, !fir.ref<!fir.type<_QMmTt3>>) -> !fir.boxchar<1>>}>>) -> !fir.boxchar<1>>) -> ((!fir.ref<!fir.char<1,4>>, index, !fir.ref<!fir.type<_QMmTt3{c:!fir.char<1,4>,p:!fir.boxproc<(!fir.ref<!fir.char<1,4>>, index, !fir.ref<!fir.type<_QMmTt3>>) -> !fir.boxchar<1>>}>>) -> !fir.boxchar<1>)
110110
! CHECK: %[[VAL_12:.*]] = fir.call %[[VAL_11]](%[[VAL_1]], %[[VAL_9]], %[[VAL_2]]#1) fastmath<contract> : (!fir.ref<!fir.char<1,4>>, index, !fir.ref<!fir.type<_QMmTt3{c:!fir.char<1,4>,p:!fir.boxproc<(!fir.ref<!fir.char<1,4>>, index, !fir.ref<!fir.type<_QMmTt3>>) -> !fir.boxchar<1>>}>>) -> !fir.boxchar<1>

0 commit comments

Comments
 (0)