diff --git a/llvm/docs/CommandGuide/llvm-remarkutil.rst b/llvm/docs/CommandGuide/llvm-remarkutil.rst
index d13a51707a93c..2e4953eb188ae 100644
--- a/llvm/docs/CommandGuide/llvm-remarkutil.rst
+++ b/llvm/docs/CommandGuide/llvm-remarkutil.rst
@@ -110,6 +110,80 @@ if `--use-debug-loc` is passed then the CSV will include the source path, line n
Source,Function,Count
path:line:column,foo,3
+.. _count_subcommand:
+
+count
+~~~~~
+
+..program:: llvm-remarkutil count
+
+USAGE: :program:`llvm-remarkutil` count [*options*]
+
+Summary
+^^^^^^^
+
+:program:`llvm-remarkutil count` counts `remarks ` based on specified properties.
+By default the tool counts remarks based on how many occour in a source file or function or total for the generated remark file.
+The tool also supports collecting count based on specific remark arguments. The specified arguments should have an integer value to be able to report a count.
+
+The tool contains utilities to filter the remark count based on remark name, pass name, argument value and remark type.
+OPTIONS
+-------
+
+.. option:: --parser=
+
+ Select the type of input remark parser. Required.
+ * ``yaml``: The tool will parse YAML remarks.
+ * ``bitstream``: The tool will parse bitstream remarks.
+
+.. option:: --count-by
+ Select option to collect remarks by.
+ * ``remark-name``: count how many individual remarks exist.
+ * ``arg``: count remarks based on specified arguments passed by --(r)args. The argument value must be a number.
+
+.. option:: --group-by=
+ group count of remarks by property.
+ * ``source``: Count will be collected per source path. Remarks with no debug location will not be counted.
+ * ``function``: Count is collected per function.
+ * ``function-with-loc``: Count is collected per function per source. Remarks with no debug location will not be counted.
+ * ``Total``: Report a count for the provided remark file.
+
+.. option:: --args[=arguments]
+ If `count-by` is set to `arg` this flag can be used to collect from specified remark arguments represented as a comma seperated string.
+ The arguments must have a numeral value to be able to count remarks by
+
+.. option:: --rargs[=arguments]
+ If `count-by` is set to `arg` this flag can be used to collect from specified remark arguments using regular expression.
+ The arguments must have a numeral value to be able to count remarks by
+
+.. option:: --pass-name[=]
+ Filter count by pass name.
+
+.. option:: --rpass-name[=]
+ Filter count by pass name using regular expressions.
+
+.. option:: --remark-name[=]
+ Filter count by remark name.
+
+.. option:: --rremark-name[=]
+ Filter count by remark name using regular expressions.
+
+.. option:: --filter-arg-by[=]
+ Filter count by argument value.
+
+.. option:: --rfilter-arg-by[=]
+ Filter count by argument value using regular expressions.
+
+.. option:: --remark-type=
+ Filter remarks by type with the following options.
+ * ``unknown``
+ * ``passed``
+ * ``missed``
+ * ``analysis``
+ * ``analysis-fp-commute``
+ * ``analysis-aliasing``
+ * ``failure``
+
.. _size-diff_subcommand:
size-diff
diff --git a/llvm/include/llvm/Remarks/Remark.h b/llvm/include/llvm/Remarks/Remark.h
index a66f7ed73f2f5..de81c5a992805 100644
--- a/llvm/include/llvm/Remarks/Remark.h
+++ b/llvm/include/llvm/Remarks/Remark.h
@@ -52,6 +52,10 @@ struct Argument {
/// Implement operator<< on Argument.
void print(raw_ostream &OS) const;
+ /// Return the value of argument as int.
+ std::optional getValAsInt() const;
+ /// Check if the argument value can be parsed as int.
+ bool isValInt() const;
};
// Create wrappers for C Binding types (see CBindingWrapping.h).
diff --git a/llvm/lib/Remarks/Remark.cpp b/llvm/lib/Remarks/Remark.cpp
index 1b248db41747e..ef42271a3c8da 100644
--- a/llvm/lib/Remarks/Remark.cpp
+++ b/llvm/lib/Remarks/Remark.cpp
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/Remarks/Remark.h"
+#include "llvm/ADT/APInt.h"
#include "llvm/ADT/ArrayRef.h"
#include
@@ -25,6 +26,16 @@ std::string Remark::getArgsAsMsg() const {
return OS.str();
}
+/// Returns the value of a specified key parsed from StringRef.
+std::optional Argument::getValAsInt() const {
+ APInt KeyVal;
+ if (Val.getAsInteger(10, KeyVal))
+ return std::nullopt;
+ return KeyVal.getSExtValue();
+}
+
+bool Argument::isValInt() const { return getValAsInt().has_value(); }
+
void RemarkLocation::print(raw_ostream &OS) const {
OS << "{ "
<< "File: " << SourceFilePath << ", Line: " << SourceLine
diff --git a/llvm/test/tools/llvm-remarkutil/annotation-count-with-dbg-loc.test b/llvm/test/tools/llvm-remarkutil/annotation-count-with-dbg-loc.test
index cdc731c003f6a..673a38001f760 100644
--- a/llvm/test/tools/llvm-remarkutil/annotation-count-with-dbg-loc.test
+++ b/llvm/test/tools/llvm-remarkutil/annotation-count-with-dbg-loc.test
@@ -1,7 +1,14 @@
RUN: llvm-remarkutil annotation-count --use-debug-loc --parser=yaml --annotation-type=remark %p/Inputs/annotation-count-with-dbg-loc.yaml | FileCheck %s
RUN: llvm-remarkutil yaml2bitstream %p/Inputs/annotation-count-with-dbg-loc.yaml | llvm-remarkutil annotation-count --use-debug-loc --parser=bitstream --annotation-type=remark | FileCheck %s
+RUN: llvm-remarkutil count --parser=yaml --count-by=arg --group-by=function-with-loc --remark-name="AnnotationSummary" %p/Inputs/annotation-count-with-dbg-loc.yaml | FileCheck %s --check-prefix=COUNT-CHECK
+RUN: llvm-remarkutil yaml2bitstream %p/Inputs/annotation-count-with-dbg-loc.yaml | llvm-remarkutil count --parser=bitstream --count-by=arg --group-by=function-with-loc --remark-name="AnnotationSummary" | FileCheck %s --check-prefix=COUNT-CHECK
; CHECK-LABEL: Source,Function,Count
; CHECK: path/to/anno.c:1:2,func1,1
; CHECK: path/to/anno2.c:1:2,func2,2
; CHECK: path/to/anno3.c:1:2,func3,3
+
+; COUNT-CHECK-LABEL: FuctionWithDebugLoc,count
+; COUNT-CHECK: path/to/anno.c:func1,1
+; COUNT-CHECK: path/to/anno2.c:func2,2
+; COUNT-CHECK: path/to/anno3.c:func3,3
diff --git a/llvm/test/tools/llvm-remarkutil/annotation-count.test b/llvm/test/tools/llvm-remarkutil/annotation-count.test
index 73582402201dc..e006220c64f38 100644
--- a/llvm/test/tools/llvm-remarkutil/annotation-count.test
+++ b/llvm/test/tools/llvm-remarkutil/annotation-count.test
@@ -1,7 +1,14 @@
RUN: llvm-remarkutil annotation-count --parser=yaml --annotation-type=remark %p/Inputs/annotation-count.yaml | FileCheck %s
RUN: llvm-remarkutil yaml2bitstream %p/Inputs/annotation-count.yaml | llvm-remarkutil annotation-count --parser=bitstream --annotation-type=remark | FileCheck %s
+RUN: llvm-remarkutil count --parser=yaml --count-by=arg --group-by=function --remark-name="AnnotationSummary" %p/Inputs/annotation-count.yaml | FileCheck %s --check-prefix=COUNT-CHECK
+RUN: llvm-remarkutil yaml2bitstream %p/Inputs/annotation-count.yaml | llvm-remarkutil count --parser=bitstream --count-by=arg --group-by=function --remark-name="AnnotationSummary" | FileCheck %s --check-prefix=COUNT-CHECK
; CHECK-LABEL: Function,Count
; CHECK: func1,1
; CHECK: func2,2
; CHECK: func3,3
+
+; COUNT-CHECK-LABEL: Function,count
+; COUNT-CHECK: func1,1
+; COUNT-CHECK: func2,2
+; COUNT-CHECK: func3,3
diff --git a/llvm/test/tools/llvm-remarkutil/broken-bitstream-remark.test b/llvm/test/tools/llvm-remarkutil/broken-bitstream-remark.test
index 515c096f540eb..78011aece08f7 100644
--- a/llvm/test/tools/llvm-remarkutil/broken-bitstream-remark.test
+++ b/llvm/test/tools/llvm-remarkutil/broken-bitstream-remark.test
@@ -1,4 +1,6 @@
RUN: not llvm-remarkutil bitstream2yaml %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
RUN: not llvm-remarkutil instruction-count --parser=bitstream %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
RUN: not llvm-remarkutil annotation-count --parser=bitstream --annotation-type=remark %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
+RUN: not llvm-remarkutil count --parser=bitstream %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
+
CHECK: error: Unknown magic number: expecting RMRK, got --- .
diff --git a/llvm/test/tools/llvm-remarkutil/broken-yaml-remark.test b/llvm/test/tools/llvm-remarkutil/broken-yaml-remark.test
index df87e3db80442..0f06506603363 100644
--- a/llvm/test/tools/llvm-remarkutil/broken-yaml-remark.test
+++ b/llvm/test/tools/llvm-remarkutil/broken-yaml-remark.test
@@ -1,4 +1,6 @@
RUN: not llvm-remarkutil yaml2bitstream %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
RUN: not llvm-remarkutil instruction-count --parser=yaml %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
RUN: not llvm-remarkutil annotation-count --parser=yaml --annotation-type=remark %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
+RUN: not llvm-remarkutil count --parser=yaml %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
+
CHECK: error: Type, Pass, Name or Function missing
diff --git a/llvm/test/tools/llvm-remarkutil/count/Inputs/remark-count-by.yaml b/llvm/test/tools/llvm-remarkutil/count/Inputs/remark-count-by.yaml
new file mode 100644
index 0000000000000..3bd0783b7a0a1
--- /dev/null
+++ b/llvm/test/tools/llvm-remarkutil/count/Inputs/remark-count-by.yaml
@@ -0,0 +1,43 @@
+--- !Analysis
+Pass: generic-remarks-pass
+Name: Remark
+DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 }
+Function: func1
+Args:
+ - count1: '1'
+ - count2: '2'
+ - count3: '3'
+ - count4: '4'
+ - String: ' instructions with '
+ - type: remark
+--- !Analysis
+Pass: generic-remarks-pass
+Name: Remark2
+DebugLoc: { File: path/to/anno2.c, Line: 1, Column: 2 }
+Function: func1
+Args:
+ - count1: '1'
+ - count2: '2'
+ - count3: '3'
+ - String: ' instructions with '
+ - type: remark
+--- !Analysis
+Pass: generic-remarks-pass
+Name: Remark3
+DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 }
+Function: func1
+Args:
+ - count1: '1'
+ - String: ' instructions with '
+ - type: remark
+--- !Analysis
+Pass: generic-remarks-pass
+Name: Remark
+DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 }
+Function: func2
+Args:
+ - count1: '1'
+ - count2: '2'
+ - count3: '3'
+ - String: ' instructions with '
+ - type: remark
diff --git a/llvm/test/tools/llvm-remarkutil/count/Inputs/remark-filter-by.yaml b/llvm/test/tools/llvm-remarkutil/count/Inputs/remark-filter-by.yaml
new file mode 100644
index 0000000000000..e9267bd940484
--- /dev/null
+++ b/llvm/test/tools/llvm-remarkutil/count/Inputs/remark-filter-by.yaml
@@ -0,0 +1,40 @@
+--- !Analysis
+Pass: generic-remarks-pass
+Name: Remark
+DebugLoc: { File: path/to/anno2.c, Line: 1, Column: 2 }
+Function: func1
+Args:
+ - count1: '1'
+ - String: ' instructions with '
+ - type: remark
+--- !Analysis
+Pass: generic-remarks-pass2
+Name: Remark2
+DebugLoc: { File: path/to/anno2.c, Line: 1, Column: 2 }
+Function: func1
+Args:
+ - count1: '1'
+ - count2: '2'
+ - count3: '3'
+ - String: ' instructions with '
+ - type: remark
+--- !Missed
+Pass: generic-remarks-pass
+Name: Remark3
+DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 }
+Function: func1
+Args:
+ - count1: '1'
+ - String: ' instructions with '
+ - type: remark
+--- !Passed
+Pass: generic-remarks-pass
+Name: Remark
+DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 }
+Function: func1
+Args:
+ - count1: '1'
+ - count2: '2'
+ - count3: '3'
+ - String: ' instructions with '
+ - type: remark
diff --git a/llvm/test/tools/llvm-remarkutil/count/Inputs/remark-group-by.yaml b/llvm/test/tools/llvm-remarkutil/count/Inputs/remark-group-by.yaml
new file mode 100644
index 0000000000000..5f9222214f2c7
--- /dev/null
+++ b/llvm/test/tools/llvm-remarkutil/count/Inputs/remark-group-by.yaml
@@ -0,0 +1,54 @@
+--- !Analysis
+Pass: generic-remarks-pass
+Name: Remark
+DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 }
+Function: func1
+Args:
+ - count: '1'
+ - String: ' instructions with '
+ - type: remark
+--- !Missed
+Pass: generic-remarks-pass
+Name: Remark
+DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 }
+Function: func1
+Args:
+ - count: '3'
+ - String: ' instructions with '
+ - type: remark
+--- !Passed
+Pass: generic-remarks-pass
+Name: Remark
+DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 }
+Function: func2
+Args:
+ - count: '3'
+ - String: ' instructions with '
+ - type: remark
+--- !Analysis
+Pass: generic-remarks-pass2
+Name: Remark
+DebugLoc: { File: path/to/anno3.c, Line: 1, Column: 2 }
+Function: func1
+Args:
+ - count: '3'
+ - String: ' instructions with '
+ - type: remark
+--- !Analysis
+Pass: generic-remarks-pass3
+Name: Remark
+DebugLoc: { File: path/to/anno.c, Line: 1, Column: 2 }
+Function: func2
+Args:
+ - count: '2'
+ - String: ' instructions with '
+ - type: remark
+--- !Analysis
+Pass: generic-remarks-pass4
+Name: Remark
+DebugLoc: { File: path/to/anno2.c, Line: 1, Column: 2 }
+Function: func3
+Args:
+ - count: '2'
+ - String: ' instructions with '
+ - type: remark
diff --git a/llvm/test/tools/llvm-remarkutil/count/count-by-keys.test b/llvm/test/tools/llvm-remarkutil/count/count-by-keys.test
new file mode 100644
index 0000000000000..dc414620c3aa5
--- /dev/null
+++ b/llvm/test/tools/llvm-remarkutil/count/count-by-keys.test
@@ -0,0 +1,20 @@
+RUN: llvm-remarkutil count --parser=yaml --count-by=arg --group-by=source %p/Inputs/remark-count-by.yaml | FileCheck %s
+RUN: llvm-remarkutil count --parser=yaml --count-by=arg --group-by=function %p/Inputs/remark-count-by.yaml | FileCheck %s --check-prefix=CHECKFUNC
+RUN: llvm-remarkutil count --parser=yaml --count-by=arg --group-by=function-with-loc %p/Inputs/remark-count-by.yaml | FileCheck %s --check-prefix=CHECKFUNCLOC
+RUN: llvm-remarkutil count --parser=yaml --count-by=arg --group-by=total %p/Inputs/remark-count-by.yaml | FileCheck %s --check-prefix=CHECKTOTAL
+
+; CHECK-LABEL: Source,count1,count2,count3,count4
+; CHECK: path/to/anno.c,3,4,6,4
+; CHECK: path/to/anno2.c,1,2,3,0
+
+; CHECKFUNC-LABEL: Function,count1,count2,count3,count4
+; CHECKFUNC: func1,3,4,6,4
+; CHECKFUNC: func2,1,2,3,0
+
+; CHECKFUNCLOC-LABEL: FuctionWithDebugLoc,count1,count2,count3,count4
+; CHECKFUNCLOC: path/to/anno.c:func1,2,2,3,4
+; CHECKFUNCLOC: path/to/anno.c:func2,1,2,3,0
+; CHECKFUNCLOC: path/to/anno2.c:func1,1,2,3,0
+
+; CHECKTOTAL-LABEL: Total,count1,count2,count3,count4
+; CHECKTOTAL: Total,4,6,9,4
diff --git a/llvm/test/tools/llvm-remarkutil/count/count-by-remark.test b/llvm/test/tools/llvm-remarkutil/count/count-by-remark.test
new file mode 100644
index 0000000000000..b0248b9b6ec71
--- /dev/null
+++ b/llvm/test/tools/llvm-remarkutil/count/count-by-remark.test
@@ -0,0 +1,20 @@
+RUN: llvm-remarkutil count --parser=yaml --count-by=remark-name --group-by=source %p/Inputs/remark-count-by.yaml | FileCheck %s
+RUN: llvm-remarkutil count --parser=yaml --count-by=remark-name --group-by=function %p/Inputs/remark-count-by.yaml | FileCheck %s --check-prefix=CHECKFUNC
+RUN: llvm-remarkutil count --parser=yaml --count-by=remark-name --group-by=function-with-loc %p/Inputs/remark-count-by.yaml | FileCheck %s --check-prefix=CHECKFUNCLOC
+RUN: llvm-remarkutil count --parser=yaml --count-by=remark-name --group-by=total %p/Inputs/remark-count-by.yaml | FileCheck %s --check-prefix=CHECKTOTAL
+
+; CHECK-LABEL: Source,Count
+; CHECK: path/to/anno.c,3
+; CHECK: path/to/anno2.c,1
+
+; CHECKFUNC-LABEL: Function,Count
+; CHECKFUNC: func1,3
+; CHECKFUNC: func2,1
+
+; CHECKFUNCLOC-LABEL: FuctionWithDebugLoc,Count
+; CHECKFUNCLOC: path/to/anno.c:func1,2
+; CHECKFUNCLOC: path/to/anno.c:func2,1
+; CHECKFUNCLOC: path/to/anno2.c:func1,1
+
+; CHECKTOTAL-LABEL: Total,Count
+; CHECKTOTAL: Total,4
diff --git a/llvm/test/tools/llvm-remarkutil/count/filter-by-pass-name.test b/llvm/test/tools/llvm-remarkutil/count/filter-by-pass-name.test
new file mode 100644
index 0000000000000..481d6fd2f5820
--- /dev/null
+++ b/llvm/test/tools/llvm-remarkutil/count/filter-by-pass-name.test
@@ -0,0 +1,10 @@
+RUN: llvm-remarkutil count --parser=yaml --pass-name=generic-remarks-pass %p/Inputs/remark-filter-by.yaml | FileCheck %s
+RUN: llvm-remarkutil count --parser=yaml --rpass-name=.* %p/Inputs/remark-filter-by.yaml | FileCheck %s --check-prefix=CHECKALL
+
+; CHECK-LABEL: Source,Count
+; CHECK: path/to/anno.c,2
+; CHECK: path/to/anno2.c,1
+
+; CHECKALL-LABEL: Source,Count
+; CHECKALL: path/to/anno.c,2
+; CHECKALL: path/to/anno2.c,2
diff --git a/llvm/test/tools/llvm-remarkutil/count/filter-by-remark-name.test b/llvm/test/tools/llvm-remarkutil/count/filter-by-remark-name.test
new file mode 100644
index 0000000000000..20684d57f648c
--- /dev/null
+++ b/llvm/test/tools/llvm-remarkutil/count/filter-by-remark-name.test
@@ -0,0 +1,10 @@
+RUN: llvm-remarkutil count --parser=yaml --remark-name=Remark %p/Inputs/remark-filter-by.yaml | FileCheck %s
+RUN: llvm-remarkutil count --parser=yaml --rremark-name=R.* %p/Inputs/remark-filter-by.yaml | FileCheck %s --check-prefix=CHECKALL
+
+; CHECK-LABEL: Source,Count
+; CHECK: path/to/anno.c,1
+; CHECK: path/to/anno2.c,1
+
+; CHECKALL-LABEL: Source,Count
+; CHECKALL: path/to/anno.c,2
+; CHECKALL: path/to/anno2.c,2
diff --git a/llvm/test/tools/llvm-remarkutil/count/filter-by-type.test b/llvm/test/tools/llvm-remarkutil/count/filter-by-type.test
new file mode 100644
index 0000000000000..c392fe43aa199
--- /dev/null
+++ b/llvm/test/tools/llvm-remarkutil/count/filter-by-type.test
@@ -0,0 +1,16 @@
+RUN: llvm-remarkutil count --parser=yaml --remark-type=missed %p/Inputs/remark-filter-by.yaml | FileCheck %s --check-prefix=MISSED
+RUN: llvm-remarkutil count --parser=yaml --remark-type=passed %p/Inputs/remark-filter-by.yaml | FileCheck %s --check-prefix=PASSED
+RUN: llvm-remarkutil count --parser=yaml --remark-type=analysis %p/Inputs/remark-filter-by.yaml | FileCheck %s --check-prefix=ANALYSIS
+RUN: llvm-remarkutil count --parser=yaml --remark-type=unknown %p/Inputs/remark-filter-by.yaml | FileCheck %s --check-prefix=UNKNOWN
+
+; MISSED-LABEL: Source,Count
+; MISSED: path/to/anno.c,1
+
+; PASSED-LABEL: Source,Count
+; PASSED: path/to/anno.c,1
+
+; ANALYSIS-LABEL: Source,Count
+; ANALYSIS: path/to/anno2.c,2
+
+; UNKNOWN: Source,Count
+; UNKNOWN-EMPTY:
diff --git a/llvm/test/tools/llvm-remarkutil/count/group-by-function-with-loc.test b/llvm/test/tools/llvm-remarkutil/count/group-by-function-with-loc.test
new file mode 100644
index 0000000000000..2ceb45b38f29f
--- /dev/null
+++ b/llvm/test/tools/llvm-remarkutil/count/group-by-function-with-loc.test
@@ -0,0 +1,7 @@
+RUN: llvm-remarkutil count --parser=yaml --group-by=function-with-loc %p/Inputs/remark-group-by.yaml | FileCheck %s
+
+; CHECK-LABEL: FuctionWithDebugLoc,Count
+; CHECK: path/to/anno.c:func1,2
+; CHECK: path/to/anno.c:func2,2
+; CHECK: path/to/anno2.c:func3,1
+; CHECK: path/to/anno3.c:func1,1
diff --git a/llvm/test/tools/llvm-remarkutil/count/group-by-function.test b/llvm/test/tools/llvm-remarkutil/count/group-by-function.test
new file mode 100644
index 0000000000000..f3d04bb00c269
--- /dev/null
+++ b/llvm/test/tools/llvm-remarkutil/count/group-by-function.test
@@ -0,0 +1,7 @@
+
+RUN: llvm-remarkutil count --parser=yaml --group-by=function %p/Inputs/remark-group-by.yaml | FileCheck %s
+
+; CHECK-LABEL: Function,Count
+; CHECK: func1,3
+; CHECK: func2,2
+; CHECK: func3,1
diff --git a/llvm/test/tools/llvm-remarkutil/count/group-by-source.test b/llvm/test/tools/llvm-remarkutil/count/group-by-source.test
new file mode 100644
index 0000000000000..0a11ac63925f1
--- /dev/null
+++ b/llvm/test/tools/llvm-remarkutil/count/group-by-source.test
@@ -0,0 +1,6 @@
+RUN: llvm-remarkutil count --parser=yaml --group-by=source %p/Inputs/remark-group-by.yaml | FileCheck %s
+
+; CHECK-LABEL: Source,Count
+; CHECK: path/to/anno.c,4
+; CHECK: path/to/anno2.c,1
+; CHECK: path/to/anno3.c,1
diff --git a/llvm/test/tools/llvm-remarkutil/empty-file.test b/llvm/test/tools/llvm-remarkutil/empty-file.test
index aa2ee41dd70bd..abbf8e02cfa30 100644
--- a/llvm/test/tools/llvm-remarkutil/empty-file.test
+++ b/llvm/test/tools/llvm-remarkutil/empty-file.test
@@ -1,9 +1,11 @@
RUN: not llvm-remarkutil yaml2bitstream %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --check-prefix=YAMLPARSER
RUN: not llvm-remarkutil instruction-count --parser=yaml %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --check-prefix=YAMLPARSER
RUN: not llvm-remarkutil annotation-count --parser=yaml --annotation-type=remark %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --check-prefix=YAMLPARSER
+RUN: not llvm-remarkutil count --parser=yaml %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --check-prefix=YAMLPARSER
RUN: llvm-remarkutil bitstream2yaml %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=BITSTREAM2YAML
RUN: llvm-remarkutil instruction-count --parser=bitstream %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=SIZEBITSTREAM
RUN: llvm-remarkutil annotation-count --parser=bitstream --annotation-type=remark %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=ANNOTATIONBITSTREAM
+RUN: llvm-remarkutil count --parser=bitstream %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=COUNTBITSTREAM
; YAMLPARSER: error: document root is not of mapping type.
@@ -15,3 +17,6 @@ RUN: llvm-remarkutil annotation-count --parser=bitstream --annotation-type=remar
; ANNOTATIONBITSTREAM-LABEL: Function,Count
; ANNOTATIONBITSTREAM-EMPTY:
+
+; COUNTBITSTREAM-LABEL: Source,Count
+; COUNTBITSTREAM-EMPTY:
diff --git a/llvm/test/tools/llvm-remarkutil/instruction-count-with-dbg-loc.test b/llvm/test/tools/llvm-remarkutil/instruction-count-with-dbg-loc.test
index ac6c8256febe0..f1efd44d688d8 100644
--- a/llvm/test/tools/llvm-remarkutil/instruction-count-with-dbg-loc.test
+++ b/llvm/test/tools/llvm-remarkutil/instruction-count-with-dbg-loc.test
@@ -1,7 +1,14 @@
RUN: llvm-remarkutil instruction-count --use-debug-loc --parser=yaml %p/Inputs/instruction-count-with-dbg-loc.yaml | FileCheck %s
RUN: llvm-remarkutil yaml2bitstream %p/Inputs/instruction-count-with-dbg-loc.yaml | llvm-remarkutil instruction-count --use-debug-loc --parser=bitstream | FileCheck %s
+RUN: llvm-remarkutil count --parser=yaml --count-by=arg --group-by=function-with-loc --remark-name="InstructionCount" %p/Inputs/instruction-count-with-dbg-loc.yaml | FileCheck %s --check-prefix=COUNT-CHECK
+RUN: llvm-remarkutil yaml2bitstream %p/Inputs/instruction-count-with-dbg-loc.yaml | llvm-remarkutil count --parser=bitstream --count-by=arg --group-by=function-with-loc --remark-name="InstructionCount" | FileCheck %s --check-prefix=COUNT-CHECK
; CHECK-LABEL: Source,Function,InstructionCount
; CHECK: path/to/inst.c:1:2,func1,1
; CHECK: path/to/inst2.c:1:2,func2,2
; CHECK: path/to/inst3.c:1:2,func3,3
+
+; COUNT-CHECK-LABEL: FuctionWithDebugLoc,NumInstructions
+; COUNT-CHECK: path/to/inst.c:func1,1
+; COUNT-CHECK: path/to/inst2.c:func2,2
+; COUNT-CHECK: path/to/inst3.c:func3,3
diff --git a/llvm/test/tools/llvm-remarkutil/instruction-count.test b/llvm/test/tools/llvm-remarkutil/instruction-count.test
index d5f62e36c13ee..c4bc3e5780e3f 100644
--- a/llvm/test/tools/llvm-remarkutil/instruction-count.test
+++ b/llvm/test/tools/llvm-remarkutil/instruction-count.test
@@ -1,7 +1,14 @@
RUN: llvm-remarkutil instruction-count --parser=yaml %p/Inputs/instruction-count.yaml | FileCheck %s
RUN: llvm-remarkutil yaml2bitstream %p/Inputs/instruction-count.yaml | llvm-remarkutil instruction-count --parser=bitstream | FileCheck %s
+RUN: llvm-remarkutil count --parser=yaml --count-by=arg --group-by=function --remark-name="InstructionCount" %p/Inputs/instruction-count.yaml | FileCheck %s --check-prefix=COUNT-CHECK
+RUN: llvm-remarkutil yaml2bitstream %p/Inputs/instruction-count.yaml | llvm-remarkutil count --parser=bitstream --count-by=arg --group-by=function --remark-name="InstructionCount" | FileCheck %s --check-prefix=COUNT-CHECK
; CHECK-LABEL: Function,InstructionCount
; CHECK: func1,1
; CHECK: func2,2
; CHECK: func3,3
+
+; COUNT-CHECK-LABEL: Function,NumInstructions
+; COUNT-CHECK: func1,1
+; COUNT-CHECK: func2,2
+; COUNT-CHECK: func3,3
diff --git a/llvm/tools/llvm-remarkutil/CMakeLists.txt b/llvm/tools/llvm-remarkutil/CMakeLists.txt
index c1dd311e97984..48aeb9397cda1 100644
--- a/llvm/tools/llvm-remarkutil/CMakeLists.txt
+++ b/llvm/tools/llvm-remarkutil/CMakeLists.txt
@@ -7,6 +7,7 @@ set(LLVM_LINK_COMPONENTS
add_llvm_tool(llvm-remarkutil
RemarkConvert.cpp
RemarkCount.cpp
+ RemarkCounter.cpp
RemarkSizeDiff.cpp
RemarkUtil.cpp
RemarkUtilHelpers.cpp
diff --git a/llvm/tools/llvm-remarkutil/RemarkCounter.cpp b/llvm/tools/llvm-remarkutil/RemarkCounter.cpp
new file mode 100644
index 0000000000000..b7cbebc0ca8e6
--- /dev/null
+++ b/llvm/tools/llvm-remarkutil/RemarkCounter.cpp
@@ -0,0 +1,337 @@
+//===- RemarkCounter.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Generic tool to count remarks based on properties
+//
+//===----------------------------------------------------------------------===//
+
+#include "RemarkCounter.h"
+#include "RemarkUtilRegistry.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Regex.h"
+
+using namespace llvm;
+using namespace remarks;
+using namespace llvm::remarkutil;
+
+static cl::SubCommand CountSub("count",
+ "Collect remarks based on specified criteria.");
+
+INPUT_FORMAT_COMMAND_LINE_OPTIONS(CountSub)
+INPUT_OUTPUT_COMMAND_LINE_OPTIONS(CountSub)
+
+static cl::list
+ Keys("args", cl::desc("Specify remark argument/s to count by."),
+ cl::value_desc("arguments"), cl::sub(CountSub), cl::ValueOptional);
+static cl::list RKeys(
+ "rargs",
+ cl::desc(
+ "Specify remark argument/s to count (accepts regular expressions)."),
+ cl::value_desc("arguments"), cl::sub(CountSub), cl::ValueOptional);
+static cl::opt
+ RemarkNameOpt("remark-name",
+ cl::desc("Optional remark name to filter collection by."),
+ cl::ValueOptional, cl::sub(CountSub));
+static cl::opt
+ PassNameOpt("pass-name", cl::ValueOptional,
+ cl::desc("Optional remark pass name to filter collection by."),
+ cl::sub(CountSub));
+
+static cl::opt RemarkFilterArgByOpt(
+ "filter-arg-by", cl::desc("Optional remark arg to filter collection by."),
+ cl::ValueOptional, cl::sub(CountSub));
+static cl::opt
+ RemarkNameOptRE("rremark-name",
+ cl::desc("Optional remark name to filter collection by "
+ "(accepts regular expressions)."),
+ cl::ValueOptional, cl::sub(CountSub));
+static cl::opt
+ RemarkArgFilterOptRE("rfilter-arg-by",
+ cl::desc("Optional remark arg to filter collection by "
+ "(accepts regular expressions)."),
+ cl::sub(CountSub), cl::ValueOptional);
+static cl::opt
+ PassNameOptRE("rpass-name", cl::ValueOptional,
+ cl::desc("Optional remark pass name to filter collection "
+ "by (accepts regular expressions)."),
+ cl::sub(CountSub));
+static cl::opt RemarkTypeOpt(
+ "remark-type", cl::desc("Optional remark type to filter collection by."),
+ cl::values(clEnumValN(Type::Unknown, "unknown", "UNKOWN"),
+ clEnumValN(Type::Passed, "passed", "PASSED"),
+ clEnumValN(Type::Missed, "missed", "MISSED"),
+ clEnumValN(Type::Analysis, "analysis", "ANALYSIS"),
+ clEnumValN(Type::AnalysisFPCommute, "analysis-fp-commute",
+ "ANALYSIS_FP_COMMUTE"),
+ clEnumValN(Type::AnalysisAliasing, "analysis-aliasing",
+ "ANALYSIS_ALIASING"),
+ clEnumValN(Type::Failure, "failure", "FAILURE")),
+ cl::init(Type::Failure), cl::sub(CountSub));
+static cl::opt CountByOpt(
+ "count-by", cl::desc("Specify the property to collect remarks by."),
+ cl::values(
+ clEnumValN(CountBy::REMARK, "remark-name",
+ "Counts individual remarks based on how many of the remark "
+ "exists."),
+ clEnumValN(CountBy::ARGUMENT, "arg",
+ "Counts based on the value each specified argument has. The "
+ "argument has to have a number value to be considered.")),
+ cl::init(CountBy::REMARK), cl::sub(CountSub));
+static cl::opt GroupByOpt(
+ "group-by", cl::desc("Specify the property to group remarks by."),
+ cl::values(
+ clEnumValN(
+ GroupBy::PER_SOURCE, "source",
+ "Display the count broken down by the filepath of each remark "
+ "emitted. Requires remarks to have DebugLoc information."),
+ clEnumValN(GroupBy::PER_FUNCTION, "function",
+ "Breakdown the count by function name."),
+ clEnumValN(
+ GroupBy::PER_FUNCTION_WITH_DEBUG_LOC, "function-with-loc",
+ "Breakdown the count by function name taking into consideration "
+ "the filepath info from the DebugLoc of the remark."),
+ clEnumValN(GroupBy::TOTAL, "total",
+ "Output the total number corresponding to the count for the "
+ "provided input file.")),
+ cl::init(GroupBy::PER_SOURCE), cl::sub(CountSub));
+
+/// Look for matching argument with \p Key in \p Remark and return the parsed
+/// integer value or 0 if it is has no integer value.
+static unsigned getValForKey(StringRef Key, const Remark &Remark) {
+ auto *RemarkArg = find_if(Remark.Args, [&Key](const Argument &Arg) {
+ return Arg.Key == Key && Arg.isValInt();
+ });
+ if (RemarkArg == Remark.Args.end())
+ return 0;
+ return *RemarkArg->getValAsInt();
+}
+
+Error Filters::regexArgumentsValid() {
+ if (RemarkNameFilter && RemarkNameFilter->IsRegex)
+ if (auto E = checkRegex(RemarkNameFilter->FilterRE))
+ return E;
+ if (PassNameFilter && PassNameFilter->IsRegex)
+ if (auto E = checkRegex(PassNameFilter->FilterRE))
+ return E;
+ if (ArgFilter && ArgFilter->IsRegex)
+ if (auto E = checkRegex(ArgFilter->FilterRE))
+ return E;
+ return Error::success();
+}
+
+bool Filters::filterRemark(const Remark &Remark) {
+ if (RemarkNameFilter && !RemarkNameFilter->match(Remark.RemarkName))
+ return false;
+ if (PassNameFilter && !PassNameFilter->match(Remark.PassName))
+ return false;
+ if (RemarkTypeFilter)
+ return *RemarkTypeFilter == Remark.RemarkType;
+ if (ArgFilter) {
+ if (!any_of(Remark.Args,
+ [this](Argument Arg) { return ArgFilter->match(Arg.Val); }))
+ return false;
+ }
+ return true;
+}
+
+Error ArgumentCounter::getAllMatchingArgumentsInRemark(
+ StringRef Buffer, ArrayRef Arguments, Filters &Filter) {
+ auto MaybeParser = createRemarkParser(InputFormat, Buffer);
+ if (!MaybeParser)
+ return MaybeParser.takeError();
+ auto &Parser = **MaybeParser;
+ auto MaybeRemark = Parser.next();
+ for (; MaybeRemark; MaybeRemark = Parser.next()) {
+ auto &Remark = **MaybeRemark;
+ // Only collect keys from remarks included in the filter.
+ if (!Filter.filterRemark(Remark))
+ continue;
+ for (auto &Key : Arguments) {
+ for (Argument Arg : Remark.Args)
+ if (Key.match(Arg.Key) && Arg.isValInt())
+ ArgumentSetIdxMap.insert({Arg.Key, ArgumentSetIdxMap.size()});
+ }
+ }
+
+ auto E = MaybeRemark.takeError();
+ if (!E.isA())
+ return E;
+ consumeError(std::move(E));
+ return Error::success();
+}
+
+std::optional Counter::getGroupByKey(const Remark &Remark) {
+
+ switch (GroupBy) {
+ case GroupBy::PER_FUNCTION:
+ return Remark.FunctionName.str();
+ case GroupBy::TOTAL:
+ return "Total";
+ case GroupBy::PER_SOURCE:
+ case GroupBy::PER_FUNCTION_WITH_DEBUG_LOC:
+ if (!Remark.Loc.has_value())
+ return std::nullopt;
+
+ if (GroupBy == GroupBy::PER_FUNCTION_WITH_DEBUG_LOC)
+ return Remark.Loc->SourceFilePath.str() + ":" + Remark.FunctionName.str();
+ return Remark.Loc->SourceFilePath.str();
+ }
+}
+
+void ArgumentCounter::collect(const Remark &Remark) {
+ SmallVector Row(ArgumentSetIdxMap.size());
+ std::optional GroupByKey = getGroupByKey(Remark);
+ // Early return if we don't have a value
+ if (!GroupByKey)
+ return;
+ auto GroupVal = *GroupByKey;
+ CountByKeysMap.insert({GroupVal, Row});
+ for (auto [Key, Idx] : ArgumentSetIdxMap) {
+ auto Count = getValForKey(Key, Remark);
+ CountByKeysMap[GroupVal][Idx] += Count;
+ }
+}
+
+void RemarkCounter::collect(const Remark &Remark) {
+ std::optional Key = getGroupByKey(Remark);
+ if (!Key.has_value())
+ return;
+ auto Iter = CountedByRemarksMap.insert({*Key, 1});
+ if (!Iter.second)
+ Iter.first->second += 1;
+}
+
+Error ArgumentCounter::print(StringRef OutputFileName) {
+ auto MaybeOF =
+ getOutputFileWithFlags(OutputFileName, sys::fs::OF_TextWithCRLF);
+ if (!MaybeOF)
+ return MaybeOF.takeError();
+
+ auto OF = std::move(*MaybeOF);
+ OF->os() << groupByToStr(GroupBy) << ",";
+ unsigned Idx = 0;
+ for (auto [Key, _] : ArgumentSetIdxMap) {
+ OF->os() << Key;
+ if (Idx != ArgumentSetIdxMap.size() - 1)
+ OF->os() << ",";
+ Idx++;
+ }
+ OF->os() << "\n";
+ for (auto [Header, CountVector] : CountByKeysMap) {
+ OF->os() << Header << ",";
+ unsigned Idx = 0;
+ for (auto Count : CountVector) {
+ OF->os() << Count;
+ if (Idx != ArgumentSetIdxMap.size() - 1)
+ OF->os() << ",";
+ Idx++;
+ }
+ OF->os() << "\n";
+ }
+ return Error::success();
+}
+
+Error RemarkCounter::print(StringRef OutputFileName) {
+ auto MaybeOF =
+ getOutputFileWithFlags(OutputFileName, sys::fs::OF_TextWithCRLF);
+ if (!MaybeOF)
+ return MaybeOF.takeError();
+
+ auto OF = std::move(*MaybeOF);
+ OF->os() << groupByToStr(GroupBy) << ","
+ << "Count\n";
+ for (auto [Key, Count] : CountedByRemarksMap)
+ OF->os() << Key << "," << Count << "\n";
+ OF->keep();
+ return Error::success();
+}
+
+Expected getRemarkFilter() {
+ // Create Filter properties.
+ std::optional RemarkNameFilter;
+ std::optional PassNameFilter;
+ std::optional RemarkArgFilter;
+ std::optional RemarkType;
+ if (!RemarkNameOpt.empty())
+ RemarkNameFilter = {RemarkNameOpt, false};
+ else if (!RemarkNameOptRE.empty())
+ RemarkNameFilter = {RemarkNameOptRE, true};
+ if (!PassNameOpt.empty())
+ PassNameFilter = {PassNameOpt, false};
+ else if (!PassNameOptRE.empty())
+ PassNameFilter = {PassNameOptRE, true};
+ if (RemarkTypeOpt != Type::Failure)
+ RemarkType = RemarkTypeOpt;
+ if (!RemarkFilterArgByOpt.empty())
+ RemarkArgFilter = {RemarkFilterArgByOpt, false};
+ else if (!RemarkArgFilterOptRE.empty())
+ RemarkArgFilter = {RemarkArgFilterOptRE, true};
+ // Create RemarkFilter.
+ return Filters::createRemarkFilter(std::move(RemarkNameFilter),
+ std::move(PassNameFilter),
+ std::move(RemarkArgFilter), RemarkType);
+}
+
+Error useCollectRemark(StringRef Buffer, Counter &Counter, Filters &Filter) {
+ // Create Parser.
+ auto MaybeParser = createRemarkParser(InputFormat, Buffer);
+ if (!MaybeParser)
+ return MaybeParser.takeError();
+ auto &Parser = **MaybeParser;
+ auto MaybeRemark = Parser.next();
+ for (; MaybeRemark; MaybeRemark = Parser.next()) {
+ const Remark &Remark = **MaybeRemark;
+ if (Filter.filterRemark(Remark))
+ Counter.collect(Remark);
+ }
+
+ if (auto E = Counter.print(OutputFileName))
+ return E;
+ auto E = MaybeRemark.takeError();
+ if (!E.isA())
+ return E;
+ consumeError(std::move(E));
+ return Error::success();
+}
+
+static Error collectRemarks() {
+ // Create a parser for the user-specified input format.
+ auto MaybeBuf = getInputMemoryBuffer(InputFileName);
+ if (!MaybeBuf)
+ return MaybeBuf.takeError();
+ StringRef Buffer = (*MaybeBuf)->getBuffer();
+ auto MaybeFilter = getRemarkFilter();
+ if (!MaybeFilter)
+ return MaybeFilter.takeError();
+ auto &Filter = *MaybeFilter;
+ if (CountByOpt == CountBy::REMARK) {
+ RemarkCounter RC(GroupByOpt);
+ if (auto E = useCollectRemark(Buffer, RC, Filter))
+ return E;
+ } else if (CountByOpt == CountBy::ARGUMENT) {
+ SmallVector ArgumentsVector;
+ if (!Keys.empty()) {
+ for (auto &Key : Keys)
+ ArgumentsVector.push_back({Key, false});
+ } else if (!RKeys.empty())
+ for (auto Key : RKeys)
+ ArgumentsVector.push_back({Key, true});
+ else
+ ArgumentsVector.push_back({".*", true});
+
+ Expected AC = ArgumentCounter::createArgumentCounter(
+ GroupByOpt, ArgumentsVector, Buffer, Filter);
+ if (!AC)
+ return AC.takeError();
+ if (auto E = useCollectRemark(Buffer, *AC, Filter))
+ return E;
+ }
+ return Error::success();
+}
+
+static CommandRegistration CountReg(&CountSub, collectRemarks);
diff --git a/llvm/tools/llvm-remarkutil/RemarkCounter.h b/llvm/tools/llvm-remarkutil/RemarkCounter.h
new file mode 100644
index 0000000000000..aa9eaf698849c
--- /dev/null
+++ b/llvm/tools/llvm-remarkutil/RemarkCounter.h
@@ -0,0 +1,217 @@
+//===- RemarkCounter.h ----------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Generic tool to count remarks based on properties
+//
+//===----------------------------------------------------------------------===//
+#ifndef TOOLS_LLVM_REMARKCOUNTER_H
+#define TOOLS_LLVM_REMARKCOUNTER_H
+#include "RemarkUtilHelpers.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/Support/Regex.h"
+#include
+
+namespace llvm {
+namespace remarks {
+
+/// Collect remarks by counting the existance of a remark or by looking through
+/// the keys and summing through the total count.
+enum class CountBy { REMARK, ARGUMENT };
+
+/// Summarize the count by either emitting one count for the remark file, or
+/// grouping the count by source file or by function name.
+enum class GroupBy {
+ TOTAL,
+ PER_SOURCE,
+ PER_FUNCTION,
+ PER_FUNCTION_WITH_DEBUG_LOC
+};
+
+/// Convert \p GroupBy to a std::string.
+inline std::string groupByToStr(GroupBy GroupBy) {
+ switch (GroupBy) {
+ default:
+ return "Total";
+ case GroupBy::PER_FUNCTION:
+ return "Function";
+ case GroupBy::PER_FUNCTION_WITH_DEBUG_LOC:
+ return "FuctionWithDebugLoc";
+ case GroupBy::PER_SOURCE:
+ return "Source";
+ }
+}
+
+/// Filter object which can be either a string or a regex to match with the
+/// remark properties.
+struct FilterMatcher {
+ Regex FilterRE;
+ std::string FilterStr;
+ bool IsRegex;
+ FilterMatcher(std::string Filter, bool IsRegex) : IsRegex(IsRegex) {
+ if (IsRegex)
+ FilterRE = Regex(Filter);
+ else
+ FilterStr = Filter;
+ }
+
+ bool match(StringRef StringToMatch) const {
+ if (IsRegex)
+ return FilterRE.match(StringToMatch);
+ return FilterStr == StringToMatch.trim().str();
+ }
+};
+
+/// Filter out remarks based on remark properties based on name, pass name,
+/// argument and type.
+struct Filters {
+ std::optional RemarkNameFilter;
+ std::optional PassNameFilter;
+ std::optional ArgFilter;
+ std::optional RemarkTypeFilter;
+ /// Returns a filter object if all the arguments provided are valid regex
+ /// types otherwise return an error.
+ static Expected
+ createRemarkFilter(std::optional RemarkNameFilter,
+ std::optional PassNameFilter,
+ std::optional ArgFilter,
+ std::optional RemarkTypeFilter) {
+ Filters Filter;
+ Filter.RemarkNameFilter = std::move(RemarkNameFilter);
+ Filter.PassNameFilter = std::move(PassNameFilter);
+ Filter.ArgFilter = std::move(ArgFilter);
+ Filter.RemarkTypeFilter = std::move(RemarkTypeFilter);
+ if (auto E = Filter.regexArgumentsValid())
+ return E;
+ return Filter;
+ }
+ /// Returns true if \p Remark satisfies all the provided filters.
+ bool filterRemark(const Remark &Remark);
+
+private:
+ /// Check if arguments can be parsed as valid regex types.
+ Error regexArgumentsValid();
+};
+
+/// Convert Regex string error to an error object.
+inline Error checkRegex(const Regex &Regex) {
+ std::string Error;
+ if (!Regex.isValid(Error))
+ return createStringError(make_error_code(std::errc::invalid_argument),
+ Twine("Regex: ", Error));
+ return Error::success();
+}
+
+/// Abstract counter class used to define the general required methods for
+/// counting a remark.
+struct Counter {
+ GroupBy GroupBy;
+ Counter(){};
+ Counter(enum GroupBy GroupBy) : GroupBy(GroupBy) {}
+ /// Obtain the field for collecting remark info based on how we are
+ /// collecting. Remarks are grouped by FunctionName, Source, Source and
+ /// Function or collect by file.
+ std::optional getGroupByKey(const Remark &Remark);
+
+ /// Collect count information from \p Remark organized based on \p GroupBy
+ /// property.
+ virtual void collect(const Remark &) = 0;
+ /// Output the final count to the file \p OutputFileName
+ virtual Error print(StringRef OutputFileName) = 0;
+ virtual ~Counter() = default;
+};
+
+/// Count remarks based on the provided \p Keys argument and summing up the
+/// value for each matching key organized by source, function or reporting a
+/// total for the specified remark file.
+/// Reporting count grouped by source:
+///
+/// | source | key1 | key2 | key3 |
+/// |---------------|------|------|------|
+/// | path/to/file1 | 0 | 1 | 3 |
+/// | path/to/file2 | 1 | 0 | 2 |
+/// | path/to/file3 | 2 | 3 | 1 |
+///
+/// Reporting count grouped by function:
+///
+/// | Function | key1 | key2 | key3 |
+/// |---------------|------|------|------|
+/// | function1 | 0 | 1 | 3 |
+/// | function2 | 1 | 0 | 2 |
+/// | function3 | 2 | 3 | 1 |
+struct ArgumentCounter : Counter {
+ /// The internal object to keep the count for the remarks. The first argument
+ /// corresponds to the property we are collecting for this can be either a
+ /// source or function. The second argument is a row of integers where each
+ /// item in the row is the count for a specified key.
+ std::map> CountByKeysMap;
+ /// A set of all the remark argument found in the remark file. The second
+ /// argument is the index of each of those arguments which can be used in
+ /// `CountByKeysMap` to fill count information for that argument.
+ MapVector ArgumentSetIdxMap;
+ /// Create an argument counter. If the provided \p Arguments represent a regex
+ /// vector then we need to check that the provided regular expressions are
+ /// valid if not we return an Error.
+ static Expected
+ createArgumentCounter(enum GroupBy GroupBy, ArrayRef Arguments,
+ StringRef Buffer, Filters &Filter) {
+ ArgumentCounter AC;
+ AC.GroupBy = GroupBy;
+ for (auto &Arg : Arguments) {
+ if (Arg.IsRegex) {
+ if (auto E = checkRegex(Arg.FilterRE))
+ return E;
+ }
+ }
+ if (auto E = AC.getAllMatchingArgumentsInRemark(Buffer, Arguments, Filter))
+ return E;
+ return AC;
+ }
+
+ /// Update the internal count map based on the remark integer arguments that
+ /// correspond the the user specified argument keys to collect for.
+ void collect(const Remark &) override;
+
+ /// Print a CSV table consisting of an index which is specified by \p
+ /// `GroupBy` and can be a function name, source file name or function name
+ /// with the full source path and columns of user specified remark arguments
+ /// to collect the count for.
+ Error print(StringRef OutputFileName) override;
+
+private:
+ /// collect all the arguments that match the list of \p Arguments provided by
+ /// parsing through \p Buffer of remarks and filling \p ArgumentSetIdxMap
+ /// acting as a row for for all the keys that we are interested in collecting
+ /// information for.
+ Error getAllMatchingArgumentsInRemark(StringRef Buffer,
+ ArrayRef Arguments,
+ Filters &Filter);
+};
+
+/// Collect remarks based by counting the existance of individual remarks. The
+/// reported table will be structured based on the provided \p GroupBy argument
+/// by reporting count for functions, source or total count for the provided
+/// remark file.
+struct RemarkCounter : Counter {
+ std::map CountedByRemarksMap;
+ RemarkCounter(enum GroupBy GroupBy) : Counter(GroupBy) {}
+
+ /// Advance the internal map count broken by \p GroupBy when
+ /// seeing \p Remark.
+ void collect(const Remark &) override;
+
+ /// Print a CSV table consisting of an index which is specified by \p
+ /// `GroupBy` and can be a function name, source file name or function name
+ /// with the full source path and a counts column corresponding to the count
+ /// of each individual remark at th index.
+ Error print(StringRef OutputFileName) override;
+};
+} // namespace remarks
+
+} // namespace llvm
+#endif // TOOLS_LLVM_REMARKCOUNTER_H