[flang][debug] Handle array types with variable size/bounds. (#110686)

The debug information generated by flang did not handle the cases where
dimension or lower bounds of the arrays were variable. This PR fixes
this issue. It will help distinguish assumed size arrays from cases
where array size are variable. It also handles the variable lower bounds
for assumed shape arrays.
    
Fixes #98879.
This commit is contained in:
Abid Qadeer
2024-10-03 21:29:47 +01:00
committed by GitHub
parent 428ae0f12e
commit fc4b1a303b
11 changed files with 164 additions and 49 deletions

View File

@@ -78,6 +78,35 @@ static mlir::LLVM::DITypeAttr genPlaceholderType(mlir::MLIRContext *context) {
/*bitSize=*/32, llvm::dwarf::DW_ATE_signed);
}
// Helper function to create DILocalVariableAttr and DbgValueOp when information
// about the size or dimension of a variable etc lives in an mlir::Value.
mlir::LLVM::DILocalVariableAttr DebugTypeGenerator::generateArtificialVariable(
mlir::MLIRContext *context, mlir::Value val,
mlir::LLVM::DIFileAttr fileAttr, mlir::LLVM::DIScopeAttr scope,
fir::cg::XDeclareOp declOp) {
// 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.
auto varID = std::distance(
declOp.getOperands().begin(),
std::find(declOp.getOperands().begin(), declOp.getOperands().end(), val));
mlir::OpBuilder builder(context);
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()) {
type = builder.getIntegerType(64);
val = builder.create<fir::ConvertOp>(declOp.getLoc(), type, val);
}
mlir::LLVM::DITypeAttr Ty = convertType(type, fileAttr, scope, declOp);
auto lvAttr = mlir::LLVM::DILocalVariableAttr::get(
context, scope, name, fileAttr, /*line=*/0, /*argNo=*/0,
/*alignInBits=*/0, Ty, mlir::LLVM::DIFlags::Artificial);
builder.create<mlir::LLVM::DbgValueOp>(declOp.getLoc(), val, lvAttr, nullptr);
return lvAttr;
}
mlir::LLVM::DITypeAttr DebugTypeGenerator::convertBoxedSequenceType(
fir::SequenceType seqTy, mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DIScopeAttr scope, fir::cg::XDeclareOp declOp,
@@ -122,11 +151,12 @@ mlir::LLVM::DITypeAttr DebugTypeGenerator::convertBoxedSequenceType(
mlir::Attribute lowerAttr = nullptr;
// If declaration has a lower bound, use it.
if (declOp && declOp.getShift().size() > index) {
// TODO: Handle case where lower bound is a variable (instead of a
// constant as handled here)
if (std::optional<std::int64_t> optint =
getIntIfConstant(declOp.getShift()[index]))
lowerAttr = mlir::IntegerAttr::get(intTy, llvm::APInt(64, *optint));
else
lowerAttr = generateArtificialVariable(
context, declOp.getShift()[index], fileAttr, scope, declOp);
}
// FIXME: If `indexSize` happens to be bigger than address size on the
// system then we may have to change 'DW_OP_deref' here.
@@ -320,31 +350,43 @@ mlir::LLVM::DITypeAttr DebugTypeGenerator::convertSequenceType(
unsigned index = 0;
auto intTy = mlir::IntegerType::get(context, 64);
for (fir::SequenceType::Extent dim : seqTy.getShape()) {
int64_t shift = 1;
mlir::Attribute lowerAttr = nullptr;
mlir::Attribute countAttr = nullptr;
// If declOp is present, we use the shift in it to get the lower bound of
// the array. If it is constant, that is used. If it is not constant, we
// create a variable that represents its location and use that as lower
// bound. As an optimization, we don't create a lower bound when shift is a
// constant 1 as that is the default.
if (declOp && declOp.getShift().size() > index) {
if (std::optional<std::int64_t> optint =
getIntIfConstant(declOp.getShift()[index]))
shift = *optint;
getIntIfConstant(declOp.getShift()[index])) {
if (*optint != 1)
lowerAttr = mlir::IntegerAttr::get(intTy, llvm::APInt(64, *optint));
} else
lowerAttr = generateArtificialVariable(
context, declOp.getShift()[index], fileAttr, scope, declOp);
}
if (dim == seqTy.getUnknownExtent()) {
mlir::IntegerAttr lowerAttr = nullptr;
if (declOp && declOp.getShift().size() > index)
lowerAttr = mlir::IntegerAttr::get(intTy, llvm::APInt(64, shift));
// FIXME: This path is taken for assumed size arrays but also for arrays
// with non constant extent. For the latter case, the DISubrangeAttr
// should point to a variable which will have the extent at runtime.
auto subrangeTy = mlir::LLVM::DISubrangeAttr::get(
context, /*count=*/nullptr, lowerAttr, /*upperBound*/ nullptr,
/*stride*/ nullptr);
elements.push_back(subrangeTy);
} else {
auto countAttr = mlir::IntegerAttr::get(intTy, llvm::APInt(64, dim));
auto lowerAttr = mlir::IntegerAttr::get(intTy, llvm::APInt(64, shift));
auto subrangeTy = mlir::LLVM::DISubrangeAttr::get(
context, countAttr, lowerAttr, /*upperBound=*/nullptr,
/*stride=*/nullptr);
elements.push_back(subrangeTy);
}
// This path is taken for both assumed size array or when the size of the
// array is variable. In the case of variable size, we create a variable
// to use as countAttr. Note that fir has a constant size of -1 for
// assumed size array. So !optint check makes sure we don't generate
// variable in that case.
if (declOp && declOp.getShape().size() > index) {
std::optional<std::int64_t> optint =
getIntIfConstant(declOp.getShape()[index]);
if (!optint)
countAttr = generateArtificialVariable(
context, declOp.getShape()[index], fileAttr, scope, declOp);
}
} else
countAttr = mlir::IntegerAttr::get(intTy, llvm::APInt(64, dim));
auto subrangeTy = mlir::LLVM::DISubrangeAttr::get(
context, countAttr, lowerAttr, /*upperBound=*/nullptr,
/*stride=*/nullptr);
elements.push_back(subrangeTy);
++index;
}
// Apart from arrays, the `DICompositeTypeAttr` is used for other things like
@@ -400,23 +442,8 @@ mlir::LLVM::DITypeAttr DebugTypeGenerator::convertCharacterType(
// variable that will contain that length. This variable is used as
// 'stringLength' in DIStringTypeAttr.
if (declOp && !declOp.getTypeparams().empty()) {
auto name =
mlir::StringAttr::get(context, "." + declOp.getUniqName().str());
mlir::OpBuilder builder(context);
builder.setInsertionPoint(declOp);
mlir::Value sizeVal = declOp.getTypeparams()[0];
mlir::Type type = sizeVal.getType();
if (!mlir::isa<mlir::IntegerType>(type) || !type.isSignlessInteger()) {
type = builder.getIntegerType(64);
sizeVal =
builder.create<fir::ConvertOp>(declOp.getLoc(), type, sizeVal);
}
mlir::LLVM::DITypeAttr Ty = convertType(type, fileAttr, scope, declOp);
auto lvAttr = mlir::LLVM::DILocalVariableAttr::get(
context, scope, name, fileAttr, /*line=*/0, /*argNo=*/0,
/*alignInBits=*/0, Ty, mlir::LLVM::DIFlags::Artificial);
builder.create<mlir::LLVM::DbgValueOp>(declOp.getLoc(), sizeVal, lvAttr,
nullptr);
mlir::LLVM::DILocalVariableAttr lvAttr = generateArtificialVariable(
context, declOp.getTypeparams()[0], fileAttr, scope, declOp);
varAttr = mlir::cast<mlir::LLVM::DIVariableAttr>(lvAttr);
}
}

View File

@@ -64,6 +64,11 @@ private:
fir::cg::XDeclareOp declOp,
bool genAllocated,
bool genAssociated);
mlir::LLVM::DILocalVariableAttr
generateArtificialVariable(mlir::MLIRContext *context, mlir::Value Val,
mlir::LLVM::DIFileAttr fileAttr,
mlir::LLVM::DIScopeAttr scope,
fir::cg::XDeclareOp declOp);
mlir::ModuleOp module;
mlir::SymbolTable *symbolTable;

