Index: lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -568,13 +568,41 @@ return Var; } +/// Determine whether a variable appears in a count: expression. +static bool dependsOn(DbgVariable *A, DbgVariable *B) { + if (auto *Array = dyn_cast(A->getType())) { + for (auto *El : Array->getElements()) { + if (auto *Subrange = dyn_cast(El)) { + auto Count = Subrange->getCount(); + if (auto *Var = Count.dyn_cast()) + return Var == B->getVariable(); + return false; + } + } + } + return false; +} + +/// Sort local variables so that variables that variables that appear inside of +/// helper expressions come first. +static bool sortLocalVars(DbgVariable *A, DbgVariable *B) { + return dependsOn(B, A); +} + DIE *DwarfCompileUnit::createScopeChildrenDIE(LexicalScope *Scope, SmallVectorImpl &Children, bool *HasNonScopeChildren) { assert(Children.empty()); DIE *ObjectPointer = nullptr; - for (DbgVariable *DV : DU->getScopeVariables().lookup(Scope)) + // Emit function arguments (order is significant). + auto Vars = DU->getScopeVariables().lookup(Scope); + for (auto &DV : Vars.Args) + Children.push_back(constructVariableDIE(*DV.second, *Scope, ObjectPointer)); + + // Emit local variables. + std::stable_sort(Vars.Locals.begin(), Vars.Locals.end(), sortLocalVars); + for (DbgVariable *DV : Vars.Locals) Children.push_back(constructVariableDIE(*DV, *Scope, ObjectPointer)); // Skip imported directives in gmlt-like data. Index: lib/CodeGen/AsmPrinter/DwarfFile.h =================================================================== --- lib/CodeGen/AsmPrinter/DwarfFile.h +++ lib/CodeGen/AsmPrinter/DwarfFile.h @@ -17,6 +17,7 @@ #include "llvm/CodeGen/DIE.h" #include "llvm/IR/Metadata.h" #include "llvm/Support/Allocator.h" +#include #include #include @@ -47,8 +48,12 @@ /// the string offsets table. The contribution is shared by all units. MCSymbol *StringOffsetsStartSym = nullptr; - // Collection of dbg variables of a scope. - DenseMap> ScopeVariables; + struct ScopeVars { + std::map Args; + SmallVector Locals; + }; + /// Collection of DbgVariables of each lexical scope. + DenseMap ScopeVariables; // Collection of abstract subprogram DIEs. DenseMap AbstractSPDies; @@ -109,7 +114,7 @@ /// \returns false if the variable was merged with a previous one. bool addScopeVariable(LexicalScope *LS, DbgVariable *Var); - DenseMap> &getScopeVariables() { + DenseMap &getScopeVariables() { return ScopeVariables; } Index: lib/CodeGen/AsmPrinter/DwarfFile.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfFile.cpp +++ lib/CodeGen/AsmPrinter/DwarfFile.cpp @@ -103,37 +103,18 @@ } bool DwarfFile::addScopeVariable(LexicalScope *LS, DbgVariable *Var) { - SmallVectorImpl &Vars = ScopeVariables[LS]; + auto &ScopeVars = ScopeVariables[LS]; const DILocalVariable *DV = Var->getVariable(); - // Variables with positive arg numbers are parameters. if (unsigned ArgNum = DV->getArg()) { - // Keep all parameters in order at the start of the variable list to ensure - // function types are correct (no out-of-order parameters) - // - // This could be improved by only doing it for optimized builds (unoptimized - // builds have the right order to begin with), searching from the back (this - // would catch the unoptimized case quickly), or doing a binary search - // rather than linear search. - auto I = Vars.begin(); - while (I != Vars.end()) { - unsigned CurNum = (*I)->getVariable()->getArg(); - // A local (non-parameter) variable has been found, insert immediately - // before it. - if (CurNum == 0) - break; - // A later indexed parameter has been found, insert immediately before it. - if (CurNum > ArgNum) - break; - if (CurNum == ArgNum) { - (*I)->addMMIEntry(*Var); - return false; - } - ++I; + auto Cached = ScopeVars.Args.find(ArgNum); + if (Cached == ScopeVars.Args.end()) + ScopeVars.Args[ArgNum] = Var; + else { + Cached->second->addMMIEntry(*Var); + return false; } - Vars.insert(I, Var); - return true; - } - - Vars.push_back(Var); + } else { + ScopeVars.Locals.push_back(Var); + } return true; } Index: test/DebugInfo/X86/vla-dependencies.ll =================================================================== --- /dev/null +++ test/DebugInfo/X86/vla-dependencies.ll @@ -0,0 +1,154 @@ +; RUN: llc -mtriple=x86_64-apple-darwin %s -o - -filetype=obj | llvm-dwarfdump - | FileCheck %s +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name ("g") +; CHECK: 0x00000[[VLAEXPR:.*]]: DW_TAG_variable +; CHECK-NEXT: DW_AT_name ("vla_expr") +;0x000000b1: DW_TAG_array_type +; DW_AT_type (cu + 0x0066 "unsigned char") +; +;0x000000b6: DW_TAG_subrange_type +; DW_AT_type (cu + 0x0079 "sizetype") +; DW_AT_count (cu + 0x[[VLAEXPR]]) +; +; +; Generated from: +; +; static int h(unsigned char *data) { +; unsigned char tmp[3 + 1]; +; for (int i = 0; i < 3; i++) +; tmp[i + 1] = data[i]; +; return use(tmp); +; } +; static void g(int len) { +; unsigned char data[len + 2]; +; h(data); +; } +; void f() { g(3); } + +source_filename = "test.c" +target datalayout = "e-m:o-p:32:32-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32" +target triple = "thumbv7-apple-ios10.0.0" + +; Function Attrs: minsize nounwind optsize +define void @f() local_unnamed_addr #0 !dbg !8 { +entry: + %tmp.i.i = alloca [4 x i8], align 1 + call void @llvm.dbg.declare(metadata [4 x i8]* %tmp.i.i, metadata !12, metadata !DIExpression()), !dbg !26 + %vla1.i = alloca [5 x i8], align 1, !dbg !40 + call void @llvm.dbg.declare(metadata [5 x i8]* %vla1.i, metadata !35, metadata !DIExpression(DW_OP_stack_value)), !dbg !40 + %0 = getelementptr inbounds [5 x i8], [5 x i8]* %vla1.i, i32 0, i32 0, !dbg !41 + call void @llvm.lifetime.start.p0i8(i64 5, i8* nonnull %0), !dbg !41 + call void @llvm.dbg.value(metadata i32 3, metadata !32, metadata !DIExpression()) #4, !dbg !41 + call void @llvm.dbg.value(metadata i32 5, metadata !33, metadata !DIExpression()) #4, !dbg !40 + call void @llvm.dbg.value(metadata [5 x i8]* %vla1.i, metadata !20, metadata !DIExpression(DW_OP_stack_value)) #4, !dbg !42 + %1 = getelementptr inbounds [4 x i8], [4 x i8]* %tmp.i.i, i32 0, i32 0, !dbg !26 + call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %1) #4, !dbg !26 + call void @llvm.dbg.value(metadata i32 0, metadata !21, metadata !DIExpression()) #4, !dbg !43 + br label %for.cond.i.i, !dbg !43 + +for.cond.i.i: ; preds = %for.body.i.i, %entry + %i.0.i.i = phi i32 [ 0, %entry ], [ %add.i.i, %for.body.i.i ] + call void @llvm.dbg.value(metadata i32 %i.0.i.i, metadata !21, metadata !DIExpression()) #4, !dbg !43 + %exitcond.i.i = icmp eq i32 %i.0.i.i, 3, !dbg !44 + br i1 %exitcond.i.i, label %g.exit, label %for.body.i.i, !dbg !43 + +for.body.i.i: ; preds = %for.cond.i.i + %arrayidx.i.i = getelementptr inbounds [5 x i8], [5 x i8]* %vla1.i, i32 0, i32 %i.0.i.i, !dbg !46 + %2 = load i8, i8* %arrayidx.i.i, align 1, !dbg !46, !tbaa !47 + %add.i.i = add nuw nsw i32 %i.0.i.i, 1, !dbg !46 + %arrayidx1.i.i = getelementptr inbounds [4 x i8], [4 x i8]* %tmp.i.i, i32 0, i32 %add.i.i, !dbg !46 + store i8 %2, i8* %arrayidx1.i.i, align 1, !dbg !46, !tbaa !47 + call void @llvm.dbg.value(metadata i32 %add.i.i, metadata !21, metadata !DIExpression()) #4, !dbg !43 + br label %for.cond.i.i, !dbg !44, !llvm.loop !50 + +g.exit: ; preds = %for.cond.i.i + %call.i.i = call i32 bitcast (i32 (...)* @use to i32 (i8*)*)(i8* nonnull %1) #5, !dbg !53 + call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %1) #4, !dbg !54 + call void @llvm.lifetime.end.p0i8(i64 5, i8* nonnull %0), !dbg !55 + ret void, !dbg !56 +} + +; Function Attrs: nounwind readnone speculatable +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: argmemonly nounwind +declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #2 + +; Function Attrs: argmemonly nounwind +declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #2 + +; Function Attrs: minsize optsize +declare i32 @use(...) local_unnamed_addr #3 + +; Function Attrs: nounwind readnone speculatable +declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + +attributes #0 = { minsize nounwind optsize } +attributes #1 = { nounwind readnone speculatable } +attributes #2 = { argmemonly nounwind } +attributes #3 = { minsize optsize } +attributes #4 = { nounwind } +attributes #5 = { minsize nounwind optsize } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 7.0.0 (trunk 324259) (llvm/trunk 324261)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "", directory: "/") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{i32 1, !"min_enum_size", i32 4} +!7 = !{!"clang version 7.0.0 (trunk 324259) (llvm/trunk 324261)"} +!8 = distinct !DISubprogram(name: "f", scope: !9, file: !9, line: 11, type: !10, isLocal: false, isDefinition: true, scopeLine: 11, isOptimized: true, unit: !0, variables: !2) +!9 = !DIFile(filename: "test.c", directory: "/") +!10 = !DISubroutineType(types: !11) +!11 = !{null} +!12 = !DILocalVariable(name: "tmp", scope: !13, file: !9, line: 2, type: !23) +!13 = distinct !DISubprogram(name: "h", scope: !9, file: !9, line: 1, type: !14, isLocal: true, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !19) +!14 = !DISubroutineType(types: !15) +!15 = !{!16, !17} +!16 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 32) +!18 = !DIBasicType(name: "unsigned char", size: 8, encoding: DW_ATE_unsigned_char) +!19 = !{!20, !12, !21} +!20 = !DILocalVariable(name: "data", arg: 1, scope: !13, file: !9, line: 1, type: !17) +!21 = !DILocalVariable(name: "i", scope: !22, file: !9, line: 3, type: !16) +!22 = distinct !DILexicalBlock(scope: !13, file: !9, line: 3) +!23 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 32, elements: !24) +!24 = !{!25} +!25 = !DISubrange(count: 4) +!26 = !DILocation(line: 2, scope: !13, inlinedAt: !27) +!27 = distinct !DILocation(line: 9, scope: !28, inlinedAt: !39) +!28 = distinct !DISubprogram(name: "g", scope: !9, file: !9, line: 7, type: !29, isLocal: true, isDefinition: true, scopeLine: 7, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !31) +!29 = !DISubroutineType(types: !30) +!30 = !{null, !16} +!31 = !{!32, !33, !35} +!32 = !DILocalVariable(name: "len", arg: 1, scope: !28, file: !9, line: 7, type: !16) +!33 = !DILocalVariable(name: "vla_expr", scope: !28, file: !9, line: 8, type: !34) +!34 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) +!35 = !DILocalVariable(name: "data", scope: !28, file: !9, line: 8, type: !36) +!36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, elements: !37) +!37 = !{!38} +!38 = !DISubrange(count: !33) +!39 = distinct !DILocation(line: 11, scope: !8) +!40 = !DILocation(line: 8, scope: !28, inlinedAt: !39) +!41 = !DILocation(line: 7, scope: !28, inlinedAt: !39) +!42 = !DILocation(line: 1, scope: !13, inlinedAt: !27) +!43 = !DILocation(line: 3, scope: !22, inlinedAt: !27) +!44 = !DILocation(line: 3, scope: !45, inlinedAt: !27) +!45 = distinct !DILexicalBlock(scope: !22, file: !9, line: 3) +!46 = !DILocation(line: 4, scope: !45, inlinedAt: !27) +!47 = !{!48, !48, i64 0} +!48 = !{!"omnipotent char", !49, i64 0} +!49 = !{!"Simple C/C++ TBAA"} +!50 = distinct !{!50, !51, !52} +!51 = !DILocation(line: 3, scope: !22) +!52 = !DILocation(line: 4, scope: !22) +!53 = !DILocation(line: 5, scope: !13, inlinedAt: !27) +!54 = !DILocation(line: 6, scope: !13, inlinedAt: !27) +!55 = !DILocation(line: 10, scope: !28, inlinedAt: !39) +!56 = !DILocation(line: 11, scope: !8) +