[flang-commits] [flang] f32db55 - [flang][debug] Keep the bounds of variables in stack slots at -O0. (#229876)
via flang-commits
flang-commits at lists.llvm.org
Wed Oct 14 02:57:50 PDT 2026
Author: Abid Qadeer
Date: 2026-10-14T10:57:44+01:00
New Revision: f32db5597b51854d651927701c5bb63ca5193153
URL: https://github.com/llvm/llvm-project/commit/f32db5597b51854d651927701c5bb63ca5193153
DIFF: https://github.com/llvm/llvm-project/commit/f32db5597b51854d651927701c5bb63ca5193153.diff
LOG: [flang][debug] Keep the bounds of variables in stack slots at -O0. (#229876)
flang uses hidden variables to carry the bounds and length parameters of
an array or character in the debug info. The fake_use thing was added to
keep them alive throughout the function. It works in many but not all
cases, and the ones it does not cover take a number of different forms.
Most of them show up on GPU, but several can be reproduced on the host
as well, two of which are
https://github.com/llvm/llvm-project/issues/229434 and the GDB fortran
test gdb.fortran/mixed-lang-stack.exp.
For VLA, clang emits a stack slot to carry each dimension and points the
DISubrange at a dbg.declare on that slot. The optimizer promotes the
slot away, so it is at -O0 that it survives and gives the dimension a
location valid throughout the function.
Using similar approach, at -O0, AddDebugInfo gives each of these hidden
variables a stack slot of its own: a fir.alloca in the alloca block of
the enclosing region, with the bound or length parameter stored into it
immediately before the declaration that uses it. The hidden variable
then describes the slot through a fircg.ext_declare, which CodeGen
lowers to a #dbg_declare. The slots are controlled by the
bounds-in-stack-slots option of the pass, which the driver turns on at
-O0. So flang generated these slots at -O0 -g.
---------
Co-authored-by: Cursor <cursoragent at cursor.com>
Added:
flang/test/Integration/debug-bounds-stack-slot.f90
flang/test/Transforms/debug-bounds-stack-slot.fir
Modified:
flang/include/flang/Optimizer/Transforms/DebugTypeGenerator.h
flang/include/flang/Optimizer/Transforms/Passes.td
flang/lib/Optimizer/Passes/Pipelines.cpp
flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp
Removed:
################################################################################
diff --git a/flang/include/flang/Optimizer/Transforms/DebugTypeGenerator.h b/flang/include/flang/Optimizer/Transforms/DebugTypeGenerator.h
index 155b41f688579..aff9e07e727fe 100644
--- a/flang/include/flang/Optimizer/Transforms/DebugTypeGenerator.h
+++ b/flang/include/flang/Optimizer/Transforms/DebugTypeGenerator.h
@@ -97,13 +97,18 @@ class DerivedTypeCache {
class DebugTypeGenerator {
public:
DebugTypeGenerator(mlir::ModuleOp module, mlir::SymbolTable *symbolTable,
- const mlir::DataLayout &dl);
+ const mlir::DataLayout &dl,
+ bool boundsInStackSlots = false);
mlir::LLVM::DITypeAttr convertType(mlir::Type Ty,
mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DIScopeAttr scope,
fir::cg::XDeclareOp declOp);
+ /// Whether \p val is held in a stack slot for the variable declared by
+ /// \p declOp, which makes a fake use of it unnecessary.
+ bool hasBoundSlot(mlir::Value val, fir::cg::XDeclareOp declOp) const;
+
private:
mlir::LLVM::DITypeAttr convertRecordType(fir::RecordType Ty,
mlir::LLVM::DIFileAttr fileAttr,
@@ -154,6 +159,16 @@ class DebugTypeGenerator {
mlir::ModuleOp module;
mlir::SymbolTable *symbolTable;
const mlir::DataLayout *dataLayout;
+ bool boundsInStackSlots;
+ struct ArtificialVar {
+ /// Null unless the value is held in a stack slot.
+ fir::AllocaOp slot;
+ mlir::LLVM::DILocalVariableAttr var;
+ };
+ /// For a bound and the declaration it belongs to, the artificial variable
+ /// made to describe that bound and the slot that holds its value.
+ llvm::DenseMap<std::pair<mlir::Value, mlir::Operation *>, ArtificialVar>
+ artificialVars;
KindMapping kindMapping;
fir::LLVMTypeConverter llvmTypeConverter;
std::uint64_t dimsSize;
diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td
index edc98e2f1ec14..07d3296a17be9 100644
--- a/flang/include/flang/Optimizer/Transforms/Passes.td
+++ b/flang/include/flang/Optimizer/Transforms/Passes.td
@@ -263,7 +263,12 @@ def AddDebugInfo : Pass<"add-debug-info", "mlir::ModuleOp"> {
"Command-line flags to append to DWARF producer">,
Option<"emitFakeUseForDebugVars", "emit-fake-use-for-debug-vars",
"bool", /*default=*/"false",
- "Emit fake use for debug variables to extend their lifetime">
+ "Emit fake use for debug variables to extend their lifetime">,
+ Option<"boundsInStackSlots", "bounds-in-stack-slots",
+ "bool", /*default=*/"false",
+ "Keep the bounds and length parameters of a variable in stack "
+ "slots, so that they can be described wherever the variable is "
+ "visible">
];
}
diff --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp
index d965400e028d2..afa7fc7c58a54 100644
--- a/flang/lib/Optimizer/Passes/Pipelines.cpp
+++ b/flang/lib/Optimizer/Passes/Pipelines.cpp
@@ -93,6 +93,7 @@ void addDebugInfoPass(mlir::PassManager &pm,
options.emitFakeUseForDebugVars =
(config.OptLevel == llvm::OptimizationLevel::O0) &&
!disableArgumentFakeUse;
+ options.boundsInStackSlots = config.OptLevel == llvm::OptimizationLevel::O0;
addPassConditionally(pm, disableDebugInfo,
[&]() { return fir::createAddDebugInfoPass(options); });
}
diff --git a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
index 64cef884a5275..6d69a38f403fa 100644
--- a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
+++ b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp
@@ -439,17 +439,21 @@ void AddDebugInfoPass::handleLocalVariable(Op declOp, llvm::StringRef name,
if (emitFakeUseForDebugVars) {
// Create fake uses for internal variables that represent count and lower
- // bound of dynamic arrays to ensure they are not optimized out.
+ // bound of dynamic arrays to ensure they are not optimized out. A value
+ // that convertType has put in a stack slot needs none, as the debug info
+ // describes the slot and nothing has to keep the value itself alive.
if (auto arrayTy =
mlir::dyn_cast<mlir::LLVM::DICompositeTypeAttr>(tyAttr)) {
if (arrayTy.getTag() == llvm::dwarf::DW_TAG_array_type) {
if constexpr (std::is_same_v<Op, fir::cg::XDeclareOp>) {
// Count is represented as a value in the shape attribute
for (auto val : declOp.getShape())
- InsertFakeUseForDebugVar(builder, declOp, val);
+ if (!typeGen.hasBoundSlot(val, declOp))
+ InsertFakeUseForDebugVar(builder, declOp, val);
// Lower bound is represented as a value in the shift attribute
for (auto val : declOp.getShift())
- InsertFakeUseForDebugVar(builder, declOp, val);
+ if (!typeGen.hasBoundSlot(val, declOp))
+ InsertFakeUseForDebugVar(builder, declOp, val);
}
}
}
@@ -458,7 +462,8 @@ void AddDebugInfoPass::handleLocalVariable(Op declOp, llvm::StringRef name,
// are not optimized out.
if constexpr (std::is_same_v<Op, fir::cg::XDeclareOp>) {
for (auto val : declOp.getTypeparams())
- InsertFakeUseForDebugVar(builder, declOp, val);
+ if (!typeGen.hasBoundSlot(val, declOp))
+ InsertFakeUseForDebugVar(builder, declOp, val);
}
}
@@ -1218,7 +1223,8 @@ void AddDebugInfoPass::runOnOperation() {
mlir::LLVM::ModuleFlagsOp::create(builder, module.getLoc(),
builder.getArrayAttr(moduleFlags));
}
- fir::DebugTypeGenerator typeGen(module, &symbolTable, *dl);
+ fir::DebugTypeGenerator typeGen(module, &symbolTable, *dl,
+ boundsInStackSlots);
// We need 2 type of file paths here.
// 1. Name of the file as was presented to compiler. This can be absolute
// or relative to 2.
diff --git a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp
index 1a6a2601be90f..05b7992c28a54 100644
--- a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp
+++ b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp
@@ -13,6 +13,7 @@
#define DEBUG_TYPE "flang-debug-type-generator"
#include "flang/Optimizer/Transforms/DebugTypeGenerator.h"
+#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/CodeGen/DescriptorModel.h"
#include "flang/Optimizer/Support/InternalNames.h"
#include "flang/Optimizer/Support/Utils.h"
@@ -45,9 +46,11 @@ std::uint64_t getComponentOffset<0>(const mlir::DataLayout &dl,
DebugTypeGenerator::DebugTypeGenerator(mlir::ModuleOp m,
mlir::SymbolTable *symbolTable_,
- const mlir::DataLayout &dl)
+ const mlir::DataLayout &dl,
+ bool boundsInStackSlots_)
: module(m), symbolTable(symbolTable_), dataLayout{&dl},
- kindMapping(getKindMapping(m)), llvmTypeConverter(m, false, false, dl) {
+ boundsInStackSlots(boundsInStackSlots_), kindMapping(getKindMapping(m)),
+ llvmTypeConverter(m, false, false, dl) {
LLVM_DEBUG(llvm::dbgs() << "DITypeAttr generator\n");
mlir::MLIRContext *context = module.getContext();
@@ -91,12 +94,40 @@ static mlir::LLVM::DITypeAttr genPlaceholderType(mlir::MLIRContext *context) {
/*bitSize=*/32, llvm::dwarf::DW_ATE_signed);
}
+/// Return a stack slot holding \p val wherever the variable declared by
+/// \p declOp is visible.
+static fir::AllocaOp createBoundSlot(mlir::Value val,
+ fir::cg::XDeclareOp declOp) {
+ // Generate the slot and store with UnknownLoc so that they don't impact the
+ // line table.
+ mlir::OpBuilder builder(declOp);
+ mlir::Location loc = builder.getUnknownLoc();
+ builder.setInsertionPointToStart(
+ fir::getAllocaBlock(*declOp->getParentRegion()));
+ fir::AllocaOp slot = fir::AllocaOp::create(builder, loc, val.getType());
+ builder.setInsertionPoint(declOp);
+ fir::StoreOp::create(builder, loc, val, slot);
+ return slot;
+}
+
+bool DebugTypeGenerator::hasBoundSlot(mlir::Value val,
+ fir::cg::XDeclareOp declOp) const {
+ auto it = artificialVars.find({val, declOp});
+ return it != artificialVars.end() && it->second.slot;
+}
+
// Helper function to create DILocalVariableAttr and DbgValueOp when information
-// about the size or dimension of a variable etc lives in an mlir::Value.
+// about the size or dimension of a variable etc lives in an mlir::Value. If the
+// value is kept in a stack slot, the variable describes the slot instead, which
+// holds the value wherever the variable is visible.
mlir::LLVM::DILocalVariableAttr DebugTypeGenerator::generateArtificialVariable(
mlir::MLIRContext *context, mlir::Value val,
mlir::LLVM::DIFileAttr fileAttr, mlir::LLVM::DIScopeAttr scope,
fir::cg::XDeclareOp declOp) {
+ std::pair<mlir::Value, mlir::Operation *> key{val, declOp};
+ if (auto it = artificialVars.find(key); it != artificialVars.end())
+ return it->second.var;
+
// There can be multiple artificial variable for a single declOp. To help
// distinguish them, we pad the name with a counter. The counter is the
// position of 'val' in the operands of declOp.
@@ -107,8 +138,12 @@ mlir::LLVM::DILocalVariableAttr DebugTypeGenerator::generateArtificialVariable(
auto name = mlir::StringAttr::get(context, "." + declOp.getUniqName().str() +
std::to_string(varID));
builder.setInsertionPoint(declOp);
- mlir::Type type = val.getType();
- if (!mlir::isa<mlir::IntegerType>(type) || !type.isSignlessInteger()) {
+ fir::AllocaOp slot;
+ if (boundsInStackSlots)
+ slot = createBoundSlot(val, declOp);
+ mlir::Type type = slot ? slot.getInType() : val.getType();
+ if (!slot &&
+ (!mlir::isa<mlir::IntegerType>(type) || !type.isSignlessInteger())) {
type = builder.getIntegerType(64);
val = fir::ConvertOp::create(builder, declOp.getLoc(), type, val);
}
@@ -116,8 +151,18 @@ mlir::LLVM::DILocalVariableAttr DebugTypeGenerator::generateArtificialVariable(
auto lvAttr = mlir::LLVM::DILocalVariableAttr::get(
context, scope, name, fileAttr, /*line=*/0, /*argNo=*/0,
/*alignInBits=*/0, Ty, mlir::LLVM::DIFlags::Artificial);
- mlir::LLVM::DbgValueOp::create(builder, declOp.getLoc(), val, lvAttr,
- nullptr);
+ if (slot)
+ fir::cg::XDeclareOp::create(
+ builder, builder.getFusedLoc({declOp.getLoc()}, lvAttr), slot.getType(),
+ slot, /*shape=*/mlir::ValueRange{},
+ /*shift=*/mlir::ValueRange{}, /*typeparams=*/mlir::ValueRange{},
+ /*dummy_scope=*/mlir::Value{}, /*storage=*/mlir::Value{},
+ /*storage_offset=*/0, name, /*data_attr=*/cuf::DataAttributeAttr{},
+ /*dummy_arg_no=*/mlir::IntegerAttr{});
+ else
+ mlir::LLVM::DbgValueOp::create(builder, declOp.getLoc(), val, lvAttr,
+ nullptr);
+ artificialVars[key] = {slot, lvAttr};
return lvAttr;
}
diff --git a/flang/test/Integration/debug-bounds-stack-slot.f90 b/flang/test/Integration/debug-bounds-stack-slot.f90
new file mode 100644
index 0000000000000..3bb1743adf6fc
--- /dev/null
+++ b/flang/test/Integration/debug-bounds-stack-slot.f90
@@ -0,0 +1,62 @@
+! RUN: %flang_fc1 -emit-llvm -debug-info-kind=standalone %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-llvm -O2 -debug-info-kind=standalone %s -o - | FileCheck %s --check-prefix=OPT
+! RUN: %flang_fc1 -emit-llvm %s -o - | FileCheck %s --check-prefix=NOSLOT
+! RUN: %flang_fc1 -emit-llvm -debug-info-kind=line-tables-only %s -o - | FileCheck %s --check-prefix=NOSLOT
+
+subroutine array(a, lb, n)
+ integer :: lb, n
+ real :: a(lb:n)
+ a(lb) = 0.0
+end subroutine
+
+subroutine char_len(c)
+ character(*) :: c
+ c(1:1) = 'x'
+end subroutine
+
+! CHECK-LABEL: define void @array_(
+! CHECK: %[[LB_SLOT:[0-9]+]] = alloca
+! CHECK: %[[COUNT_SLOT:[0-9]+]] = alloca
+! CHECK: store i64 %{{[0-9]+}}, ptr %[[LB_SLOT]]
+! CHECK-NEXT: #dbg_declare(ptr %[[LB_SLOT]], ![[LB:[0-9]+]], !DIExpression(),
+! CHECK-NEXT: store i64 %{{[0-9]+}}, ptr %[[COUNT_SLOT]]
+! CHECK-NEXT: #dbg_declare(ptr %[[COUNT_SLOT]], ![[COUNT:[0-9]+]], !DIExpression(),
+! CHECK-NOT: #dbg_value
+! Test that no fake.use is generated with an integer type i{{[0-9]+}}.
+! CHECK-NOT: @llvm.fake.use(i{{[0-9]+}} %
+! CHECK: ret void
+
+! CHECK-LABEL: define void @char_len_(
+! CHECK: %[[LEN_SLOT:[0-9]+]] = alloca
+! CHECK: store i64 %{{[0-9]+}}, ptr %[[LEN_SLOT]]
+! CHECK-NEXT: #dbg_declare(ptr %[[LEN_SLOT]], ![[LEN:[0-9]+]], !DIExpression(),
+! CHECK-NOT: #dbg_value
+! CHECK-NOT: @llvm.fake.use(i{{[0-9]+}} %
+! CHECK: ret void
+
+! CHECK-DAG: ![[LB]] = !DILocalVariable(name: "._QFarrayEa2"{{.*}}flags: DIFlagArtificial)
+! CHECK-DAG: ![[COUNT]] = !DILocalVariable(name: "._QFarrayEa1"{{.*}}flags: DIFlagArtificial)
+! CHECK-DAG: !DILocalVariable(name: "a"{{.*}}type: ![[ATY:[0-9]+]])
+! CHECK-DAG: ![[ATY]] = !DICompositeType(tag: DW_TAG_array_type{{.*}}elements: ![[ELEMS:[0-9]+]])
+! CHECK-DAG: ![[ELEMS]] = !{![[SUBRANGE:[0-9]+]]}
+! CHECK-DAG: ![[SUBRANGE]] = !DISubrange(count: ![[COUNT]], lowerBound: ![[LB]])
+! CHECK-DAG: ![[LEN]] = !DILocalVariable(name: "._QFchar_lenEc1"{{.*}}flags: DIFlagArtificial)
+! CHECK-DAG: !DILocalVariable(name: "c"{{.*}}type: ![[CTY:[0-9]+]])
+! CHECK-DAG: ![[CTY]] = !DIStringType(stringLength: ![[LEN]], encoding: DW_ATE_ASCII)
+
+! The slots are only made at -O0. Above it a bound is described by its value,
+! as it was before, so a debugger has to find that value in a register.
+! OPT-LABEL: define void @array_(
+! OPT: #dbg_value({{.*}}![[OPT_LB:[0-9]+]], !DIExpression
+! OPT: #dbg_value({{.*}}![[OPT_COUNT:[0-9]+]], !DIExpression
+! OPT-LABEL: define void @char_len_(
+! OPT: #dbg_value({{.*}}![[OPT_LEN:[0-9]+]], !DIExpression
+
+! OPT-DAG: ![[OPT_LB]] = !DILocalVariable(name: "._QFarrayEa2"{{.*}}flags: DIFlagArtificial)
+! OPT-DAG: ![[OPT_COUNT]] = !DILocalVariable(name: "._QFarrayEa1"{{.*}}flags: DIFlagArtificial)
+! OPT-DAG: ![[OPT_LEN]] = !DILocalVariable(name: "._QFchar_lenEc1"{{.*}}flags: DIFlagArtificial)
+
+! A slot exists only to describe a variable, so neither is made when no
+! variables are asked for.
+! NOSLOT-NOT: #dbg_declare
+! NOSLOT-NOT: #dbg_value
diff --git a/flang/test/Transforms/debug-bounds-stack-slot.fir b/flang/test/Transforms/debug-bounds-stack-slot.fir
new file mode 100644
index 0000000000000..ca76259daa57f
--- /dev/null
+++ b/flang/test/Transforms/debug-bounds-stack-slot.fir
@@ -0,0 +1,120 @@
+// RUN: fir-opt --add-debug-info="emit-fake-use-for-debug-vars=true bounds-in-stack-slots=true" --mlir-print-debuginfo %s | FileCheck %s
+// RUN: fir-opt --add-debug-info="emit-fake-use-for-debug-vars=true" %s | FileCheck %s --check-prefix=NOSLOT
+
+#loc1 = loc("test.f90":1:1)
+#loc3 = loc("test.f90":3:14)
+#loc5 = loc("test.f90":5:1)
+#loc = loc("test.f90":0:0)
+
+module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
+ func.func private @foo()
+
+ func.func @test_dynamic_array(%arg0: !fir.ref<!fir.array<?xi32>> {fir.bindc_name = "arr"} loc(#loc1), %arg1: index loc(#loc1), %arg2: index loc(#loc1)) attributes {fir.internal_name = "_QPtest_dynamic_array"} {
+ %0 = fir.undefined !fir.dscope loc(#loc1)
+ %1 = fircg.ext_declare %arg0(%arg1) origin %arg2 dummy_scope %0 arg 1 uniq_name("_QFtest_dynamic_arrayEarr") : (!fir.ref<!fir.array<?xi32>>, index, index, !fir.dscope) -> !fir.ref<!fir.array<?xi32>> loc(#loc3)
+ fir.call @foo() : () -> ()
+ return loc(#loc5)
+ } loc(#loc1)
+
+// CHECK-LABEL: func.func @test_dynamic_array(
+// CHECK: %[[COUNT_SLOT:.*]] = fir.alloca index loc(#[[UNK:loc[0-9]+]])
+// CHECK: %[[LB_SLOT:.*]] = fir.alloca index loc(#[[UNK]])
+// CHECK-NOT: llvm.intr.dbg.value
+// CHECK: fir.store %arg2 to %[[LB_SLOT]] : !fir.ref<index> loc(#[[UNK]])
+// CHECK: fircg.ext_declare %[[LB_SLOT]] uniq_name("._QFtest_dynamic_arrayEarr2")
+// CHECK: fir.store %arg1 to %[[COUNT_SLOT]] : !fir.ref<index> loc(#[[UNK]])
+// CHECK: fircg.ext_declare %[[COUNT_SLOT]] uniq_name("._QFtest_dynamic_arrayEarr1")
+// CHECK: fircg.ext_declare %arg0(%arg1) origin %arg2
+// CHECK: fir.call @foo()
+// CHECK-NEXT: fir.fake_use %arg0
+// CHECK-NEXT: return
+
+// NOSLOT-LABEL: func.func @test_dynamic_array(
+// NOSLOT-NOT: fir.alloca
+// NOSLOT: llvm.intr.dbg.value
+// NOSLOT: fir.fake_use %arg0
+// NOSLOT-NEXT: fir.fake_use %arg1
+// NOSLOT-NEXT: fir.fake_use %arg2
+
+ func.func @test_char(%arg0: !fir.ref<!fir.char<1,?>> {fir.bindc_name = "str"} loc(#loc1), %arg1: index loc(#loc1)) attributes {fir.internal_name = "_QPtest_char"} {
+ %0 = fir.undefined !fir.dscope loc(#loc1)
+ %1 = fircg.ext_declare %arg0 typeparams %arg1 dummy_scope %0 arg 1 uniq_name("_QFtest_charEstr") : (!fir.ref<!fir.char<1,?>>, index, !fir.dscope) -> !fir.ref<!fir.char<1,?>> loc(#loc3)
+ fir.call @foo() : () -> ()
+ return loc(#loc5)
+ } loc(#loc1)
+
+// CHECK-LABEL: func.func @test_char(
+// CHECK: %[[LEN_SLOT:.*]] = fir.alloca index
+// CHECK-NOT: llvm.intr.dbg.value
+// CHECK: fir.store %arg1 to %[[LEN_SLOT]] : !fir.ref<index>
+// CHECK: fircg.ext_declare %[[LEN_SLOT]] uniq_name("._QFtest_charEstr1")
+// CHECK: fircg.ext_declare %arg0 typeparams %arg1
+// CHECK: fir.call @foo()
+// CHECK-NEXT: fir.fake_use %arg0
+// CHECK-NEXT: return
+
+ // Both dimensions are the same value, and one slot describes them both.
+ func.func @test_repeated_extent(%arg0: !fir.ref<!fir.array<?x?xi32>> {fir.bindc_name = "arr"} loc(#loc1), %arg1: index loc(#loc1)) attributes {fir.internal_name = "_QPtest_repeated_extent"} {
+ %0 = fir.undefined !fir.dscope loc(#loc1)
+ %1 = fircg.ext_declare %arg0(%arg1, %arg1) dummy_scope %0 arg 1 uniq_name("_QFtest_repeated_extentEarr") : (!fir.ref<!fir.array<?x?xi32>>, index, index, !fir.dscope) -> !fir.ref<!fir.array<?x?xi32>> loc(#loc3)
+ fir.call @foo() : () -> ()
+ return loc(#loc5)
+ } loc(#loc1)
+
+// CHECK-LABEL: func.func @test_repeated_extent(
+// CHECK: %[[SLOT:.*]] = fir.alloca index
+// CHECK-NOT: fir.alloca
+// CHECK: fir.store %arg1 to %[[SLOT]] : !fir.ref<index>
+// CHECK: fircg.ext_declare %[[SLOT]] uniq_name("._QFtest_repeated_extentEarr1")
+// CHECK-NOT: fircg.ext_declare %[[SLOT]]
+// CHECK: fircg.ext_declare %arg0(%arg1, %arg1)
+
+ // Two declarations name the same value, and each gets a slot of its own, so
+ // that a slot always belongs to the region of the declaration it describes.
+ func.func @test_shared_extent(%arg0: !fir.ref<!fir.array<?xi32>> {fir.bindc_name = "a"} loc(#loc1), %arg1: !fir.ref<!fir.array<?xi32>> {fir.bindc_name = "b"} loc(#loc1), %arg2: index loc(#loc1)) attributes {fir.internal_name = "_QPtest_shared_extent"} {
+ %0 = fir.undefined !fir.dscope loc(#loc1)
+ %1 = fircg.ext_declare %arg0(%arg2) dummy_scope %0 arg 1 uniq_name("_QFtest_shared_extentEa") : (!fir.ref<!fir.array<?xi32>>, index, !fir.dscope) -> !fir.ref<!fir.array<?xi32>> loc(#loc3)
+ %2 = fircg.ext_declare %arg1(%arg2) dummy_scope %0 arg 2 uniq_name("_QFtest_shared_extentEb") : (!fir.ref<!fir.array<?xi32>>, index, !fir.dscope) -> !fir.ref<!fir.array<?xi32>> loc(#loc3)
+ fir.call @foo() : () -> ()
+ return loc(#loc5)
+ } loc(#loc1)
+
+// The slots go to the start of the block, so the one made last comes first.
+// CHECK-LABEL: func.func @test_shared_extent(
+// CHECK: %[[B_SLOT:.*]] = fir.alloca index
+// CHECK: %[[A_SLOT:.*]] = fir.alloca index
+// CHECK: fir.store %arg2 to %[[A_SLOT]] : !fir.ref<index>
+// CHECK: fircg.ext_declare %[[A_SLOT]] uniq_name("._QFtest_shared_extentEa1")
+// CHECK: fircg.ext_declare %arg0(%arg2)
+// CHECK: fir.store %arg2 to %[[B_SLOT]] : !fir.ref<index>
+// CHECK: fircg.ext_declare %[[B_SLOT]] uniq_name("._QFtest_shared_extentEb1")
+// CHECK: fircg.ext_declare %arg1(%arg2)
+// Neither declaration needs a fake use of the extent they share.
+// CHECK: fir.call @foo()
+// CHECK-NEXT: fir.fake_use %arg0
+// CHECK-NEXT: fir.fake_use %arg1
+// CHECK-NEXT: return
+
+ // A declaration in a target region gets its slot in the region
+ func.func @test_target_region(%arg0: !fir.ref<!fir.array<?xi32>> loc(#loc1), %arg1: !fir.ref<i32> loc(#loc1)) attributes {fir.internal_name = "_QPtest_target_region"} {
+ %0 = omp.map.info var_ptr(%arg0 : !fir.ref<!fir.array<?xi32>>, !fir.array<?xi32>) map_clauses(tofrom) capture(ByRef) -> !fir.ref<!fir.array<?xi32>>
+ %1 = omp.map.info var_ptr(%arg1 : !fir.ref<i32>, i32) map_clauses(tofrom) capture(ByRef) -> !fir.ref<i32>
+ omp.target kernel_type(generic) map_entries(%0 -> %arg2, %1 -> %arg3 : !fir.ref<!fir.array<?xi32>>, !fir.ref<i32>) {
+ %2 = fir.load %arg3 : !fir.ref<i32>
+ %3 = fir.convert %2 : (i32) -> index
+ %4 = fircg.ext_declare %arg2(%3) uniq_name("_QFtest_target_regionEarr") : (!fir.ref<!fir.array<?xi32>>, index) -> !fir.ref<!fir.array<?xi32>> loc(#loc3)
+ omp.terminator
+ } loc(#loc3)
+ return loc(#loc5)
+ } loc(#loc1)
+
+// CHECK-LABEL: func.func @test_target_region(
+// CHECK: omp.target
+// CHECK-NEXT: %[[TGT_SLOT:.*]] = fir.alloca index
+// CHECK: %[[EXTENT:.*]] = fir.convert %{{.*}} : (i32) -> index
+// CHECK-NEXT: fir.store %[[EXTENT]] to %[[TGT_SLOT]] : !fir.ref<index>
+// CHECK-NEXT: fircg.ext_declare %[[TGT_SLOT]] uniq_name("._QFtest_target_regionEarr1")
+// CHECK-NEXT: fircg.ext_declare %arg2(%[[EXTENT]])
+
+// CHECK: #[[UNK]] = loc(unknown)
+} loc(#loc)
More information about the flang-commits
mailing list