v8/test/cctest/compiler/test-structured-ifbuilder-fuzzer.cc
titzer@chromium.org 496697df4e Add MachineSignature, which is an encapsulation of the machine types for parameters and return values in a graph.
This utility will be used to simplify Linkage and fix representation inference
to work with graphs where parameters and return values are something other
than tagged. It will also make testing representation inference a lot
easier, since we can then exactly nail down the machine types of parameters
and returns.

This CL also adds c-signature.h, which demonstrates how to convert C function
signatures into MachineSignatures. The CSignatures will be used in tests to
make it easier and simpler to codegen tests.

R=jarin@chromium.org
BUG=

Review URL: https://codereview.chromium.org/515173002

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23490 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2014-08-28 13:17:38 +00:00

666 lines
18 KiB
C++

// 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;
}
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<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) {
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<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<int32_t>(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<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(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<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 < 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