View File

@@ -12,7 +12,7 @@ end module helper
! CHECK-DAG: ![[TY1:[0-9]+]] = !DICompositeType(tag: DW_TAG_array_type{{.*}}elements: ![[ELEMS1:[0-9]+]]{{.*}})
! CHECK-DAG: ![[ELEMS1]] = !{![[ELM1:[0-9]+]], ![[EMPTY:[0-9]+]]}
! CHECK-DAG: ![[ELM1]] = !DISubrange(count: 5, lowerBound: 1)
! CHECK-DAG: ![[ELM1]] = !DISubrange(count: 5)
! CHECK-DAG: ![[EMPTY]] = !DISubrange()
! CHECK-DAG: ![[TY2:[0-9]+]] = !DICompositeType(tag: DW_TAG_array_type{{.*}}elements: ![[ELEMS2:[0-9]+]]{{.*}})
! CHECK-DAG: ![[ELEMS2]] = !{![[EMPTY:[0-9]+]]}

View File

@@ -23,11 +23,11 @@ end program
! CHECK-DAG: ![[INT:.*]] = !DIBasicType(name: "integer", size: 32, encoding: DW_ATE_signed)
! CHECK-DAG: ![[REAL:.*]] = !DIBasicType(name: "real", size: 32, encoding: DW_ATE_float)
! CHECK-DAG: ![[R1:.*]] = !DISubrange(count: 3, lowerBound: 1)
! CHECK-DAG: ![[R1:.*]] = !DISubrange(count: 3)
! CHECK-DAG: ![[SUB1:.*]] = !{![[R1]]}
! CHECK-DAG: ![[D1TY:.*]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[INT]], elements: ![[SUB1]])
! CHECK-DAG: ![[R21:.*]] = !DISubrange(count: 4, lowerBound: 1)
! CHECK-DAG: ![[R21:.*]] = !DISubrange(count: 4)
! CHECK-DAG: ![[R22:.*]] = !DISubrange(count: 5, lowerBound: -1)
! CHECK-DAG: ![[SUB2:.*]] = !{![[R21]], ![[R22]]}
! CHECK-DAG: ![[D2TY:.*]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[INT]], elements: ![[SUB2]])

