// 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 #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; } DCHECK(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() { DCHECK(current_expression_ == NULL || current_expression_->parent == NULL); current_expression_ = NULL; DCHECK(current_node_->then_node == NULL); current_node_->then_node = new (zone_) Node(current_node_); } void Else() { DCHECK(current_expression_ == NULL || current_expression_->parent == NULL); current_expression_ = NULL; DCHECK(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* 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 > 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* v, Expression* expression) { CHECK(expression != NULL); if (expression->conjunction) { DCHECK(!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* 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 { public: IfBuilderGenerator() : StructuredMachineAssemblerTester(kMachPtr, kMachPtr), var_(NewVariable(Int32Constant(kInitalVar))), c_(this), m_(this->zone()), one_(Int32Constant(1)), offset_(0) {} static void GenerateExpression(v8::base::RandomNumberGenerator* rng, std::vector* 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* 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(kMachInt32, Parameter(1), offset, var_.Get()); var_.Set(Int32Add(var_.Get(), Int32Constant(kIfInc))); c_.If(Load(kMachInt32, 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 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 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 > 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 inputs_; SmartArrayPointer 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 < arraysize(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 < arraysize(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