diff --git a/llvm/include/llvm/Remarks/Remark.h b/llvm/include/llvm/Remarks/Remark.h --- a/llvm/include/llvm/Remarks/Remark.h +++ b/llvm/include/llvm/Remarks/Remark.h @@ -131,7 +131,6 @@ /// @{ void print(raw_ostream &OS) const; -private: /// In order to avoid unwanted copies, "delete" the copy constructor. /// If a copy is needed, it should be done through `Remark::clone()`. Remark(const Remark &) = default; diff --git a/llvm/test/CMakeLists.txt b/llvm/test/CMakeLists.txt --- a/llvm/test/CMakeLists.txt +++ b/llvm/test/CMakeLists.txt @@ -119,6 +119,7 @@ llvm-readobj llvm-readelf llvm-reduce + llvm-remark-diff llvm-remark-size-diff llvm-remarkutil llvm-rtdyld diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py --- a/llvm/test/lit.cfg.py +++ b/llvm/test/lit.cfg.py @@ -217,6 +217,7 @@ "llvm-readelf", "llvm-readobj", "llvm-remark-size-diff", + "llvm-remark-diff" "llvm-rtdyld", "llvm-sim", "llvm-size", diff --git a/llvm/test/tools/llvm-remark-diff/Inputs/1-loc-2-args-2.yaml b/llvm/test/tools/llvm-remark-diff/Inputs/1-loc-2-args-2.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/Inputs/1-loc-2-args-2.yaml @@ -0,0 +1,8 @@ +--- !Analysis +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info' \ No newline at end of file diff --git a/llvm/test/tools/llvm-remark-diff/Inputs/1-loc-2-args.yaml b/llvm/test/tools/llvm-remark-diff/Inputs/1-loc-2-args.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/Inputs/1-loc-2-args.yaml @@ -0,0 +1,8 @@ +--- !Analysis +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '1' + - String: 'Info' \ No newline at end of file diff --git a/llvm/test/tools/llvm-remark-diff/Inputs/1-loc-4-args.yaml b/llvm/test/tools/llvm-remark-diff/Inputs/1-loc-4-args.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/Inputs/1-loc-4-args.yaml @@ -0,0 +1,10 @@ +--- !Analysis +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - Arg1: '1' + - Arg2: '2' + - Arg3: '3' + - Arg4: '4' \ No newline at end of file diff --git a/llvm/test/tools/llvm-remark-diff/Inputs/1-loc-6-args.yaml b/llvm/test/tools/llvm-remark-diff/Inputs/1-loc-6-args.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/Inputs/1-loc-6-args.yaml @@ -0,0 +1,12 @@ +--- !Analysis +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - Arg1: '1' + - Arg2: '2' + - Arg3: '3' + - Arg4: '4' + - Arg5: '5' + - Arg6: '6' \ No newline at end of file diff --git a/llvm/test/tools/llvm-remark-diff/Inputs/empty-file.yaml b/llvm/test/tools/llvm-remark-diff/Inputs/empty-file.yaml new file mode 100644 diff --git a/llvm/test/tools/llvm-remark-diff/Inputs/multiple-copies-of-remark-at-loc.yaml b/llvm/test/tools/llvm-remark-diff/Inputs/multiple-copies-of-remark-at-loc.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/Inputs/multiple-copies-of-remark-at-loc.yaml @@ -0,0 +1,24 @@ +--- !Analysis +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info' +--- !Analysis +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info' +--- !Analysis +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info' \ No newline at end of file diff --git a/llvm/test/tools/llvm-remark-diff/Inputs/multiple-copies-of-remark-at-loc2.yaml b/llvm/test/tools/llvm-remark-diff/Inputs/multiple-copies-of-remark-at-loc2.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/Inputs/multiple-copies-of-remark-at-loc2.yaml @@ -0,0 +1,24 @@ +--- !Analysis +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '3' + - String: 'Info' +--- !Analysis +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info' +--- !Analysis +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info' \ No newline at end of file diff --git a/llvm/test/tools/llvm-remark-diff/Inputs/multiple-remarks-at-loc.yaml b/llvm/test/tools/llvm-remark-diff/Inputs/multiple-remarks-at-loc.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/Inputs/multiple-remarks-at-loc.yaml @@ -0,0 +1,32 @@ +--- !Analysis +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '1' + - String: 'Info' +--- !Analysis +Pass: Pass +Name: RemarkName2 +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info' +--- !Analysis +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 3, Column: 2 } +Function: func2 +Args: + - String: '1' + - String: 'Info2' +--- !Missed +Pass: Pass +Name: RemarkName2 +DebugLoc: { File: path/to/anno.c, Line: 3, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info3' diff --git a/llvm/test/tools/llvm-remark-diff/Inputs/multiple-remarks-at-loc2.yaml b/llvm/test/tools/llvm-remark-diff/Inputs/multiple-remarks-at-loc2.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/Inputs/multiple-remarks-at-loc2.yaml @@ -0,0 +1,32 @@ +--- !Analysis +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info2' +--- !Analysis +Pass: Pass +Name: Remark2Name2 +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info' +--- !Analysis +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 3, Column: 2 } +Function: func2 +Args: + - String: '3' + - String: 'Info2' +--- !Analysis +Pass: Pass +Name: RemarkName2 +DebugLoc: { File: path/to/anno.c, Line: 3, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info3' diff --git a/llvm/test/tools/llvm-remark-diff/Inputs/multiple-remarks-at-loc3.yaml b/llvm/test/tools/llvm-remark-diff/Inputs/multiple-remarks-at-loc3.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/Inputs/multiple-remarks-at-loc3.yaml @@ -0,0 +1,40 @@ +--- !Analysis +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info' +--- !Analysis +Pass: Pass +Name: RemarkName2 +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info' +--- !Analysis +Pass: Pass +Name: RemarkName2 +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info' +--- !Analysis +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 3, Column: 2 } +Function: func2 +Args: + - String: '1' + - String: 'Info2' +--- !Missed +Pass: Pass +Name: RemarkName2 +DebugLoc: { File: path/to/anno.c, Line: 3, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info3' \ No newline at end of file diff --git a/llvm/test/tools/llvm-remark-diff/Inputs/remarks-missed.yaml b/llvm/test/tools/llvm-remark-diff/Inputs/remarks-missed.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/Inputs/remarks-missed.yaml @@ -0,0 +1,32 @@ +--- !Analysis +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info2' +--- !Analysis +Pass: Pass +Name: RemarkName2 +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info' +--- !Analysis +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 3, Column: 2 } +Function: func2 +Args: + - String: '3' + - String: 'Info2' +--- !Analysis +Pass: Pass +Name: RemarkName2 +DebugLoc: { File: path/to/anno.c, Line: 3, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info3' diff --git a/llvm/test/tools/llvm-remark-diff/Inputs/remarks-passed.yaml b/llvm/test/tools/llvm-remark-diff/Inputs/remarks-passed.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/Inputs/remarks-passed.yaml @@ -0,0 +1,32 @@ +--- !Passed +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info2' +--- !Passed +Pass: Pass +Name: RemarkName2 +DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info' +--- !Passed +Pass: Pass +Name: RemarkName +DebugLoc: { File: path/to/anno.c, Line: 3, Column: 2 } +Function: func2 +Args: + - String: '3' + - String: 'Info2' +--- !Passed +Pass: Pass +Name: RemarkName2 +DebugLoc: { File: path/to/anno.c, Line: 3, Column: 2 } +Function: func0 +Args: + - String: '2' + - String: 'Info3' diff --git a/llvm/test/tools/llvm-remark-diff/diff-at-1-loc.test b/llvm/test/tools/llvm-remark-diff/diff-at-1-loc.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/diff-at-1-loc.test @@ -0,0 +1,35 @@ +RUN: llvm-remark-diff %p/Inputs/1-loc-2-args.yaml %p/Inputs/1-loc-2-args-2.yaml --parser=yaml | FileCheck %s + +CHECK: { +CHECK-NEXT: "Diff": [ +CHECK-NEXT: { +CHECK-NEXT: "HasSameHeaderObj": [ +CHECK-NEXT: { +CHECK-NEXT: "Diff": { +CHECK-NEXT: "ArgsAtA": [ +CHECK-NEXT: { +CHECK-NEXT: "String": "1" +CHECK-NEXT: } +CHECK-NEXT: ], +CHECK-NEXT: "ArgsAtB": [ +CHECK-NEXT: { +CHECK-NEXT: "String": "2" +CHECK-NEXT: } +CHECK-NEXT: ] +CHECK-NEXT: }, +CHECK-NEXT: "FunctionName": "func0", +CHECK-NEXT: "PassName": "Pass", +CHECK-NEXT: "RemarkName": "RemarkName", +CHECK-NEXT: "RemarkType": "Analysis" +CHECK-NEXT: } +CHECK-NEXT: ], +CHECK-NEXT: "Location": "func0 path/to/anno.c 1:2", +CHECK-NEXT: "OnlyA": [], +CHECK-NEXT: "OnlyB": [] +CHECK-NEXT: } +CHECK-NEXT: ], +CHECK-NEXT: "Files": { +CHECK-NEXT: "A": "{{.*}}Inputs/1-loc-2-args.yaml", +CHECK-NEXT: "B": "{{.*}}Inputs/1-loc-2-args-2.yaml" +CHECK-NEXT: } +CHECK-NEXT: } \ No newline at end of file diff --git a/llvm/test/tools/llvm-remark-diff/diff-different-num-args.test b/llvm/test/tools/llvm-remark-diff/diff-different-num-args.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/diff-different-num-args.test @@ -0,0 +1,33 @@ +RUN: llvm-remark-diff %p/Inputs/1-loc-4-args.yaml %p/Inputs/1-loc-6-args.yaml --parser=yaml | FileCheck %s + +; CHECK: { +; CHECK-NEXT: "Diff": [ +; CHECK-NEXT: { +; CHECK-NEXT: "HasSameHeaderObj": [ +; CHECK-NEXT: { +; CHECK-NEXT: "Diff": { +; CHECK-NEXT: "ArgsAtB": [ +; CHECK-NEXT: { +; CHECK-NEXT: "Arg5": "5" +; CHECK-NEXT: }, +; CHECK-NEXT: { +; CHECK-NEXT: "Arg6": "6" +; CHECK-NEXT: } +; CHECK-NEXT: ] +; CHECK-NEXT: }, +; CHECK-NEXT: "FunctionName": "func0", +; CHECK-NEXT: "PassName": "Pass", +; CHECK-NEXT: "RemarkName": "RemarkName", +; CHECK-NEXT: "RemarkType": "Analysis" +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "Location": "func0 path/to/anno.c 1:2", +; CHECK-NEXT: "OnlyA": [], +; CHECK-NEXT: "OnlyB": [] +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "Files": { +; CHECK-NEXT: "A": "{{.*}}Inputs/1-loc-4-args.yaml", +; CHECK-NEXT: "B": "{{.*}}Inputs/1-loc-6-args.yaml" +; CHECK-NEXT: } +; CHECK-NEXT: } \ No newline at end of file diff --git a/llvm/test/tools/llvm-remark-diff/diff-empty-file.test b/llvm/test/tools/llvm-remark-diff/diff-empty-file.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/diff-empty-file.test @@ -0,0 +1,3 @@ +RUN: not llvm-remark-diff %p/Inputs/empty-file.yaml %p/Inputs/empty-file.yaml --parser=yaml 2>&1 | FileCheck %s + +; CHECK: llvm-remark-diff: error: YAML:1:1: error: document root is not of mapping type. \ No newline at end of file diff --git a/llvm/test/tools/llvm-remark-diff/diff-only-common-remarks.test b/llvm/test/tools/llvm-remark-diff/diff-only-common-remarks.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/diff-only-common-remarks.test @@ -0,0 +1,33 @@ +RUN: llvm-remark-diff --only-show-common-remarks %p/Inputs/multiple-remarks-at-loc.yaml %p/Inputs/multiple-remarks-at-loc3.yaml --parser=yaml | FileCheck %s + +; CHECK: { +; CHECK-NEXT: "Diff": [ +; CHECK-NEXT: { +; CHECK-NEXT: "HasSameHeaderObj": [ +; CHECK-NEXT: { +; CHECK-NEXT: "Diff": { +; CHECK-NEXT: "ArgsAtA": [ +; CHECK-NEXT: { +; CHECK-NEXT: "String": "1" +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "ArgsAtB": [ +; CHECK-NEXT: { +; CHECK-NEXT: "String": "2" +; CHECK-NEXT: } +; CHECK-NEXT: ] +; CHECK-NEXT: }, +; CHECK-NEXT: "FunctionName": "func0", +; CHECK-NEXT: "PassName": "Pass", +; CHECK-NEXT: "RemarkName": "RemarkName", +; CHECK-NEXT: "RemarkType": "Analysis" +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "Location": "func0 path/to/anno.c 1:2" +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "Files": { +; CHECK-NEXT: "A": "{{.*}}Inputs/multiple-remarks-at-loc.yaml", +; CHECK-NEXT: "B": "{{.*}}Inputs/multiple-remarks-at-loc3.yaml" +; CHECK-NEXT: } +; CHECK-NEXT: } diff --git a/llvm/test/tools/llvm-remark-diff/diff-remark-type.test b/llvm/test/tools/llvm-remark-diff/diff-remark-type.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/diff-remark-type.test @@ -0,0 +1,61 @@ +RUN: llvm-remark-diff --only-show-common-remarks --show-remark-type-diff-only %p/Inputs/remarks-missed.yaml %p/Inputs/remarks-passed.yaml --parser=yaml | FileCheck %s + +; CHECK: { +; CHECK-NEXT: "Diff": [ +; CHECK-NEXT: { +; CHECK-NEXT: "HasSameHeaderObj": [ +; CHECK-NEXT: { +; CHECK-NEXT: "Diff": { +; CHECK-NEXT: "RemarkTypeA": "Analysis", +; CHECK-NEXT: "RemarkTypeB": "Passed" +; CHECK-NEXT: }, +; CHECK-NEXT: "FunctionName": "func0", +; CHECK-NEXT: "PassName": "Pass", +; CHECK-NEXT: "RemarkName": "RemarkName" +; CHECK-NEXT: }, +; CHECK-NEXT: { +; CHECK-NEXT: "Diff": { +; CHECK-NEXT: "RemarkTypeA": "Analysis", +; CHECK-NEXT: "RemarkTypeB": "Passed" +; CHECK-NEXT: }, +; CHECK-NEXT: "FunctionName": "func0", +; CHECK-NEXT: "PassName": "Pass", +; CHECK-NEXT: "RemarkName": "RemarkName2" +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "Location": "func0 path/to/anno.c 1:2" +; CHECK-NEXT: }, +; CHECK-NEXT: { +; CHECK-NEXT: "HasSameHeaderObj": [ +; CHECK-NEXT: { +; CHECK-NEXT: "Diff": { +; CHECK-NEXT: "RemarkTypeA": "Analysis", +; CHECK-NEXT: "RemarkTypeB": "Passed" +; CHECK-NEXT: }, +; CHECK-NEXT: "FunctionName": "func2", +; CHECK-NEXT: "PassName": "Pass", +; CHECK-NEXT: "RemarkName": "RemarkName" +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "Location": "func2 path/to/anno.c 3:2" +; CHECK-NEXT: }, +; CHECK-NEXT: { +; CHECK-NEXT: "HasSameHeaderObj": [ +; CHECK-NEXT: { +; CHECK-NEXT: "Diff": { +; CHECK-NEXT: "RemarkTypeA": "Analysis", +; CHECK-NEXT: "RemarkTypeB": "Passed" +; CHECK-NEXT: }, +; CHECK-NEXT: "FunctionName": "func0", +; CHECK-NEXT: "PassName": "Pass", +; CHECK-NEXT: "RemarkName": "RemarkName2" +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "Location": "func0 path/to/anno.c 3:2" +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "Files": { +; CHECK-NEXT: "A": "{{.*}}Inputs/remarks-missed.yaml", +; CHECK-NEXT: "B": "{{.*}}Inputs/remarks-passed.yaml" +; CHECK-NEXT: } +; CHECK-NEXT: } \ No newline at end of file diff --git a/llvm/test/tools/llvm-remark-diff/diff-with-same-headers-only.test b/llvm/test/tools/llvm-remark-diff/diff-with-same-headers-only.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/diff-with-same-headers-only.test @@ -0,0 +1,44 @@ +RUN: llvm-remark-diff --show-remark-type-diff-only %p/Inputs/multiple-copies-of-remark-at-loc.yaml %p/Inputs/multiple-copies-of-remark-at-loc2.yaml --parser=yaml | FileCheck %s +RUN: llvm-remark-diff --show-arg-diff-only %p/Inputs/multiple-copies-of-remark-at-loc.yaml %p/Inputs/multiple-copies-of-remark-at-loc2.yaml --parser=yaml | FileCheck %s -check-prefix=CHECK-ARGS + +; CHECK: { +; CHECK-NEXT: "Diff": [], +; CHECK-NEXT: "Files": { +; CHECK-NEXT: "A": "{{.*}}Inputs/multiple-copies-of-remark-at-loc.yaml", +; CHECK-NEXT: "B": "{{.*}}Inputs/multiple-copies-of-remark-at-loc2.yaml" +; CHECK-NEXT: } +; CHECK-NEXT:} + +; CHECK-ARGS: { +; CHECK-ARGS-NEXT: "Diff": [ +; CHECK-ARGS-NEXT: { +; CHECK-ARGS-NEXT: "HasSameHeaderObj": [ +; CHECK-ARGS-NEXT: { +; CHECK-ARGS-NEXT: "Diff": { +; CHECK-ARGS-NEXT: "ArgsAtA": [ +; CHECK-ARGS-NEXT: { +; CHECK-ARGS-NEXT: "String": "2" +; CHECK-ARGS-NEXT: } +; CHECK-ARGS-NEXT: ], +; CHECK-ARGS-NEXT: "ArgsAtB": [ +; CHECK-ARGS-NEXT: { +; CHECK-ARGS-NEXT: "String": "3" +; CHECK-ARGS-NEXT: } +; CHECK-ARGS-NEXT: ] +; CHECK-ARGS-NEXT: }, +; CHECK-ARGS-NEXT: "FunctionName": "func0", +; CHECK-ARGS-NEXT: "PassName": "Pass", +; CHECK-ARGS-NEXT: "RemarkName": "RemarkName", +; CHECK-ARGS-NEXT: "RemarkType": "Analysis" +; CHECK-ARGS-NEXT: } +; CHECK-ARGS-NEXT: ], +; CHECK-ARGS-NEXT: "Location": "func0 path/to/anno.c 1:2" +; CHECK-ARGS-NEXT: "OnlyA": [], +; CHECK-ARGS-NEXT: "OnlyB": [] +; CHECK-ARGS-NEXT: } +; CHECK-ARGS-NEXT: ], +; CHECK-ARGS-NEXT: "Files": { +; CHECK-ARGS-NEXT: "A": "{{.*}}Inputs/multiple-copies-of-remark-at-loc.yaml", +; CHECK-ARGS-NEXT: "B": "{{.*}}Inputs/multiple-copies-of-remark-at-loc2.yaml" +; CHECK-ARGS-NEXT: } +; CHECK-ARGS-NEXT: } diff --git a/llvm/test/tools/llvm-remark-diff/diff-wrong-input-file.test b/llvm/test/tools/llvm-remark-diff/diff-wrong-input-file.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/diff-wrong-input-file.test @@ -0,0 +1,3 @@ +RUN: not llvm-remark-diff %p/Inputs/multiple-remarks-at-loc.yaml %p/Inputs/non-existing-file.yaml --parser=yaml 2>&1 | FileCheck %s + +; CHECK: llvm-remark-diff: error: Cannot open file '{{.*}}Inputs/non-existing-file.yaml': No such file or directory \ No newline at end of file diff --git a/llvm/test/tools/llvm-remark-diff/multiple-remarks-at-loc.test b/llvm/test/tools/llvm-remark-diff/multiple-remarks-at-loc.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/multiple-remarks-at-loc.test @@ -0,0 +1,222 @@ +RUN: llvm-remark-diff %p/Inputs/multiple-remarks-at-loc.yaml %p/Inputs/multiple-remarks-at-loc2.yaml --parser=yaml | FileCheck %s +RUN: llvm-remark-diff -v %p/Inputs/multiple-remarks-at-loc.yaml %p/Inputs/multiple-remarks-at-loc2.yaml --parser=yaml | FileCheck %s -check-prefix=CHECK-VERBOSE + +; CHECK: { +; CHECK-NEXT: "Diff": [ +; CHECK-NEXT: { +; CHECK-NEXT: "HasSameHeaderObj": [ +; CHECK-NEXT: { +; CHECK-NEXT: "Diff": { +; CHECK-NEXT: "ArgsAtA": [ +; CHECK-NEXT: { +; CHECK-NEXT: "String": "1" +; CHECK-NEXT: }, +; CHECK-NEXT: { +; CHECK-NEXT: "String": "Info" +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "ArgsAtB": [ +; CHECK-NEXT: { +; CHECK-NEXT: "String": "2" +; CHECK-NEXT: }, +; CHECK-NEXT: { +; CHECK-NEXT: "String": "Info2" +; CHECK-NEXT: } +; CHECK-NEXT: ] +; CHECK-NEXT: }, +; CHECK-NEXT: "FunctionName": "func0", +; CHECK-NEXT: "PassName": "Pass", +; CHECK-NEXT: "RemarkName": "RemarkName", +; CHECK-NEXT: "RemarkType": "Analysis" +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "Location": "func0 path/to/anno.c 1:2", +; CHECK-NEXT: "OnlyA": [ +; CHECK-NEXT: { +; CHECK-NEXT: "FunctionName": "func0", +; CHECK-NEXT: "PassName": "Pass", +; CHECK-NEXT: "RemarkName": "RemarkName2", +; CHECK-NEXT: "RemarkType": "Analysis" +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "OnlyB": [ +; CHECK-NEXT: { +; CHECK-NEXT: "FunctionName": "func0", +; CHECK-NEXT: "PassName": "Pass", +; CHECK-NEXT: "RemarkName": "Remark2Name2", +; CHECK-NEXT: "RemarkType": "Analysis" +; CHECK-NEXT: } +; CHECK-NEXT: ] +; CHECK-NEXT: }, +; CHECK-NEXT: { +; CHECK-NEXT: "HasSameHeaderObj": [ +; CHECK-NEXT: { +; CHECK-NEXT: "Diff": { +; CHECK-NEXT: "ArgsAtA": [ +; CHECK-NEXT: { +; CHECK-NEXT: "String": "1" +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "ArgsAtB": [ +; CHECK-NEXT: { +; CHECK-NEXT: "String": "3" +; CHECK-NEXT: } +; CHECK-NEXT: ] +; CHECK-NEXT: }, +; CHECK-NEXT: "FunctionName": "func2", +; CHECK-NEXT: "PassName": "Pass", +; CHECK-NEXT: "RemarkName": "RemarkName", +; CHECK-NEXT: "RemarkType": "Analysis" +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "Location": "func2 path/to/anno.c 3:2", +; CHECK-NEXT: "OnlyA": [], +; CHECK-NEXT: "OnlyB": [] +; CHECK-NEXT: }, +; CHECK-NEXT: { +; CHECK-NEXT: "HasSameHeaderObj": [ +; CHECK-NEXT: { +; CHECK-NEXT: "Diff": { +; CHECK-NEXT: "RemarkTypeA": "Missed", +; CHECK-NEXT: "RemarkTypeB": "Analysis" +; CHECK-NEXT: }, +; CHECK-NEXT: "FunctionName": "func0", +; CHECK-NEXT: "PassName": "Pass", +; CHECK-NEXT: "RemarkName": "RemarkName2" +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "Location": "func0 path/to/anno.c 3:2", +; CHECK-NEXT: "OnlyA": [], +; CHECK-NEXT: "OnlyB": [] +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "Files": { +; CHECK-NEXT: "A": "{{.*}}Inputs/multiple-remarks-at-loc.yaml", +; CHECK-NEXT: "B": "{{.*}}Inputs/multiple-remarks-at-loc2.yaml" +; CHECK-NEXT: } +; CHECK-NEXT: } + +; CHECK-VERBOSE: { +; CHECK-VERBOSE-NEXT: "Diff": [ +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "HasSameHeaderObj": [ +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "ArgsInBoth": [], +; CHECK-VERBOSE-NEXT: "Diff": { +; CHECK-VERBOSE-NEXT: "ArgsAtA": [ +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "String": "1" +; CHECK-VERBOSE-NEXT: }, +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "String": "Info" +; CHECK-VERBOSE-NEXT: } +; CHECK-VERBOSE-NEXT: ], +; CHECK-VERBOSE-NEXT: "ArgsAtB": [ +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "String": "2" +; CHECK-VERBOSE-NEXT: }, +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "String": "Info2" +; CHECK-VERBOSE-NEXT: } +; CHECK-VERBOSE-NEXT: ] +; CHECK-VERBOSE-NEXT: }, +; CHECK-VERBOSE-NEXT: "FunctionName": "func0", +; CHECK-VERBOSE-NEXT: "PassName": "Pass", +; CHECK-VERBOSE-NEXT: "RemarkName": "RemarkName", +; CHECK-VERBOSE-NEXT: "RemarkType": "Analysis" +; CHECK-VERBOSE-NEXT: } +; CHECK-VERBOSE-NEXT: ], +; CHECK-VERBOSE-NEXT: "Location": "func0 path/to/anno.c 1:2", +; CHECK-VERBOSE-NEXT: "OnlyA": [ +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "Args": [ +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "String": "2" +; CHECK-VERBOSE-NEXT: }, +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "String": "Info" +; CHECK-VERBOSE-NEXT: } +; CHECK-VERBOSE-NEXT: ], +; CHECK-VERBOSE-NEXT: "FunctionName": "func0", +; CHECK-VERBOSE-NEXT: "PassName": "Pass", +; CHECK-VERBOSE-NEXT: "RemarkName": "RemarkName2", +; CHECK-VERBOSE-NEXT: "RemarkType": "Analysis" +; CHECK-VERBOSE-NEXT: } +; CHECK-VERBOSE-NEXT: ], +; CHECK-VERBOSE-NEXT: "OnlyB": [ +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "Args": [ +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "String": "2" +; CHECK-VERBOSE-NEXT: }, +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "String": "Info" +; CHECK-VERBOSE-NEXT: } +; CHECK-VERBOSE-NEXT: ], +; CHECK-VERBOSE-NEXT: "FunctionName": "func0", +; CHECK-VERBOSE-NEXT: "PassName": "Pass", +; CHECK-VERBOSE-NEXT: "RemarkName": "Remark2Name2", +; CHECK-VERBOSE-NEXT: "RemarkType": "Analysis" +; CHECK-VERBOSE-NEXT: } +; CHECK-VERBOSE-NEXT: ] +; CHECK-VERBOSE-NEXT: }, +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "HasSameHeaderObj": [ +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "ArgsInBoth": [ +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "String": "Info2" +; CHECK-VERBOSE-NEXT: } +; CHECK-VERBOSE-NEXT: ], +; CHECK-VERBOSE-NEXT: "Diff": { +; CHECK-VERBOSE-NEXT: "ArgsAtA": [ +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "String": "1" +; CHECK-VERBOSE-NEXT: } +; CHECK-VERBOSE-NEXT: ], +; CHECK-VERBOSE-NEXT: "ArgsAtB": [ +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "String": "3" +; CHECK-VERBOSE-NEXT: } +; CHECK-VERBOSE-NEXT: ] +; CHECK-VERBOSE-NEXT: }, +; CHECK-VERBOSE-NEXT: "FunctionName": "func2", +; CHECK-VERBOSE-NEXT: "PassName": "Pass", +; CHECK-VERBOSE-NEXT: "RemarkName": "RemarkName", +; CHECK-VERBOSE-NEXT: "RemarkType": "Analysis" +; CHECK-VERBOSE-NEXT: } +; CHECK-VERBOSE-NEXT: ], +; CHECK-VERBOSE-NEXT: "Location": "func2 path/to/anno.c 3:2", +; CHECK-VERBOSE-NEXT: "OnlyA": [], +; CHECK-VERBOSE-NEXT: "OnlyB": [] +; CHECK-VERBOSE-NEXT: }, +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "HasSameHeaderObj": [ +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "ArgsInBoth": [ +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "String": "2" +; CHECK-VERBOSE-NEXT: }, +; CHECK-VERBOSE-NEXT: { +; CHECK-VERBOSE-NEXT: "String": "Info3" +; CHECK-VERBOSE-NEXT: } +; CHECK-VERBOSE-NEXT: ], +; CHECK-VERBOSE-NEXT: "Diff": { +; CHECK-VERBOSE-NEXT: "RemarkTypeA": "Missed", +; CHECK-VERBOSE-NEXT: "RemarkTypeB": "Analysis" +; CHECK-VERBOSE-NEXT: }, +; CHECK-VERBOSE-NEXT: "FunctionName": "func0", +; CHECK-VERBOSE-NEXT: "PassName": "Pass", +; CHECK-VERBOSE-NEXT: "RemarkName": "RemarkName2" +; CHECK-VERBOSE-NEXT: } +; CHECK-VERBOSE-NEXT: ], +; CHECK-VERBOSE-NEXT: "Location": "func0 path/to/anno.c 3:2", +; CHECK-VERBOSE-NEXT: "OnlyA": [], +; CHECK-VERBOSE-NEXT: "OnlyB": [] +; CHECK-VERBOSE-NEXT: } +; CHECK-VERBOSE-NEXT: ], +; CHECK-VERBOSE-NEXT: "Files": { +; CHECK-VERBOSE-NEXT: "A": "{{.*}}Inputs/multiple-remarks-at-loc.yaml", +; CHECK-VERBOSE-NEXT: "B": "{{.*}}Inputs/multiple-remarks-at-loc2.yaml" +; CHECK-VERBOSE-NEXT: } +; CHECK-VERBOSE-NEXT: } \ No newline at end of file diff --git a/llvm/test/tools/llvm-remark-diff/multiple-remarks-at-loc2.test b/llvm/test/tools/llvm-remark-diff/multiple-remarks-at-loc2.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/multiple-remarks-at-loc2.test @@ -0,0 +1,35 @@ +RUN: llvm-remark-diff %p/Inputs/multiple-copies-of-remark-at-loc.yaml %p/Inputs/multiple-copies-of-remark-at-loc2.yaml --parser=yaml | FileCheck %s + +; CHECK: { +; CHECK-NEXT: "Diff": [ +; CHECK-NEXT: { +; CHECK-NEXT: "HasSameHeaderObj": [ +; CHECK-NEXT: { +; CHECK-NEXT: "Diff": { +; CHECK-NEXT: "ArgsAtA": [ +; CHECK-NEXT: { +; CHECK-NEXT: "String": "2" +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "ArgsAtB": [ +; CHECK-NEXT: { +; CHECK-NEXT: "String": "3" +; CHECK-NEXT: } +; CHECK-NEXT: ] +; CHECK-NEXT: }, +; CHECK-NEXT: "FunctionName": "func0", +; CHECK-NEXT: "PassName": "Pass", +; CHECK-NEXT: "RemarkName": "RemarkName", +; CHECK-NEXT: "RemarkType": "Analysis" +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "Location": "func0 path/to/anno.c 1:2", +; CHECK-NEXT: "OnlyA": [], +; CHECK-NEXT: "OnlyB": [] +; CHECK-NEXT: } +; CHECK-NEXT: ], +; CHECK-NEXT: "Files": { +; CHECK-NEXT: "A": "{{.*}}Inputs/multiple-copies-of-remark-at-loc.yaml", +; CHECK-NEXT: "B": "{{.*}}Inputs/multiple-copies-of-remark-at-loc2.yaml" +; CHECK-NEXT: } +; CHECK-NEXT: } \ No newline at end of file diff --git a/llvm/test/tools/llvm-remark-diff/no-diff.test b/llvm/test/tools/llvm-remark-diff/no-diff.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-remark-diff/no-diff.test @@ -0,0 +1,9 @@ +RUN: llvm-remark-diff %p/Inputs/1-loc-2-args.yaml %p/Inputs/1-loc-2-args.yaml --parser=yaml | FileCheck %s + +; CHECK: { +; CHECK-NEXT: "Diff": [], +; CHECK-NEXT: "Files": { +; CHECK-NEXT: "A": "{{.*}}Inputs/1-loc-2-args.yaml", +; CHECK-NEXT: "B": "{{.*}}Inputs/1-loc-2-args.yaml" +; CHECK-NEXT: } +; CHECK-NEXT: } diff --git a/llvm/tools/llvm-remark-diff/CMakeLists.txt b/llvm/tools/llvm-remark-diff/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-remark-diff/CMakeLists.txt @@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS + Demangle + Remarks + Support + ) + + +add_llvm_tool(llvm-remark-diff + RemarkDiff.cpp + ) diff --git a/llvm/tools/llvm-remark-diff/RemarkDiff.cpp b/llvm/tools/llvm-remark-diff/RemarkDiff.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-remark-diff/RemarkDiff.cpp @@ -0,0 +1,508 @@ +//===-------------- llvm-remark-diff/RemarkDiff.cpp -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Diffs remarks between two remark files. +/// The tool offers different modes for comparing two versions of remarks. +/// 1. Look through common remarks between two files. +/// 2. Compare the remark type. This is useful to check if an optimzation +/// changed from passing to failing. +/// 3. Compare remark arguments. This is useful to check if a remark argument +/// changed after some compiler change. +/// +/// The results are presented as a json file. +/// +//===----------------------------------------------------------------------===// + +#include "llvm-c/Remarks.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Remarks/Remark.h" +#include "llvm/Remarks/RemarkFormat.h" +#include "llvm/Remarks/RemarkParser.h" +#include "llvm/Remarks/YAMLRemarkSerializer.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/WithColor.h" + +using namespace llvm; +using namespace remarks; + +static ExitOnError ExitOnErr; +static cl::OptionCategory RemarkDiffCategory("llvm-remark-diff options"); + +static cl::opt RemarkFileA(cl::Positional, + cl::desc(""), + cl::Required, + cl::cat(RemarkDiffCategory)); +static cl::opt RemarkFileB(cl::Positional, + cl::desc(""), + cl::Required, + cl::cat(RemarkDiffCategory)); +static cl::opt Verbose( + "v", cl::init(false), + cl::desc("Output detailed difference for remarks. By default the tool will " + "only show the remark name, type and location. If the flag is " + "added we display the arguments that are different."), + cl::cat(RemarkDiffCategory)); +static cl::opt + ShowArgDiffOnly("show-arg-diff-only", cl::init(false), + cl::desc("Show only the remarks that have the same header " + "and differ in arguments"), + cl::cat(RemarkDiffCategory)); +static cl::opt OnlyShowCommonRemarks( + "only-show-common-remarks", cl::init(false), + cl::desc("Ignore any remarks that don't exist in both and " + "."), + cl::cat(RemarkDiffCategory)); +static cl::opt ShowRemarkTypeDiffOnly( + "show-remark-type-diff-only", cl::init(false), + cl::desc("Only show diff if remarks have the same header but different " + "type"), + cl::cat(RemarkDiffCategory)); +static cl::opt StrictCompare( + "use-strict-compare", cl::init(false), + cl::desc("By default remark arguments may contain location information. If " + "the flag is added then it will display arguments that are " + "different if the location differs."), + cl::cat(RemarkDiffCategory)); + +static cl::opt InputFormat( + "parser", cl::desc("Input remark format to parse"), + cl::values(clEnumValN(Format::YAML, "yaml", "YAML"), + clEnumValN(Format::Bitstream, "bitstream", "Bitstream")), + cl::cat(RemarkDiffCategory)); + +static cl::opt OutputFileName("o", cl::init("-"), + cl::cat(RemarkDiffCategory), + cl::desc("Output"), + cl::value_desc("file")); + +static bool OnlyShowArgOrTypeDiffRemarks = false; + +/// \returns A ToolOutputFile which can be used for outputting the results of +/// some tool mode. +/// \p OutputFileName is the desired destination. +/// \p Flags controls whether or not the file is opened for writing in text +/// mode, as a binary, etc. See sys::fs::OpenFlags for more detail. +static Expected> +getOutputFileWithFlags(StringRef OutputFileName, sys::fs::OpenFlags Flags) { + if (OutputFileName == "") + OutputFileName = "-"; + std::error_code ErrorCode; + auto OF = std::make_unique(OutputFileName, ErrorCode, Flags); + if (ErrorCode) + return errorCodeToError(ErrorCode); + return std::move(OF); +} + +/// \returns A MemoryBuffer for the input file on success, and an Error +/// otherwise. +static Expected> +getInputMemoryBuffer(StringRef InputFileName) { + auto MaybeBuf = MemoryBuffer::getFileOrSTDIN(InputFileName); + if (auto ErrorCode = MaybeBuf.getError()) + return createStringError(ErrorCode, + Twine("Cannot open file '" + InputFileName + + "': " + ErrorCode.message())); + return std::move(*MaybeBuf); +} + +/// Represent a location which combines RemarkLoc and function name. +struct DebugLocation { + StringRef SourceFilePath; + StringRef FunctionName; + unsigned SourceLine = 0; + unsigned SourceColumn = 0; + DebugLocation() = default; + DebugLocation(StringRef SourceFilePath, StringRef FunctionName, + unsigned SourceLine, unsigned SourceColumn) + : SourceFilePath(SourceFilePath), FunctionName(FunctionName), + SourceLine(SourceLine), SourceColumn(SourceColumn) {} + +public: + std::string str() const { + return FunctionName.str() + " " + SourceFilePath.str() + " " + + std::to_string(SourceLine) + ":" + std::to_string(SourceColumn); + } +}; + +template <> struct DenseMapInfo { + static inline DebugLocation getEmptyKey() { return DebugLocation(); } + + static inline DebugLocation getTombstoneKey() { + auto Loc = DebugLocation(); + Loc.SourceFilePath = StringRef( + reinterpret_cast(~static_cast(1)), 0); + Loc.FunctionName = StringRef( + reinterpret_cast(~static_cast(1)), 0); + Loc.SourceColumn = ~0U - 1; + Loc.SourceLine = ~0U - 1; + return Loc; + } + + static unsigned getHashValue(const DebugLocation &Key) { + return hash_combine(Key.SourceFilePath, Key.FunctionName, Key.SourceLine, + Key.SourceColumn); + } + + static bool isEqual(const DebugLocation &LHS, const DebugLocation &RHS) { + return std::make_tuple(LHS.SourceFilePath, LHS.FunctionName, LHS.SourceLine, + LHS.SourceColumn) == + std::make_tuple(RHS.SourceFilePath, RHS.FunctionName, RHS.SourceLine, + RHS.SourceColumn); + } +}; + +/// \returns if the remark R has the same header. +/// A Remark header is defined to be RemarkName, PassName, +/// FunctionName, Loc and Hotness +static bool hasSameHeader(const Remark &LHS, const Remark &RHS) { + return LHS.RemarkName == RHS.RemarkName && LHS.PassName == RHS.PassName && + LHS.FunctionName == RHS.FunctionName && LHS.Loc == RHS.Loc && + LHS.Hotness == RHS.Hotness; +} + +/// \returns true if remark arguments are equal ignoring argument Loc when +static bool isEqualIgnoringArgDebugLoc(const Argument &Arg1, + const Argument &Arg) { + return Arg1.Key == Arg.Key && Arg1.Val == Arg.Val; +} + +/// \returns true if remarks are equal ignoring argument Loc when +/// comparing two remarks. +static bool isEqualIgnoringArgDebugLoc(const Remark &LHS, const Remark &RHS) { + if (!hasSameHeader(LHS, RHS) || LHS.RemarkType != RHS.RemarkType) + return false; + if (LHS.Args.size() != RHS.Args.size()) + return false; + for (unsigned Idx = 0; Idx < LHS.Args.size(); Idx++) + if (!isEqualIgnoringArgDebugLoc(LHS.Args[Idx], RHS.Args[Idx])) + return false; + return true; +} + +/// \returns json array representation of a vecotor of remark arguments. +static json::Array remarkArgsToJson(const SmallVectorImpl &Args) { + json::Array ArgArray; + for (auto Arg : Args) { + json::Object ArgPair({{Arg.Key.str(), Arg.Val.str()}}); + ArgArray.push_back(std::move(ArgPair)); + } + return ArgArray; +} + +static bool isRemarkEqual(const Remark &LHS, const Remark &RHS) { + return StrictCompare ? LHS == RHS : isEqualIgnoringArgDebugLoc(LHS, RHS); +} + +static bool isRemarkArgumentEqual(const Argument &LHS, const Argument &RHS) { + return StrictCompare ? LHS == RHS : isEqualIgnoringArgDebugLoc(LHS, RHS); +} + +/// \returns remark representation as a json object. +static json::Object remarkToJSON(const Remark *Remark) { + json::Object RemarkJSON; + RemarkJSON["RemarkName"] = Remark->RemarkName.str(); + RemarkJSON["FunctionName"] = Remark->FunctionName.str(); + RemarkJSON["PassName"] = Remark->PassName.str(); + RemarkJSON["RemarkType"] = typeToStr(Remark->RemarkType); + if (Verbose) + RemarkJSON["Args"] = remarkArgsToJson(Remark->Args); + return RemarkJSON; +} + +/// Represents the diff at a remark where the remark header is the same and the +/// two versions of the remark differ in type or arguments. +struct DiffAtRemark { + const Remark *BaseRemark; + std::optional> RemarkTypeDiff; + SmallVector OnlyA; + SmallVector OnlyB; + SmallVector InBoth; + +public: + /// represent remark diff as a json object where the header is the same as the + /// baseline remark and diff json key represents the differences between the + /// two versions of the remark. + json::Object toJson() const { + json::Object Object; + Object["FunctionName"] = BaseRemark->FunctionName; + Object["PassName"] = BaseRemark->PassName; + Object["RemarkName"] = BaseRemark->RemarkName; + // display remark type if it is the same between the two remarks. + if (!RemarkTypeDiff) + Object["RemarkType"] = typeToStr(BaseRemark->RemarkType); + json::Array InBothJSON; + json::Array OnlyAJson; + json::Array OnlyBJson; + for (auto Arg : InBoth) { + json::Object ArgPair({{Arg.Key.str(), Arg.Val.str()}}); + InBothJSON.push_back(std::move(ArgPair)); + } + for (auto Arg : OnlyA) { + json::Object ArgPair({{Arg.Key.str(), Arg.Val.str()}}); + OnlyAJson.push_back(std::move(ArgPair)); + } + for (auto Arg : OnlyB) { + json::Object ArgPair({{Arg.Key.str(), Arg.Val.str()}}); + OnlyBJson.push_back(std::move(ArgPair)); + } + json::Object Diff; + if (RemarkTypeDiff) { + Diff["RemarkTypeA"] = typeToStr(RemarkTypeDiff->first); + Diff["RemarkTypeB"] = typeToStr(RemarkTypeDiff->second); + } + // Only display common remark arguments if verbose is passed. + if (Verbose) + Object["ArgsInBoth"] = remarkArgsToJson( + SmallVector(InBoth.begin(), InBoth.end())); + if (!OnlyAJson.empty()) + Diff["ArgsAtA"] = remarkArgsToJson(OnlyA); + if (!OnlyBJson.empty()) + Diff["ArgsAtB"] = remarkArgsToJson(OnlyB); + Object["Diff"] = std::move(Diff); + return Object; + } +}; + +/// Represents the diff at a debug location. This can be unique remarks that +/// exist in file a or file b or remarks that share the same header but differ +/// in remark type or arguments. Any common remarks at the location are +/// discarded. +struct DiffAtLoc { + DebugLocation Loc; + SmallVector OnlyA; + SmallVector OnlyB; + // list of remarks that are different but share the same header. + SmallVector HasTheSameHeader; + +public: + bool isEmpty() { + return OnlyA.empty() && OnlyB.empty() && HasTheSameHeader.empty(); + } + json::Object toJson() const { + json::Object Obj; + json::Array DiffObj; + json::Array OnlyAObj; + json::Array OnlyBObj; + json::Array HasSameHeaderObj; + for (auto *R : OnlyA) + OnlyAObj.push_back(remarkToJSON(R)); + for (auto *R : OnlyB) + OnlyBObj.push_back(remarkToJSON(R)); + for (auto R : HasTheSameHeader) + HasSameHeaderObj.push_back(R.toJson()); + Obj["Location"] = Loc.str(); + if (!OnlyShowCommonRemarks) { + Obj["OnlyA"] = std::move(OnlyAObj); + Obj["OnlyB"] = std::move(OnlyBObj); + } + Obj["HasSameHeaderObj"] = std::move(HasSameHeaderObj); + return Obj; + } +}; + +static Error parseRemarkFile( + StringRef Buffer, RemarkParser &Parser, + MapVector> &DebugLoc2RemarkMap) { + auto MaybeRemark = Parser.next(); + for (; MaybeRemark; MaybeRemark = Parser.next()) { + Remark &Remark = **MaybeRemark; + // Only consider remarks if they have debugLoc + if (Remark.Loc.has_value()) { + DebugLocation Key(Remark.Loc->SourceFilePath, Remark.FunctionName, + Remark.Loc->SourceLine, Remark.Loc->SourceColumn); + auto Iter = DebugLoc2RemarkMap.insert({Key, {}}); + Iter.first->second.push_back(Remark.clone()); + } + } + + auto E = MaybeRemark.takeError(); + if (!E.isA()) + return E; + consumeError(std::move(E)); + return Error::success(); +} + +/// \returns DiffAtRemark object where it will look through the arguments and +/// remark type in RA and RB. +/// \p RA remark from file a. +/// \p RB remark from file b. +static DiffAtRemark computeArgDiffAtRemark(const Remark *RA, const Remark *RB) { + DiffAtRemark Diff; + Diff.BaseRemark = RA; + unsigned ArgIdx = 0; + // Loop through the remarks in RA and RB in order comparing both. + for (; ArgIdx < std::min(RA->Args.size(), RB->Args.size()); ArgIdx++) { + if (isRemarkArgumentEqual(RA->Args[ArgIdx], RB->Args[ArgIdx])) + Diff.InBoth.push_back(RA->Args[ArgIdx]); + else { + Diff.OnlyA.push_back(RA->Args[ArgIdx]); + Diff.OnlyB.push_back(RB->Args[ArgIdx]); + } + } + + // Add the remaining remarks if they exist to OnlyA or OnlyB. + const SmallVectorImpl &RemainingArgs = + RA->Args.size() > RB->Args.size() ? RA->Args : RB->Args; + const bool IsARemaining = RA->Args.size() > RB->Args.size() ? true : false; + for (; ArgIdx < RemainingArgs.size(); ArgIdx++) + if (IsARemaining) + Diff.OnlyA.push_back(RemainingArgs[ArgIdx]); + else + Diff.OnlyB.push_back(RemainingArgs[ArgIdx]); + + // Compare remark type between RA and RB. + if (RA->RemarkType != RB->RemarkType) + Diff.RemarkTypeDiff = {RA->RemarkType, RB->RemarkType}; + return Diff; +} + +static void computeDiffAtLoc(const SmallVector &RemarksA, + const SmallVector &RemarksB, + DiffAtLoc *DiffLoc) { + // A set of remarks where either they have a remark at the other file equaling + // them or share the same header. This is used to reduce the duplicates when + // looking at a location. If a remark has a counterpart in the other file then + // we aren't interested if it shares the same header with another remark. + SmallSet FoundRemarks; + SmallVector, 4> HasSameHeader; + for (auto &RA : RemarksA) { + for (auto &RB : RemarksB) { + if (FoundRemarks.contains(&RB)) + continue; + if (isRemarkEqual(RA, RB)) { + FoundRemarks.insert(&RA); + FoundRemarks.insert(&RB); + break; + } + if (hasSameHeader(RA, RB)) { + HasSameHeader.push_back({&RA, &RB}); + FoundRemarks.insert(&RB); + FoundRemarks.insert(&RA); + break; + } + } + if (!FoundRemarks.contains(&RA) && !OnlyShowCommonRemarks) + DiffLoc->OnlyA.push_back(&RA); + } + + for (auto &RB : RemarksB) { + if (!FoundRemarks.contains(&RB) && !OnlyShowCommonRemarks) + DiffLoc->OnlyB.push_back(&RB); + } + for (auto &[RA, RB] : HasSameHeader) { + + if (!OnlyShowArgOrTypeDiffRemarks) + DiffLoc->HasTheSameHeader.push_back(computeArgDiffAtRemark(RA, RB)); + // If -show-remark-type-diff-only is true only compare remarks that differ + // in type. + else if (ShowRemarkTypeDiffOnly && RA->RemarkType != RB->RemarkType) + DiffLoc->HasTheSameHeader.push_back(computeArgDiffAtRemark(RA, RB)); + // If `show-arg-diff-only` is true only compare remarks that differ in + // arguments. + else if (ShowArgDiffOnly && RA->RemarkType == RB->RemarkType) + DiffLoc->HasTheSameHeader.push_back(computeArgDiffAtRemark(RA, RB)); + } +} + +static void +computeDiff(SetVector &DebugLocs, + MapVector> &DebugLoc2RemarkA, + MapVector> &DebugLoc2RemarkB, + SmallVector &DiffAtLocs) { + // Add all debug locs from file a and file b to a unique set of Locations. + for (const DebugLocation &Loc : DebugLocs) { + const auto &ARemarksAtLoc = DebugLoc2RemarkA[Loc]; + const auto &BRemarksBtLoc = DebugLoc2RemarkB[Loc]; + DiffAtLoc DiffLoc; + DiffLoc.Loc = Loc; + computeDiffAtLoc(ARemarksAtLoc, BRemarksBtLoc, &DiffLoc); + if (!DiffLoc.isEmpty()) + DiffAtLocs.push_back(DiffLoc); + } +} + +static Error printDiff(StringRef InputFileNameA, StringRef InputFileNameB, + SmallVector &LocsDiff) { + // Create the output buffer. + auto MaybeOF = getOutputFileWithFlags(OutputFileName, + /*Flags = */ sys::fs::OF_TextWithCRLF); + if (!MaybeOF) + return MaybeOF.takeError(); + std::unique_ptr OF = std::move(*MaybeOF); + json::Object Output; + json::Object Files( + {{"A", InputFileNameA.str()}, {"B", InputFileNameB.str()}}); + Output["Files"] = std::move(Files); + json::Array DiffArr; + for (auto LocDiff : LocsDiff) + DiffArr.push_back(LocDiff.toJson()); + Output["Diff"] = std::move(DiffArr); + json::OStream JOS(OF->os(), 2); + JOS.value(std::move(Output)); + OF->os() << '\n'; + OF->keep(); + return Error::success(); +} + +static Error createRemarkDiff() { + // Get memory buffer for file a and file b. + auto RemarkAMaybeBuf = getInputMemoryBuffer(RemarkFileA); + if (!RemarkAMaybeBuf) + return RemarkAMaybeBuf.takeError(); + auto RemarkBMaybeBuf = getInputMemoryBuffer(RemarkFileB); + if (!RemarkBMaybeBuf) + return RemarkBMaybeBuf.takeError(); + // Create parsers for file a and file b remarks. + auto MaybeParser1 = + createRemarkParser(InputFormat, (*RemarkAMaybeBuf)->getBuffer()); + if (!MaybeParser1) + return MaybeParser1.takeError(); + auto MaybeParser2 = + createRemarkParser(InputFormat, (*RemarkBMaybeBuf)->getBuffer()); + if (!MaybeParser2) + return MaybeParser2.takeError(); + // Order the remarks based on their debug location and function name. + MapVector> DebugLoc2RemarkA; + MapVector> DebugLoc2RemarkB; + if (auto E = parseRemarkFile((*RemarkAMaybeBuf)->getBuffer(), **MaybeParser1, + DebugLoc2RemarkA)) + return E; + if (auto E = parseRemarkFile((*RemarkBMaybeBuf)->getBuffer(), **MaybeParser2, + DebugLoc2RemarkB)) + return E; + SetVector DebugLocs; + for (const auto &[Loc, _] : DebugLoc2RemarkA) + DebugLocs.insert(Loc); + for (const auto &[Loc, _] : DebugLoc2RemarkB) + DebugLocs.insert(Loc); + SmallVector LocsDiff; + computeDiff(DebugLocs, DebugLoc2RemarkA, DebugLoc2RemarkB, LocsDiff); + if (auto E = printDiff(RemarkFileA, RemarkFileB, LocsDiff)) + return E; + return Error::success(); +} + +int main(int argc, const char **argv) { + InitLLVM X(argc, argv); + cl::HideUnrelatedOptions(RemarkDiffCategory); + cl::ParseCommandLineOptions(argc, argv, "Remark Diff tool\n"); + OnlyShowArgOrTypeDiffRemarks = ShowArgDiffOnly || ShowRemarkTypeDiffOnly; + ExitOnErr.setBanner(std::string(argv[0]) + ": error: "); + ExitOnErr(createRemarkDiff()); +}