View File

@@ -0,0 +1,19 @@
! RUN: %flang_fc1 -emit-llvm -debug-info-kind=standalone %s -o - | FileCheck %s
subroutine foo(a, n, m, p)
integer n, m, p
integer :: a(n:m, p)
a(1, 2) = 10
print *, a
end subroutine foo
! CHECK-DAG: ![[VAR0:.*]] = !DILocalVariable(name: "._QFfooEa3"{{.*}}scope: ![[SCOPE:[0-9]+]]{{.*}}flags: DIFlagArtificial)
! CHECK-DAG: ![[VAR1:.*]] = !DILocalVariable(name: "._QFfooEa1"{{.*}}scope: ![[SCOPE]]{{.*}}flags: DIFlagArtificial)
! CHECK-DAG: ![[VAR2:.*]] = !DILocalVariable(name: "._QFfooEa2"{{.*}}scope: ![[SCOPE]]{{.*}}flags: DIFlagArtificial)
! CHECK-DAG: ![[SR1:.*]] = !DISubrange(count: ![[VAR1]], lowerBound: ![[VAR0]])
! CHECK-DAG: ![[SR2:.*]] = !DISubrange(count: ![[VAR2]])
! CHECK-DAG: ![[SR:.*]] = !{![[SR1]], ![[SR2]]}
! CHECK-DAG: ![[TY:.*]] = !DICompositeType(tag: DW_TAG_array_type{{.*}}elements: ![[SR]])
! CHECK-DAG: !DILocalVariable(name: "a"{{.*}}scope: ![[SCOPE]]{{.*}}type: ![[TY]])

View File

@@ -13,7 +13,7 @@ module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
#loc1 = loc("test.f90":5:1)
#loc2 = loc("test.f90":15:1)
// CHECK: #[[VAR:.*]] = #llvm.di_local_variable<{{.*}}name = "._QFtestEstr"{{.*}}flags = Artificial>
// CHECK: #[[VAR:.*]] = #llvm.di_local_variable<{{.*}}name = "._QFtestEstr{{.*}}flags = Artificial>
// CHECK: func.func @test
// CHECK: %[[V1:.*]]:2 = fir.unboxchar{{.*}}
// CHECK: %[[V2:.*]] = fir.convert %[[V1]]#1 : (index) -> i64

View File

