diff options
22 files changed, 248 insertions, 68 deletions
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 0a3698946e..acce5b3359 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -531,6 +531,8 @@ class CodeGenerator { template <typename LabelType> LabelType* CommonInitializeLabels() { + // We use raw array allocations instead of ArenaVector<> because Labels are + // non-constructible and non-movable and as such cannot be held in a vector. size_t size = GetGraph()->GetBlocks().size(); LabelType* labels = GetGraph()->GetArena()->AllocArray<LabelType>(size, kArenaAllocCodeGenerator); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 208260a2ad..4111671a9b 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -362,6 +362,8 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { void VisitLoadClass(HLoadClass* load_class) OVERRIDE { StartAttributeStream("gen_clinit_check") << std::boolalpha << load_class->MustGenerateClinitCheck() << std::noboolalpha; + StartAttributeStream("needs_access_check") << std::boolalpha + << load_class->NeedsAccessCheck() << std::noboolalpha; } void VisitCheckCast(HCheckCast* check_cast) OVERRIDE { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index bba5a9c350..615012eb3a 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -4510,7 +4510,8 @@ class HLoadClass : public HExpression<1> { bool CanBeMoved() const OVERRIDE { return true; } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { - return other->AsLoadClass()->type_index_ == type_index_; + return other->AsLoadClass()->type_index_ == type_index_ && + other->AsLoadClass()->needs_access_check_ == needs_access_check_; } size_t ComputeHashCode() const OVERRIDE { return type_index_; } diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index 4d43d1df9c..6fc77721e7 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -617,42 +617,40 @@ void RegisterAllocator::LinearScan() { // (2) Remove currently active intervals that are dead at this position. // Move active intervals that have a lifetime hole at this position // to inactive. - // Note: Copy elements we keep to the beginning, just like - // v.erase(std::remove(v.begin(), v.end(), value), v.end()); - auto active_kept_end = active_.begin(); - for (auto it = active_.begin(), end = active_.end(); it != end; ++it) { - LiveInterval* interval = *it; - if (interval->IsDeadAt(position)) { - handled_.push_back(interval); - } else if (!interval->Covers(position)) { - inactive_.push_back(interval); - } else { - *active_kept_end++ = interval; // Keep this interval. - } - } - // We have copied what we want to keep to [active_.begin(), active_kept_end), - // the rest of the data in active_ is junk - drop it. + auto active_kept_end = std::remove_if( + active_.begin(), + active_.end(), + [this, position](LiveInterval* interval) { + if (interval->IsDeadAt(position)) { + handled_.push_back(interval); + return true; + } else if (!interval->Covers(position)) { + inactive_.push_back(interval); + return true; + } else { + return false; // Keep this interval. + } + }); active_.erase(active_kept_end, active_.end()); // (3) Remove currently inactive intervals that are dead at this position. // Move inactive intervals that cover this position to active. - // Note: Copy elements we keep to the beginning, just like - // v.erase(std::remove(v.begin(), v.begin() + num, value), v.begin() + num); - auto inactive_kept_end = inactive_.begin(); auto inactive_to_handle_end = inactive_.begin() + inactive_intervals_to_handle; - for (auto it = inactive_.begin(); it != inactive_to_handle_end; ++it) { - LiveInterval* interval = *it; - DCHECK(interval->GetStart() < position || interval->IsFixed()); - if (interval->IsDeadAt(position)) { - handled_.push_back(interval); - } else if (interval->Covers(position)) { - active_.push_back(interval); - } else { - *inactive_kept_end++ = interval; // Keep this interval. - } - } - // We have copied what we want to keep to [inactive_.begin(), inactive_kept_end), - // the rest of the data in the processed interval is junk - drop it. + auto inactive_kept_end = std::remove_if( + inactive_.begin(), + inactive_to_handle_end, + [this, position](LiveInterval* interval) { + DCHECK(interval->GetStart() < position || interval->IsFixed()); + if (interval->IsDeadAt(position)) { + handled_.push_back(interval); + return true; + } else if (interval->Covers(position)) { + active_.push_back(interval); + return true; + } else { + return false; // Keep this interval. + } + }); inactive_.erase(inactive_kept_end, inactive_to_handle_end); if (current->IsSlowPathSafepoint()) { diff --git a/test/536-checker-needs-access-check/expected.txt b/test/536-checker-needs-access-check/expected.txt index 7dc7a33fe5..4acae95b70 100644 --- a/test/536-checker-needs-access-check/expected.txt +++ b/test/536-checker-needs-access-check/expected.txt @@ -1,3 +1,4 @@ Got expected error instanceof Got expected error instanceof null Got expected error checkcast null +Got expected error instanceof (keep LoadClass with access check) diff --git a/test/536-checker-needs-access-check/src/Main.java b/test/536-checker-needs-access-check/src/Main.java index 8b19dafd2a..7bd49c1c8c 100644 --- a/test/536-checker-needs-access-check/src/Main.java +++ b/test/536-checker-needs-access-check/src/Main.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * 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. @@ -15,10 +15,9 @@ */ import other.InaccessibleClass; +import other.InaccessibleClassProxy; public class Main { - public static final boolean VERBOSE = false; - public static void main(String[] args) { try { testInstanceOf(); @@ -37,6 +36,12 @@ public class Main { } catch (IllegalAccessError e) { System.out.println("Got expected error checkcast null"); } + + try { + testDontGvnLoadClassWithAccessChecks(new Object()); + } catch (IllegalAccessError e) { + System.out.println("Got expected error instanceof (keep LoadClass with access check)"); + } } /// CHECK-START: boolean Main.testInstanceOf() register (after) @@ -59,5 +64,19 @@ public class Main { return (InaccessibleClass) null; } + /// CHECK-START: boolean Main.testDontGvnLoadClassWithAccessChecks(java.lang.Object) inliner (before) + /// CHECK: InvokeStaticOrDirect + + /// CHECK-START: boolean Main.testDontGvnLoadClassWithAccessChecks(java.lang.Object) inliner (after) + /// CHECK-NOT: InvokeStaticOrDirect + + /// CHECK-START: boolean Main.testDontGvnLoadClassWithAccessChecks(java.lang.Object) GVN (after) + /// CHECK: LoadClass needs_access_check:false + /// CHECK: LoadClass needs_access_check:true + public static boolean testDontGvnLoadClassWithAccessChecks(Object o) { + InaccessibleClassProxy.test(o); + return ic instanceof InaccessibleClass; + } + public static InaccessibleClass ic; } diff --git a/test/536-checker-needs-access-check/src/other/InaccessibleClass.java b/test/536-checker-needs-access-check/src/other/InaccessibleClass.java index c49593da75..de2e1d7830 100644 --- a/test/536-checker-needs-access-check/src/other/InaccessibleClass.java +++ b/test/536-checker-needs-access-check/src/other/InaccessibleClass.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * 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. diff --git a/test/536-checker-needs-access-check/src/other/InaccessibleClassProxy.java b/test/536-checker-needs-access-check/src/other/InaccessibleClassProxy.java new file mode 100644 index 0000000000..4c005e4dfe --- /dev/null +++ b/test/536-checker-needs-access-check/src/other/InaccessibleClassProxy.java @@ -0,0 +1,23 @@ +/* + * 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. + */ + +package other; + +public class InaccessibleClassProxy { + public static boolean test(Object o) { + return o instanceof InaccessibleClass; + } +} diff --git a/test/536-checker-needs-access-check/src2/other/InaccessibleClass.java b/test/536-checker-needs-access-check/src2/other/InaccessibleClass.java index ac804ac00a..273226375e 100644 --- a/test/536-checker-needs-access-check/src2/other/InaccessibleClass.java +++ b/test/536-checker-needs-access-check/src2/other/InaccessibleClass.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * 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. diff --git a/test/536-checker-needs-access-check/src2/other/InaccessibleClassProxy.java b/test/536-checker-needs-access-check/src2/other/InaccessibleClassProxy.java new file mode 100644 index 0000000000..4c005e4dfe --- /dev/null +++ b/test/536-checker-needs-access-check/src2/other/InaccessibleClassProxy.java @@ -0,0 +1,23 @@ +/* + * 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. + */ + +package other; + +public class InaccessibleClassProxy { + public static boolean test(Object o) { + return o instanceof InaccessibleClass; + } +} diff --git a/test/537-checker-debuggable/expected.txt b/test/537-checker-debuggable/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/537-checker-debuggable/expected.txt diff --git a/test/537-checker-debuggable/info.txt b/test/537-checker-debuggable/info.txt new file mode 100644 index 0000000000..25597d3f13 --- /dev/null +++ b/test/537-checker-debuggable/info.txt @@ -0,0 +1 @@ +Test that CHECK-START-DEBUGGABLE runs only on --debuggable code.
\ No newline at end of file diff --git a/test/537-checker-debuggable/smali/TestCase.smali b/test/537-checker-debuggable/smali/TestCase.smali new file mode 100644 index 0000000000..8e6c7ef727 --- /dev/null +++ b/test/537-checker-debuggable/smali/TestCase.smali @@ -0,0 +1,42 @@ +# 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. + +.class public LTestCase; + +.super Ljava/lang/Object; + +# The phi in this method has no actual uses but one environment use. It will +# be eliminated in normal mode but kept live in debuggable mode. Test that +# Checker runs the correct test for each compilation mode. + +## CHECK-START: int TestCase.deadPhi(int, int, int) ssa_builder (after) +## CHECK-NOT: Phi + +## CHECK-START-DEBUGGABLE: int TestCase.deadPhi(int, int, int) ssa_builder (after) +## CHECK: Phi + +.method public static deadPhi(III)I + .registers 8 + + move v0, p1 + if-eqz p0, :after + move v0, p2 + :after + # v0 = Phi [p1, p2] with no uses + + invoke-static {}, Ljava/lang/System;->nanoTime()J # create an env use + + :return + return p2 +.end method diff --git a/test/537-checker-debuggable/src/Main.java b/test/537-checker-debuggable/src/Main.java new file mode 100644 index 0000000000..a572648109 --- /dev/null +++ b/test/537-checker-debuggable/src/Main.java @@ -0,0 +1,23 @@ +/* + * 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. + */ + +public class Main { + + // Workaround for b/18051191. + class InnerClass {} + + public static void main(String[] args) { } +} diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 9899e2dd31..537873f0f0 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -470,6 +470,7 @@ TEST_ART_BROKEN_OPTIMIZING_MIPS_RUN_TESTS := \ 532-checker-nonnull-arrayset \ 534-checker-bce-deoptimization \ 536-checker-intrinsic-optimization \ + 537-checker-debuggable \ ifeq (mips,$(TARGET_ARCH)) ifneq (,$(filter optimizing,$(COMPILER_TYPES))) diff --git a/test/run-test b/test/run-test index a5b6e92869..2892ce9f18 100755 --- a/test/run-test +++ b/test/run-test @@ -637,18 +637,24 @@ if [[ "$TEST_NAME" =~ ^[0-9]+-checker- ]]; then # on a particular DEX output, keep building them with dx for now (b/19467889). USE_JACK="false" - if [ "$runtime" = "art" -a "$image_suffix" = "-optimizing" -a "$debuggable" = "no" ]; then + if [ "$runtime" = "art" -a "$image_suffix" = "-optimizing" ]; then # In no-prebuild mode, the compiler is only invoked if both dex2oat and # patchoat are available. Disable Checker otherwise (b/22552692). if [ "$prebuild_mode" = "yes" ] || [ "$have_patchoat" = "yes" -a "$have_dex2oat" = "yes" ]; then run_checker="yes" + if [ "$target_mode" = "no" ]; then cfg_output_dir="$tmp_dir" - checker_arch_option="--arch=${host_arch_name^^}" + checker_args="--arch=${host_arch_name^^}" else cfg_output_dir="$DEX_LOCATION" - checker_arch_option="--arch=${target_arch_name^^}" + checker_args="--arch=${target_arch_name^^}" + fi + + if [ "$debuggable" = "yes" ]; then + checker_args="$checker_args --debuggable" fi + run_args="${run_args} -Xcompiler-option --dump-cfg=$cfg_output_dir/$cfg_output \ -Xcompiler-option -j1" fi @@ -702,7 +708,7 @@ if [ "$dev_mode" = "yes" ]; then if [ "$target_mode" = "yes" ]; then adb pull $cfg_output_dir/$cfg_output &> /dev/null fi - "$checker" $checker_arch_option "$cfg_output" "$tmp_dir" 2>&1 + "$checker" $checker_args "$cfg_output" "$tmp_dir" 2>&1 checker_exit="$?" if [ "$checker_exit" = "0" ]; then good="yes" @@ -727,7 +733,7 @@ elif [ "$update_mode" = "yes" ]; then if [ "$target_mode" = "yes" ]; then adb pull $cfg_output_dir/$cfg_output &> /dev/null fi - "$checker" -q $checker_arch_option "$cfg_output" "$tmp_dir" >> "$output" 2>&1 + "$checker" -q $checker_args "$cfg_output" "$tmp_dir" >> "$output" 2>&1 fi sed -e 's/[[:cntrl:]]$//g' < "$output" >"${td_expected}" good="yes" @@ -768,7 +774,7 @@ else if [ "$target_mode" = "yes" ]; then adb pull $cfg_output_dir/$cfg_output &> /dev/null fi - "$checker" -q $checker_arch_option "$cfg_output" "$tmp_dir" >> "$output" 2>&1 + "$checker" -q $checker_args "$cfg_output" "$tmp_dir" >> "$output" 2>&1 checker_exit="$?" if [ "$checker_exit" != "0" ]; then echo "checker exit status: $checker_exit" 1>&2 diff --git a/tools/checker/checker.py b/tools/checker/checker.py index bc5e17da6a..2e9faba9fb 100755 --- a/tools/checker/checker.py +++ b/tools/checker/checker.py @@ -36,7 +36,9 @@ def ParseArguments(): 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.") + help="Run tests for the specified target architecture.") + parser.add_argument("--debuggable", action="store_true", + help="Run tests for debuggable code.") parser.add_argument("-q", "--quiet", action="store_true", help="print only errors") return parser.parse_args() @@ -83,13 +85,13 @@ def FindCheckerFiles(path): Logger.fail("Source path \"" + path + "\" not found") -def RunTests(checkPrefix, checkPath, outputFilename, targetArch): +def RunTests(checkPrefix, checkPath, outputFilename, targetArch, debuggableMode): 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, targetArch) + MatchFiles(checkerFile, c1File, targetArch, debuggableMode) if __name__ == "__main__": @@ -103,4 +105,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, args.arch) + RunTests(args.check_prefix, args.source_path, args.tested_file, args.arch, args.debuggable) diff --git a/tools/checker/file_format/checker/parser.py b/tools/checker/file_format/checker/parser.py index 446302fed2..f199a50ebe 100644 --- a/tools/checker/file_format/checker/parser.py +++ b/tools/checker/file_format/checker/parser.py @@ -22,7 +22,7 @@ import re def __isCheckerLine(line): return line.startswith("///") or line.startswith("##") -def __extractLine(prefix, line, arch = None): +def __extractLine(prefix, line, arch = None, debuggable = False): """ 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. @@ -30,10 +30,11 @@ def __extractLine(prefix, line, arch = None): rIgnoreWhitespace = r"\s*" rCommentSymbols = [r"///", r"##"] arch_specifier = r"-%s" % arch if arch is not None else r"" + dbg_specifier = r"-DEBUGGABLE" if debuggable else r"" regexPrefix = rIgnoreWhitespace + \ r"(" + r"|".join(rCommentSymbols) + r")" + \ rIgnoreWhitespace + \ - prefix + arch_specifier + r":" + prefix + arch_specifier + dbg_specifier + r":" # The 'match' function succeeds only if the pattern is matched at the # beginning of the line. @@ -56,10 +57,11 @@ def __processLine(line, lineNo, prefix, fileName): # Lines beginning with 'CHECK-START' start a new test case. # 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 + for debuggable in [True, False]: + for arch in [None] + archs_list: + startLine = __extractLine(prefix + "-START", line, arch, debuggable) + if startLine is not None: + return None, startLine, (arch, debuggable) # Lines starting only with 'CHECK' are matched in order. plainLine = __extractLine(prefix, line) @@ -167,9 +169,11 @@ 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, testArch in \ + for caseName, caseLines, startLineNo, testData in \ SplitStream(stream, fnProcessLine, fnLineOutsideChunk): - testCase = TestCase(checkerFile, caseName, startLineNo, testArch) + testArch = testData[0] + forDebuggable = testData[1] + testCase = TestCase(checkerFile, caseName, startLineNo, testArch, forDebuggable) 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 7ee09cdb84..a31aa54c91 100644 --- a/tools/checker/file_format/checker/struct.py +++ b/tools/checker/file_format/checker/struct.py @@ -36,7 +36,7 @@ class CheckerFile(PrintableMixin): class TestCase(PrintableMixin): - def __init__(self, parent, name, startLineNo, testArch = None): + def __init__(self, parent, name, startLineNo, testArch = None, forDebuggable = False): assert isinstance(parent, CheckerFile) self.parent = parent @@ -44,6 +44,7 @@ class TestCase(PrintableMixin): self.assertions = [] self.startLineNo = startLineNo self.testArch = testArch + self.forDebuggable = forDebuggable 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 495dabc588..579c190d21 100644 --- a/tools/checker/file_format/checker/test.py +++ b/tools/checker/file_format/checker/test.py @@ -290,7 +290,7 @@ class CheckerParser_FileLayoutTest(unittest.TestCase): /// CHECK-NEXT: bar """) -class CheckerParser_ArchTests(unittest.TestCase): +class CheckerParser_SuffixTests(unittest.TestCase): noarch_block = """ /// CHECK-START: Group @@ -308,11 +308,12 @@ class CheckerParser_ArchTests(unittest.TestCase): /// CHECK-DAG: yoyo """ + def parse(self, checkerText): + return ParseCheckerStream("<test_file>", "CHECK", io.StringIO(ToUnicode(checkerText))) + def test_NonArchTests(self): for arch in [None] + archs_list: - checkerFile = ParseCheckerStream("<test-file>", - "CHECK", - io.StringIO(ToUnicode(self.noarch_block))) + checkerFile = self.parse(self.noarch_block) self.assertEqual(len(checkerFile.testCases), 1) self.assertEqual(len(checkerFile.testCases[0].assertions), 4) @@ -320,9 +321,7 @@ class CheckerParser_ArchTests(unittest.TestCase): 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))) + checkerFile = self.parse(checkerText) self.assertEqual(len(checkerFile.testCases), 1) self.assertEqual(len(checkerFile.testCasesForArch(testArch)), 1) self.assertEqual(len(checkerFile.testCasesForArch(targetArch)), 0) @@ -330,13 +329,42 @@ class CheckerParser_ArchTests(unittest.TestCase): 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))) + checkerFile = self.parse(checkerText) self.assertEqual(len(checkerFile.testCases), 1) self.assertEqual(len(checkerFile.testCasesForArch(arch)), 1) self.assertEqual(len(checkerFile.testCases[0].assertions), 4) + def test_NoDebugAndArch(self): + testCase = self.parse(""" + /// CHECK-START: Group + /// CHECK: foo + """).testCases[0] + self.assertFalse(testCase.forDebuggable) + self.assertEqual(testCase.testArch, None) + + def test_SetDebugNoArch(self): + testCase = self.parse(""" + /// CHECK-START-DEBUGGABLE: Group + /// CHECK: foo + """).testCases[0] + self.assertTrue(testCase.forDebuggable) + self.assertEqual(testCase.testArch, None) + + def test_NoDebugSetArch(self): + testCase = self.parse(""" + /// CHECK-START-ARM: Group + /// CHECK: foo + """).testCases[0] + self.assertFalse(testCase.forDebuggable) + self.assertEqual(testCase.testArch, "ARM") + + def test_SetDebugAndArch(self): + testCase = self.parse(""" + /// CHECK-START-ARM-DEBUGGABLE: Group + /// CHECK: foo + """).testCases[0] + self.assertTrue(testCase.forDebuggable) + self.assertEqual(testCase.testArch, "ARM") class CheckerParser_EvalTests(unittest.TestCase): def parseTestCase(self, string): diff --git a/tools/checker/match/file.py b/tools/checker/match/file.py index 6601a1e965..3ded07482f 100644 --- a/tools/checker/match/file.py +++ b/tools/checker/match/file.py @@ -159,10 +159,13 @@ def MatchTestCase(testCase, c1Pass): matchFrom = match.scope.end + 1 variables = match.variables -def MatchFiles(checkerFile, c1File, targetArch): +def MatchFiles(checkerFile, c1File, targetArch, debuggableMode): for testCase in checkerFile.testCases: if testCase.testArch not in [None, targetArch]: continue + if testCase.forDebuggable != debuggableMode: + 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 2e8f2083b5..a0d274df25 100755 --- a/tools/checker/run_unit_tests.py +++ b/tools/checker/run_unit_tests.py @@ -19,7 +19,7 @@ from file_format.c1visualizer.test import C1visualizerParser_Test from file_format.checker.test import CheckerParser_PrefixTest, \ CheckerParser_TestExpressionTest, \ CheckerParser_FileLayoutTest, \ - CheckerParser_ArchTests, \ + CheckerParser_SuffixTests, \ CheckerParser_EvalTests from match.test import MatchLines_Test, \ MatchFiles_Test |