diff --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/check_attrs.ll.funcattrs.expected b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/check_attrs.ll.funcattrs.expected --- a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/check_attrs.ll.funcattrs.expected +++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/check_attrs.ll.funcattrs.expected @@ -10,14 +10,14 @@ define i32* @foo(%struct.ST* %s) nounwind uwtable readnone optsize ssp { ; IS__TUNIT____: Function Attrs: nofree nosync nounwind optsize readnone ssp uwtable willreturn ; IS__TUNIT____-LABEL: define {{[^@]+}}@foo -; IS__TUNIT____-SAME: (%struct.ST* nofree readnone [[S:%.*]]) #0 +; IS__TUNIT____-SAME: (%struct.ST* nofree readnone [[S:%.*]]) [[ATTRIBUTES0:#.*]] ; IS__TUNIT____-NEXT: entry: ; IS__TUNIT____-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ST:%.*]], %struct.ST* [[S]], i64 1, i32 2, i32 1, i64 5, i64 13 ; IS__TUNIT____-NEXT: ret i32* [[ARRAYIDX]] ; ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind optsize readnone ssp uwtable willreturn ; IS__CGSCC____-LABEL: define {{[^@]+}}@foo -; IS__CGSCC____-SAME: (%struct.ST* nofree readnone [[S:%.*]]) #0 +; IS__CGSCC____-SAME: (%struct.ST* nofree readnone [[S:%.*]]) [[ATTRIBUTES0:#.*]] ; IS__CGSCC____-NEXT: entry: ; IS__CGSCC____-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ST:%.*]], %struct.ST* [[S]], i64 1, i32 2, i32 1, i64 5, i64 13 ; IS__CGSCC____-NEXT: ret i32* [[ARRAYIDX]] diff --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/check_attrs.ll.plain.expected b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/check_attrs.ll.plain.expected --- a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/check_attrs.ll.plain.expected +++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/check_attrs.ll.plain.expected @@ -9,7 +9,7 @@ define i32* @foo(%struct.ST* %s) nounwind uwtable readnone optsize ssp { ; CHECK-LABEL: define {{[^@]+}}@foo -; CHECK-SAME: (%struct.ST* nofree readnone [[S:%.*]]) #0 +; CHECK-SAME: (%struct.ST* nofree readnone [[S:%.*]]) [[ATTRIBUTES0:#.*]] ; CHECK-NEXT: entry: ; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[STRUCT_ST:%.*]], %struct.ST* [[S]], i64 1, i32 2, i32 1, i64 5, i64 13 ; CHECK-NEXT: ret i32* [[ARRAYIDX]] diff --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/scrub_attrs.ll.plain.expected b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/scrub_attrs.ll.plain.expected --- a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/scrub_attrs.ll.plain.expected +++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/scrub_attrs.ll.plain.expected @@ -5,7 +5,7 @@ define internal void @bar() { ; CHECK-LABEL: @bar( -; CHECK-NEXT: call void @foo() #0 +; CHECK-NEXT: call void @foo() [[ATTRIBUTES0:#.*]] ; CHECK-NEXT: ret void ; call void @foo() readnone diff --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values.ll b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values.ll new file mode 100644 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values.ll @@ -0,0 +1,117 @@ +; Just run it through opt, no passes needed. +; RUN: opt < %s -S | FileCheck %s + +; Function Attrs: nounwind uwtable +define dso_local void @foo(i32* %A, float* %B) #0 !dbg !7 { +entry: + %A.addr = alloca i32*, align 8 + %B.addr = alloca float*, align 8 + %i = alloca i32, align 4 + store i32* %A, i32** %A.addr, align 8, !tbaa !20 + call void @llvm.dbg.declare(metadata i32** %A.addr, metadata !16, metadata !DIExpression()), !dbg !24 + store float* %B, float** %B.addr, align 8, !tbaa !20 + call void @llvm.dbg.declare(metadata float** %B.addr, metadata !17, metadata !DIExpression()), !dbg !25 + %0 = bitcast i32* %i to i8*, !dbg !26 + call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #3, !dbg !26 + call void @llvm.dbg.declare(metadata i32* %i, metadata !18, metadata !DIExpression()), !dbg !27 + store i32 0, i32* %i, align 4, !dbg !27, !tbaa !28 + br label %for.cond, !dbg !26 + +for.cond: ; preds = %for.inc, %entry + %1 = load i32, i32* %i, align 4, !dbg !30, !tbaa !28 + %cmp = icmp slt i32 %1, 100, !dbg !32 + br i1 %cmp, label %for.body, label %for.cond.cleanup, !dbg !33 + +for.cond.cleanup: ; preds = %for.cond + %2 = bitcast i32* %i to i8*, !dbg !34 + call void @llvm.lifetime.end.p0i8(i64 4, i8* %2) #3, !dbg !34 + br label %for.end + +for.body: ; preds = %for.cond + %3 = load float*, float** %B.addr, align 8, !dbg !35, !tbaa !20 + %4 = load i32, i32* %i, align 4, !dbg !36, !tbaa !28 + %idxprom = sext i32 %4 to i64, !dbg !35 + %arrayidx = getelementptr inbounds float, float* %3, i64 %idxprom, !dbg !35 + %5 = load float, float* %arrayidx, align 4, !dbg !35, !tbaa !37 + %conv = fptosi float %5 to i32, !dbg !35 + %6 = load i32*, i32** %A.addr, align 8, !dbg !39, !tbaa !20 + %7 = load i32, i32* %i, align 4, !dbg !40, !tbaa !28 + %idxprom1 = sext i32 %7 to i64, !dbg !39 + %arrayidx2 = getelementptr inbounds i32, i32* %6, i64 %idxprom1, !dbg !39 + store i32 %conv, i32* %arrayidx2, align 4, !dbg !41, !tbaa !28 + br label %for.inc, !dbg !39 + +for.inc: ; preds = %for.body + %8 = load i32, i32* %i, align 4, !dbg !42, !tbaa !28 + %inc = add nsw i32 %8, 1, !dbg !42 + store i32 %inc, i32* %i, align 4, !dbg !42, !tbaa !28 + br label %for.cond, !dbg !34, !llvm.loop !43 + +for.end: ; preds = %for.cond.cleanup + ret void, !dbg !45 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #2 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #2 + +attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "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"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone speculatable willreturn } +attributes #2 = { argmemonly nounwind willreturn } +attributes #3 = { 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 11.0.0 (git@github.com:llvm/llvm-project.git 1d5da8cd30fce1c0a2c2fa6ba656dbfaa36192c8)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "/data/src/llvm-project/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/vec.c", directory: "/data/build/llvm-project") +!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 11.0.0 (git@github.com:llvm/llvm-project.git 1d5da8cd30fce1c0a2c2fa6ba656dbfaa36192c8)"} +!7 = distinct !DISubprogram(name: "foo", scope: !8, file: !8, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15) +!8 = !DIFile(filename: "src/llvm-project/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/vec.c", directory: "/data") +!9 = !DISubroutineType(types: !10) +!10 = !{null, !11, !13} +!11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) +!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) +!14 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!15 = !{!16, !17, !18} +!16 = !DILocalVariable(name: "A", arg: 1, scope: !7, file: !8, line: 1, type: !11) +!17 = !DILocalVariable(name: "B", arg: 2, scope: !7, file: !8, line: 1, type: !13) +!18 = !DILocalVariable(name: "i", scope: !19, file: !8, line: 3, type: !12) +!19 = distinct !DILexicalBlock(scope: !7, file: !8, line: 3, column: 3) +!20 = !{!21, !21, i64 0} +!21 = !{!"any pointer", !22, i64 0} +!22 = !{!"omnipotent char", !23, i64 0} +!23 = !{!"Simple C/C++ TBAA"} +!24 = !DILocation(line: 1, column: 15, scope: !7) +!25 = !DILocation(line: 1, column: 25, scope: !7) +!26 = !DILocation(line: 3, column: 8, scope: !19) +!27 = !DILocation(line: 3, column: 12, scope: !19) +!28 = !{!29, !29, i64 0} +!29 = !{!"int", !22, i64 0} +!30 = !DILocation(line: 3, column: 19, scope: !31) +!31 = distinct !DILexicalBlock(scope: !19, file: !8, line: 3, column: 3) +!32 = !DILocation(line: 3, column: 21, scope: !31) +!33 = !DILocation(line: 3, column: 3, scope: !19) +!34 = !DILocation(line: 3, column: 3, scope: !31) +!35 = !DILocation(line: 4, column: 12, scope: !31) +!36 = !DILocation(line: 4, column: 14, scope: !31) +!37 = !{!38, !38, i64 0} +!38 = !{!"float", !22, i64 0} +!39 = !DILocation(line: 4, column: 5, scope: !31) +!40 = !DILocation(line: 4, column: 7, scope: !31) +!41 = !DILocation(line: 4, column: 10, scope: !31) +!42 = !DILocation(line: 3, column: 28, scope: !31) +!43 = distinct !{!43, !33, !44} +!44 = !DILocation(line: 4, column: 15, scope: !19) +!45 = !DILocation(line: 5, column: 1, scope: !7) diff --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values.ll.expected b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values.ll.expected new file mode 100644 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values.ll.expected @@ -0,0 +1,161 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; Just run it through opt, no passes needed. +; RUN: opt < %s -S | FileCheck %s + +; Function Attrs: nounwind uwtable +define dso_local void @foo(i32* %A, float* %B) #0 !dbg !7 { +; CHECK-LABEL: @foo( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32*, align 8 +; CHECK-NEXT: [[B_ADDR:%.*]] = alloca float*, align 8 +; CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 +; CHECK-NEXT: store i32* [[A:%.*]], i32** [[A_ADDR]], align 8, [[TBAA20:!tbaa !.*]] +; CHECK-NEXT: call void @llvm.dbg.declare(metadata i32** [[A_ADDR]], metadata !16, metadata !DIExpression()), [[DEBUG24:!dbg !.*]] +; CHECK-NEXT: store float* [[B:%.*]], float** [[B_ADDR]], align 8, [[TBAA20]] +; CHECK-NEXT: call void @llvm.dbg.declare(metadata float** [[B_ADDR]], metadata !17, metadata !DIExpression()), [[DEBUG25:!dbg !.*]] +; CHECK-NEXT: [[TMP0:%.*]] = bitcast i32* [[I]] to i8*, [[DEBUG26:!dbg !.*]] +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* [[TMP0]]) [[ATTRIBUTES3:#.*]], [[DEBUG26]] +; CHECK-NEXT: call void @llvm.dbg.declare(metadata i32* [[I]], metadata !18, metadata !DIExpression()), [[DEBUG27:!dbg !.*]] +; CHECK-NEXT: store i32 0, i32* [[I]], align 4, [[DEBUG27]], [[TBAA28:!tbaa !.*]] +; CHECK-NEXT: br label [[FOR_COND:%.*]], [[DEBUG26]] +; CHECK: for.cond: +; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[I]], align 4, [[DEBUG30:!dbg !.*]], [[TBAA28]] +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[TMP1]], 100, [[DEBUG32:!dbg !.*]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_COND_CLEANUP:%.*]], [[DEBUG33:!dbg !.*]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: [[TMP2:%.*]] = bitcast i32* [[I]] to i8*, [[DEBUG34:!dbg !.*]] +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* [[TMP2]]) [[ATTRIBUTES3]], [[DEBUG34]] +; CHECK-NEXT: br label [[FOR_END:%.*]] +; CHECK: for.body: +; CHECK-NEXT: [[TMP3:%.*]] = load float*, float** [[B_ADDR]], align 8, [[DEBUG35:!dbg !.*]], [[TBAA20]] +; CHECK-NEXT: [[TMP4:%.*]] = load i32, i32* [[I]], align 4, [[DEBUG36:!dbg !.*]], [[TBAA28]] +; CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP4]] to i64, [[DEBUG35]] +; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[TMP3]], i64 [[IDXPROM]], [[DEBUG35]] +; CHECK-NEXT: [[TMP5:%.*]] = load float, float* [[ARRAYIDX]], align 4, [[DEBUG35]], [[TBAA37:!tbaa !.*]] +; CHECK-NEXT: [[CONV:%.*]] = fptosi float [[TMP5]] to i32, [[DEBUG35]] +; CHECK-NEXT: [[TMP6:%.*]] = load i32*, i32** [[A_ADDR]], align 8, [[DEBUG39:!dbg !.*]], [[TBAA20]] +; CHECK-NEXT: [[TMP7:%.*]] = load i32, i32* [[I]], align 4, [[DEBUG40:!dbg !.*]], [[TBAA28]] +; CHECK-NEXT: [[IDXPROM1:%.*]] = sext i32 [[TMP7]] to i64, [[DEBUG39]] +; CHECK-NEXT: [[ARRAYIDX2:%.*]] = getelementptr inbounds i32, i32* [[TMP6]], i64 [[IDXPROM1]], [[DEBUG39]] +; CHECK-NEXT: store i32 [[CONV]], i32* [[ARRAYIDX2]], align 4, [[DEBUG41:!dbg !.*]], [[TBAA28]] +; CHECK-NEXT: br label [[FOR_INC:%.*]], [[DEBUG39]] +; CHECK: for.inc: +; CHECK-NEXT: [[TMP8:%.*]] = load i32, i32* [[I]], align 4, [[DEBUG42:!dbg !.*]], [[TBAA28]] +; CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP8]], 1, [[DEBUG42]] +; CHECK-NEXT: store i32 [[INC]], i32* [[I]], align 4, [[DEBUG42]], [[TBAA28]] +; CHECK-NEXT: br label [[FOR_COND]], [[DEBUG34]], [[LOOP43:!llvm.loop !.*]] +; CHECK: for.end: +; CHECK-NEXT: ret void, [[DEBUG45:!dbg !.*]] +; +entry: + %A.addr = alloca i32*, align 8 + %B.addr = alloca float*, align 8 + %i = alloca i32, align 4 + store i32* %A, i32** %A.addr, align 8, !tbaa !20 + call void @llvm.dbg.declare(metadata i32** %A.addr, metadata !16, metadata !DIExpression()), !dbg !24 + store float* %B, float** %B.addr, align 8, !tbaa !20 + call void @llvm.dbg.declare(metadata float** %B.addr, metadata !17, metadata !DIExpression()), !dbg !25 + %0 = bitcast i32* %i to i8*, !dbg !26 + call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #3, !dbg !26 + call void @llvm.dbg.declare(metadata i32* %i, metadata !18, metadata !DIExpression()), !dbg !27 + store i32 0, i32* %i, align 4, !dbg !27, !tbaa !28 + br label %for.cond, !dbg !26 + +for.cond: ; preds = %for.inc, %entry + %1 = load i32, i32* %i, align 4, !dbg !30, !tbaa !28 + %cmp = icmp slt i32 %1, 100, !dbg !32 + br i1 %cmp, label %for.body, label %for.cond.cleanup, !dbg !33 + +for.cond.cleanup: ; preds = %for.cond + %2 = bitcast i32* %i to i8*, !dbg !34 + call void @llvm.lifetime.end.p0i8(i64 4, i8* %2) #3, !dbg !34 + br label %for.end + +for.body: ; preds = %for.cond + %3 = load float*, float** %B.addr, align 8, !dbg !35, !tbaa !20 + %4 = load i32, i32* %i, align 4, !dbg !36, !tbaa !28 + %idxprom = sext i32 %4 to i64, !dbg !35 + %arrayidx = getelementptr inbounds float, float* %3, i64 %idxprom, !dbg !35 + %5 = load float, float* %arrayidx, align 4, !dbg !35, !tbaa !37 + %conv = fptosi float %5 to i32, !dbg !35 + %6 = load i32*, i32** %A.addr, align 8, !dbg !39, !tbaa !20 + %7 = load i32, i32* %i, align 4, !dbg !40, !tbaa !28 + %idxprom1 = sext i32 %7 to i64, !dbg !39 + %arrayidx2 = getelementptr inbounds i32, i32* %6, i64 %idxprom1, !dbg !39 + store i32 %conv, i32* %arrayidx2, align 4, !dbg !41, !tbaa !28 + br label %for.inc, !dbg !39 + +for.inc: ; preds = %for.body + %8 = load i32, i32* %i, align 4, !dbg !42, !tbaa !28 + %inc = add nsw i32 %8, 1, !dbg !42 + store i32 %inc, i32* %i, align 4, !dbg !42, !tbaa !28 + br label %for.cond, !dbg !34, !llvm.loop !43 + +for.end: ; preds = %for.cond.cleanup + ret void, !dbg !45 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #2 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #2 + +attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "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"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone speculatable willreturn } +attributes #2 = { argmemonly nounwind willreturn } +attributes #3 = { 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 11.0.0 (git@github.com:llvm/llvm-project.git 1d5da8cd30fce1c0a2c2fa6ba656dbfaa36192c8)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "/data/src/llvm-project/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/vec.c", directory: "/data/build/llvm-project") +!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 11.0.0 (git@github.com:llvm/llvm-project.git 1d5da8cd30fce1c0a2c2fa6ba656dbfaa36192c8)"} +!7 = distinct !DISubprogram(name: "foo", scope: !8, file: !8, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15) +!8 = !DIFile(filename: "src/llvm-project/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/vec.c", directory: "/data") +!9 = !DISubroutineType(types: !10) +!10 = !{null, !11, !13} +!11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) +!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) +!14 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!15 = !{!16, !17, !18} +!16 = !DILocalVariable(name: "A", arg: 1, scope: !7, file: !8, line: 1, type: !11) +!17 = !DILocalVariable(name: "B", arg: 2, scope: !7, file: !8, line: 1, type: !13) +!18 = !DILocalVariable(name: "i", scope: !19, file: !8, line: 3, type: !12) +!19 = distinct !DILexicalBlock(scope: !7, file: !8, line: 3, column: 3) +!20 = !{!21, !21, i64 0} +!21 = !{!"any pointer", !22, i64 0} +!22 = !{!"omnipotent char", !23, i64 0} +!23 = !{!"Simple C/C++ TBAA"} +!24 = !DILocation(line: 1, column: 15, scope: !7) +!25 = !DILocation(line: 1, column: 25, scope: !7) +!26 = !DILocation(line: 3, column: 8, scope: !19) +!27 = !DILocation(line: 3, column: 12, scope: !19) +!28 = !{!29, !29, i64 0} +!29 = !{!"int", !22, i64 0} +!30 = !DILocation(line: 3, column: 19, scope: !31) +!31 = distinct !DILexicalBlock(scope: !19, file: !8, line: 3, column: 3) +!32 = !DILocation(line: 3, column: 21, scope: !31) +!33 = !DILocation(line: 3, column: 3, scope: !19) +!34 = !DILocation(line: 3, column: 3, scope: !31) +!35 = !DILocation(line: 4, column: 12, scope: !31) +!36 = !DILocation(line: 4, column: 14, scope: !31) +!37 = !{!38, !38, i64 0} +!38 = !{!"float", !22, i64 0} +!39 = !DILocation(line: 4, column: 5, scope: !31) +!40 = !DILocation(line: 4, column: 7, scope: !31) +!41 = !DILocation(line: 4, column: 10, scope: !31) +!42 = !DILocation(line: 3, column: 28, scope: !31) +!43 = distinct !{!43, !33, !44} +!44 = !DILocation(line: 4, column: 15, scope: !19) +!45 = !DILocation(line: 5, column: 1, scope: !7) diff --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values.ll.funcsig.expected b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values.ll.funcsig.expected new file mode 100644 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/various_ir_values.ll.funcsig.expected @@ -0,0 +1,162 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature +; Just run it through opt, no passes needed. +; RUN: opt < %s -S | FileCheck %s + +; Function Attrs: nounwind uwtable +define dso_local void @foo(i32* %A, float* %B) #0 !dbg !7 { +; CHECK-LABEL: define {{[^@]+}}@foo +; CHECK-SAME: (i32* [[A:%.*]], float* [[B:%.*]]) [[ATTRIBUTES0:#.*]] [[DEBUG7:!dbg !.*]] +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A_ADDR:%.*]] = alloca i32*, align 8 +; CHECK-NEXT: [[B_ADDR:%.*]] = alloca float*, align 8 +; CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 +; CHECK-NEXT: store i32* [[A]], i32** [[A_ADDR]], align 8, [[TBAA20:!tbaa !.*]] +; CHECK-NEXT: call void @llvm.dbg.declare(metadata i32** [[A_ADDR]], metadata !16, metadata !DIExpression()), [[DEBUG24:!dbg !.*]] +; CHECK-NEXT: store float* [[B]], float** [[B_ADDR]], align 8, [[TBAA20]] +; CHECK-NEXT: call void @llvm.dbg.declare(metadata float** [[B_ADDR]], metadata !17, metadata !DIExpression()), [[DEBUG25:!dbg !.*]] +; CHECK-NEXT: [[TMP0:%.*]] = bitcast i32* [[I]] to i8*, [[DEBUG26:!dbg !.*]] +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* [[TMP0]]) [[ATTRIBUTES3:#.*]], [[DEBUG26]] +; CHECK-NEXT: call void @llvm.dbg.declare(metadata i32* [[I]], metadata !18, metadata !DIExpression()), [[DEBUG27:!dbg !.*]] +; CHECK-NEXT: store i32 0, i32* [[I]], align 4, [[DEBUG27]], [[TBAA28:!tbaa !.*]] +; CHECK-NEXT: br label [[FOR_COND:%.*]], [[DEBUG26]] +; CHECK: for.cond: +; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[I]], align 4, [[DEBUG30:!dbg !.*]], [[TBAA28]] +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[TMP1]], 100, [[DEBUG32:!dbg !.*]] +; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_COND_CLEANUP:%.*]], [[DEBUG33:!dbg !.*]] +; CHECK: for.cond.cleanup: +; CHECK-NEXT: [[TMP2:%.*]] = bitcast i32* [[I]] to i8*, [[DEBUG34:!dbg !.*]] +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* [[TMP2]]) [[ATTRIBUTES3]], [[DEBUG34]] +; CHECK-NEXT: br label [[FOR_END:%.*]] +; CHECK: for.body: +; CHECK-NEXT: [[TMP3:%.*]] = load float*, float** [[B_ADDR]], align 8, [[DEBUG35:!dbg !.*]], [[TBAA20]] +; CHECK-NEXT: [[TMP4:%.*]] = load i32, i32* [[I]], align 4, [[DEBUG36:!dbg !.*]], [[TBAA28]] +; CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[TMP4]] to i64, [[DEBUG35]] +; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds float, float* [[TMP3]], i64 [[IDXPROM]], [[DEBUG35]] +; CHECK-NEXT: [[TMP5:%.*]] = load float, float* [[ARRAYIDX]], align 4, [[DEBUG35]], [[TBAA37:!tbaa !.*]] +; CHECK-NEXT: [[CONV:%.*]] = fptosi float [[TMP5]] to i32, [[DEBUG35]] +; CHECK-NEXT: [[TMP6:%.*]] = load i32*, i32** [[A_ADDR]], align 8, [[DEBUG39:!dbg !.*]], [[TBAA20]] +; CHECK-NEXT: [[TMP7:%.*]] = load i32, i32* [[I]], align 4, [[DEBUG40:!dbg !.*]], [[TBAA28]] +; CHECK-NEXT: [[IDXPROM1:%.*]] = sext i32 [[TMP7]] to i64, [[DEBUG39]] +; CHECK-NEXT: [[ARRAYIDX2:%.*]] = getelementptr inbounds i32, i32* [[TMP6]], i64 [[IDXPROM1]], [[DEBUG39]] +; CHECK-NEXT: store i32 [[CONV]], i32* [[ARRAYIDX2]], align 4, [[DEBUG41:!dbg !.*]], [[TBAA28]] +; CHECK-NEXT: br label [[FOR_INC:%.*]], [[DEBUG39]] +; CHECK: for.inc: +; CHECK-NEXT: [[TMP8:%.*]] = load i32, i32* [[I]], align 4, [[DEBUG42:!dbg !.*]], [[TBAA28]] +; CHECK-NEXT: [[INC:%.*]] = add nsw i32 [[TMP8]], 1, [[DEBUG42]] +; CHECK-NEXT: store i32 [[INC]], i32* [[I]], align 4, [[DEBUG42]], [[TBAA28]] +; CHECK-NEXT: br label [[FOR_COND]], [[DEBUG34]], [[LOOP43:!llvm.loop !.*]] +; CHECK: for.end: +; CHECK-NEXT: ret void, [[DEBUG45:!dbg !.*]] +; +entry: + %A.addr = alloca i32*, align 8 + %B.addr = alloca float*, align 8 + %i = alloca i32, align 4 + store i32* %A, i32** %A.addr, align 8, !tbaa !20 + call void @llvm.dbg.declare(metadata i32** %A.addr, metadata !16, metadata !DIExpression()), !dbg !24 + store float* %B, float** %B.addr, align 8, !tbaa !20 + call void @llvm.dbg.declare(metadata float** %B.addr, metadata !17, metadata !DIExpression()), !dbg !25 + %0 = bitcast i32* %i to i8*, !dbg !26 + call void @llvm.lifetime.start.p0i8(i64 4, i8* %0) #3, !dbg !26 + call void @llvm.dbg.declare(metadata i32* %i, metadata !18, metadata !DIExpression()), !dbg !27 + store i32 0, i32* %i, align 4, !dbg !27, !tbaa !28 + br label %for.cond, !dbg !26 + +for.cond: ; preds = %for.inc, %entry + %1 = load i32, i32* %i, align 4, !dbg !30, !tbaa !28 + %cmp = icmp slt i32 %1, 100, !dbg !32 + br i1 %cmp, label %for.body, label %for.cond.cleanup, !dbg !33 + +for.cond.cleanup: ; preds = %for.cond + %2 = bitcast i32* %i to i8*, !dbg !34 + call void @llvm.lifetime.end.p0i8(i64 4, i8* %2) #3, !dbg !34 + br label %for.end + +for.body: ; preds = %for.cond + %3 = load float*, float** %B.addr, align 8, !dbg !35, !tbaa !20 + %4 = load i32, i32* %i, align 4, !dbg !36, !tbaa !28 + %idxprom = sext i32 %4 to i64, !dbg !35 + %arrayidx = getelementptr inbounds float, float* %3, i64 %idxprom, !dbg !35 + %5 = load float, float* %arrayidx, align 4, !dbg !35, !tbaa !37 + %conv = fptosi float %5 to i32, !dbg !35 + %6 = load i32*, i32** %A.addr, align 8, !dbg !39, !tbaa !20 + %7 = load i32, i32* %i, align 4, !dbg !40, !tbaa !28 + %idxprom1 = sext i32 %7 to i64, !dbg !39 + %arrayidx2 = getelementptr inbounds i32, i32* %6, i64 %idxprom1, !dbg !39 + store i32 %conv, i32* %arrayidx2, align 4, !dbg !41, !tbaa !28 + br label %for.inc, !dbg !39 + +for.inc: ; preds = %for.body + %8 = load i32, i32* %i, align 4, !dbg !42, !tbaa !28 + %inc = add nsw i32 %8, 1, !dbg !42 + store i32 %inc, i32* %i, align 4, !dbg !42, !tbaa !28 + br label %for.cond, !dbg !34, !llvm.loop !43 + +for.end: ; preds = %for.cond.cleanup + ret void, !dbg !45 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #2 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #2 + +attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "denormal-fp-math"="ieee,ieee" "denormal-fp-math-f32"="ieee,ieee" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "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"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone speculatable willreturn } +attributes #2 = { argmemonly nounwind willreturn } +attributes #3 = { 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 11.0.0 (git@github.com:llvm/llvm-project.git 1d5da8cd30fce1c0a2c2fa6ba656dbfaa36192c8)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "/data/src/llvm-project/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/vec.c", directory: "/data/build/llvm-project") +!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 11.0.0 (git@github.com:llvm/llvm-project.git 1d5da8cd30fce1c0a2c2fa6ba656dbfaa36192c8)"} +!7 = distinct !DISubprogram(name: "foo", scope: !8, file: !8, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15) +!8 = !DIFile(filename: "src/llvm-project/llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/vec.c", directory: "/data") +!9 = !DISubroutineType(types: !10) +!10 = !{null, !11, !13} +!11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) +!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) +!14 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!15 = !{!16, !17, !18} +!16 = !DILocalVariable(name: "A", arg: 1, scope: !7, file: !8, line: 1, type: !11) +!17 = !DILocalVariable(name: "B", arg: 2, scope: !7, file: !8, line: 1, type: !13) +!18 = !DILocalVariable(name: "i", scope: !19, file: !8, line: 3, type: !12) +!19 = distinct !DILexicalBlock(scope: !7, file: !8, line: 3, column: 3) +!20 = !{!21, !21, i64 0} +!21 = !{!"any pointer", !22, i64 0} +!22 = !{!"omnipotent char", !23, i64 0} +!23 = !{!"Simple C/C++ TBAA"} +!24 = !DILocation(line: 1, column: 15, scope: !7) +!25 = !DILocation(line: 1, column: 25, scope: !7) +!26 = !DILocation(line: 3, column: 8, scope: !19) +!27 = !DILocation(line: 3, column: 12, scope: !19) +!28 = !{!29, !29, i64 0} +!29 = !{!"int", !22, i64 0} +!30 = !DILocation(line: 3, column: 19, scope: !31) +!31 = distinct !DILexicalBlock(scope: !19, file: !8, line: 3, column: 3) +!32 = !DILocation(line: 3, column: 21, scope: !31) +!33 = !DILocation(line: 3, column: 3, scope: !19) +!34 = !DILocation(line: 3, column: 3, scope: !31) +!35 = !DILocation(line: 4, column: 12, scope: !31) +!36 = !DILocation(line: 4, column: 14, scope: !31) +!37 = !{!38, !38, i64 0} +!38 = !{!"float", !22, i64 0} +!39 = !DILocation(line: 4, column: 5, scope: !31) +!40 = !DILocation(line: 4, column: 7, scope: !31) +!41 = !DILocation(line: 4, column: 10, scope: !31) +!42 = !DILocation(line: 3, column: 28, scope: !31) +!43 = distinct !{!43, !33, !44} +!44 = !DILocation(line: 4, column: 15, scope: !19) +!45 = !DILocation(line: 5, column: 1, scope: !7) diff --git a/llvm/test/tools/UpdateTestChecks/update_test_checks/various_ir_values.test b/llvm/test/tools/UpdateTestChecks/update_test_checks/various_ir_values.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_test_checks/various_ir_values.test @@ -0,0 +1,16 @@ +## Basic test checking that update_test_checks.py works correctly on various "IR value" kinds +# RUN: cp -f %S/Inputs/various_ir_values.ll %t.ll && %update_test_checks %t.ll +# RUN: diff -u %t.ll %S/Inputs/various_ir_values.ll.expected +## Check that running the script again does not change the result: +# RUN: %update_test_checks %t.ll +# RUN: diff -u %t.ll %S/Inputs/various_ir_values.ll.expected +## Also try the --function-signature flag +# RUN: %update_test_checks %t.ll --function-signature +# RUN: diff -u %t.ll %S/Inputs/various_ir_values.ll.funcsig.expected +## Verify that running without the --function-signature flag does not removes +## the -SAME: lines since the generated file will have --function-signature in +## an UTC_ARGS: comment in the first line (from the invocation above) which is +## added to the update invocation below. +# RUN: %update_test_checks %t.ll +# RUN: diff -u %t.ll %S/Inputs/various_ir_values.ll.funcsig.expected + diff --git a/llvm/utils/UpdateTestChecks/common.py b/llvm/utils/UpdateTestChecks/common.py --- a/llvm/utils/UpdateTestChecks/common.py +++ b/llvm/utils/UpdateTestChecks/common.py @@ -226,12 +226,12 @@ def is_same_except_arg_names(self, extrascrub, args_and_sig, attrs): arg_names = set() def drop_arg_names(match): - arg_names.add(match.group(2)) - return match.group(1) + match.group(3) + arg_names.add(match.group(3)) + return match.group(1) + match.group(match.lastindex) def repl_arg_names(match): - if match.group(2) in arg_names: - return match.group(1) + match.group(3) - return match.group(1) + match.group(2) + match.group(3) + if match.group(3) in arg_names: + return match.group(1) + match.group(match.lastindex) + return match.group(1) + match.group(2) + match.group(match.lastindex) if self.attrs != attrs: return False ans0 = IR_VALUE_RE.sub(drop_arg_names, self.args_and_sig) @@ -294,29 +294,74 @@ SCRUB_IR_COMMENT_RE = re.compile(r'\s*;.*') -# Match things that look at identifiers, but only if they are followed by -# spaces, commas, paren, or end of the string -IR_VALUE_RE = re.compile(r'(\s+)%([\w.-]+?)([,\s\(\)]|\Z)') - -NAMELESS_PREFIX = "TMP" +# TODO: We should also derive check lines for global, debug, loop declarations, etc.. + +# The prefixes we use in check lines for unamed values and the prefixes we use +# in IR for these kind of values. The position in these arrays needs to match +# the position of the corresponding froup in the IR_VALUE_RE regexp. Appending +# to these arrays and to the disjunction in the regexp (the middle group with +# subgroups) should work. +NAMELESS_PREFIXES = [r'', r'', r'', 'TMP', 'GLOBAL', 'DEBUG', 'LOOP', 'ATTRIBUTES', 'TBAA'] +IR_VALUE_PREFIXES = [r'', r'', r'', '%', '@', '!dbg !', '!llvm.loop !', '#', '!tbaa !'] + +# The regexp that matches an IR value of a certain kind, e.g., (debug) metadata +# is identified by a number. Must match the above prefixes. +# TODO: Add an option to match for all kinds of global symbols, not only "nameless" ones. +IR_VALUE_KIND_REGEXP = [r'', r'', r'', r'[\w.-]+?', r'[0-9]+?', r'[0-9]+?', r'[0-9]+?', r'[0-9]+?', r'[0-9]+?'] + +# Build the regexp that matches an "IR value". This can be a local variable, +# argument, global, or metadata, anything that is "named". It is important that +# the PREFIX and SUFFIX below only contain a single group, if that changes +# other locations will need adjustment as well. +IR_VALUE_REGEXP_PREFIX = r'(\s+)' +IR_VALUE_REGEXP_STRING = r'' +for i, kind_regexp in enumerate(IR_VALUE_KIND_REGEXP): + if not kind_regexp: + continue + if IR_VALUE_REGEXP_STRING: + IR_VALUE_REGEXP_STRING += '|' + IR_VALUE_REGEXP_STRING += IR_VALUE_PREFIXES[i] + r'(' + kind_regexp + r')' +IR_VALUE_REGEXP_SUFFIX = r'([,\s\(\)]|\Z)' +IR_VALUE_RE = re.compile(IR_VALUE_REGEXP_PREFIX + r'(' + IR_VALUE_REGEXP_STRING + r')' + IR_VALUE_REGEXP_SUFFIX) + +# Check a match for IR_VALUE_RE and inspect it to determine if it was a local +# value, %..., global @..., debug number !dbg !..., etc. See the PREFIXES above. +def get_idx_from_ir_value_match(match): + for i in range(3, match.lastindex): + if match.group(i) is not None: + return i + error("Unable to identify the kind of IR value from the match, guessing") + return 2; + +# See get_idx_from_ir_value_match +def get_name_from_ir_value_match(match): + return match.group(get_idx_from_ir_value_match(match)) + +# Return the nameless prefix we use for this kind or IR value, see also +# get_idx_from_ir_value_match +def get_nameless_prefix_from_ir_value_match(match): + return NAMELESS_PREFIXES[get_idx_from_ir_value_match(match)] + +# Return the IR prefix we use for this kind or IR value, e.g., % for locals, +# see also get_idx_from_ir_value_match +def get_ir_prefix_from_ir_value_match(match): + return IR_VALUE_PREFIXES[get_idx_from_ir_value_match(match)] # Create a FileCheck variable name based on an IR name. -def get_value_name(var): +def get_value_name(var, match): if var.isdigit(): - var = NAMELESS_PREFIX + var + var = get_nameless_prefix_from_ir_value_match(match) + var var = var.replace('.', '_') var = var.replace('-', '_') return var.upper() - # Create a FileCheck variable from regex. -def get_value_definition(var): - return '[[' + get_value_name(var) + ':%.*]]' - +def get_value_definition(var, match): + return '[[' + get_value_name(var, match) + ':' + get_ir_prefix_from_ir_value_match(match) + '.*]]' # Use a FileCheck variable. -def get_value_use(var): - return '[[' + get_value_name(var) + ']]' +def get_value_use(var, match): + return '[[' + get_value_name(var, match) + ']]' # Replace IR value defs and uses with FileCheck variables. def genericize_check_lines(lines, is_analyze, vars_seen): @@ -324,19 +369,21 @@ # a line. We transform variables we haven't seen # into defs, and variables we have seen into uses. def transform_line_vars(match): - var = match.group(2) - if NAMELESS_PREFIX.lower() in var.lower(): - warn("Change IR value name '%s' to prevent possible conflict with scripted FileCheck name." % (var,)) - if var in vars_seen: - rv = get_value_use(var) + pre = get_ir_prefix_from_ir_value_match(match) + var = get_name_from_ir_value_match(match) + for nameless_prefix in NAMELESS_PREFIXES: + if nameless_prefix and re.fullmatch(nameless_prefix + r'[0-9]+?', var, re.IGNORECASE): + warn("Change IR value name '%s' to prevent possible conflict with scripted FileCheck name." % (var,)) + if (pre, var) in vars_seen: + rv = get_value_use(var, match) else: - vars_seen.add(var) - rv = get_value_definition(var) + vars_seen.add((pre, var)) + rv = get_value_definition(var, match) # re.sub replaces the entire regex match # with whatever you return, so we have # to make sure to hand it back everything # including the commas and spaces. - return match.group(1) + rv + match.group(3) + return match.group(1) + rv + match.group(match.lastindex) lines_with_def = [] @@ -345,10 +392,14 @@ line = line.replace('%.', '%dot') # Ignore any comments, since the check lines will too. scrubbed_line = SCRUB_IR_COMMENT_RE.sub(r'', line) - if is_analyze: - lines[i] = scrubbed_line - else: - lines[i] = IR_VALUE_RE.sub(transform_line_vars, scrubbed_line) + lines[i] = scrubbed_line + if not is_analyze: + # It can happen that two matches are back-to-back and for some reason sub + # will not replace both of them. For now we work around this by + # substituting until there is no more match. + changed = True + while changed: + (lines[i], changed) = IR_VALUE_RE.subn(transform_line_vars, lines[i]) return lines