Index: test/cctest/compiler/test-structured-ifbuilder-fuzzer.cc |
diff --git a/test/cctest/compiler/test-structured-ifbuilder-fuzzer.cc b/test/cctest/compiler/test-structured-ifbuilder-fuzzer.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..156ab8da85ad877e517a6016a213c81b563aa448 |
--- /dev/null |
+++ b/test/cctest/compiler/test-structured-ifbuilder-fuzzer.cc |
@@ -0,0 +1,666 @@ |
+// Copyright 2014 the V8 project authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include <string> |
+ |
+#include "src/v8.h" |
+#include "test/cctest/cctest.h" |
+ |
+#include "src/base/utils/random-number-generator.h" |
+#include "test/cctest/compiler/codegen-tester.h" |
+ |
+#if V8_TURBOFAN_TARGET |
+ |
+using namespace v8::internal; |
+using namespace v8::internal::compiler; |
+ |
+typedef StructuredMachineAssembler::IfBuilder IfBuilder; |
+typedef StructuredMachineAssembler::LoopBuilder Loop; |
+ |
+static const int32_t kUninitializedVariableOffset = -1; |
+static const int32_t kUninitializedOutput = -1; |
+static const int32_t kVerifiedOutput = -2; |
+ |
+static const int32_t kInitalVar = 1013; |
+static const int32_t kConjunctionInc = 1069; |
+static const int32_t kDisjunctionInc = 1151; |
+static const int32_t kThenInc = 1223; |
+static const int32_t kElseInc = 1291; |
+static const int32_t kIfInc = 1373; |
+ |
+class IfBuilderModel { |
+ public: |
+ explicit IfBuilderModel(Zone* zone) |
+ : zone_(zone), |
+ variable_offset_(0), |
+ root_(new (zone_) Node(NULL)), |
+ current_node_(root_), |
+ current_expression_(NULL) {} |
+ |
+ void If() { |
+ if (current_node_->else_node != NULL) { |
+ current_node_ = current_node_->else_node; |
+ } else if (current_node_->then_node != NULL) { |
+ current_node_ = current_node_->then_node; |
+ } |
+ ASSERT(current_expression_ == NULL); |
+ current_expression_ = new (zone_) Expression(zone_, NULL); |
+ current_node_->condition = current_expression_; |
+ } |
+ void IfNode() { LastChild()->variable_offset = variable_offset_++; } |
+ |
+ void OpenParen() { current_expression_ = LastChild(); } |
+ void CloseParen() { current_expression_ = current_expression_->parent; } |
+ |
+ void And() { NewChild()->conjunction = true; } |
+ void Or() { NewChild()->disjunction = true; } |
+ |
+ void Then() { |
+ ASSERT(current_expression_ == NULL || current_expression_->parent == NULL); |
+ current_expression_ = NULL; |
+ ASSERT(current_node_->then_node == NULL); |
+ current_node_->then_node = new (zone_) Node(current_node_); |
+ } |
+ void Else() { |
+ ASSERT(current_expression_ == NULL || current_expression_->parent == NULL); |
+ current_expression_ = NULL; |
+ ASSERT(current_node_->else_node == NULL); |
+ current_node_->else_node = new (zone_) Node(current_node_); |
+ } |
+ void Return() { |
+ if (current_node_->else_node != NULL) { |
+ current_node_->else_node->returns = true; |
+ } else if (current_node_->then_node != NULL) { |
+ current_node_->then_node->returns = true; |
+ } else { |
+ CHECK(false); |
+ } |
+ } |
+ void End() {} |
+ |
+ void Print(std::vector<char>* v) { PrintRecursive(v, root_); } |
+ |
+ struct VerificationState { |
+ int32_t* inputs; |
+ int32_t* outputs; |
+ int32_t var; |
+ }; |
+ |
+ int32_t Verify(int length, int32_t* inputs, int32_t* outputs) { |
+ CHECK_EQ(variable_offset_, length); |
+ // Input/Output verification. |
+ for (int i = 0; i < length; ++i) { |
+ CHECK(inputs[i] == 0 || inputs[i] == 1); |
+ CHECK(outputs[i] == kUninitializedOutput || outputs[i] >= 0); |
+ } |
+ // Do verification. |
+ VerificationState state; |
+ state.inputs = inputs; |
+ state.outputs = outputs; |
+ state.var = kInitalVar; |
+ VerifyRecursive(root_, &state); |
+ // Verify all outputs marked. |
+ for (int i = 0; i < length; ++i) { |
+ CHECK(outputs[i] == kUninitializedOutput || |
+ outputs[i] == kVerifiedOutput); |
+ } |
+ return state.var; |
+ } |
+ |
+ private: |
+ struct Expression; |
+ typedef std::vector<Expression*, zone_allocator<Expression*> > Expressions; |
+ |
+ struct Expression : public ZoneObject { |
+ Expression(Zone* zone, Expression* p) |
+ : variable_offset(kUninitializedVariableOffset), |
+ disjunction(false), |
+ conjunction(false), |
+ parent(p), |
+ children(Expressions::allocator_type(zone)) {} |
+ int variable_offset; |
+ bool disjunction; |
+ bool conjunction; |
+ Expression* parent; |
+ Expressions children; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(Expression); |
+ }; |
+ |
+ struct Node : public ZoneObject { |
+ explicit Node(Node* p) |
+ : parent(p), |
+ condition(NULL), |
+ then_node(NULL), |
+ else_node(NULL), |
+ returns(false) {} |
+ Node* parent; |
+ Expression* condition; |
+ Node* then_node; |
+ Node* else_node; |
+ bool returns; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(Node); |
+ }; |
+ |
+ Expression* LastChild() { |
+ if (current_expression_->children.empty()) { |
+ current_expression_->children.push_back( |
+ new (zone_) Expression(zone_, current_expression_)); |
+ } |
+ return current_expression_->children.back(); |
+ } |
+ |
+ Expression* NewChild() { |
+ Expression* child = new (zone_) Expression(zone_, current_expression_); |
+ current_expression_->children.push_back(child); |
+ return child; |
+ } |
+ |
+ static void PrintRecursive(std::vector<char>* v, Expression* expression) { |
+ CHECK(expression != NULL); |
+ if (expression->conjunction) { |
+ ASSERT(!expression->disjunction); |
+ v->push_back('&'); |
+ } else if (expression->disjunction) { |
+ v->push_back('|'); |
+ } |
+ if (expression->variable_offset != kUninitializedVariableOffset) { |
+ v->push_back('v'); |
+ } |
+ Expressions& children = expression->children; |
+ if (children.empty()) return; |
+ v->push_back('('); |
+ for (Expressions::iterator i = children.begin(); i != children.end(); ++i) { |
+ PrintRecursive(v, *i); |
+ } |
+ v->push_back(')'); |
+ } |
+ |
+ static void PrintRecursive(std::vector<char>* v, Node* node) { |
+ // Termination condition. |
+ if (node->condition == NULL) { |
+ CHECK(node->then_node == NULL && node->else_node == NULL); |
+ if (node->returns) v->push_back('r'); |
+ return; |
+ } |
+ CHECK(!node->returns); |
+ v->push_back('i'); |
+ PrintRecursive(v, node->condition); |
+ if (node->then_node != NULL) { |
+ v->push_back('t'); |
+ PrintRecursive(v, node->then_node); |
+ } |
+ if (node->else_node != NULL) { |
+ v->push_back('e'); |
+ PrintRecursive(v, node->else_node); |
+ } |
+ } |
+ |
+ static bool VerifyRecursive(Expression* expression, |
+ VerificationState* state) { |
+ bool result = false; |
+ bool first_iteration = true; |
+ Expressions& children = expression->children; |
+ CHECK(!children.empty()); |
+ for (Expressions::iterator i = children.begin(); i != children.end(); ++i) { |
+ Expression* child = *i; |
+ // Short circuit evaluation, |
+ // but mixes of &&s and ||s have weird semantics. |
+ if ((child->conjunction && !result) || (child->disjunction && result)) { |
+ continue; |
+ } |
+ if (child->conjunction) state->var += kConjunctionInc; |
+ if (child->disjunction) state->var += kDisjunctionInc; |
+ bool child_result; |
+ if (child->variable_offset != kUninitializedVariableOffset) { |
+ // Verify output |
+ CHECK_EQ(state->var, state->outputs[child->variable_offset]); |
+ state->outputs[child->variable_offset] = kVerifiedOutput; // Mark seen. |
+ child_result = state->inputs[child->variable_offset]; |
+ CHECK(child->children.empty()); |
+ state->var += kIfInc; |
+ } else { |
+ child_result = VerifyRecursive(child, state); |
+ } |
+ if (child->conjunction) { |
+ result &= child_result; |
+ } else if (child->disjunction) { |
+ result |= child_result; |
+ } else { |
+ CHECK(first_iteration); |
+ result = child_result; |
+ } |
+ first_iteration = false; |
+ } |
+ return result; |
+ } |
+ |
+ static void VerifyRecursive(Node* node, VerificationState* state) { |
+ if (node->condition == NULL) return; |
+ bool result = VerifyRecursive(node->condition, state); |
+ if (result) { |
+ if (node->then_node) { |
+ state->var += kThenInc; |
+ return VerifyRecursive(node->then_node, state); |
+ } |
+ } else { |
+ if (node->else_node) { |
+ state->var += kElseInc; |
+ return VerifyRecursive(node->else_node, state); |
+ } |
+ } |
+ } |
+ |
+ Zone* zone_; |
+ int variable_offset_; |
+ Node* root_; |
+ Node* current_node_; |
+ Expression* current_expression_; |
+ DISALLOW_COPY_AND_ASSIGN(IfBuilderModel); |
+}; |
+ |
+ |
+class IfBuilderGenerator : public StructuredMachineAssemblerTester<int32_t> { |
+ public: |
+ IfBuilderGenerator() |
+ : StructuredMachineAssemblerTester(MachineOperatorBuilder::pointer_rep(), |
+ MachineOperatorBuilder::pointer_rep()), |
+ var_(NewVariable(Int32Constant(kInitalVar))), |
+ c_(this), |
+ m_(this->zone()), |
+ one_(Int32Constant(1)), |
+ offset_(0) {} |
+ |
+ static void GenerateExpression(v8::base::RandomNumberGenerator* rng, |
+ std::vector<char>* v, int n_vars) { |
+ int depth = 1; |
+ v->push_back('('); |
+ bool need_if = true; |
+ bool populated = false; |
+ while (n_vars != 0) { |
+ if (need_if) { |
+ // can nest a paren or do a variable |
+ if (rng->NextBool()) { |
+ v->push_back('v'); |
+ n_vars--; |
+ need_if = false; |
+ populated = true; |
+ } else { |
+ v->push_back('('); |
+ depth++; |
+ populated = false; |
+ } |
+ } else { |
+ // can pop, do && or do || |
+ int options = 3; |
+ if (depth == 1 || !populated) { |
+ options--; |
+ } |
+ switch (rng->NextInt(options)) { |
+ case 0: |
+ v->push_back('&'); |
+ need_if = true; |
+ break; |
+ case 1: |
+ v->push_back('|'); |
+ need_if = true; |
+ break; |
+ case 2: |
+ v->push_back(')'); |
+ depth--; |
+ break; |
+ } |
+ } |
+ } |
+ CHECK(!need_if); |
+ while (depth != 0) { |
+ v->push_back(')'); |
+ depth--; |
+ } |
+ } |
+ |
+ static void GenerateIfThenElse(v8::base::RandomNumberGenerator* rng, |
+ std::vector<char>* v, int n_ifs, |
+ int max_exp_length) { |
+ CHECK_GT(n_ifs, 0); |
+ CHECK_GT(max_exp_length, 0); |
+ bool have_env = true; |
+ bool then_done = false; |
+ bool else_done = false; |
+ bool first_iteration = true; |
+ while (n_ifs != 0) { |
+ if (have_env) { |
+ int options = 3; |
+ if (else_done || first_iteration) { // Don't do else or return |
+ options -= 2; |
+ first_iteration = false; |
+ } |
+ switch (rng->NextInt(options)) { |
+ case 0: |
+ v->push_back('i'); |
+ n_ifs--; |
+ have_env = false; |
+ GenerateExpression(rng, v, rng->NextInt(max_exp_length) + 1); |
+ break; |
+ case 1: |
+ v->push_back('r'); |
+ have_env = false; |
+ break; |
+ case 2: |
+ v->push_back('e'); |
+ else_done = true; |
+ then_done = false; |
+ break; |
+ default: |
+ CHECK(false); |
+ } |
+ } else { // Can only do then or else |
+ int options = 2; |
+ if (then_done) options--; |
+ switch (rng->NextInt(options)) { |
+ case 0: |
+ v->push_back('e'); |
+ else_done = true; |
+ then_done = false; |
+ break; |
+ case 1: |
+ v->push_back('t'); |
+ then_done = true; |
+ else_done = false; |
+ break; |
+ default: |
+ CHECK(false); |
+ } |
+ have_env = true; |
+ } |
+ } |
+ // Last instruction must have been an if, can complete it in several ways. |
+ int options = 2; |
+ if (then_done && !else_done) options++; |
+ switch (rng->NextInt(3)) { |
+ case 0: |
+ // Do nothing. |
+ break; |
+ case 1: |
+ v->push_back('t'); |
+ switch (rng->NextInt(3)) { |
+ case 0: |
+ v->push_back('r'); |
+ break; |
+ case 1: |
+ v->push_back('e'); |
+ break; |
+ case 2: |
+ v->push_back('e'); |
+ v->push_back('r'); |
+ break; |
+ default: |
+ CHECK(false); |
+ } |
+ break; |
+ case 2: |
+ v->push_back('e'); |
+ if (rng->NextBool()) v->push_back('r'); |
+ break; |
+ default: |
+ CHECK(false); |
+ } |
+ } |
+ |
+ std::string::const_iterator ParseExpression(std::string::const_iterator it, |
+ std::string::const_iterator end) { |
+ // Prepare for expression. |
+ m_.If(); |
+ c_.If(); |
+ int depth = 0; |
+ for (; it != end; ++it) { |
+ switch (*it) { |
+ case 'v': |
+ m_.IfNode(); |
+ { |
+ Node* offset = Int32Constant(offset_ * 4); |
+ Store(kMachineWord32, Parameter(1), offset, var_.Get()); |
+ var_.Set(Int32Add(var_.Get(), Int32Constant(kIfInc))); |
+ c_.If(Load(kMachineWord32, Parameter(0), offset)); |
+ offset_++; |
+ } |
+ break; |
+ case '&': |
+ m_.And(); |
+ c_.And(); |
+ var_.Set(Int32Add(var_.Get(), Int32Constant(kConjunctionInc))); |
+ break; |
+ case '|': |
+ m_.Or(); |
+ c_.Or(); |
+ var_.Set(Int32Add(var_.Get(), Int32Constant(kDisjunctionInc))); |
+ break; |
+ case '(': |
+ if (depth != 0) { |
+ m_.OpenParen(); |
+ c_.OpenParen(); |
+ } |
+ depth++; |
+ break; |
+ case ')': |
+ depth--; |
+ if (depth == 0) return it; |
+ m_.CloseParen(); |
+ c_.CloseParen(); |
+ break; |
+ default: |
+ CHECK(false); |
+ } |
+ } |
+ CHECK(false); |
+ return it; |
+ } |
+ |
+ void ParseIfThenElse(const std::string& str) { |
+ int n_vars = 0; |
+ for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { |
+ if (*it == 'v') n_vars++; |
+ } |
+ InitializeConstants(n_vars); |
+ for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { |
+ switch (*it) { |
+ case 'i': { |
+ it++; |
+ CHECK(it != str.end()); |
+ CHECK_EQ('(', *it); |
+ it = ParseExpression(it, str.end()); |
+ CHECK_EQ(')', *it); |
+ break; |
+ } |
+ case 't': |
+ m_.Then(); |
+ c_.Then(); |
+ var_.Set(Int32Add(var_.Get(), Int32Constant(kThenInc))); |
+ break; |
+ case 'e': |
+ m_.Else(); |
+ c_.Else(); |
+ var_.Set(Int32Add(var_.Get(), Int32Constant(kElseInc))); |
+ break; |
+ case 'r': |
+ m_.Return(); |
+ Return(var_.Get()); |
+ break; |
+ default: |
+ CHECK(false); |
+ } |
+ } |
+ m_.End(); |
+ c_.End(); |
+ Return(var_.Get()); |
+ // Compare generated model to parsed version. |
+ { |
+ std::vector<char> v; |
+ m_.Print(&v); |
+ std::string m_str(v.begin(), v.end()); |
+ CHECK(m_str == str); |
+ } |
+ } |
+ |
+ void ParseExpression(const std::string& str) { |
+ CHECK(inputs_.is_empty()); |
+ std::string wrapped = "i(" + str + ")te"; |
+ ParseIfThenElse(wrapped); |
+ } |
+ |
+ void ParseRandomIfThenElse(v8::base::RandomNumberGenerator* rng, int n_ifs, |
+ int n_vars) { |
+ std::vector<char> v; |
+ GenerateIfThenElse(rng, &v, n_ifs, n_vars); |
+ std::string str(v.begin(), v.end()); |
+ ParseIfThenElse(str); |
+ } |
+ |
+ void RunRandom(v8::base::RandomNumberGenerator* rng) { |
+ // TODO(dcarney): permute inputs via model. |
+ // TODO(dcarney): compute test_cases from n_ifs and n_vars. |
+ int test_cases = 100; |
+ for (int test = 0; test < test_cases; test++) { |
+ Initialize(); |
+ for (int i = 0; i < offset_; i++) { |
+ inputs_[i] = rng->NextBool(); |
+ } |
+ DoCall(); |
+ } |
+ } |
+ |
+ void Run(const std::string& str, int32_t expected) { |
+ Initialize(); |
+ int offset = 0; |
+ for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { |
+ switch (*it) { |
+ case 't': |
+ inputs_[offset++] = 1; |
+ break; |
+ case 'f': |
+ inputs_[offset++] = 0; |
+ break; |
+ default: |
+ CHECK(false); |
+ } |
+ } |
+ CHECK_EQ(offset_, offset); |
+ // Call. |
+ int32_t result = DoCall(); |
+ CHECK_EQ(result, expected); |
+ } |
+ |
+ private: |
+ typedef std::vector<int32_t, zone_allocator<int32_t> > IOVector; |
+ |
+ void InitializeConstants(int n_vars) { |
+ CHECK(inputs_.is_empty()); |
+ inputs_.Reset(new int32_t[n_vars]); |
+ outputs_.Reset(new int32_t[n_vars]); |
+ } |
+ |
+ void Initialize() { |
+ for (int i = 0; i < offset_; i++) { |
+ inputs_[i] = 0; |
+ outputs_[i] = kUninitializedOutput; |
+ } |
+ } |
+ |
+ int32_t DoCall() { |
+ int32_t result = Call(inputs_.get(), outputs_.get()); |
+ int32_t expected = m_.Verify(offset_, inputs_.get(), outputs_.get()); |
+ CHECK_EQ(result, expected); |
+ return result; |
+ } |
+ |
+ const v8::internal::compiler::Variable var_; |
+ IfBuilder c_; |
+ IfBuilderModel m_; |
+ Node* one_; |
+ int32_t offset_; |
+ SmartArrayPointer<int32_t> inputs_; |
+ SmartArrayPointer<int32_t> outputs_; |
+}; |
+ |
+ |
+TEST(RunExpressionString) { |
+ IfBuilderGenerator m; |
+ m.ParseExpression("((v|v)|v)"); |
+ m.Run("ttt", kInitalVar + 1 * kIfInc + kThenInc); |
+ m.Run("ftt", kInitalVar + 2 * kIfInc + kDisjunctionInc + kThenInc); |
+ m.Run("fft", kInitalVar + 3 * kIfInc + 2 * kDisjunctionInc + kThenInc); |
+ m.Run("fff", kInitalVar + 3 * kIfInc + 2 * kDisjunctionInc + kElseInc); |
+} |
+ |
+ |
+TEST(RunExpressionStrings) { |
+ const char* strings[] = { |
+ "v", "(v)", "((v))", "v|v", |
+ "(v|v)", "((v|v))", "v&v", "(v&v)", |
+ "((v&v))", "v&(v)", "v&(v|v)", "v&(v|v)&v", |
+ "v|(v)", "v|(v&v)", "v|(v&v)|v", "v|(((v)|(v&v)|(v)|v)&(v))|v", |
+ }; |
+ v8::base::RandomNumberGenerator rng; |
+ for (size_t i = 0; i < ARRAY_SIZE(strings); i++) { |
+ IfBuilderGenerator m; |
+ m.ParseExpression(strings[i]); |
+ m.RunRandom(&rng); |
+ } |
+} |
+ |
+ |
+TEST(RunSimpleIfElseTester) { |
+ const char* tests[] = { |
+ "i(v)", "i(v)t", "i(v)te", |
+ "i(v)er", "i(v)ter", "i(v)ti(v)trei(v)ei(v)ei(v)ei(v)ei(v)ei(v)ei(v)e"}; |
+ v8::base::RandomNumberGenerator rng; |
+ for (size_t i = 0; i < ARRAY_SIZE(tests); ++i) { |
+ IfBuilderGenerator m; |
+ m.ParseIfThenElse(tests[i]); |
+ m.RunRandom(&rng); |
+ } |
+} |
+ |
+ |
+TEST(RunRandomExpressions) { |
+ v8::base::RandomNumberGenerator rng; |
+ for (int n_vars = 1; n_vars < 12; n_vars++) { |
+ for (int i = 0; i < n_vars * n_vars + 10; i++) { |
+ IfBuilderGenerator m; |
+ m.ParseRandomIfThenElse(&rng, 1, n_vars); |
+ m.RunRandom(&rng); |
+ } |
+ } |
+} |
+ |
+ |
+TEST(RunRandomIfElse) { |
+ v8::base::RandomNumberGenerator rng; |
+ for (int n_ifs = 1; n_ifs < 12; n_ifs++) { |
+ for (int i = 0; i < n_ifs * n_ifs + 10; i++) { |
+ IfBuilderGenerator m; |
+ m.ParseRandomIfThenElse(&rng, n_ifs, 1); |
+ m.RunRandom(&rng); |
+ } |
+ } |
+} |
+ |
+ |
+TEST(RunRandomIfElseExpressions) { |
+ v8::base::RandomNumberGenerator rng; |
+ for (int n_vars = 2; n_vars < 6; n_vars++) { |
+ for (int n_ifs = 2; n_ifs < 7; n_ifs++) { |
+ for (int i = 0; i < n_ifs * n_vars + 10; i++) { |
+ IfBuilderGenerator m; |
+ m.ParseRandomIfThenElse(&rng, n_ifs, n_vars); |
+ m.RunRandom(&rng); |
+ } |
+ } |
+ } |
+} |
+ |
+#endif |