ART: Added Checker, a pattern matching test engine
This patch adds a Python script which implements a domain-specific
mini-language similar to that of LLVM's FileCheck. It is primarily
intended for writing tests for the optimizing compiler but could be
configured for other use cases too. It is implemented from scratch in
order to avoid dependency on LLVM.
Checker tests are written in Java and dex2oat is invoked with a flag
which dumps the CFG before and after each pass of the optimizing
compiler. The output is then compared against assertions in the
test's comments parsed by Checker. See comments in tools/checker.py
for more details about the currently supported language features.
This initial CL implements only one type of assertion - whether the
output contains lines matching a desired pattern in the given order -
but supports both plain text and regex matching and allows for
equivalency testing by matching for the outcome of a previous match.
See the tests in compiler/optimizing/test/ConstantFolding.java for
examples.
Change-Id: I1ad7431b399c38dc0391ccee74d2c643ba0b0675
diff --git a/compiler/compiler.h b/compiler/compiler.h
index 07e2fd6..d688ead 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -41,7 +41,7 @@
static Compiler* Create(CompilerDriver* driver, Kind kind);
- virtual void Init() const = 0;
+ virtual void Init() = 0;
virtual void UnInit() const = 0;
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc
index c14e22e..102ce17 100644
--- a/compiler/dex/quick/quick_compiler.cc
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -41,7 +41,7 @@
public:
explicit QuickCompiler(CompilerDriver* driver) : Compiler(driver, 100) {}
- void Init() const OVERRIDE;
+ void Init() OVERRIDE;
void UnInit() const OVERRIDE;
@@ -574,7 +574,7 @@
cu.disable_opt |= kDisabledOptimizationsPerISA[cu.instruction_set];
}
-void QuickCompiler::Init() const {
+void QuickCompiler::Init() {
CHECK(GetCompilerDriver()->GetCompilerContext() == nullptr);
}
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 5d1703e..b14b0a7 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -167,6 +167,15 @@
}
output_ << "]";
}
+ if (instruction->IsIntConstant()) {
+ output_ << " " << instruction->AsIntConstant()->GetValue();
+ } else if (instruction->IsLongConstant()) {
+ output_ << " " << instruction->AsLongConstant()->GetValue();
+ } else if (instruction->IsFloatConstant()) {
+ output_ << " " << instruction->AsFloatConstant()->GetValue();
+ } else if (instruction->IsDoubleConstant()) {
+ output_ << " " << instruction->AsDoubleConstant()->GetValue();
+ }
if (pass_name_ == kLivenessPassName && instruction->GetLifetimePosition() != kNoLifetime) {
output_ << " (liveness: " << instruction->GetLifetimePosition();
if (instruction->HasLiveInterval()) {
@@ -270,7 +279,7 @@
const char* string_filter,
const CodeGenerator& codegen,
const char* method_name)
- : output_(output), graph_(graph), codegen_(codegen), is_enabled_(false) {
+ : output_(output), graph_(graph), codegen_(codegen), is_enabled_(false) {
if (output == nullptr) {
return;
}
@@ -279,7 +288,7 @@
}
is_enabled_ = true;
- HGraphVisualizerPrinter printer(graph, *output_, "", codegen_);
+ HGraphVisualizerPrinter printer(graph_, *output_, "", codegen_);
printer.StartTag("compilation");
printer.PrintProperty("name", method_name);
printer.PrintProperty("method", method_name);
@@ -287,12 +296,12 @@
printer.EndTag("compilation");
}
-void HGraphVisualizer::DumpGraph(const char* pass_name) const {
- if (!is_enabled_) {
- return;
+void HGraphVisualizer::DumpGraph(const char* pass_name, bool is_after_pass) const {
+ if (is_enabled_) {
+ std::string pass_desc = std::string(pass_name) + (is_after_pass ? " (after)" : " (before)");
+ HGraphVisualizerPrinter printer(graph_, *output_, pass_desc.c_str(), codegen_);
+ printer.Run();
}
- HGraphVisualizerPrinter printer(graph_, *output_, pass_name, codegen_);
- printer.Run();
}
} // namespace art
diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h
index b5baed9..b90d15e 100644
--- a/compiler/optimizing/graph_visualizer.h
+++ b/compiler/optimizing/graph_visualizer.h
@@ -32,28 +32,18 @@
static const char* kRegisterAllocatorPassName = "register";
/**
- * If enabled, emits compilation information suitable for the c1visualizer tool
- * and IRHydra.
- * Currently only works if the compiler is single threaded.
+ * This class outputs the HGraph in the C1visualizer format.
+ * Note: Currently only works if the compiler is single threaded.
*/
class HGraphVisualizer : public ValueObject {
public:
- /**
- * If output is not null, and the method name of the dex compilation
- * unit contains `string_filter`, the compilation information will be
- * emitted.
- */
HGraphVisualizer(std::ostream* output,
HGraph* graph,
const char* string_filter,
const CodeGenerator& codegen,
const char* method_name);
- /**
- * If this visualizer is enabled, emit the compilation information
- * in `output_`.
- */
- void DumpGraph(const char* pass_name) const;
+ void DumpGraph(const char* pass_name, bool is_after_pass = true) const;
private:
std::ostream* const output_;
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index deebaf7..94751f8 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -68,13 +68,8 @@
};
/**
- * If set to true, generates a file suitable for the c1visualizer tool and IRHydra.
- */
-static bool kIsVisualizerEnabled = false;
-
-/**
* Filter to apply to the visualizer. Methods whose name contain that filter will
- * be in the file.
+ * be dumped.
*/
static const char* kStringFilter = "";
@@ -114,7 +109,7 @@
void InitCompilationUnit(CompilationUnit& cu ATTRIBUTE_UNUSED) const OVERRIDE {}
- void Init() const OVERRIDE {}
+ void Init() OVERRIDE;
void UnInit() const OVERRIDE {}
@@ -136,8 +131,16 @@
: Compiler(driver, kMaximumCompilationTimeBeforeWarning),
run_optimizations_(
driver->GetCompilerOptions().GetCompilerFilter() != CompilerOptions::kTime),
- compilation_stats_() {
- if (kIsVisualizerEnabled) {
+ compilation_stats_() {}
+
+void OptimizingCompiler::Init() {
+ // Enable C1visualizer output. Must be done in Init() because the compiler
+ // driver is not fully initialized when passed to the compiler's constructor.
+ CompilerDriver* driver = GetCompilerDriver();
+ if (driver->GetDumpPasses()) {
+ CHECK_EQ(driver->GetThreadCount(), 1U)
+ << "Graph visualizer requires the compiler to run single-threaded. "
+ << "Invoke the compiler with '-j1'.";
visualizer_output_.reset(new std::ofstream("art.cfg"));
}
}
@@ -213,8 +216,9 @@
for (size_t i = 0; i < arraysize(optimizations); ++i) {
HOptimization* optimization = optimizations[i];
+ visualizer.DumpGraph(optimization->GetPassName(), /*is_after=*/false);
optimization->Run();
- visualizer.DumpGraph(optimization->GetPassName());
+ visualizer.DumpGraph(optimization->GetPassName(), /*is_after=*/true);
optimization->Check();
}
}
diff --git a/compiler/optimizing/test/ConstantFolding.java b/compiler/optimizing/test/ConstantFolding.java
new file mode 100644
index 0000000..7fac5a9
--- /dev/null
+++ b/compiler/optimizing/test/ConstantFolding.java
@@ -0,0 +1,221 @@
+/*
+* Copyright (C) 2014 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 ConstantFolding {
+
+ /**
+ * Tiny three-register program exercising int constant folding
+ * on negation.
+ */
+
+ // CHECK-START: int ConstantFolding.IntNegation() constant_folding (before)
+ // CHECK: [[Const42:i[0-9]+]] IntConstant 42
+ // CHECK: [[Neg:i[0-9]+]] Neg [ [[Const42]] ]
+ // CHECK: Return [ [[Neg]] ]
+
+ // CHECK-START: int ConstantFolding.IntNegation() constant_folding (after)
+ // CHECK: [[ConstN42:i[0-9]+]] IntConstant -42
+ // CHECK: Return [ [[ConstN42]] ]
+
+ public static int IntNegation() {
+ int x, y;
+ x = 42;
+ y = -x;
+ return y;
+ }
+
+ /**
+ * Tiny three-register program exercising int constant folding
+ * on addition.
+ */
+
+ // CHECK-START: int ConstantFolding.IntAddition1() constant_folding (before)
+ // CHECK: [[Const1:i[0-9]+]] IntConstant 1
+ // CHECK: [[Const2:i[0-9]+]] IntConstant 2
+ // CHECK: [[Add:i[0-9]+]] Add [ [[Const1]] [[Const2]] ]
+ // CHECK: Return [ [[Add]] ]
+
+ // CHECK-START: int ConstantFolding.IntAddition1() constant_folding (after)
+ // CHECK: [[Const3:i[0-9]+]] IntConstant 3
+ // CHECK: Return [ [[Const3]] ]
+
+ public static int IntAddition1() {
+ int a, b, c;
+ a = 1;
+ b = 2;
+ c = a + b;
+ return c;
+ }
+
+ /**
+ * Small three-register program exercising int constant folding
+ * on addition.
+ */
+
+ // CHECK-START: int ConstantFolding.IntAddition2() constant_folding (before)
+ // CHECK: [[Const1:i[0-9]+]] IntConstant 1
+ // CHECK: [[Const2:i[0-9]+]] IntConstant 2
+ // CHECK: [[Const5:i[0-9]+]] IntConstant 5
+ // CHECK: [[Const6:i[0-9]+]] IntConstant 6
+ // CHECK: [[Add1:i[0-9]+]] Add [ [[Const1]] [[Const2]] ]
+ // CHECK: [[Add2:i[0-9]+]] Add [ [[Const5]] [[Const6]] ]
+ // CHECK: [[Add3:i[0-9]+]] Add [ [[Add1]] [[Add2]] ]
+ // CHECK: Return [ [[Add3]] ]
+
+ // CHECK-START: int ConstantFolding.IntAddition2() constant_folding (after)
+ // CHECK: [[Const14:i[0-9]+]] IntConstant 14
+ // CHECK: Return [ [[Const14]] ]
+
+ public static int IntAddition2() {
+ int a, b, c;
+ a = 1;
+ b = 2;
+ a += b;
+ b = 5;
+ c = 6;
+ b += c;
+ c = a + b;
+ return c;
+ }
+
+ /**
+ * Tiny three-register program exercising int constant folding
+ * on subtraction.
+ */
+
+ // CHECK-START: int ConstantFolding.IntSubtraction() constant_folding (before)
+ // CHECK: [[Const5:i[0-9]+]] IntConstant 5
+ // CHECK: [[Const2:i[0-9]+]] IntConstant 2
+ // CHECK: [[Sub:i[0-9]+]] Sub [ [[Const5]] [[Const2]] ]
+ // CHECK: Return [ [[Sub]] ]
+
+ // CHECK-START: int ConstantFolding.IntSubtraction() constant_folding (after)
+ // CHECK: [[Const3:i[0-9]+]] IntConstant 3
+ // CHECK: Return [ [[Const3]] ]
+
+ public static int IntSubtraction() {
+ int a, b, c;
+ a = 5;
+ b = 2;
+ c = a - b;
+ return c;
+ }
+
+ /**
+ * Tiny three-register program exercising long constant folding
+ * on addition.
+ */
+
+ // CHECK-START: long ConstantFolding.LongAddition() constant_folding (before)
+ // CHECK: [[Const1:j[0-9]+]] LongConstant 1
+ // CHECK: [[Const2:j[0-9]+]] LongConstant 2
+ // CHECK: [[Add:j[0-9]+]] Add [ [[Const1]] [[Const2]] ]
+ // CHECK: Return [ [[Add]] ]
+
+ // CHECK-START: long ConstantFolding.LongAddition() constant_folding (after)
+ // CHECK: [[Const3:j[0-9]+]] LongConstant 3
+ // CHECK: Return [ [[Const3]] ]
+
+ public static long LongAddition() {
+ long a, b, c;
+ a = 1L;
+ b = 2L;
+ c = a + b;
+ return c;
+ }
+
+ /**
+ * Tiny three-register program exercising long constant folding
+ * on subtraction.
+ */
+
+ // CHECK-START: long ConstantFolding.LongSubtraction() constant_folding (before)
+ // CHECK: [[Const5:j[0-9]+]] LongConstant 5
+ // CHECK: [[Const2:j[0-9]+]] LongConstant 2
+ // CHECK: [[Sub:j[0-9]+]] Sub [ [[Const5]] [[Const2]] ]
+ // CHECK: Return [ [[Sub]] ]
+
+ // CHECK-START: long ConstantFolding.LongSubtraction() constant_folding (after)
+ // CHECK: [[Const3:j[0-9]+]] LongConstant 3
+ // CHECK: Return [ [[Const3]] ]
+
+ public static long LongSubtraction() {
+ long a, b, c;
+ a = 5L;
+ b = 2L;
+ c = a - b;
+ return c;
+ }
+
+ /**
+ * Three-register program with a constant (static) condition.
+ */
+
+ // CHECK-START: int ConstantFolding.StaticCondition() constant_folding (before)
+ // CHECK: [[Const5:i[0-9]+]] IntConstant 5
+ // CHECK: [[Const2:i[0-9]+]] IntConstant 2
+ // CHECK: [[Cond:z[0-9]+]] GreaterThanOrEqual [ [[Const5]] [[Const2]] ]
+ // CHECK: If [ [[Cond]] ]
+
+ // CHECK-START: int ConstantFolding.StaticCondition() constant_folding (after)
+ // CHECK: [[Const1:i[0-9]+]] IntConstant 1
+ // CHECK: If [ [[Const1]] ]
+
+ public static int StaticCondition() {
+ int a, b, c;
+ a = 5;
+ b = 2;
+ if (a < b)
+ c = a + b;
+ else
+ c = a - b;
+ return c;
+ }
+
+ /**
+ * Four-variable program with jumps leading to the creation of many
+ * blocks.
+ *
+ * The intent of this test is to ensure that all constant expressions
+ * are actually evaluated at compile-time, thanks to the reverse
+ * (forward) post-order traversal of the the dominator tree.
+ */
+
+ // CHECK-START: int ConstantFolding.JumpsAndConditionals(boolean) constant_folding (before)
+ // CHECK: [[Const5:i[0-9]+]] IntConstant 5
+ // CHECK: [[Const2:i[0-9]+]] IntConstant 2
+ // CHECK: [[Add:i[0-9]+]] Add [ [[Const5]] [[Const2]] ]
+ // CHECK: [[Phi:i[0-9]+]] Phi [ [[Add]] [[Sub:i[0-9]+]] ]
+ // CHECK: Return [ [[Phi]] ]
+ // CHECK: [[Sub]] Sub [ [[Const5]] [[Const2]] ]
+
+ // CHECK-START: int ConstantFolding.JumpsAndConditionals(boolean) constant_folding (after)
+ // CHECK: [[Const7:i[0-9]+]] IntConstant 7
+ // CHECK: [[Phi:i[0-9]+]] Phi [ [[Const7]] [[Const3:i[0-9]+]] ]
+ // CHECK: Return [ [[Phi]] ]
+ // CHECK: [[Const3]] IntConstant 3
+
+ public static int JumpsAndConditionals(boolean cond) {
+ int a, b, c;
+ a = 5;
+ b = 2;
+ if (cond)
+ c = a + b;
+ else
+ c = a - b;
+ return c;
+ }
+}