summaryrefslogtreecommitdiff
path: root/tools/checker
diff options
context:
space:
mode:
Diffstat (limited to 'tools/checker')
-rw-r--r--tools/checker/README8
-rwxr-xr-xtools/checker/checker.py11
-rw-r--r--tools/checker/common/archs.py15
-rw-r--r--tools/checker/file_format/c1visualizer/parser.py21
-rw-r--r--tools/checker/file_format/checker/parser.py36
-rw-r--r--tools/checker/file_format/checker/struct.py6
-rw-r--r--tools/checker/file_format/checker/test.py49
-rw-r--r--tools/checker/file_format/common.py11
-rw-r--r--tools/checker/match/file.py4
-rwxr-xr-xtools/checker/run_unit_tests.py3
10 files changed, 129 insertions, 35 deletions
diff --git a/tools/checker/README b/tools/checker/README
index 858a773768..259691e500 100644
--- a/tools/checker/README
+++ b/tools/checker/README
@@ -52,3 +52,11 @@ Example:
The engine will attempt to match the check lines against the output of the
group named on the first line. Together they verify that the CFG after
constant folding returns an integer constant with value either 11 or 22.
+
+A group of check lines can be made architecture-specific by inserting '-<arch>'
+after the 'CHECK-START' keyword. The previous example can be updated to run for
+arm64 only with:
+
+ // CHECK-START-ARM64: int MyClass.MyMethod() constant_folding (after)
+ // CHECK: <<ID:i\d+>> IntConstant {{11|22}}
+ // CHECK: Return [<<ID>>]
diff --git a/tools/checker/checker.py b/tools/checker/checker.py
index ed630e3d12..bc5e17da6a 100755
--- a/tools/checker/checker.py
+++ b/tools/checker/checker.py
@@ -17,6 +17,7 @@
import argparse
import os
+from common.archs import archs_list
from common.logger import Logger
from file_format.c1visualizer.parser import ParseC1visualizerStream
from file_format.checker.parser import ParseCheckerStream
@@ -34,6 +35,8 @@ def ParseArguments():
help="print a list of all passes found in the tested file")
parser.add_argument("--dump-pass", dest="dump_pass", metavar="PASS",
help="print a compiler pass dump")
+ parser.add_argument("--arch", dest="arch", choices=archs_list,
+ help="Run the tests for the specified target architecture.")
parser.add_argument("-q", "--quiet", action="store_true",
help="print only errors")
return parser.parse_args()
@@ -62,7 +65,7 @@ def DumpPass(outputFilename, passName):
def FindCheckerFiles(path):
""" Returns a list of files to scan for check annotations in the given path.
Path to a file is returned as a single-element list, directories are
- recursively traversed and all '.java' files returned.
+ recursively traversed and all '.java' and '.smali' files returned.
"""
if not path:
Logger.fail("No source path provided")
@@ -80,13 +83,13 @@ def FindCheckerFiles(path):
Logger.fail("Source path \"" + path + "\" not found")
-def RunTests(checkPrefix, checkPath, outputFilename):
+def RunTests(checkPrefix, checkPath, outputFilename, targetArch):
c1File = ParseC1visualizerStream(os.path.basename(outputFilename), open(outputFilename, "r"))
for checkFilename in FindCheckerFiles(checkPath):
checkerFile = ParseCheckerStream(os.path.basename(checkFilename),
checkPrefix,
open(checkFilename, "r"))
- MatchFiles(checkerFile, c1File)
+ MatchFiles(checkerFile, c1File, targetArch)
if __name__ == "__main__":
@@ -100,4 +103,4 @@ if __name__ == "__main__":
elif args.dump_pass:
DumpPass(args.tested_file, args.dump_pass)
else:
- RunTests(args.check_prefix, args.source_path, args.tested_file)
+ RunTests(args.check_prefix, args.source_path, args.tested_file, args.arch)
diff --git a/tools/checker/common/archs.py b/tools/checker/common/archs.py
new file mode 100644
index 0000000000..84bded9281
--- /dev/null
+++ b/tools/checker/common/archs.py
@@ -0,0 +1,15 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+archs_list = ['ARM', 'ARM64', 'MIPS64', 'X86', 'X86_64']
diff --git a/tools/checker/file_format/c1visualizer/parser.py b/tools/checker/file_format/c1visualizer/parser.py
index 335a195883..bdcde9db51 100644
--- a/tools/checker/file_format/c1visualizer/parser.py
+++ b/tools/checker/file_format/c1visualizer/parser.py
@@ -27,10 +27,12 @@ class C1ParserState:
def __parseC1Line(line, lineNo, state, fileName):
""" This function is invoked on each line of the output file and returns
- a pair which instructs the parser how the line should be handled. If the
+ a triplet which instructs the parser how the line should be handled. If the
line is to be included in the current group, it is returned in the first
value. If the line starts a new output group, the name of the group is
- returned in the second value.
+ returned in the second value. The third value is only here to make the
+ function prototype compatible with `SplitStream` and is always set to
+ `None` here.
"""
if state.currentState == C1ParserState.StartingCfgBlock:
# Previous line started a new 'cfg' block which means that this one must
@@ -39,16 +41,16 @@ def __parseC1Line(line, lineNo, state, fileName):
# Extract the pass name, prepend it with the name of the method and
# return as the beginning of a new group.
state.currentState = C1ParserState.InsideCfgBlock
- return (None, state.lastMethodName + " " + line.split("\"")[1])
+ return (None, state.lastMethodName + " " + line.split("\"")[1], None)
else:
Logger.fail("Expected output group name", fileName, lineNo)
elif state.currentState == C1ParserState.InsideCfgBlock:
if line == "end_cfg":
state.currentState = C1ParserState.OutsideBlock
- return (None, None)
+ return (None, None, None)
else:
- return (line, None)
+ return (line, None, None)
elif state.currentState == C1ParserState.InsideCompilationBlock:
# Search for the method's name. Format: method "<name>"
@@ -59,7 +61,7 @@ def __parseC1Line(line, lineNo, state, fileName):
state.lastMethodName = methodName
elif line == "end_compilation":
state.currentState = C1ParserState.OutsideBlock
- return (None, None)
+ return (None, None, None)
else:
assert state.currentState == C1ParserState.OutsideBlock
@@ -69,10 +71,10 @@ def __parseC1Line(line, lineNo, state, fileName):
if state.lastMethodName is None:
Logger.fail("Expected method header", fileName, lineNo)
state.currentState = C1ParserState.StartingCfgBlock
- return (None, None)
+ return (None, None, None)
elif line == "begin_compilation":
state.currentState = C1ParserState.InsideCompilationBlock
- return (None, None)
+ return (None, None, None)
else:
Logger.fail("C1visualizer line not inside a group", fileName, lineNo)
@@ -82,6 +84,7 @@ def ParseC1visualizerStream(fileName, stream):
fnProcessLine = lambda line, lineNo: __parseC1Line(line, lineNo, state, fileName)
fnLineOutsideChunk = lambda line, lineNo: \
Logger.fail("C1visualizer line not inside a group", fileName, lineNo)
- for passName, passLines, startLineNo in SplitStream(stream, fnProcessLine, fnLineOutsideChunk):
+ for passName, passLines, startLineNo, testArch in \
+ SplitStream(stream, fnProcessLine, fnLineOutsideChunk):
C1visualizerPass(c1File, passName, passLines, startLineNo + 1)
return c1File
diff --git a/tools/checker/file_format/checker/parser.py b/tools/checker/file_format/checker/parser.py
index 33735cbea0..001f72a225 100644
--- a/tools/checker/file_format/checker/parser.py
+++ b/tools/checker/file_format/checker/parser.py
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from common.archs import archs_list
from common.logger import Logger
from file_format.common import SplitStream
from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, RegexExpression
@@ -21,17 +22,18 @@ import re
def __isCheckerLine(line):
return line.startswith("///") or line.startswith("##")
-def __extractLine(prefix, line):
+def __extractLine(prefix, line, arch = None):
""" Attempts to parse a check line. The regex searches for a comment symbol
followed by the CHECK keyword, given attribute and a colon at the very
beginning of the line. Whitespaces are ignored.
"""
rIgnoreWhitespace = r"\s*"
rCommentSymbols = [r"///", r"##"]
+ arch_specifier = r"-%s" % arch if arch is not None else r""
regexPrefix = rIgnoreWhitespace + \
r"(" + r"|".join(rCommentSymbols) + r")" + \
rIgnoreWhitespace + \
- prefix + r":"
+ prefix + arch_specifier + r":"
# The 'match' function succeeds only if the pattern is matched at the
# beginning of the line.
@@ -42,41 +44,44 @@ def __extractLine(prefix, line):
return None
def __processLine(line, lineNo, prefix, fileName):
- """ This function is invoked on each line of the check file and returns a pair
+ """ This function is invoked on each line of the check file and returns a triplet
which instructs the parser how the line should be handled. If the line is
to be included in the current check group, it is returned in the first
value. If the line starts a new check group, the name of the group is
- returned in the second value.
+ returned in the second value. The third value indicates whether the line
+ contained an architecture-specific suffix.
"""
if not __isCheckerLine(line):
- return None, None
+ return None, None, None
# Lines beginning with 'CHECK-START' start a new test case.
- startLine = __extractLine(prefix + "-START", line)
- if startLine is not None:
- return None, startLine
+ # We currently only consider the architecture suffix in "CHECK-START" lines.
+ for arch in [None] + archs_list:
+ startLine = __extractLine(prefix + "-START", line, arch)
+ if startLine is not None:
+ return None, startLine, arch
# Lines starting only with 'CHECK' are matched in order.
plainLine = __extractLine(prefix, line)
if plainLine is not None:
- return (plainLine, TestAssertion.Variant.InOrder, lineNo), None
+ return (plainLine, TestAssertion.Variant.InOrder, lineNo), None, None
# 'CHECK-NEXT' lines are in-order but must match the very next line.
nextLine = __extractLine(prefix + "-NEXT", line)
if nextLine is not None:
- return (nextLine, TestAssertion.Variant.NextLine, lineNo), None
+ return (nextLine, TestAssertion.Variant.NextLine, lineNo), None, None
# 'CHECK-DAG' lines are no-order assertions.
dagLine = __extractLine(prefix + "-DAG", line)
if dagLine is not None:
- return (dagLine, TestAssertion.Variant.DAG, lineNo), None
+ return (dagLine, TestAssertion.Variant.DAG, lineNo), None, None
# 'CHECK-NOT' lines are no-order negative assertions.
notLine = __extractLine(prefix + "-NOT", line)
if notLine is not None:
- return (notLine, TestAssertion.Variant.Not, lineNo), None
+ return (notLine, TestAssertion.Variant.Not, lineNo), None, None
- Logger.fail("Checker assertion could not be parsed", fileName, lineNo)
+ Logger.fail("Checker assertion could not be parsed: '" + line + "'", fileName, lineNo)
def __isMatchAtStart(match):
""" Tests if the given Match occurred at the beginning of the line. """
@@ -146,8 +151,9 @@ def ParseCheckerStream(fileName, prefix, stream):
fnProcessLine = lambda line, lineNo: __processLine(line, lineNo, prefix, fileName)
fnLineOutsideChunk = lambda line, lineNo: \
Logger.fail("Checker line not inside a group", fileName, lineNo)
- for caseName, caseLines, startLineNo in SplitStream(stream, fnProcessLine, fnLineOutsideChunk):
- testCase = TestCase(checkerFile, caseName, startLineNo)
+ for caseName, caseLines, startLineNo, testArch in \
+ SplitStream(stream, fnProcessLine, fnLineOutsideChunk):
+ testCase = TestCase(checkerFile, caseName, startLineNo, testArch)
for caseLine in caseLines:
ParseCheckerAssertion(testCase, caseLine[0], caseLine[1], caseLine[2])
return checkerFile
diff --git a/tools/checker/file_format/checker/struct.py b/tools/checker/file_format/checker/struct.py
index 6a541428df..2b2e4429e7 100644
--- a/tools/checker/file_format/checker/struct.py
+++ b/tools/checker/file_format/checker/struct.py
@@ -26,6 +26,9 @@ class CheckerFile(PrintableMixin):
def addTestCase(self, new_test_case):
self.testCases.append(new_test_case)
+ def testCasesForArch(self, targetArch):
+ return [t for t in self.testCases if t.testArch == targetArch]
+
def __eq__(self, other):
return isinstance(other, self.__class__) \
and self.testCases == other.testCases
@@ -33,13 +36,14 @@ class CheckerFile(PrintableMixin):
class TestCase(PrintableMixin):
- def __init__(self, parent, name, startLineNo):
+ def __init__(self, parent, name, startLineNo, testArch = None):
assert isinstance(parent, CheckerFile)
self.parent = parent
self.name = name
self.assertions = []
self.startLineNo = startLineNo
+ self.testArch = testArch
if not self.name:
Logger.fail("Test case does not have a name", self.fileName, self.startLineNo)
diff --git a/tools/checker/file_format/checker/test.py b/tools/checker/file_format/checker/test.py
index ff24cc1239..36ed4b1592 100644
--- a/tools/checker/file_format/checker/test.py
+++ b/tools/checker/file_format/checker/test.py
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from common.archs import archs_list
from common.testing import ToUnicode
from file_format.checker.parser import ParseCheckerStream
from file_format.checker.struct import CheckerFile, TestCase, TestAssertion, RegexExpression
@@ -280,3 +281,51 @@ class CheckerParser_FileLayoutTest(unittest.TestCase):
/// CHECK-START: Example Group
/// CHECK-NEXT: bar
""")
+
+
+class CheckerParser_ArchTests(unittest.TestCase):
+
+ noarch_block = """
+ /// CHECK-START: Group
+ /// CHECK: foo
+ /// CHECK-NEXT: bar
+ /// CHECK-NOT: baz
+ /// CHECK-DAG: yoyo
+ """
+
+ arch_block = """
+ /// CHECK-START-{test_arch}: Group
+ /// CHECK: foo
+ /// CHECK-NEXT: bar
+ /// CHECK-NOT: baz
+ /// CHECK-DAG: yoyo
+ """
+
+ def test_NonArchTests(self):
+ for arch in [None] + archs_list:
+ checkerFile = ParseCheckerStream("<test-file>",
+ "CHECK",
+ io.StringIO(ToUnicode(self.noarch_block)))
+ self.assertEqual(len(checkerFile.testCases), 1)
+ self.assertEqual(len(checkerFile.testCases[0].assertions), 4)
+
+ def test_IgnoreNonTargetArch(self):
+ for targetArch in archs_list:
+ for testArch in [a for a in archs_list if a != targetArch]:
+ checkerText = self.arch_block.format(test_arch = testArch)
+ checkerFile = ParseCheckerStream("<test-file>",
+ "CHECK",
+ io.StringIO(ToUnicode(checkerText)))
+ self.assertEqual(len(checkerFile.testCases), 1)
+ self.assertEqual(len(checkerFile.testCasesForArch(testArch)), 1)
+ self.assertEqual(len(checkerFile.testCasesForArch(targetArch)), 0)
+
+ def test_Arch(self):
+ for arch in archs_list:
+ checkerText = self.arch_block.format(test_arch = arch)
+ checkerFile = ParseCheckerStream("<test-file>",
+ "CHECK",
+ io.StringIO(ToUnicode(checkerText)))
+ self.assertEqual(len(checkerFile.testCases), 1)
+ self.assertEqual(len(checkerFile.testCasesForArch(arch)), 1)
+ self.assertEqual(len(checkerFile.testCases[0].assertions), 4)
diff --git a/tools/checker/file_format/common.py b/tools/checker/file_format/common.py
index f91fdeb9cc..4931550355 100644
--- a/tools/checker/file_format/common.py
+++ b/tools/checker/file_format/common.py
@@ -18,8 +18,9 @@ def SplitStream(stream, fnProcessLine, fnLineOutsideChunk):
Arguments:
- fnProcessLine: Called on each line with the text and line number. Must
- return a pair, name of the chunk started on this line and data extracted
- from this line (or None in both cases).
+ return a triplet, composed of the name of the chunk started on this line,
+ the data extracted, and the name of the architecture this test applies to
+ (or None to indicate that all architectures should run this test).
- fnLineOutsideChunk: Called on attempt to attach data prior to creating
a chunk.
"""
@@ -36,9 +37,11 @@ def SplitStream(stream, fnProcessLine, fnLineOutsideChunk):
# Let the child class process the line and return information about it.
# The _processLine method can modify the content of the line (or delete it
# entirely) and specify whether it starts a new group.
- processedLine, newChunkName = fnProcessLine(line, lineNo)
+ processedLine, newChunkName, testArch = fnProcessLine(line, lineNo)
+ # Currently, only a full chunk can be specified as architecture-specific.
+ assert testArch is None or newChunkName is not None
if newChunkName is not None:
- currentChunk = (newChunkName, [], lineNo)
+ currentChunk = (newChunkName, [], lineNo, testArch)
allChunks.append(currentChunk)
if processedLine is not None:
if currentChunk is not None:
diff --git a/tools/checker/match/file.py b/tools/checker/match/file.py
index b22211ab56..42ca7df439 100644
--- a/tools/checker/match/file.py
+++ b/tools/checker/match/file.py
@@ -150,8 +150,10 @@ def MatchTestCase(testCase, c1Pass):
matchFrom = match.scope.end + 1
variables = match.variables
-def MatchFiles(checkerFile, c1File):
+def MatchFiles(checkerFile, c1File, targetArch):
for testCase in checkerFile.testCases:
+ if testCase.testArch not in [None, targetArch]:
+ continue
# TODO: Currently does not handle multiple occurrences of the same group
# name, e.g. when a pass is run multiple times. It will always try to
# match a check group against the first output group of the same name.
diff --git a/tools/checker/run_unit_tests.py b/tools/checker/run_unit_tests.py
index 01708dbd27..2f5b1feaa6 100755
--- a/tools/checker/run_unit_tests.py
+++ b/tools/checker/run_unit_tests.py
@@ -18,7 +18,8 @@ from common.logger import Logger
from file_format.c1visualizer.test import C1visualizerParser_Test
from file_format.checker.test import CheckerParser_PrefixTest, \
CheckerParser_RegexExpressionTest, \
- CheckerParser_FileLayoutTest
+ CheckerParser_FileLayoutTest, \
+ CheckerParser_ArchTests
from match.test import MatchLines_Test, \
MatchFiles_Test