| ; RUN: opt %s -S -passes=instcombine -o - | FileCheck %s |
| |
| ;; $ cat test.cpp |
| ;; class a { |
| ;; float b; |
| ;; }; |
| ;; class c { |
| ;; public: |
| ;; a d(); |
| ;; }; |
| ;; class e { |
| ;; public: |
| ;; c &f(); |
| ;; }; |
| ;; class g { |
| ;; public: |
| ;; void h(a &); |
| ;; }; |
| ;; class j { |
| ;; g k; |
| ;; e l; |
| ;; e m; |
| ;; bool n; |
| ;; void o(); |
| ;; }; |
| ;; void j::o() { |
| ;; int i; |
| ;; a p; |
| ;; i = 0; |
| ;; for (; i < 3; i++) |
| ;; if (n) |
| ;; p = l.f().d(); |
| ;; else |
| ;; p = m.f().d(); |
| ;; k.h(p); |
| ;; } |
| ;; |
| ;; Generated by grabbing IR before instcombine in: |
| ;; $ clang++ -O2 -g -c test.cpp -Xclang -fexperimental-assignment-tracking |
| |
| ;; Before instcombine runs we have an unrolled loop (3 iterations). Each |
| ;; unrolled section is an if-diamond with a store in if.then and if.else |
| ;; block. The "same" stores in each unrolled section have the same |
| ;; DIAssignID. Instcombine is going to sink the stores from if.then and if.else |
| ;; into if.end for each unrolled section. This involves merging the DIAssignID |
| ;; of the two stores. Check that each merge updates all linked instructions |
| ;; with the same DIAssignID attachments too. |
| |
| ; CHECK: if.then: |
| ; CHECK: #dbg_assign(float %call2, ![[var:[0-9]+]], !DIExpression(), ![[id:[0-9]+]], ptr %p, !DIExpression(), |
| ; CHECK: br label %for.inc |
| |
| ; CHECK: if.else: |
| ; CHECK: #dbg_assign(float %call5, ![[var]], !DIExpression(), ![[id]], ptr %p, !DIExpression(), |
| ; CHECK: br label %for.inc |
| |
| ; CHECK: for.inc: |
| ; CHECK-NEXT: %storemerge = phi float [ %call2, %if.then ], [ %call5, %if.else ] |
| ; CHECK-NEXT: store float %storemerge, ptr %p, align 4{{.+}}!DIAssignID ![[id]] |
| |
| ; CHECK: if.then.1: |
| ; CHECK: #dbg_assign(float %call2.1, ![[var]], !DIExpression(), ![[id]], ptr %p, !DIExpression(), |
| ; CHECK: br label %for.inc.1 |
| |
| ; CHECK: if.else.1: |
| ; CHECK: #dbg_assign(float %call5.1, ![[var]], !DIExpression(), ![[id]], ptr %p, !DIExpression(), |
| ; CHECK: br label %for.inc.1 |
| |
| ; CHECK: for.inc.1: |
| ; CHECK-NEXT: %storemerge1 = phi float [ %call2.1, %if.then.1 ], [ %call5.1, %if.else.1 ] |
| ; CHECK-NEXT: store float %storemerge1, ptr %p, align 4{{.+}}!DIAssignID ![[id]] |
| |
| ; CHECK: if.then.2: |
| ; CHECK: #dbg_assign(float %call2.2, ![[var]], !DIExpression(), ![[id]], ptr %p, !DIExpression(), |
| ; CHECK: br label %for.inc.2 |
| |
| ; CHECK: if.else.2: |
| ; CHECK: #dbg_assign(float %call5.2, ![[var]], !DIExpression(), ![[id]], ptr %p, !DIExpression(), |
| ; CHECK: br label %for.inc.2 |
| |
| ; CHECK: for.inc.2: |
| ; CHECK-NEXT: %storemerge2 = phi float [ %call2.2, %if.then.2 ], [ %call5.2, %if.else.2 ] |
| ; CHECK-NEXT: store float %storemerge2, ptr %p, align 4{{.+}}!DIAssignID ![[id]] |
| |
| %class.j = type { %class.g, %class.e, %class.e, i8 } |
| %class.g = type { i8 } |
| %class.e = type { i8 } |
| %class.a = type { float } |
| %class.c = type { i8 } |
| |
| ; Function Attrs: uwtable |
| define dso_local void @_ZN1j1oEv(ptr %this) local_unnamed_addr #0 align 2 !dbg !7 { |
| entry: |
| %p = alloca %class.a, align 4, !DIAssignID !49 |
| call void @llvm.dbg.assign(metadata i1 undef, metadata !48, metadata !DIExpression(), metadata !49, metadata ptr %p, metadata !DIExpression()), !dbg !50 |
| %0 = bitcast ptr %p to ptr, !dbg !51 |
| call void @llvm.lifetime.start.p0(i64 4, ptr nonnull %0) #4, !dbg !51 |
| %n = getelementptr inbounds %class.j, ptr %this, i64 0, i32 3 |
| %l = getelementptr inbounds %class.j, ptr %this, i64 0, i32 1 |
| %ref.tmp.sroa.0.0..sroa_idx = getelementptr inbounds %class.a, ptr %p, i64 0, i32 0 |
| %m = getelementptr inbounds %class.j, ptr %this, i64 0, i32 2 |
| %1 = load i8, ptr %n, align 1, !dbg !52 |
| %tobool.not = icmp eq i8 %1, 0, !dbg !52 |
| br i1 %tobool.not, label %if.else, label %if.then, !dbg !64 |
| |
| if.then: ; preds = %entry |
| %call = tail call nonnull align 1 dereferenceable(1) ptr @_ZN1e1fEv(ptr nonnull %l), !dbg !65 |
| %call2 = tail call float @_ZN1c1dEv(ptr nonnull %call), !dbg !66 |
| store float %call2, ptr %ref.tmp.sroa.0.0..sroa_idx, align 4, !dbg !67, !DIAssignID !71 |
| call void @llvm.dbg.assign(metadata float %call2, metadata !48, metadata !DIExpression(), metadata !71, metadata ptr %ref.tmp.sroa.0.0..sroa_idx, metadata !DIExpression()), !dbg !50 |
| br label %for.inc, !dbg !72 |
| |
| if.else: ; preds = %entry |
| %call4 = tail call nonnull align 1 dereferenceable(1) ptr @_ZN1e1fEv(ptr nonnull %m), !dbg !73 |
| %call5 = tail call float @_ZN1c1dEv(ptr nonnull %call4), !dbg !74 |
| store float %call5, ptr %ref.tmp.sroa.0.0..sroa_idx, align 4, !dbg !75, !DIAssignID !76 |
| call void @llvm.dbg.assign(metadata float %call5, metadata !48, metadata !DIExpression(), metadata !76, metadata ptr %ref.tmp.sroa.0.0..sroa_idx, metadata !DIExpression()), !dbg !50 |
| br label %for.inc |
| |
| for.inc: ; preds = %if.then, %if.else |
| %2 = load i8, ptr %n, align 1, !dbg !52 |
| %tobool.not.1 = icmp eq i8 %2, 0, !dbg !52 |
| br i1 %tobool.not.1, label %if.else.1, label %if.then.1, !dbg !64 |
| |
| if.then.1: ; preds = %for.inc |
| %call.1 = tail call nonnull align 1 dereferenceable(1) ptr @_ZN1e1fEv(ptr nonnull %l), !dbg !65 |
| %call2.1 = tail call float @_ZN1c1dEv(ptr nonnull %call.1), !dbg !66 |
| store float %call2.1, ptr %ref.tmp.sroa.0.0..sroa_idx, align 4, !dbg !67, !DIAssignID !71 |
| call void @llvm.dbg.assign(metadata float %call2.1, metadata !48, metadata !DIExpression(), metadata !71, metadata ptr %ref.tmp.sroa.0.0..sroa_idx, metadata !DIExpression()), !dbg !50 |
| br label %for.inc.1, !dbg !72 |
| |
| if.else.1: ; preds = %for.inc |
| %call4.1 = tail call nonnull align 1 dereferenceable(1) ptr @_ZN1e1fEv(ptr nonnull %m), !dbg !73 |
| %call5.1 = tail call float @_ZN1c1dEv(ptr nonnull %call4.1), !dbg !74 |
| store float %call5.1, ptr %ref.tmp.sroa.0.0..sroa_idx, align 4, !dbg !75, !DIAssignID !76 |
| call void @llvm.dbg.assign(metadata float %call5.1, metadata !48, metadata !DIExpression(), metadata !76, metadata ptr %ref.tmp.sroa.0.0..sroa_idx, metadata !DIExpression()), !dbg !50 |
| br label %for.inc.1 |
| |
| for.inc.1: ; preds = %if.else.1, %if.then.1 |
| %3 = load i8, ptr %n, align 1, !dbg !52 |
| %tobool.not.2 = icmp eq i8 %3, 0, !dbg !52 |
| br i1 %tobool.not.2, label %if.else.2, label %if.then.2, !dbg !64 |
| |
| if.then.2: ; preds = %for.inc.1 |
| %call.2 = tail call nonnull align 1 dereferenceable(1) ptr @_ZN1e1fEv(ptr nonnull %l), !dbg !65 |
| %call2.2 = tail call float @_ZN1c1dEv(ptr nonnull %call.2), !dbg !66 |
| store float %call2.2, ptr %ref.tmp.sroa.0.0..sroa_idx, align 4, !dbg !67, !DIAssignID !71 |
| call void @llvm.dbg.assign(metadata float %call2.2, metadata !48, metadata !DIExpression(), metadata !71, metadata ptr %ref.tmp.sroa.0.0..sroa_idx, metadata !DIExpression()), !dbg !50 |
| br label %for.inc.2, !dbg !72 |
| |
| if.else.2: ; preds = %for.inc.1 |
| %call4.2 = tail call nonnull align 1 dereferenceable(1) ptr @_ZN1e1fEv(ptr nonnull %m), !dbg !73 |
| %call5.2 = tail call float @_ZN1c1dEv(ptr nonnull %call4.2), !dbg !74 |
| store float %call5.2, ptr %ref.tmp.sroa.0.0..sroa_idx, align 4, !dbg !75, !DIAssignID !76 |
| call void @llvm.dbg.assign(metadata float %call5.2, metadata !48, metadata !DIExpression(), metadata !76, metadata ptr %ref.tmp.sroa.0.0..sroa_idx, metadata !DIExpression()), !dbg !50 |
| br label %for.inc.2 |
| |
| for.inc.2: ; preds = %if.else.2, %if.then.2 |
| %k = getelementptr inbounds %class.j, ptr %this, i64 0, i32 0, !dbg !77 |
| call void @_ZN1g1hER1a(ptr %k, ptr nonnull align 4 dereferenceable(4) %p), !dbg !78 |
| call void @llvm.lifetime.end.p0(i64 4, ptr nonnull %0) #4, !dbg !79 |
| ret void, !dbg !79 |
| } |
| |
| ; Function Attrs: argmemonly nofree nosync nounwind willreturn |
| declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #1 |
| |
| declare dso_local nonnull align 1 dereferenceable(1) ptr @_ZN1e1fEv(ptr) local_unnamed_addr #2 |
| |
| declare dso_local float @_ZN1c1dEv(ptr) local_unnamed_addr #2 |
| |
| ; Function Attrs: argmemonly nofree nosync nounwind willreturn |
| declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #1 |
| |
| declare dso_local void @_ZN1g1hER1a(ptr, ptr nonnull align 4 dereferenceable(4)) local_unnamed_addr #2 |
| |
| ; Function Attrs: nofree nosync nounwind readnone speculatable willreturn |
| declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #3 |
| |
| !llvm.dbg.cu = !{!0} |
| !llvm.module.flags = !{!3, !4, !5, !1000} |
| !llvm.ident = !{!6} |
| |
| !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) |
| !1 = !DIFile(filename: "test.cpp", directory: "/") |
| !2 = !{} |
| !3 = !{i32 7, !"Dwarf Version", i32 4} |
| !4 = !{i32 2, !"Debug Info Version", i32 3} |
| !5 = !{i32 1, !"wchar_size", i32 4} |
| !6 = !{!"clang version 12.0.0"} |
| !7 = distinct !DISubprogram(name: "o", linkageName: "_ZN1j1oEv", scope: !8, file: !1, line: 23, type: !40, scopeLine: 23, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, declaration: !39, retainedNodes: !43) |
| !8 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "j", file: !1, line: 16, size: 32, flags: DIFlagTypePassByValue, elements: !9, identifier: "_ZTS1j") |
| !9 = !{!10, !22, !36, !37, !39} |
| !10 = !DIDerivedType(tag: DW_TAG_member, name: "k", scope: !8, file: !1, line: 17, baseType: !11, size: 8) |
| !11 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "g", file: !1, line: 12, size: 8, flags: DIFlagTypePassByValue, elements: !12, identifier: "_ZTS1g") |
| !12 = !{!13} |
| !13 = !DISubprogram(name: "h", linkageName: "_ZN1g1hER1a", scope: !11, file: !1, line: 14, type: !14, scopeLine: 14, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized) |
| !14 = !DISubroutineType(types: !15) |
| !15 = !{null, !16, !17} |
| !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !11, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) |
| !17 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !18, size: 64) |
| !18 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "a", file: !1, line: 1, size: 32, flags: DIFlagTypePassByValue, elements: !19, identifier: "_ZTS1a") |
| !19 = !{!20} |
| !20 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !18, file: !1, line: 2, baseType: !21, size: 32) |
| !21 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) |
| !22 = !DIDerivedType(tag: DW_TAG_member, name: "l", scope: !8, file: !1, line: 18, baseType: !23, size: 8, offset: 8) |
| !23 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "e", file: !1, line: 8, size: 8, flags: DIFlagTypePassByValue, elements: !24, identifier: "_ZTS1e") |
| !24 = !{!25} |
| !25 = !DISubprogram(name: "f", linkageName: "_ZN1e1fEv", scope: !23, file: !1, line: 10, type: !26, scopeLine: 10, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized) |
| !26 = !DISubroutineType(types: !27) |
| !27 = !{!28, !35} |
| !28 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !29, size: 64) |
| !29 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "c", file: !1, line: 4, size: 8, flags: DIFlagTypePassByValue, elements: !30, identifier: "_ZTS1c") |
| !30 = !{!31} |
| !31 = !DISubprogram(name: "d", linkageName: "_ZN1c1dEv", scope: !29, file: !1, line: 6, type: !32, scopeLine: 6, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized) |
| !32 = !DISubroutineType(types: !33) |
| !33 = !{!18, !34} |
| !34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) |
| !35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !23, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) |
| !36 = !DIDerivedType(tag: DW_TAG_member, name: "m", scope: !8, file: !1, line: 19, baseType: !23, size: 8, offset: 16) |
| !37 = !DIDerivedType(tag: DW_TAG_member, name: "n", scope: !8, file: !1, line: 20, baseType: !38, size: 8, offset: 24) |
| !38 = !DIBasicType(name: "bool", size: 8, encoding: DW_ATE_boolean) |
| !39 = !DISubprogram(name: "o", linkageName: "_ZN1j1oEv", scope: !8, file: !1, line: 21, type: !40, scopeLine: 21, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) |
| !40 = !DISubroutineType(types: !41) |
| !41 = !{null, !42} |
| !42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) |
| !43 = !{!44, !46, !48} |
| !44 = !DILocalVariable(name: "this", arg: 1, scope: !7, type: !45, flags: DIFlagArtificial | DIFlagObjectPointer) |
| !45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64) |
| !46 = !DILocalVariable(name: "i", scope: !7, file: !1, line: 24, type: !47) |
| !47 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) |
| !48 = !DILocalVariable(name: "p", scope: !7, file: !1, line: 25, type: !18) |
| !49 = distinct !DIAssignID() |
| !50 = !DILocation(line: 0, scope: !7) |
| !51 = !DILocation(line: 25, column: 3, scope: !7) |
| !52 = !DILocation(line: 28, column: 9, scope: !53) |
| !53 = distinct !DILexicalBlock(scope: !54, file: !1, line: 28, column: 9) |
| !54 = distinct !DILexicalBlock(scope: !55, file: !1, line: 27, column: 3) |
| !55 = distinct !DILexicalBlock(scope: !7, file: !1, line: 27, column: 3) |
| !64 = !DILocation(line: 28, column: 9, scope: !54) |
| !65 = !DILocation(line: 29, column: 13, scope: !53) |
| !66 = !DILocation(line: 29, column: 17, scope: !53) |
| !67 = !DILocation(line: 29, column: 9, scope: !53) |
| !71 = distinct !DIAssignID() |
| !72 = !DILocation(line: 29, column: 7, scope: !53) |
| !73 = !DILocation(line: 31, column: 13, scope: !53) |
| !74 = !DILocation(line: 31, column: 17, scope: !53) |
| !75 = !DILocation(line: 31, column: 9, scope: !53) |
| !76 = distinct !DIAssignID() |
| !77 = !DILocation(line: 32, column: 3, scope: !7) |
| !78 = !DILocation(line: 32, column: 5, scope: !7) |
| !79 = !DILocation(line: 33, column: 1, scope: !7) |
| !1000 = !{i32 7, !"debug-info-assignment-tracking", i1 true} |