@@ -0,0 +1,23 @@
// RUN: fir-opt --add-debug-info --mlir-print-debuginfo %s | FileCheck %s
// Test assumed shape array with variable lower bound.
module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
func.func private @_QFPfn(%arg0: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "b"}, %arg1: !fir.ref<i32> {fir.bindc_name = "n"}) attributes {} {
%c23_i32 = arith.constant 23 : i32
%c6_i32 = arith.constant 6 : i32
%c20_i32 = arith.constant 20 : i32
%0 = fir.undefined !fir.dscope
%1 = fircg.ext_declare %arg1 dummy_scope %0 {uniq_name = "_QFFfnEn"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32> loc(#loc1)
%2 = fir.load %1 : !fir.ref<i32>
%3 = fir.convert %2 : (i32) -> index
%4 = fircg.ext_declare %arg0 origin %3 dummy_scope %0 {uniq_name = "_QFFfnEb"} : (!fir.box<!fir.array<?xi32>>, index, !fir.dscope) -> !fir.box<!fir.array<?xi32>> loc(#loc2)
return
} loc(#loc3)
}
#loc1 = loc("test1.f90":1:1)
#loc2 = loc("test1.f90":3:16)
#loc3 = loc("test1.f90":4:16)
// CHECK: #[[VAR:.*]] = #llvm.di_local_variable<{{.*}}name = "._QFFfnEb1"{{.*}}flags = Artificial>
// CHECK: #llvm.di_composite_type<tag = DW_TAG_array_type{{.*}}elements = #llvm.di_subrange<count = #llvm.di_expression<[{{.*}}]>, lowerBound = #[[VAR]], stride = #llvm.di_expression<[{{.*}}]>>, dataLocation = <[DW_OP_push_object_address, DW_OP_deref]>>

View File

@@ -16,7 +16,7 @@ module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
#loc1 = loc("test.f90":3:1)
#loc2 = loc("test.f90":4:1)
// CHECK-DAG: #[[TY1:.*]] = #llvm.di_composite_type<tag = DW_TAG_array_type{{.*}}elements = #llvm.di_subrange<count = 5 : i64, lowerBound = 1 : i64>, #llvm.di_subrange<>>
// CHECK-DAG: #[[TY1:.*]] = #llvm.di_composite_type<tag = DW_TAG_array_type{{.*}}elements = #llvm.di_subrange<count = 5 : i64>, #llvm.di_subrange<>>
// CHECK-DAG: #[[TY2:.*]] = #llvm.di_composite_type<tag = DW_TAG_array_type{{.*}}elements = #llvm.di_subrange<lowerBound = 2 : i64>>
// CHECK-DAG: #llvm.di_local_variable<{{.*}}name = "a1"{{.*}}type = #[[TY1]]>
// CHECK-DAG: #llvm.di_local_variable<{{.*}}name = "a2"{{.*}}type = #[[TY2]]>

View File

@@ -31,9 +31,9 @@ module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
// CHECK-DAG: #[[INT:.*]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "integer", sizeInBits = 32, encoding = DW_ATE_signed>
// CHECK-DAG: #[[REAL:.*]] = #llvm.di_basic_type<tag = DW_TAG_base_type, name = "real", sizeInBits = 32, encoding = DW_ATE_float>
// CHECK-DAG: #[[D1TY:.*]] = #llvm.di_composite_type<tag = DW_TAG_array_type{{.*}}baseType = #[[INT]], elements = #llvm.di_subrange<count = 3 : i64, lowerBound = 1 : i64>>
// CHECK-DAG: #[[D2TY:.*]] = #llvm.di_composite_type<tag = DW_TAG_array_type{{.*}}baseType = #[[INT]], elements = #llvm.di_subrange<count = 2 : i64, lowerBound = 1 : i64>, #llvm.di_subrange<count = 5 : i64, lowerBound = 1 : i64>>
// CHECK-DAG: #[[D3TY:.*]] = #llvm.di_composite_type<tag = DW_TAG_array_type{{.*}}baseType = #[[REAL]], elements = #llvm.di_subrange<count = 6 : i64, lowerBound = 1 : i64>, #llvm.di_subrange<count = 8 : i64, lowerBound = 1 : i64>, #llvm.di_subrange<count = 7 : i64, lowerBound = 1 : i64>>
// CHECK-DAG: #[[D1TY:.*]] = #llvm.di_composite_type<tag = DW_TAG_array_type{{.*}}baseType = #[[INT]], elements = #llvm.di_subrange<count = 3 : i64>>
// CHECK-DAG: #[[D2TY:.*]] = #llvm.di_composite_type<tag = DW_TAG_array_type{{.*}}baseType = #[[INT]], elements = #llvm.di_subrange<count = 2 : i64>, #llvm.di_subrange<count = 5 : i64>>
// CHECK-DAG: #[[D3TY:.*]] = #llvm.di_composite_type<tag = DW_TAG_array_type{{.*}}baseType = #[[REAL]], elements = #llvm.di_subrange<count = 6 : i64>, #llvm.di_subrange<count = 8 : i64>, #llvm.di_subrange<count = 7 : i64>>
// CHECK-DAG: #[[D4TY:.*]] = #llvm.di_composite_type<tag = DW_TAG_array_type, baseType = #di_basic_type, elements = #llvm.di_subrange<count = 6 : i64, lowerBound = -2 : i64>, #llvm.di_subrange<count = 7 : i64, lowerBound = 4 : i64>>
// CHECK-DAG: #llvm.di_local_variable<{{.*}}name = "d1"{{.*}}type = #[[D1TY]]>
// CHECK-DAG: #llvm.di_local_variable<{{.*}}name = "d2"{{.*}}type = #[[D2TY]]>

View File

@@ -0,0 +1,41 @@
// RUN: fir-opt --add-debug-info --mlir-print-debuginfo %s | FileCheck %s
module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
func.func @foo_(%arg0: !fir.ref<!fir.array<?x?xi32>> {fir.bindc_name = "a"}, %arg1: !fir.ref<i32> {fir.bindc_name = "n"}, %arg2: !fir.ref<i32> {fir.bindc_name = "m"}, %arg3: !fir.ref<i32> {fir.bindc_name = "p"}) attributes {fir.internal_name = "_QPfoo"} {
%c5_i32 = arith.constant 5 : i32
%c6_i32 = arith.constant 6 : i32
%c2 = arith.constant 2 : index
%c10_i32 = arith.constant 10 : i32
%c0 = arith.constant 0 : index
%c1 = arith.constant 1 : index
%0 = fir.undefined !fir.dscope
%1 = fircg.ext_declare %arg1 dummy_scope %0 {uniq_name = "_QFfooEn"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32> loc(#loc2)
%2 = fircg.ext_declare %arg2 dummy_scope %0 {uniq_name = "_QFfooEm"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32> loc(#loc3)
%3 = fircg.ext_declare %arg3 dummy_scope %0 {uniq_name = "_QFfooEp"} : (!fir.ref<i32>, !fir.dscope) -> !fir.ref<i32> loc(#loc4)
%4 = fir.load %1 : !fir.ref<i32>
%5 = fir.convert %4 : (i32) -> index
%6 = fir.load %2 : !fir.ref<i32>
%7 = fir.convert %6 : (i32) -> index
%8 = arith.subi %7, %5 : index
%9 = arith.addi %8, %c1 : index
%10 = arith.cmpi sgt, %9, %c0 : index
%11 = arith.select %10, %9, %c0 : index
%12 = fir.load %3 : !fir.ref<i32>
%13 = fir.convert %12 : (i32) -> index
%14 = arith.cmpi sgt, %13, %c0 : index
%15 = arith.select %14, %13, %c0 : index
%16 = fircg.ext_declare %arg0(%11, %15) origin %5, %c1 dummy_scope %0 {uniq_name = "_QFfooEa"} : (!fir.ref<!fir.array<?x?xi32>>, index, index, index, index, !fir.dscope) -> !fir.ref<!fir.array<?x?xi32>> loc(#loc5)
return
} loc(#loc1)
}
#loc1 = loc("test.f90":5:1)
#loc2 = loc("test.f90":6:11)
#loc3 = loc("test.f90":7:11)
#loc4 = loc("test.f90":2:8)
#loc5 = loc("test.f90":8:11)
// CHECK-DAG: #[[VAR0:.*]] = #llvm.di_local_variable<{{.*}}name = "._QFfooEa3"{{.*}}flags = Artificial>
// CHECK-DAG: #[[VAR1:.*]] = #llvm.di_local_variable<{{.*}}name = "._QFfooEa1"{{.*}}flags = Artificial>
// CHECK-DAG: #[[VAR2:.*]] = #llvm.di_local_variable<{{.*}}name = "._QFfooEa2"{{.*}}flags = Artificial>
// CHECK-DAG: #llvm.di_composite_type<tag = DW_TAG_array_type{{.*}}elements = #llvm.di_subrange<count = #[[VAR1]], lowerBound = #[[VAR0]]>, #llvm.di_subrange<count = #[[VAR2]]>>

View File

@@ -22,7 +22,7 @@ module attributes {dlti.dl_spec = #dlti.dl_spec<>} {
#loc2 = loc("test.f90":17:1)
#loc3 = loc("test.f90":15:1)
// CHECK: #[[VAR:.*]] = #llvm.di_local_variable<{{.*}}name = "._QFfooEstr1"{{.*}}flags = Artificial>
// CHECK: #[[VAR:.*]] = #llvm.di_local_variable<{{.*}}name = "._QFfooEstr1{{.*}}flags = Artificial>
// CHECK: func.func @foo
// CHECK: llvm.intr.dbg.value #[[VAR]]
// CHECK: return