From: Reid Kleckner Date: Thu, 15 Sep 2016 22:05:08 +0000 (+0000) Subject: [codeview] Optimize the size of defranges with gaps X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8f94ffd054b62e5d65fe6bada5388ae2172ee1d8;p=llvm [codeview] Optimize the size of defranges with gaps For small, discontiguous local variable regions, CodeView can use a single defrange record with a gap, rather than having two defrange records. I expect that this optimization will only have a minor impact on debug info size. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@281664 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/MC/MCCodeView.cpp b/lib/MC/MCCodeView.cpp index acea1ecb2b4..ec5dc20a13e 100644 --- a/lib/MC/MCCodeView.cpp +++ b/lib/MC/MCCodeView.cpp @@ -453,16 +453,41 @@ void CodeViewContext::encodeDefRange(MCAsmLayout &Layout, Fixups.clear(); raw_svector_ostream OS(Contents); - // Write down each range where the variable is defined. + // Compute all the sizes up front. + SmallVector, 4> GapAndRangeSizes; + const MCSymbol *LastLabel = nullptr; for (std::pair Range : Frag.getRanges()) { + unsigned GapSize = + LastLabel ? computeLabelDiff(Layout, LastLabel, Range.first) : 0; unsigned RangeSize = computeLabelDiff(Layout, Range.first, Range.second); + GapAndRangeSizes.push_back({GapSize, RangeSize}); + LastLabel = Range.second; + } + + // Write down each range where the variable is defined. + for (size_t I = 0, E = Frag.getRanges().size(); I != E;) { + // If the range size of multiple consecutive ranges is under the max, + // combine the ranges and emit some gaps. + const MCSymbol *RangeBegin = Frag.getRanges()[I].first; + unsigned RangeSize = GapAndRangeSizes[I].second; + size_t J = I + 1; + for (; J != E; ++J) { + unsigned GapAndRangeSize = GapAndRangeSizes[J].first + GapAndRangeSizes[J].second; + if (RangeSize + GapAndRangeSize > MaxDefRange) + break; + RangeSize += GapAndRangeSize; + } + unsigned NumGaps = J - I - 1; + + support::endian::Writer LEWriter(OS); + unsigned Bias = 0; // We must split the range into chunks of MaxDefRange, this is a fundamental // limitation of the file format. do { uint16_t Chunk = std::min((uint32_t)MaxDefRange, RangeSize); - const MCSymbolRefExpr *SRE = MCSymbolRefExpr::create(Range.first, Ctx); + const MCSymbolRefExpr *SRE = MCSymbolRefExpr::create(RangeBegin, Ctx); const MCBinaryExpr *BE = MCBinaryExpr::createAdd(SRE, MCConstantExpr::create(Bias, Ctx), Ctx); MCValue Res; @@ -473,8 +498,8 @@ void CodeViewContext::encodeDefRange(MCAsmLayout &Layout, StringRef FixedSizePortion = Frag.getFixedSizePortion(); // Our record is a fixed sized prefix and a LocalVariableAddrRange that we // are artificially constructing. - size_t RecordSize = - FixedSizePortion.size() + sizeof(LocalVariableAddrRange); + size_t RecordSize = FixedSizePortion.size() + + sizeof(LocalVariableAddrRange) + 4 * NumGaps; // Write out the recrod size. support::endian::Writer(OS).write(RecordSize); // Write out the fixed size prefix. @@ -487,12 +512,25 @@ void CodeViewContext::encodeDefRange(MCAsmLayout &Layout, Fixups.push_back(MCFixup::create(Contents.size(), BE, FK_SecRel_2)); Contents.resize(Contents.size() + 2); // Fixup for section index. // Write down the range's extent. - support::endian::Writer(OS).write(Chunk); + LEWriter.write(Chunk); // Move on to the next range. Bias += Chunk; RangeSize -= Chunk; } while (RangeSize > 0); + + // Emit the gaps afterwards. + assert((NumGaps == 0 || Bias < MaxDefRange) && + "large ranges should not have gaps"); + unsigned GapStartOffset = GapAndRangeSizes[I].second; + for (++I; I != J; ++I) { + unsigned GapSize, RangeSize; + assert(I < GapAndRangeSizes.size()); + std::tie(GapSize, RangeSize) = GapAndRangeSizes[I]; + LEWriter.write(GapStartOffset); + LEWriter.write(RangeSize); + GapStartOffset += GapSize + RangeSize; + } } } diff --git a/test/DebugInfo/COFF/local-variable-gap.ll b/test/DebugInfo/COFF/local-variable-gap.ll new file mode 100644 index 00000000000..a2d05eaa03e --- /dev/null +++ b/test/DebugInfo/COFF/local-variable-gap.ll @@ -0,0 +1,174 @@ +; RUN: llc -mtriple=x86_64-windows-msvc < %s | FileCheck %s --check-prefix=ASM +; RUN: llc -mtriple=x86_64-windows-msvc < %s -filetype=obj | llvm-readobj -codeview - | FileCheck %s --check-prefix=OBJ + +; This test attempts to exercise gaps in local variables. The local variable 'p' +; will end up in some CSR (ESI), which will be used in both the BB scheduled +; discontiguously out of line and the normal return BB. The best way to encode +; this is to use a LocalVariableAddrGap. If the gap is too large, multiple +; ranges should be emitted. + +; Source to regenerate: +; int barrier(); +; int vardef(); +; void use(int); +; void __declspec(noreturn) call_noreturn(int); +; int f() { +; if (barrier()) { +; int p = vardef(); +; if (barrier()) // Unlikely, will be placed after return +; call_noreturn(p); +; use(p); +; } else { +; barrier(); +; } +; return 0; +; } + +; ASM: f: # @f +; ASM: pushq %rsi +; ASM: subq $32, %rsp +; ASM: callq barrier +; ASM: testl %eax, %eax +; ASM: je .LBB0_3 +; ASM: callq vardef +; ASM: movl %eax, %esi +; ASM: [[p_b1:\.Ltmp[0-9]+]]: +; ASM: #DEBUG_VALUE: p <- %ESI +; ASM: callq barrier +; ASM: movl %esi, %ecx +; ASM: testl %eax, %eax +; ASM: jne .LBB0_5 +; ASM: # BB#2: # %if.end +; ASM: #DEBUG_VALUE: p <- %ESI +; ASM: callq use +; ASM: jmp .LBB0_4 +; ASM: [[p_e1:\.Ltmp[0-9]+]]: +; ASM: .LBB0_3: # %if.else +; ASM: callq barrier +; ASM: .LBB0_4: # %if.end6 +; ASM: xorl %eax, %eax +; ASM: addq $32, %rsp +; ASM: popq %rsi +; ASM: retq +; ASM: .LBB0_5: # %if.then4 +; ASM: [[p_b2:\.Ltmp[0-9]+]]: +; ASM: #DEBUG_VALUE: p <- %ESI +; ASM: callq call_noreturn +; ASM: ud2 +; ASM: .Lfunc_end0: + +; ASM: .short {{.*}} # Record length +; ASM: .short 4414 # Record kind: S_LOCAL +; ASM: .long 116 # TypeIndex +; ASM: .short 0 # Flags +; ASM: .asciz "p" +; ASM: .cv_def_range [[p_b1]] [[p_e1]] [[p_b2]] .Lfunc_end0, "A\021\027\000\000\000" +; ASM: .short 2 # Record length +; ASM: .short 4431 # Record kind: S_PROC_ID_END + +; OBJ: Local { +; OBJ: Type: int (0x74) +; OBJ: VarName: p +; OBJ: } +; OBJ-NOT: Local { +; OBJ: DefRangeRegister { +; OBJ-NEXT: Register: 23 +; OBJ-NEXT: MayHaveNoName: 0 +; OBJ-NEXT: LocalVariableAddrRange { +; OBJ-NEXT: OffsetStart: .text+0x{{.*}} +; OBJ-NEXT: ISectStart: 0x0 +; OBJ-NEXT: Range: 0x{{.*}} +; OBJ-NEXT: } +; OBJ-NEXT: LocalVariableAddrGap [ +; OBJ-NEXT: GapStartOffset: 0x{{.*}} +; OBJ-NEXT: Range: 0x{{.*}} +; OBJ-NEXT: ] +; OBJ-NEXT: } + +; ModuleID = 't.cpp' +source_filename = "t.cpp" +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc19.0.24210" + +; Function Attrs: nounwind uwtable +define i32 @f() local_unnamed_addr #0 !dbg !7 { +entry: + %call = tail call i32 bitcast (i32 (...)* @barrier to i32 ()*)() #4, !dbg !15 + %tobool = icmp eq i32 %call, 0, !dbg !15 + br i1 %tobool, label %if.else, label %if.then, !dbg !16 + +if.then: ; preds = %entry + %call1 = tail call i32 bitcast (i32 (...)* @vardef to i32 ()*)() #4, !dbg !17 + tail call void @llvm.dbg.value(metadata i32 %call1, i64 0, metadata !12, metadata !18), !dbg !19 + %call2 = tail call i32 bitcast (i32 (...)* @barrier to i32 ()*)() #4, !dbg !20 + %tobool3 = icmp eq i32 %call2, 0, !dbg !20 + br i1 %tobool3, label %if.end, label %if.then4, !dbg !22 + +if.then4: ; preds = %if.then + tail call void @call_noreturn(i32 %call1) #5, !dbg !23 + unreachable, !dbg !23 + +if.end: ; preds = %if.then + tail call void @use(i32 %call1) #4, !dbg !24 + br label %if.end6, !dbg !25 + +if.else: ; preds = %entry + %call5 = tail call i32 bitcast (i32 (...)* @barrier to i32 ()*)() #4, !dbg !26 + br label %if.end6 + +if.end6: ; preds = %if.else, %if.end + ret i32 0, !dbg !28 +} + +declare i32 @barrier(...) local_unnamed_addr #1 + +declare i32 @vardef(...) local_unnamed_addr #1 + +; Function Attrs: noreturn +declare void @call_noreturn(i32) local_unnamed_addr #2 + +declare void @use(i32) local_unnamed_addr #1 + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.value(metadata, i64, metadata, metadata) #3 + +attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { noreturn "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { nounwind readnone } +attributes #4 = { nounwind } +attributes #5 = { noreturn nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 4.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "t.cpp", directory: "C:\5Csrc\5Cllvm\5Cbuild") +!2 = !{} +!3 = !{i32 2, !"CodeView", i32 1} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"PIC Level", i32 2} +!6 = !{!"clang version 4.0.0 "} +!7 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 19, type: !8, isLocal: false, isDefinition: true, scopeLine: 19, isOptimized: true, unit: !0, variables: !11) +!8 = !DISubroutineType(types: !9) +!9 = !{!10} +!10 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!11 = !{!12} +!12 = !DILocalVariable(name: "p", scope: !13, file: !1, line: 21, type: !10) +!13 = distinct !DILexicalBlock(scope: !14, file: !1, line: 20, column: 18) +!14 = distinct !DILexicalBlock(scope: !7, file: !1, line: 20, column: 7) +!15 = !DILocation(line: 20, column: 7, scope: !14) +!16 = !DILocation(line: 20, column: 7, scope: !7) +!17 = !DILocation(line: 21, column: 13, scope: !13) +!18 = !DIExpression() +!19 = !DILocation(line: 21, column: 9, scope: !13) +!20 = !DILocation(line: 22, column: 9, scope: !21) +!21 = distinct !DILexicalBlock(scope: !13, file: !1, line: 22, column: 9) +!22 = !DILocation(line: 22, column: 9, scope: !13) +!23 = !DILocation(line: 23, column: 7, scope: !21) +!24 = !DILocation(line: 24, column: 5, scope: !13) +!25 = !DILocation(line: 25, column: 3, scope: !13) +!26 = !DILocation(line: 26, column: 5, scope: !27) +!27 = distinct !DILexicalBlock(scope: !14, file: !1, line: 25, column: 10) +!28 = !DILocation(line: 28, column: 3, scope: !7) diff --git a/test/MC/COFF/cv-def-range-gap.s b/test/MC/COFF/cv-def-range-gap.s new file mode 100644 index 00000000000..243e777d92e --- /dev/null +++ b/test/MC/COFF/cv-def-range-gap.s @@ -0,0 +1,101 @@ +# RUN: llvm-mc -triple=x86_64-pc-win32 -filetype=obj < %s | llvm-readobj -codeview | FileCheck %s + +# This tries to test defrange gap edge cases. + +# CHECK: Local { +# CHECK: Type: int (0x74) +# CHECK: VarName: p +# CHECK: } +# CHECK-NOT: Local { +# CHECK: DefRangeRegister { +# CHECK-NEXT: Register: 23 +# CHECK-NEXT: MayHaveNoName: 0 +# CHECK-NEXT: LocalVariableAddrRange { +# CHECK-NEXT: OffsetStart: .text+0x5 +# CHECK-NEXT: ISectStart: 0x0 +# CHECK-NEXT: Range: 0x5 +# CHECK-NEXT: } +# CHECK-NEXT: LocalVariableAddrGap [ +# CHECK-NEXT: GapStartOffset: 0x3 +# CHECK-NEXT: Range: 0x1 +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: DefRangeRegister { +# CHECK-NEXT: Register: 23 +# CHECK-NEXT: MayHaveNoName: 0 +# CHECK-NEXT: LocalVariableAddrRange { +# CHECK-NEXT: OffsetStart: .text+0x10015 +# CHECK-NEXT: ISectStart: 0x0 +# CHECK-NEXT: Range: 0x6 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: DefRangeRegister { +# CHECK-NEXT: Register: 23 +# CHECK-NEXT: MayHaveNoName: 0 +# CHECK-NEXT: LocalVariableAddrRange { +# CHECK-NEXT: OffsetStart: .text+0x2001B +# CHECK-NEXT: ISectStart: 0x0 +# CHECK-NEXT: Range: 0x1 +# CHECK-NEXT: } +# CHECK-NEXT: } + + .text +f: # @f + mov $42, %esi +.Lbegin0: + nop + jmp .Lbegin1 +.Lend0: + nop +.Lbegin1: + nop +.Lend1: + .p2align 4 + .fill 0x10000, 1, 0x90 + + mov $42, %esi +.Lbegin2: + nop + jmp .Lbegin3 +.Lend2: + .fill 0x10000, 1, 0x90 +.Lbegin3: + nop +.Lend3: + ret +.Lfunc_end0: + + .section .debug$S,"dr" + .p2align 2 + .long 4 # Debug section magic + .long 241 # Symbol subsection for f + .long .Ltmp15-.Ltmp14 # Subsection size +.Ltmp14: + .short .Ltmp17-.Ltmp16 # Record length +.Ltmp16: + .short 4423 # Record kind: S_GPROC32_ID + .long 0 # PtrParent + .long 0 # PtrEnd + .long 0 # PtrNext + .long .Lfunc_end0-f # Code size + .long 0 # Offset after prologue + .long 0 # Offset before epilogue + .long 4098 # Function type index + .secrel32 f # Function section relative address + .secidx f # Function section index + .byte 0 # Flags + .asciz "f" # Function name +.Ltmp17: + .short .Ltmp19-.Ltmp18 # Record length +.Ltmp18: + .short 4414 # Record kind: S_LOCAL + .long 116 # TypeIndex + .short 0 # Flags + .asciz "p" +.Ltmp19: + .cv_def_range .Lbegin0 .Lend0 .Lbegin1 .Lend1 .Lbegin2 .Lend2 .Lbegin3 .Lend3, "A\021\027\000\000\000" + .short 2 # Record length + .short 4431 # Record kind: S_PROC_ID_END +.Ltmp15: + .cv_filechecksums # File index to string table offset subsection + .cv_stringtable # String table