-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[DebugInfo] Handle DW_OP_LLVM_extract_bits in SROA #94638
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[DebugInfo] Handle DW_OP_LLVM_extract_bits in SROA #94638
Conversation
✅ With the latest revision this PR passed the C/C++ code formatter. |
This doesn't need any work to be done in SROA itself, but rather in functions that it uses. Specifically: * DIExpression::createFragmentExpression is made to understand DW_OP_LLVM_extract_bits * valueCoversEntireFragment is made to check the active bits instead of the fragment size, so that it handles extract_bits correctly
eaea27c
to
438181a
Compare
@llvm/pr-subscribers-llvm-transforms Author: John Brawn (john-brawn-arm) ChangesThis doesn't need any work to be done in SROA itself, but rather in functions that it uses. Specifically:
Patch is 20.16 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/94638.diff 4 Files Affected:
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index c15d64b842293..524945862e8d4 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -2910,6 +2910,12 @@ class DIExpression : public MDNode {
}
};
+ /// Return the number of bits that have an active value, i.e. those that
+ /// aren't known to be zero/sign (depending on the type of Var) and which
+ /// are within the size of this fragment (if it is one). If we can't deduce
+ /// anything from the expression this will return the size of Var.
+ std::optional<uint64_t> getActiveBits(DIVariable *Var);
+
/// Retrieve the details of this fragment expression.
static std::optional<FragmentInfo> getFragmentInfo(expr_op_iterator Start,
expr_op_iterator End);
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 2b45932093f0f..161a30dfb3828 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -1679,6 +1679,41 @@ DIExpression::getFragmentInfo(expr_op_iterator Start, expr_op_iterator End) {
return std::nullopt;
}
+std::optional<uint64_t> DIExpression::getActiveBits(DIVariable *Var) {
+ std::optional<uint64_t> InitialActiveBits = Var->getSizeInBits();
+ std::optional<uint64_t> ActiveBits = InitialActiveBits;
+ for (auto Op : expr_ops()) {
+ switch (Op.getOp()) {
+ default:
+ // We assume the worst case for anything we don't currently handle and
+ // revert to the initial active bits.
+ ActiveBits = InitialActiveBits;
+ break;
+ case dwarf::DW_OP_LLVM_extract_bits_zext:
+ case dwarf::DW_OP_LLVM_extract_bits_sext: {
+ // We can't handle an extract whose sign doesn't match that of the
+ // variable.
+ std::optional<DIBasicType::Signedness> VarSign = Var->getSignedness();
+ bool VarSigned = (VarSign == DIBasicType::Signedness::Signed);
+ bool OpSigned = (Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_sext);
+ if (!VarSign || VarSigned != OpSigned) {
+ ActiveBits = InitialActiveBits;
+ break;
+ }
+ [[fallthrough]];
+ }
+ case dwarf::DW_OP_LLVM_fragment:
+ // Extract or fragment narrows the active bits
+ if (ActiveBits)
+ ActiveBits = std::min(*ActiveBits, Op.getArg(1));
+ else
+ ActiveBits = Op.getArg(1);
+ break;
+ }
+ }
+ return ActiveBits;
+}
+
void DIExpression::appendOffset(SmallVectorImpl<uint64_t> &Ops,
int64_t Offset) {
if (Offset > 0) {
@@ -1931,6 +1966,8 @@ std::optional<DIExpression *> DIExpression::createFragmentExpression(
// Track whether it's safe to split the value at the top of the DWARF stack,
// assuming that it'll be used as an implicit location value.
bool CanSplitValue = true;
+ // Track whether we need to add a fragment expression to the end of Expr.
+ bool EmitFragment = true;
// Copy over the expression, but leave off any trailing DW_OP_LLVM_fragment.
if (Expr) {
for (auto Op : Expr->expr_ops()) {
@@ -1966,6 +2003,11 @@ std::optional<DIExpression *> DIExpression::createFragmentExpression(
return std::nullopt;
break;
case dwarf::DW_OP_LLVM_fragment: {
+ // If we've decided we don't need a fragment then give up if we see that
+ // there's already a fragment expression.
+ // FIXME: We could probably do better here
+ if (!EmitFragment)
+ return std::nullopt;
// Make the new offset point into the existing fragment.
uint64_t FragmentOffsetInBits = Op.getArg(0);
uint64_t FragmentSizeInBits = Op.getArg(1);
@@ -1975,15 +2017,38 @@ std::optional<DIExpression *> DIExpression::createFragmentExpression(
OffsetInBits += FragmentOffsetInBits;
continue;
}
+ case dwarf::DW_OP_LLVM_extract_bits_zext:
+ case dwarf::DW_OP_LLVM_extract_bits_sext: {
+ // If we're extracting bits from inside of the fragment that we're
+ // creating then we don't have a fragment after all, and just need to
+ // adjust the offset that we're extracting from.
+ uint64_t ExtractOffsetInBits = Op.getArg(0);
+ uint64_t ExtractSizeInBits = Op.getArg(1);
+ if (ExtractOffsetInBits >= OffsetInBits &&
+ ExtractOffsetInBits + ExtractSizeInBits <=
+ OffsetInBits + SizeInBits) {
+ Ops.push_back(Op.getOp());
+ Ops.push_back(ExtractOffsetInBits - OffsetInBits);
+ Ops.push_back(ExtractSizeInBits);
+ EmitFragment = false;
+ continue;
+ }
+ // If the extracted bits aren't fully contained within the fragment then
+ // give up.
+ // FIXME: We could probably do better here
+ return std::nullopt;
+ }
}
Op.appendToVector(Ops);
}
}
assert((!Expr->isImplicit() || CanSplitValue) && "Expr can't be split");
assert(Expr && "Unknown DIExpression");
- Ops.push_back(dwarf::DW_OP_LLVM_fragment);
- Ops.push_back(OffsetInBits);
- Ops.push_back(SizeInBits);
+ if (EmitFragment) {
+ Ops.push_back(dwarf::DW_OP_LLVM_fragment);
+ Ops.push_back(OffsetInBits);
+ Ops.push_back(SizeInBits);
+ }
return DIExpression::get(Expr->getContext(), Ops);
}
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp
index ce0f4c7668a40..bcddfce7897ca 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -1603,7 +1603,8 @@ static bool PhiHasDebugValue(DILocalVariable *DIVar,
static bool valueCoversEntireFragment(Type *ValTy, DbgVariableIntrinsic *DII) {
const DataLayout &DL = DII->getModule()->getDataLayout();
TypeSize ValueSize = DL.getTypeAllocSizeInBits(ValTy);
- if (std::optional<uint64_t> FragmentSize = DII->getFragmentSizeInBits())
+ if (std::optional<uint64_t> FragmentSize =
+ DII->getExpression()->getActiveBits(DII->getVariable()))
return TypeSize::isKnownGE(ValueSize, TypeSize::getFixed(*FragmentSize));
// We can't always calculate the size of the DI variable (e.g. if it is a
@@ -1629,7 +1630,8 @@ static bool valueCoversEntireFragment(Type *ValTy, DbgVariableIntrinsic *DII) {
static bool valueCoversEntireFragment(Type *ValTy, DbgVariableRecord *DVR) {
const DataLayout &DL = DVR->getModule()->getDataLayout();
TypeSize ValueSize = DL.getTypeAllocSizeInBits(ValTy);
- if (std::optional<uint64_t> FragmentSize = DVR->getFragmentSizeInBits())
+ if (std::optional<uint64_t> FragmentSize =
+ DVR->getExpression()->getActiveBits(DVR->getVariable()))
return TypeSize::isKnownGE(ValueSize, TypeSize::getFixed(*FragmentSize));
// We can't always calculate the size of the DI variable (e.g. if it is a
diff --git a/llvm/test/DebugInfo/Generic/sroa-extract-bits.ll b/llvm/test/DebugInfo/Generic/sroa-extract-bits.ll
new file mode 100644
index 0000000000000..a7e2a203b2e61
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/sroa-extract-bits.ll
@@ -0,0 +1,205 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt -passes='sroa<preserve-cfg>' %s -S | FileCheck %s
+; RUN: opt -passes='sroa<modify-cfg>' %s -S | FileCheck %s
+
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #0
+
+; The alloca is split into two fragments: variable x is in the first, variables y and z are in the second
+define i8 @test1(i32 %arg) {
+; CHECK-LABEL: define i8 @test1(
+; CHECK-SAME: i32 [[ARG:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG]] to i8
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]], metadata [[META2:![0-9]+]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg [[DBG7:![0-9]+]]
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG]], 8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i24
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i24 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META8:![0-9]+]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 8, 16)), !dbg [[DBG7]]
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i24 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META9:![0-9]+]], metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 8, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 16, 16)), !dbg !7
+ store i32 %arg, ptr %ptr, align 4
+ %ret = load i8, ptr %ptr, align 4
+ ret i8 %ret
+}
+
+; The alloca is split into three fragments corresponding to the variables x, y, z
+define i8 @test2(i32 %arg1, i8 %arg2) {
+; CHECK-LABEL: define i8 @test2(
+; CHECK-SAME: i32 [[ARG1:%.*]], i8 [[ARG2:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG1]] to i8
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]], metadata [[META2]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i16
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i16 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META9]], metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 16)), !dbg [[DBG7]]
+; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 24
+; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_21_0_EXTRACT_SHIFT]] to i8
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[PTR_SROA_21_0_EXTRACT_TRUNC]], metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[ARG2]], metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 8, 16)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 24, 8)), !dbg !7
+ store i32 %arg1, ptr %ptr, align 4
+ %gep = getelementptr i8, ptr %ptr, i32 3
+ store i8 %arg2, ptr %gep, align 1
+ %ret = load i8, ptr %ptr, align 4
+ ret i8 %ret
+}
+
+; The alloca is split into two fragments, with variable x being half in one and half in the other
+; FIXME: We currently generate no debug info for x in this case
+define i8 @test3(i32 %arg) {
+; CHECK-LABEL: define i8 @test3(
+; CHECK-SAME: i32 [[ARG:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG]] to i8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG]], 8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i24
+; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 16)), !dbg !7
+ store i32 %arg, ptr %ptr, align 4
+ %ret = load i8, ptr %ptr, align 4
+ ret i8 %ret
+}
+
+; The alloca is split into two fragments, with variable y being half in one and half in the other
+; FIXME: We currently generate no debug info for y in this case
+define i16 @test4(i32 %arg) {
+; CHECK-LABEL: define i16 @test4(
+; CHECK-SAME: i32 [[ARG:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG]] to i16
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i16 [[PTR_SROA_0_0_EXTRACT_TRUNC]], metadata [[META2]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG]], 16
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i16
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i16 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 8, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: ret i16 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 8, 16)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 24, 8)), !dbg !7
+ store i32 %arg, ptr %ptr, align 4
+ %ret = load i16, ptr %ptr, align 4
+ ret i16 %ret
+}
+
+; Struct where the first element is an ordinary char, the second is a bitfield of two elements, and the third is padding
+%struct.struct_t = type <{ i8, i16, i8 }>
+define i8 @test5(i32 %arg) {
+; CHECK-LABEL: define i8 @test5(
+; CHECK-SAME: i32 [[ARG:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG]] to i8
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]], metadata [[META11:![0-9]+]], metadata !DIExpression()), !dbg [[DBG7]]
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG]], 8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i24
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i24 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 8, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i24 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META9]], metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca %struct.struct_t, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !11, metadata !DIExpression()), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 8, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 16, 8)), !dbg !7
+ store i32 %arg, ptr %ptr, align 4
+ %ret = load i8, ptr %ptr, align 4
+ ret i8 %ret
+}
+
+; Sign mismatch between extract expression and debug variable type.
+define i8 @test6(i32 %arg1, i8 %arg2) {
+; CHECK-LABEL: define i8 @test6(
+; CHECK-SAME: i32 [[ARG1:%.*]], i8 [[ARG2:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG1]] to i8
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 undef, metadata [[META2]], metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i16
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i16 undef, metadata [[META9]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 16)), !dbg [[DBG7]]
+; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 24
+; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_21_0_EXTRACT_SHIFT]] to i8
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 undef, metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 undef, metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 8, 16)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 24, 8)), !dbg !7
+ store i32 %arg1, ptr %ptr, align 4
+ %gep = getelementptr i8, ptr %ptr, i32 3
+ store i8 %arg2, ptr %gep, align 1
+ %ret = load i8, ptr %ptr, align 4
+ ret i8 %ret
+}
+
+; Combine extract_bits and fragment in a way such that the bottom 8 bits of
+; the alloca are the top 8 bits of variable x and vice versa.
+; FIXME: We currently don't handle this and generate no debug info
+define i8 @test7(i32 %arg1, i8 %arg2) {
+; CHECK-LABEL: define i8 @test7(
+; CHECK-SAME: i32 [[ARG1:%.*]], i8 [[ARG2:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG1]] to i8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i16
+; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 24
+; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_21_0_EXTRACT_SHIFT]] to i8
+; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8, DW_OP_LLVM_fragment, 24, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 8, 16, DW_OP_LLVM_fragment, 8, 16)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 24, 8, DW_OP_LLVM_fragment, 0, 8)), !dbg !7
+ store i32 %arg1, ptr %ptr, align 4
+ %gep = getelementptr i8, ptr %ptr, i32 3
+ store i8 %arg2, ptr %gep, align 1
+ %ret = load i8, ptr %ptr, align 4
+ ret i8 %ret
+}
+
+!llvm.module.flags = !{!0, !1}
+!0 = !{i32 7, !"Dwarf Version", i32 5}
+!1 = !{i32 2, !"Debug Info Version", i32 3}
+!2 = !DILocalVariable(name: "x", scope: !3, type: !6)
+!3 = distinct !DISubprogram(name: "test", unit: !4)
+!4 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !5, emissionKind: FullDebug)
+!5 = !DIFile(filename: "dbg-bit-piece.cpp", directory: "")
+!6 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!7 = !DILocation(line: 0, column: 0, scope: !3)
+!8 = !DILocalVariable(name: "z", scope: !3, type: !6)
+!9 = !DILocalVariable(name: "y", scope: !3, type: !10)
+!10 = !DIBasicType(name: "signed int", size: 32, encoding: DW_ATE_signed)
+!11 = !DILocalVariable(name: "x", scope: !3, type: !12)
+!12 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
+
+;.
+; CHECK: [[META2]] = !DILocalVariable(name: "x", scope: [[META3:![0-9]+]], type: [[META6:![0-9]+]])
+; CHECK: [[META3]] = distinct !DISubprogram(name: "test", scope: null, spFlags: DISPFlagDefinition, unit: [[META4:![0-9]+]])
+; CHECK: [[META4]] = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: [[META5:![0-9]+]], isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+; CHECK: [[META5]] = !DIFile(filename: "dbg-bit-piece.cpp", directory: "")
+; CHECK: [[META6]] = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+; CHECK: [[DBG7]] = !DILocation(line: 0, scope: [[META3]])
+; CHECK: [[META8]] = !DILocalVariable(name: "z", scope: [[META3]], type: [[META6]])
+; CHECK: [[META9]] = !DILocalVariable(name: "y", scope: [[META3]], type: [[META10:![0-9]+]])
+; CHECK: [[META10]] = !DIBasicType(name: "signed int", size: 32, encoding: DW_ATE_signed)
+; CHECK: [[META11]] = !DIL...
[truncated]
|
@llvm/pr-subscribers-llvm-ir Author: John Brawn (john-brawn-arm) ChangesThis doesn't need any work to be done in SROA itself, but rather in functions that it uses. Specifically:
Patch is 20.16 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/94638.diff 4 Files Affected:
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index c15d64b842293..524945862e8d4 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -2910,6 +2910,12 @@ class DIExpression : public MDNode {
}
};
+ /// Return the number of bits that have an active value, i.e. those that
+ /// aren't known to be zero/sign (depending on the type of Var) and which
+ /// are within the size of this fragment (if it is one). If we can't deduce
+ /// anything from the expression this will return the size of Var.
+ std::optional<uint64_t> getActiveBits(DIVariable *Var);
+
/// Retrieve the details of this fragment expression.
static std::optional<FragmentInfo> getFragmentInfo(expr_op_iterator Start,
expr_op_iterator End);
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 2b45932093f0f..161a30dfb3828 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -1679,6 +1679,41 @@ DIExpression::getFragmentInfo(expr_op_iterator Start, expr_op_iterator End) {
return std::nullopt;
}
+std::optional<uint64_t> DIExpression::getActiveBits(DIVariable *Var) {
+ std::optional<uint64_t> InitialActiveBits = Var->getSizeInBits();
+ std::optional<uint64_t> ActiveBits = InitialActiveBits;
+ for (auto Op : expr_ops()) {
+ switch (Op.getOp()) {
+ default:
+ // We assume the worst case for anything we don't currently handle and
+ // revert to the initial active bits.
+ ActiveBits = InitialActiveBits;
+ break;
+ case dwarf::DW_OP_LLVM_extract_bits_zext:
+ case dwarf::DW_OP_LLVM_extract_bits_sext: {
+ // We can't handle an extract whose sign doesn't match that of the
+ // variable.
+ std::optional<DIBasicType::Signedness> VarSign = Var->getSignedness();
+ bool VarSigned = (VarSign == DIBasicType::Signedness::Signed);
+ bool OpSigned = (Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_sext);
+ if (!VarSign || VarSigned != OpSigned) {
+ ActiveBits = InitialActiveBits;
+ break;
+ }
+ [[fallthrough]];
+ }
+ case dwarf::DW_OP_LLVM_fragment:
+ // Extract or fragment narrows the active bits
+ if (ActiveBits)
+ ActiveBits = std::min(*ActiveBits, Op.getArg(1));
+ else
+ ActiveBits = Op.getArg(1);
+ break;
+ }
+ }
+ return ActiveBits;
+}
+
void DIExpression::appendOffset(SmallVectorImpl<uint64_t> &Ops,
int64_t Offset) {
if (Offset > 0) {
@@ -1931,6 +1966,8 @@ std::optional<DIExpression *> DIExpression::createFragmentExpression(
// Track whether it's safe to split the value at the top of the DWARF stack,
// assuming that it'll be used as an implicit location value.
bool CanSplitValue = true;
+ // Track whether we need to add a fragment expression to the end of Expr.
+ bool EmitFragment = true;
// Copy over the expression, but leave off any trailing DW_OP_LLVM_fragment.
if (Expr) {
for (auto Op : Expr->expr_ops()) {
@@ -1966,6 +2003,11 @@ std::optional<DIExpression *> DIExpression::createFragmentExpression(
return std::nullopt;
break;
case dwarf::DW_OP_LLVM_fragment: {
+ // If we've decided we don't need a fragment then give up if we see that
+ // there's already a fragment expression.
+ // FIXME: We could probably do better here
+ if (!EmitFragment)
+ return std::nullopt;
// Make the new offset point into the existing fragment.
uint64_t FragmentOffsetInBits = Op.getArg(0);
uint64_t FragmentSizeInBits = Op.getArg(1);
@@ -1975,15 +2017,38 @@ std::optional<DIExpression *> DIExpression::createFragmentExpression(
OffsetInBits += FragmentOffsetInBits;
continue;
}
+ case dwarf::DW_OP_LLVM_extract_bits_zext:
+ case dwarf::DW_OP_LLVM_extract_bits_sext: {
+ // If we're extracting bits from inside of the fragment that we're
+ // creating then we don't have a fragment after all, and just need to
+ // adjust the offset that we're extracting from.
+ uint64_t ExtractOffsetInBits = Op.getArg(0);
+ uint64_t ExtractSizeInBits = Op.getArg(1);
+ if (ExtractOffsetInBits >= OffsetInBits &&
+ ExtractOffsetInBits + ExtractSizeInBits <=
+ OffsetInBits + SizeInBits) {
+ Ops.push_back(Op.getOp());
+ Ops.push_back(ExtractOffsetInBits - OffsetInBits);
+ Ops.push_back(ExtractSizeInBits);
+ EmitFragment = false;
+ continue;
+ }
+ // If the extracted bits aren't fully contained within the fragment then
+ // give up.
+ // FIXME: We could probably do better here
+ return std::nullopt;
+ }
}
Op.appendToVector(Ops);
}
}
assert((!Expr->isImplicit() || CanSplitValue) && "Expr can't be split");
assert(Expr && "Unknown DIExpression");
- Ops.push_back(dwarf::DW_OP_LLVM_fragment);
- Ops.push_back(OffsetInBits);
- Ops.push_back(SizeInBits);
+ if (EmitFragment) {
+ Ops.push_back(dwarf::DW_OP_LLVM_fragment);
+ Ops.push_back(OffsetInBits);
+ Ops.push_back(SizeInBits);
+ }
return DIExpression::get(Expr->getContext(), Ops);
}
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp
index ce0f4c7668a40..bcddfce7897ca 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -1603,7 +1603,8 @@ static bool PhiHasDebugValue(DILocalVariable *DIVar,
static bool valueCoversEntireFragment(Type *ValTy, DbgVariableIntrinsic *DII) {
const DataLayout &DL = DII->getModule()->getDataLayout();
TypeSize ValueSize = DL.getTypeAllocSizeInBits(ValTy);
- if (std::optional<uint64_t> FragmentSize = DII->getFragmentSizeInBits())
+ if (std::optional<uint64_t> FragmentSize =
+ DII->getExpression()->getActiveBits(DII->getVariable()))
return TypeSize::isKnownGE(ValueSize, TypeSize::getFixed(*FragmentSize));
// We can't always calculate the size of the DI variable (e.g. if it is a
@@ -1629,7 +1630,8 @@ static bool valueCoversEntireFragment(Type *ValTy, DbgVariableIntrinsic *DII) {
static bool valueCoversEntireFragment(Type *ValTy, DbgVariableRecord *DVR) {
const DataLayout &DL = DVR->getModule()->getDataLayout();
TypeSize ValueSize = DL.getTypeAllocSizeInBits(ValTy);
- if (std::optional<uint64_t> FragmentSize = DVR->getFragmentSizeInBits())
+ if (std::optional<uint64_t> FragmentSize =
+ DVR->getExpression()->getActiveBits(DVR->getVariable()))
return TypeSize::isKnownGE(ValueSize, TypeSize::getFixed(*FragmentSize));
// We can't always calculate the size of the DI variable (e.g. if it is a
diff --git a/llvm/test/DebugInfo/Generic/sroa-extract-bits.ll b/llvm/test/DebugInfo/Generic/sroa-extract-bits.ll
new file mode 100644
index 0000000000000..a7e2a203b2e61
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/sroa-extract-bits.ll
@@ -0,0 +1,205 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt -passes='sroa<preserve-cfg>' %s -S | FileCheck %s
+; RUN: opt -passes='sroa<modify-cfg>' %s -S | FileCheck %s
+
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #0
+
+; The alloca is split into two fragments: variable x is in the first, variables y and z are in the second
+define i8 @test1(i32 %arg) {
+; CHECK-LABEL: define i8 @test1(
+; CHECK-SAME: i32 [[ARG:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG]] to i8
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]], metadata [[META2:![0-9]+]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg [[DBG7:![0-9]+]]
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG]], 8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i24
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i24 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META8:![0-9]+]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 8, 16)), !dbg [[DBG7]]
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i24 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META9:![0-9]+]], metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 8, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 16, 16)), !dbg !7
+ store i32 %arg, ptr %ptr, align 4
+ %ret = load i8, ptr %ptr, align 4
+ ret i8 %ret
+}
+
+; The alloca is split into three fragments corresponding to the variables x, y, z
+define i8 @test2(i32 %arg1, i8 %arg2) {
+; CHECK-LABEL: define i8 @test2(
+; CHECK-SAME: i32 [[ARG1:%.*]], i8 [[ARG2:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG1]] to i8
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]], metadata [[META2]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i16
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i16 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META9]], metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 16)), !dbg [[DBG7]]
+; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 24
+; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_21_0_EXTRACT_SHIFT]] to i8
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[PTR_SROA_21_0_EXTRACT_TRUNC]], metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[ARG2]], metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 8, 16)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 24, 8)), !dbg !7
+ store i32 %arg1, ptr %ptr, align 4
+ %gep = getelementptr i8, ptr %ptr, i32 3
+ store i8 %arg2, ptr %gep, align 1
+ %ret = load i8, ptr %ptr, align 4
+ ret i8 %ret
+}
+
+; The alloca is split into two fragments, with variable x being half in one and half in the other
+; FIXME: We currently generate no debug info for x in this case
+define i8 @test3(i32 %arg) {
+; CHECK-LABEL: define i8 @test3(
+; CHECK-SAME: i32 [[ARG:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG]] to i8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG]], 8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i24
+; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 16)), !dbg !7
+ store i32 %arg, ptr %ptr, align 4
+ %ret = load i8, ptr %ptr, align 4
+ ret i8 %ret
+}
+
+; The alloca is split into two fragments, with variable y being half in one and half in the other
+; FIXME: We currently generate no debug info for y in this case
+define i16 @test4(i32 %arg) {
+; CHECK-LABEL: define i16 @test4(
+; CHECK-SAME: i32 [[ARG:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG]] to i16
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i16 [[PTR_SROA_0_0_EXTRACT_TRUNC]], metadata [[META2]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG]], 16
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i16
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i16 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 8, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: ret i16 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 8, 16)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 24, 8)), !dbg !7
+ store i32 %arg, ptr %ptr, align 4
+ %ret = load i16, ptr %ptr, align 4
+ ret i16 %ret
+}
+
+; Struct where the first element is an ordinary char, the second is a bitfield of two elements, and the third is padding
+%struct.struct_t = type <{ i8, i16, i8 }>
+define i8 @test5(i32 %arg) {
+; CHECK-LABEL: define i8 @test5(
+; CHECK-SAME: i32 [[ARG:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG]] to i8
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]], metadata [[META11:![0-9]+]], metadata !DIExpression()), !dbg [[DBG7]]
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG]], 8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i24
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i24 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 8, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i24 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META9]], metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca %struct.struct_t, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !11, metadata !DIExpression()), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 8, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 16, 8)), !dbg !7
+ store i32 %arg, ptr %ptr, align 4
+ %ret = load i8, ptr %ptr, align 4
+ ret i8 %ret
+}
+
+; Sign mismatch between extract expression and debug variable type.
+define i8 @test6(i32 %arg1, i8 %arg2) {
+; CHECK-LABEL: define i8 @test6(
+; CHECK-SAME: i32 [[ARG1:%.*]], i8 [[ARG2:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG1]] to i8
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 undef, metadata [[META2]], metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i16
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i16 undef, metadata [[META9]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 16)), !dbg [[DBG7]]
+; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 24
+; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_21_0_EXTRACT_SHIFT]] to i8
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 undef, metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 undef, metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 8, 16)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 24, 8)), !dbg !7
+ store i32 %arg1, ptr %ptr, align 4
+ %gep = getelementptr i8, ptr %ptr, i32 3
+ store i8 %arg2, ptr %gep, align 1
+ %ret = load i8, ptr %ptr, align 4
+ ret i8 %ret
+}
+
+; Combine extract_bits and fragment in a way such that the bottom 8 bits of
+; the alloca are the top 8 bits of variable x and vice versa.
+; FIXME: We currently don't handle this and generate no debug info
+define i8 @test7(i32 %arg1, i8 %arg2) {
+; CHECK-LABEL: define i8 @test7(
+; CHECK-SAME: i32 [[ARG1:%.*]], i8 [[ARG2:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG1]] to i8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i16
+; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 24
+; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_21_0_EXTRACT_SHIFT]] to i8
+; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8, DW_OP_LLVM_fragment, 24, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 8, 16, DW_OP_LLVM_fragment, 8, 16)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 24, 8, DW_OP_LLVM_fragment, 0, 8)), !dbg !7
+ store i32 %arg1, ptr %ptr, align 4
+ %gep = getelementptr i8, ptr %ptr, i32 3
+ store i8 %arg2, ptr %gep, align 1
+ %ret = load i8, ptr %ptr, align 4
+ ret i8 %ret
+}
+
+!llvm.module.flags = !{!0, !1}
+!0 = !{i32 7, !"Dwarf Version", i32 5}
+!1 = !{i32 2, !"Debug Info Version", i32 3}
+!2 = !DILocalVariable(name: "x", scope: !3, type: !6)
+!3 = distinct !DISubprogram(name: "test", unit: !4)
+!4 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !5, emissionKind: FullDebug)
+!5 = !DIFile(filename: "dbg-bit-piece.cpp", directory: "")
+!6 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!7 = !DILocation(line: 0, column: 0, scope: !3)
+!8 = !DILocalVariable(name: "z", scope: !3, type: !6)
+!9 = !DILocalVariable(name: "y", scope: !3, type: !10)
+!10 = !DIBasicType(name: "signed int", size: 32, encoding: DW_ATE_signed)
+!11 = !DILocalVariable(name: "x", scope: !3, type: !12)
+!12 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
+
+;.
+; CHECK: [[META2]] = !DILocalVariable(name: "x", scope: [[META3:![0-9]+]], type: [[META6:![0-9]+]])
+; CHECK: [[META3]] = distinct !DISubprogram(name: "test", scope: null, spFlags: DISPFlagDefinition, unit: [[META4:![0-9]+]])
+; CHECK: [[META4]] = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: [[META5:![0-9]+]], isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+; CHECK: [[META5]] = !DIFile(filename: "dbg-bit-piece.cpp", directory: "")
+; CHECK: [[META6]] = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+; CHECK: [[DBG7]] = !DILocation(line: 0, scope: [[META3]])
+; CHECK: [[META8]] = !DILocalVariable(name: "z", scope: [[META3]], type: [[META6]])
+; CHECK: [[META9]] = !DILocalVariable(name: "y", scope: [[META3]], type: [[META10:![0-9]+]])
+; CHECK: [[META10]] = !DIBasicType(name: "signed int", size: 32, encoding: DW_ATE_signed)
+; CHECK: [[META11]] = !DIL...
[truncated]
|
@llvm/pr-subscribers-debuginfo Author: John Brawn (john-brawn-arm) ChangesThis doesn't need any work to be done in SROA itself, but rather in functions that it uses. Specifically:
Patch is 20.16 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/94638.diff 4 Files Affected:
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index c15d64b842293..524945862e8d4 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -2910,6 +2910,12 @@ class DIExpression : public MDNode {
}
};
+ /// Return the number of bits that have an active value, i.e. those that
+ /// aren't known to be zero/sign (depending on the type of Var) and which
+ /// are within the size of this fragment (if it is one). If we can't deduce
+ /// anything from the expression this will return the size of Var.
+ std::optional<uint64_t> getActiveBits(DIVariable *Var);
+
/// Retrieve the details of this fragment expression.
static std::optional<FragmentInfo> getFragmentInfo(expr_op_iterator Start,
expr_op_iterator End);
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 2b45932093f0f..161a30dfb3828 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -1679,6 +1679,41 @@ DIExpression::getFragmentInfo(expr_op_iterator Start, expr_op_iterator End) {
return std::nullopt;
}
+std::optional<uint64_t> DIExpression::getActiveBits(DIVariable *Var) {
+ std::optional<uint64_t> InitialActiveBits = Var->getSizeInBits();
+ std::optional<uint64_t> ActiveBits = InitialActiveBits;
+ for (auto Op : expr_ops()) {
+ switch (Op.getOp()) {
+ default:
+ // We assume the worst case for anything we don't currently handle and
+ // revert to the initial active bits.
+ ActiveBits = InitialActiveBits;
+ break;
+ case dwarf::DW_OP_LLVM_extract_bits_zext:
+ case dwarf::DW_OP_LLVM_extract_bits_sext: {
+ // We can't handle an extract whose sign doesn't match that of the
+ // variable.
+ std::optional<DIBasicType::Signedness> VarSign = Var->getSignedness();
+ bool VarSigned = (VarSign == DIBasicType::Signedness::Signed);
+ bool OpSigned = (Op.getOp() == dwarf::DW_OP_LLVM_extract_bits_sext);
+ if (!VarSign || VarSigned != OpSigned) {
+ ActiveBits = InitialActiveBits;
+ break;
+ }
+ [[fallthrough]];
+ }
+ case dwarf::DW_OP_LLVM_fragment:
+ // Extract or fragment narrows the active bits
+ if (ActiveBits)
+ ActiveBits = std::min(*ActiveBits, Op.getArg(1));
+ else
+ ActiveBits = Op.getArg(1);
+ break;
+ }
+ }
+ return ActiveBits;
+}
+
void DIExpression::appendOffset(SmallVectorImpl<uint64_t> &Ops,
int64_t Offset) {
if (Offset > 0) {
@@ -1931,6 +1966,8 @@ std::optional<DIExpression *> DIExpression::createFragmentExpression(
// Track whether it's safe to split the value at the top of the DWARF stack,
// assuming that it'll be used as an implicit location value.
bool CanSplitValue = true;
+ // Track whether we need to add a fragment expression to the end of Expr.
+ bool EmitFragment = true;
// Copy over the expression, but leave off any trailing DW_OP_LLVM_fragment.
if (Expr) {
for (auto Op : Expr->expr_ops()) {
@@ -1966,6 +2003,11 @@ std::optional<DIExpression *> DIExpression::createFragmentExpression(
return std::nullopt;
break;
case dwarf::DW_OP_LLVM_fragment: {
+ // If we've decided we don't need a fragment then give up if we see that
+ // there's already a fragment expression.
+ // FIXME: We could probably do better here
+ if (!EmitFragment)
+ return std::nullopt;
// Make the new offset point into the existing fragment.
uint64_t FragmentOffsetInBits = Op.getArg(0);
uint64_t FragmentSizeInBits = Op.getArg(1);
@@ -1975,15 +2017,38 @@ std::optional<DIExpression *> DIExpression::createFragmentExpression(
OffsetInBits += FragmentOffsetInBits;
continue;
}
+ case dwarf::DW_OP_LLVM_extract_bits_zext:
+ case dwarf::DW_OP_LLVM_extract_bits_sext: {
+ // If we're extracting bits from inside of the fragment that we're
+ // creating then we don't have a fragment after all, and just need to
+ // adjust the offset that we're extracting from.
+ uint64_t ExtractOffsetInBits = Op.getArg(0);
+ uint64_t ExtractSizeInBits = Op.getArg(1);
+ if (ExtractOffsetInBits >= OffsetInBits &&
+ ExtractOffsetInBits + ExtractSizeInBits <=
+ OffsetInBits + SizeInBits) {
+ Ops.push_back(Op.getOp());
+ Ops.push_back(ExtractOffsetInBits - OffsetInBits);
+ Ops.push_back(ExtractSizeInBits);
+ EmitFragment = false;
+ continue;
+ }
+ // If the extracted bits aren't fully contained within the fragment then
+ // give up.
+ // FIXME: We could probably do better here
+ return std::nullopt;
+ }
}
Op.appendToVector(Ops);
}
}
assert((!Expr->isImplicit() || CanSplitValue) && "Expr can't be split");
assert(Expr && "Unknown DIExpression");
- Ops.push_back(dwarf::DW_OP_LLVM_fragment);
- Ops.push_back(OffsetInBits);
- Ops.push_back(SizeInBits);
+ if (EmitFragment) {
+ Ops.push_back(dwarf::DW_OP_LLVM_fragment);
+ Ops.push_back(OffsetInBits);
+ Ops.push_back(SizeInBits);
+ }
return DIExpression::get(Expr->getContext(), Ops);
}
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp
index ce0f4c7668a40..bcddfce7897ca 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -1603,7 +1603,8 @@ static bool PhiHasDebugValue(DILocalVariable *DIVar,
static bool valueCoversEntireFragment(Type *ValTy, DbgVariableIntrinsic *DII) {
const DataLayout &DL = DII->getModule()->getDataLayout();
TypeSize ValueSize = DL.getTypeAllocSizeInBits(ValTy);
- if (std::optional<uint64_t> FragmentSize = DII->getFragmentSizeInBits())
+ if (std::optional<uint64_t> FragmentSize =
+ DII->getExpression()->getActiveBits(DII->getVariable()))
return TypeSize::isKnownGE(ValueSize, TypeSize::getFixed(*FragmentSize));
// We can't always calculate the size of the DI variable (e.g. if it is a
@@ -1629,7 +1630,8 @@ static bool valueCoversEntireFragment(Type *ValTy, DbgVariableIntrinsic *DII) {
static bool valueCoversEntireFragment(Type *ValTy, DbgVariableRecord *DVR) {
const DataLayout &DL = DVR->getModule()->getDataLayout();
TypeSize ValueSize = DL.getTypeAllocSizeInBits(ValTy);
- if (std::optional<uint64_t> FragmentSize = DVR->getFragmentSizeInBits())
+ if (std::optional<uint64_t> FragmentSize =
+ DVR->getExpression()->getActiveBits(DVR->getVariable()))
return TypeSize::isKnownGE(ValueSize, TypeSize::getFixed(*FragmentSize));
// We can't always calculate the size of the DI variable (e.g. if it is a
diff --git a/llvm/test/DebugInfo/Generic/sroa-extract-bits.ll b/llvm/test/DebugInfo/Generic/sroa-extract-bits.ll
new file mode 100644
index 0000000000000..a7e2a203b2e61
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/sroa-extract-bits.ll
@@ -0,0 +1,205 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt -passes='sroa<preserve-cfg>' %s -S | FileCheck %s
+; RUN: opt -passes='sroa<modify-cfg>' %s -S | FileCheck %s
+
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #0
+
+; The alloca is split into two fragments: variable x is in the first, variables y and z are in the second
+define i8 @test1(i32 %arg) {
+; CHECK-LABEL: define i8 @test1(
+; CHECK-SAME: i32 [[ARG:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG]] to i8
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]], metadata [[META2:![0-9]+]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg [[DBG7:![0-9]+]]
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG]], 8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i24
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i24 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META8:![0-9]+]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 8, 16)), !dbg [[DBG7]]
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i24 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META9:![0-9]+]], metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 8, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 16, 16)), !dbg !7
+ store i32 %arg, ptr %ptr, align 4
+ %ret = load i8, ptr %ptr, align 4
+ ret i8 %ret
+}
+
+; The alloca is split into three fragments corresponding to the variables x, y, z
+define i8 @test2(i32 %arg1, i8 %arg2) {
+; CHECK-LABEL: define i8 @test2(
+; CHECK-SAME: i32 [[ARG1:%.*]], i8 [[ARG2:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG1]] to i8
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]], metadata [[META2]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i16
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i16 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META9]], metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 16)), !dbg [[DBG7]]
+; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 24
+; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_21_0_EXTRACT_SHIFT]] to i8
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[PTR_SROA_21_0_EXTRACT_TRUNC]], metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[ARG2]], metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 8, 16)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 24, 8)), !dbg !7
+ store i32 %arg1, ptr %ptr, align 4
+ %gep = getelementptr i8, ptr %ptr, i32 3
+ store i8 %arg2, ptr %gep, align 1
+ %ret = load i8, ptr %ptr, align 4
+ ret i8 %ret
+}
+
+; The alloca is split into two fragments, with variable x being half in one and half in the other
+; FIXME: We currently generate no debug info for x in this case
+define i8 @test3(i32 %arg) {
+; CHECK-LABEL: define i8 @test3(
+; CHECK-SAME: i32 [[ARG:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG]] to i8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG]], 8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i24
+; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 16)), !dbg !7
+ store i32 %arg, ptr %ptr, align 4
+ %ret = load i8, ptr %ptr, align 4
+ ret i8 %ret
+}
+
+; The alloca is split into two fragments, with variable y being half in one and half in the other
+; FIXME: We currently generate no debug info for y in this case
+define i16 @test4(i32 %arg) {
+; CHECK-LABEL: define i16 @test4(
+; CHECK-SAME: i32 [[ARG:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG]] to i16
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i16 [[PTR_SROA_0_0_EXTRACT_TRUNC]], metadata [[META2]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG]], 16
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i16
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i16 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 8, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: ret i16 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 8, 16)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 24, 8)), !dbg !7
+ store i32 %arg, ptr %ptr, align 4
+ %ret = load i16, ptr %ptr, align 4
+ ret i16 %ret
+}
+
+; Struct where the first element is an ordinary char, the second is a bitfield of two elements, and the third is padding
+%struct.struct_t = type <{ i8, i16, i8 }>
+define i8 @test5(i32 %arg) {
+; CHECK-LABEL: define i8 @test5(
+; CHECK-SAME: i32 [[ARG:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG]] to i8
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]], metadata [[META11:![0-9]+]], metadata !DIExpression()), !dbg [[DBG7]]
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG]], 8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i24
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i24 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 8, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i24 [[PTR_SROA_2_0_EXTRACT_TRUNC]], metadata [[META9]], metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca %struct.struct_t, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !11, metadata !DIExpression()), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 8, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 16, 8)), !dbg !7
+ store i32 %arg, ptr %ptr, align 4
+ %ret = load i8, ptr %ptr, align 4
+ ret i8 %ret
+}
+
+; Sign mismatch between extract expression and debug variable type.
+define i8 @test6(i32 %arg1, i8 %arg2) {
+; CHECK-LABEL: define i8 @test6(
+; CHECK-SAME: i32 [[ARG1:%.*]], i8 [[ARG2:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG1]] to i8
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 undef, metadata [[META2]], metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i16
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i16 undef, metadata [[META9]], metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 16)), !dbg [[DBG7]]
+; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 24
+; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_21_0_EXTRACT_SHIFT]] to i8
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 undef, metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: tail call void @llvm.dbg.value(metadata i8 undef, metadata [[META8]], metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 8)), !dbg [[DBG7]]
+; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 0, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !9, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 8, 16)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !8, metadata !DIExpression(DW_OP_LLVM_extract_bits_sext, 24, 8)), !dbg !7
+ store i32 %arg1, ptr %ptr, align 4
+ %gep = getelementptr i8, ptr %ptr, i32 3
+ store i8 %arg2, ptr %gep, align 1
+ %ret = load i8, ptr %ptr, align 4
+ ret i8 %ret
+}
+
+; Combine extract_bits and fragment in a way such that the bottom 8 bits of
+; the alloca are the top 8 bits of variable x and vice versa.
+; FIXME: We currently don't handle this and generate no debug info
+define i8 @test7(i32 %arg1, i8 %arg2) {
+; CHECK-LABEL: define i8 @test7(
+; CHECK-SAME: i32 [[ARG1:%.*]], i8 [[ARG2:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[PTR_SROA_0_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[ARG1]] to i8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 8
+; CHECK-NEXT: [[PTR_SROA_2_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_2_0_EXTRACT_SHIFT]] to i16
+; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_SHIFT:%.*]] = lshr i32 [[ARG1]], 24
+; CHECK-NEXT: [[PTR_SROA_21_0_EXTRACT_TRUNC:%.*]] = trunc i32 [[PTR_SROA_21_0_EXTRACT_SHIFT]] to i8
+; CHECK-NEXT: ret i8 [[PTR_SROA_0_0_EXTRACT_TRUNC]]
+;
+entry:
+ %ptr = alloca i32, align 4
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 0, 8, DW_OP_LLVM_fragment, 24, 8)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 8, 16, DW_OP_LLVM_fragment, 8, 16)), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr %ptr, metadata !2, metadata !DIExpression(DW_OP_LLVM_extract_bits_zext, 24, 8, DW_OP_LLVM_fragment, 0, 8)), !dbg !7
+ store i32 %arg1, ptr %ptr, align 4
+ %gep = getelementptr i8, ptr %ptr, i32 3
+ store i8 %arg2, ptr %gep, align 1
+ %ret = load i8, ptr %ptr, align 4
+ ret i8 %ret
+}
+
+!llvm.module.flags = !{!0, !1}
+!0 = !{i32 7, !"Dwarf Version", i32 5}
+!1 = !{i32 2, !"Debug Info Version", i32 3}
+!2 = !DILocalVariable(name: "x", scope: !3, type: !6)
+!3 = distinct !DISubprogram(name: "test", unit: !4)
+!4 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !5, emissionKind: FullDebug)
+!5 = !DIFile(filename: "dbg-bit-piece.cpp", directory: "")
+!6 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!7 = !DILocation(line: 0, column: 0, scope: !3)
+!8 = !DILocalVariable(name: "z", scope: !3, type: !6)
+!9 = !DILocalVariable(name: "y", scope: !3, type: !10)
+!10 = !DIBasicType(name: "signed int", size: 32, encoding: DW_ATE_signed)
+!11 = !DILocalVariable(name: "x", scope: !3, type: !12)
+!12 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
+
+;.
+; CHECK: [[META2]] = !DILocalVariable(name: "x", scope: [[META3:![0-9]+]], type: [[META6:![0-9]+]])
+; CHECK: [[META3]] = distinct !DISubprogram(name: "test", scope: null, spFlags: DISPFlagDefinition, unit: [[META4:![0-9]+]])
+; CHECK: [[META4]] = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: [[META5:![0-9]+]], isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+; CHECK: [[META5]] = !DIFile(filename: "dbg-bit-piece.cpp", directory: "")
+; CHECK: [[META6]] = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+; CHECK: [[DBG7]] = !DILocation(line: 0, scope: [[META3]])
+; CHECK: [[META8]] = !DILocalVariable(name: "z", scope: [[META3]], type: [[META6]])
+; CHECK: [[META9]] = !DILocalVariable(name: "y", scope: [[META3]], type: [[META10:![0-9]+]])
+; CHECK: [[META10]] = !DIBasicType(name: "signed int", size: 32, encoding: DW_ATE_signed)
+; CHECK: [[META11]] = !DIL...
[truncated]
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this looks reasonable, I would potentially consider adding some unit tests for appendOffset() mostly for documentation purposes of the expected behavior.
This breaks tests: http://45.33.8.238/linux/140753/step_11.txt |
HI, |
It looks like the failures are because of #91724 which went in between this being approved and merged. I think I just need to run update_test_checks. |
Should be fixed by #95774 |
This doesn't need any work to be done in SROA itself, but rather in functions that it uses. Specifically: