Skip to content

[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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions llvm/include/llvm/IR/DebugInfoMetadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
71 changes: 68 additions & 3 deletions llvm/lib/IR/DebugInfoMetadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}

Expand Down
6 changes: 4 additions & 2 deletions llvm/lib/Transforms/Utils/Local.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
205 changes: 205 additions & 0 deletions llvm/test/DebugInfo/Generic/sroa-extract-bits.ll
Original file line number Diff line number Diff line change
@@ -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]] = !DILocalVariable(name: "x", scope: [[META3]], type: [[META12:![0-9]+]])
; CHECK: [[META12]] = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
;.
Loading