OLD | NEW |
| (Empty) |
1 // Copyright 2014 the V8 project authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include <string> | |
6 | |
7 #include "src/v8.h" | |
8 #include "test/cctest/cctest.h" | |
9 | |
10 #include "src/base/utils/random-number-generator.h" | |
11 #include "test/cctest/compiler/codegen-tester.h" | |
12 | |
13 #if V8_TURBOFAN_TARGET | |
14 | |
15 using namespace v8::internal; | |
16 using namespace v8::internal::compiler; | |
17 | |
18 typedef StructuredMachineAssembler::IfBuilder IfBuilder; | |
19 typedef StructuredMachineAssembler::LoopBuilder Loop; | |
20 | |
21 static const int32_t kUninitializedVariableOffset = -1; | |
22 static const int32_t kUninitializedOutput = -1; | |
23 static const int32_t kVerifiedOutput = -2; | |
24 | |
25 static const int32_t kInitalVar = 1013; | |
26 static const int32_t kConjunctionInc = 1069; | |
27 static const int32_t kDisjunctionInc = 1151; | |
28 static const int32_t kThenInc = 1223; | |
29 static const int32_t kElseInc = 1291; | |
30 static const int32_t kIfInc = 1373; | |
31 | |
32 class IfBuilderModel { | |
33 public: | |
34 explicit IfBuilderModel(Zone* zone) | |
35 : zone_(zone), | |
36 variable_offset_(0), | |
37 root_(new (zone_) Node(NULL)), | |
38 current_node_(root_), | |
39 current_expression_(NULL) {} | |
40 | |
41 void If() { | |
42 if (current_node_->else_node != NULL) { | |
43 current_node_ = current_node_->else_node; | |
44 } else if (current_node_->then_node != NULL) { | |
45 current_node_ = current_node_->then_node; | |
46 } | |
47 DCHECK(current_expression_ == NULL); | |
48 current_expression_ = new (zone_) Expression(zone_, NULL); | |
49 current_node_->condition = current_expression_; | |
50 } | |
51 void IfNode() { LastChild()->variable_offset = variable_offset_++; } | |
52 | |
53 void OpenParen() { current_expression_ = LastChild(); } | |
54 void CloseParen() { current_expression_ = current_expression_->parent; } | |
55 | |
56 void And() { NewChild()->conjunction = true; } | |
57 void Or() { NewChild()->disjunction = true; } | |
58 | |
59 void Then() { | |
60 DCHECK(current_expression_ == NULL || current_expression_->parent == NULL); | |
61 current_expression_ = NULL; | |
62 DCHECK(current_node_->then_node == NULL); | |
63 current_node_->then_node = new (zone_) Node(current_node_); | |
64 } | |
65 void Else() { | |
66 DCHECK(current_expression_ == NULL || current_expression_->parent == NULL); | |
67 current_expression_ = NULL; | |
68 DCHECK(current_node_->else_node == NULL); | |
69 current_node_->else_node = new (zone_) Node(current_node_); | |
70 } | |
71 void Return() { | |
72 if (current_node_->else_node != NULL) { | |
73 current_node_->else_node->returns = true; | |
74 } else if (current_node_->then_node != NULL) { | |
75 current_node_->then_node->returns = true; | |
76 } else { | |
77 CHECK(false); | |
78 } | |
79 } | |
80 void End() {} | |
81 | |
82 void Print(std::vector<char>* v) { PrintRecursive(v, root_); } | |
83 | |
84 struct VerificationState { | |
85 int32_t* inputs; | |
86 int32_t* outputs; | |
87 int32_t var; | |
88 }; | |
89 | |
90 int32_t Verify(int length, int32_t* inputs, int32_t* outputs) { | |
91 CHECK_EQ(variable_offset_, length); | |
92 // Input/Output verification. | |
93 for (int i = 0; i < length; ++i) { | |
94 CHECK(inputs[i] == 0 || inputs[i] == 1); | |
95 CHECK(outputs[i] == kUninitializedOutput || outputs[i] >= 0); | |
96 } | |
97 // Do verification. | |
98 VerificationState state; | |
99 state.inputs = inputs; | |
100 state.outputs = outputs; | |
101 state.var = kInitalVar; | |
102 VerifyRecursive(root_, &state); | |
103 // Verify all outputs marked. | |
104 for (int i = 0; i < length; ++i) { | |
105 CHECK(outputs[i] == kUninitializedOutput || | |
106 outputs[i] == kVerifiedOutput); | |
107 } | |
108 return state.var; | |
109 } | |
110 | |
111 private: | |
112 struct Expression; | |
113 typedef std::vector<Expression*, zone_allocator<Expression*> > Expressions; | |
114 | |
115 struct Expression : public ZoneObject { | |
116 Expression(Zone* zone, Expression* p) | |
117 : variable_offset(kUninitializedVariableOffset), | |
118 disjunction(false), | |
119 conjunction(false), | |
120 parent(p), | |
121 children(Expressions::allocator_type(zone)) {} | |
122 int variable_offset; | |
123 bool disjunction; | |
124 bool conjunction; | |
125 Expression* parent; | |
126 Expressions children; | |
127 | |
128 private: | |
129 DISALLOW_COPY_AND_ASSIGN(Expression); | |
130 }; | |
131 | |
132 struct Node : public ZoneObject { | |
133 explicit Node(Node* p) | |
134 : parent(p), | |
135 condition(NULL), | |
136 then_node(NULL), | |
137 else_node(NULL), | |
138 returns(false) {} | |
139 Node* parent; | |
140 Expression* condition; | |
141 Node* then_node; | |
142 Node* else_node; | |
143 bool returns; | |
144 | |
145 private: | |
146 DISALLOW_COPY_AND_ASSIGN(Node); | |
147 }; | |
148 | |
149 Expression* LastChild() { | |
150 if (current_expression_->children.empty()) { | |
151 current_expression_->children.push_back( | |
152 new (zone_) Expression(zone_, current_expression_)); | |
153 } | |
154 return current_expression_->children.back(); | |
155 } | |
156 | |
157 Expression* NewChild() { | |
158 Expression* child = new (zone_) Expression(zone_, current_expression_); | |
159 current_expression_->children.push_back(child); | |
160 return child; | |
161 } | |
162 | |
163 static void PrintRecursive(std::vector<char>* v, Expression* expression) { | |
164 CHECK(expression != NULL); | |
165 if (expression->conjunction) { | |
166 DCHECK(!expression->disjunction); | |
167 v->push_back('&'); | |
168 } else if (expression->disjunction) { | |
169 v->push_back('|'); | |
170 } | |
171 if (expression->variable_offset != kUninitializedVariableOffset) { | |
172 v->push_back('v'); | |
173 } | |
174 Expressions& children = expression->children; | |
175 if (children.empty()) return; | |
176 v->push_back('('); | |
177 for (Expressions::iterator i = children.begin(); i != children.end(); ++i) { | |
178 PrintRecursive(v, *i); | |
179 } | |
180 v->push_back(')'); | |
181 } | |
182 | |
183 static void PrintRecursive(std::vector<char>* v, Node* node) { | |
184 // Termination condition. | |
185 if (node->condition == NULL) { | |
186 CHECK(node->then_node == NULL && node->else_node == NULL); | |
187 if (node->returns) v->push_back('r'); | |
188 return; | |
189 } | |
190 CHECK(!node->returns); | |
191 v->push_back('i'); | |
192 PrintRecursive(v, node->condition); | |
193 if (node->then_node != NULL) { | |
194 v->push_back('t'); | |
195 PrintRecursive(v, node->then_node); | |
196 } | |
197 if (node->else_node != NULL) { | |
198 v->push_back('e'); | |
199 PrintRecursive(v, node->else_node); | |
200 } | |
201 } | |
202 | |
203 static bool VerifyRecursive(Expression* expression, | |
204 VerificationState* state) { | |
205 bool result = false; | |
206 bool first_iteration = true; | |
207 Expressions& children = expression->children; | |
208 CHECK(!children.empty()); | |
209 for (Expressions::iterator i = children.begin(); i != children.end(); ++i) { | |
210 Expression* child = *i; | |
211 // Short circuit evaluation, | |
212 // but mixes of &&s and ||s have weird semantics. | |
213 if ((child->conjunction && !result) || (child->disjunction && result)) { | |
214 continue; | |
215 } | |
216 if (child->conjunction) state->var += kConjunctionInc; | |
217 if (child->disjunction) state->var += kDisjunctionInc; | |
218 bool child_result; | |
219 if (child->variable_offset != kUninitializedVariableOffset) { | |
220 // Verify output | |
221 CHECK_EQ(state->var, state->outputs[child->variable_offset]); | |
222 state->outputs[child->variable_offset] = kVerifiedOutput; // Mark seen. | |
223 child_result = state->inputs[child->variable_offset]; | |
224 CHECK(child->children.empty()); | |
225 state->var += kIfInc; | |
226 } else { | |
227 child_result = VerifyRecursive(child, state); | |
228 } | |
229 if (child->conjunction) { | |
230 result &= child_result; | |
231 } else if (child->disjunction) { | |
232 result |= child_result; | |
233 } else { | |
234 CHECK(first_iteration); | |
235 result = child_result; | |
236 } | |
237 first_iteration = false; | |
238 } | |
239 return result; | |
240 } | |
241 | |
242 static void VerifyRecursive(Node* node, VerificationState* state) { | |
243 if (node->condition == NULL) return; | |
244 bool result = VerifyRecursive(node->condition, state); | |
245 if (result) { | |
246 if (node->then_node) { | |
247 state->var += kThenInc; | |
248 return VerifyRecursive(node->then_node, state); | |
249 } | |
250 } else { | |
251 if (node->else_node) { | |
252 state->var += kElseInc; | |
253 return VerifyRecursive(node->else_node, state); | |
254 } | |
255 } | |
256 } | |
257 | |
258 Zone* zone_; | |
259 int variable_offset_; | |
260 Node* root_; | |
261 Node* current_node_; | |
262 Expression* current_expression_; | |
263 DISALLOW_COPY_AND_ASSIGN(IfBuilderModel); | |
264 }; | |
265 | |
266 | |
267 class IfBuilderGenerator : public StructuredMachineAssemblerTester<int32_t> { | |
268 public: | |
269 IfBuilderGenerator() | |
270 : StructuredMachineAssemblerTester<int32_t>(kMachPtr, kMachPtr), | |
271 var_(NewVariable(Int32Constant(kInitalVar))), | |
272 c_(this), | |
273 m_(this->zone()), | |
274 one_(Int32Constant(1)), | |
275 offset_(0) {} | |
276 | |
277 static void GenerateExpression(v8::base::RandomNumberGenerator* rng, | |
278 std::vector<char>* v, int n_vars) { | |
279 int depth = 1; | |
280 v->push_back('('); | |
281 bool need_if = true; | |
282 bool populated = false; | |
283 while (n_vars != 0) { | |
284 if (need_if) { | |
285 // can nest a paren or do a variable | |
286 if (rng->NextBool()) { | |
287 v->push_back('v'); | |
288 n_vars--; | |
289 need_if = false; | |
290 populated = true; | |
291 } else { | |
292 v->push_back('('); | |
293 depth++; | |
294 populated = false; | |
295 } | |
296 } else { | |
297 // can pop, do && or do || | |
298 int options = 3; | |
299 if (depth == 1 || !populated) { | |
300 options--; | |
301 } | |
302 switch (rng->NextInt(options)) { | |
303 case 0: | |
304 v->push_back('&'); | |
305 need_if = true; | |
306 break; | |
307 case 1: | |
308 v->push_back('|'); | |
309 need_if = true; | |
310 break; | |
311 case 2: | |
312 v->push_back(')'); | |
313 depth--; | |
314 break; | |
315 } | |
316 } | |
317 } | |
318 CHECK(!need_if); | |
319 while (depth != 0) { | |
320 v->push_back(')'); | |
321 depth--; | |
322 } | |
323 } | |
324 | |
325 static void GenerateIfThenElse(v8::base::RandomNumberGenerator* rng, | |
326 std::vector<char>* v, int n_ifs, | |
327 int max_exp_length) { | |
328 CHECK_GT(n_ifs, 0); | |
329 CHECK_GT(max_exp_length, 0); | |
330 bool have_env = true; | |
331 bool then_done = false; | |
332 bool else_done = false; | |
333 bool first_iteration = true; | |
334 while (n_ifs != 0) { | |
335 if (have_env) { | |
336 int options = 3; | |
337 if (else_done || first_iteration) { // Don't do else or return | |
338 options -= 2; | |
339 first_iteration = false; | |
340 } | |
341 switch (rng->NextInt(options)) { | |
342 case 0: | |
343 v->push_back('i'); | |
344 n_ifs--; | |
345 have_env = false; | |
346 GenerateExpression(rng, v, rng->NextInt(max_exp_length) + 1); | |
347 break; | |
348 case 1: | |
349 v->push_back('r'); | |
350 have_env = false; | |
351 break; | |
352 case 2: | |
353 v->push_back('e'); | |
354 else_done = true; | |
355 then_done = false; | |
356 break; | |
357 default: | |
358 CHECK(false); | |
359 } | |
360 } else { // Can only do then or else | |
361 int options = 2; | |
362 if (then_done) options--; | |
363 switch (rng->NextInt(options)) { | |
364 case 0: | |
365 v->push_back('e'); | |
366 else_done = true; | |
367 then_done = false; | |
368 break; | |
369 case 1: | |
370 v->push_back('t'); | |
371 then_done = true; | |
372 else_done = false; | |
373 break; | |
374 default: | |
375 CHECK(false); | |
376 } | |
377 have_env = true; | |
378 } | |
379 } | |
380 // Last instruction must have been an if, can complete it in several ways. | |
381 int options = 2; | |
382 if (then_done && !else_done) options++; | |
383 switch (rng->NextInt(3)) { | |
384 case 0: | |
385 // Do nothing. | |
386 break; | |
387 case 1: | |
388 v->push_back('t'); | |
389 switch (rng->NextInt(3)) { | |
390 case 0: | |
391 v->push_back('r'); | |
392 break; | |
393 case 1: | |
394 v->push_back('e'); | |
395 break; | |
396 case 2: | |
397 v->push_back('e'); | |
398 v->push_back('r'); | |
399 break; | |
400 default: | |
401 CHECK(false); | |
402 } | |
403 break; | |
404 case 2: | |
405 v->push_back('e'); | |
406 if (rng->NextBool()) v->push_back('r'); | |
407 break; | |
408 default: | |
409 CHECK(false); | |
410 } | |
411 } | |
412 | |
413 std::string::const_iterator ParseExpression(std::string::const_iterator it, | |
414 std::string::const_iterator end) { | |
415 // Prepare for expression. | |
416 m_.If(); | |
417 c_.If(); | |
418 int depth = 0; | |
419 for (; it != end; ++it) { | |
420 switch (*it) { | |
421 case 'v': | |
422 m_.IfNode(); | |
423 { | |
424 Node* offset = Int32Constant(offset_ * 4); | |
425 Store(kMachInt32, Parameter(1), offset, var_.Get()); | |
426 var_.Set(Int32Add(var_.Get(), Int32Constant(kIfInc))); | |
427 c_.If(Load(kMachInt32, Parameter(0), offset)); | |
428 offset_++; | |
429 } | |
430 break; | |
431 case '&': | |
432 m_.And(); | |
433 c_.And(); | |
434 var_.Set(Int32Add(var_.Get(), Int32Constant(kConjunctionInc))); | |
435 break; | |
436 case '|': | |
437 m_.Or(); | |
438 c_.Or(); | |
439 var_.Set(Int32Add(var_.Get(), Int32Constant(kDisjunctionInc))); | |
440 break; | |
441 case '(': | |
442 if (depth != 0) { | |
443 m_.OpenParen(); | |
444 c_.OpenParen(); | |
445 } | |
446 depth++; | |
447 break; | |
448 case ')': | |
449 depth--; | |
450 if (depth == 0) return it; | |
451 m_.CloseParen(); | |
452 c_.CloseParen(); | |
453 break; | |
454 default: | |
455 CHECK(false); | |
456 } | |
457 } | |
458 CHECK(false); | |
459 return it; | |
460 } | |
461 | |
462 void ParseIfThenElse(const std::string& str) { | |
463 int n_vars = 0; | |
464 for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { | |
465 if (*it == 'v') n_vars++; | |
466 } | |
467 InitializeConstants(n_vars); | |
468 for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { | |
469 switch (*it) { | |
470 case 'i': { | |
471 it++; | |
472 CHECK(it != str.end()); | |
473 CHECK_EQ('(', *it); | |
474 it = ParseExpression(it, str.end()); | |
475 CHECK_EQ(')', *it); | |
476 break; | |
477 } | |
478 case 't': | |
479 m_.Then(); | |
480 c_.Then(); | |
481 var_.Set(Int32Add(var_.Get(), Int32Constant(kThenInc))); | |
482 break; | |
483 case 'e': | |
484 m_.Else(); | |
485 c_.Else(); | |
486 var_.Set(Int32Add(var_.Get(), Int32Constant(kElseInc))); | |
487 break; | |
488 case 'r': | |
489 m_.Return(); | |
490 Return(var_.Get()); | |
491 break; | |
492 default: | |
493 CHECK(false); | |
494 } | |
495 } | |
496 m_.End(); | |
497 c_.End(); | |
498 Return(var_.Get()); | |
499 // Compare generated model to parsed version. | |
500 { | |
501 std::vector<char> v; | |
502 m_.Print(&v); | |
503 std::string m_str(v.begin(), v.end()); | |
504 CHECK(m_str == str); | |
505 } | |
506 } | |
507 | |
508 void ParseExpression(const std::string& str) { | |
509 CHECK(inputs_.is_empty()); | |
510 std::string wrapped = "i(" + str + ")te"; | |
511 ParseIfThenElse(wrapped); | |
512 } | |
513 | |
514 void ParseRandomIfThenElse(v8::base::RandomNumberGenerator* rng, int n_ifs, | |
515 int n_vars) { | |
516 std::vector<char> v; | |
517 GenerateIfThenElse(rng, &v, n_ifs, n_vars); | |
518 std::string str(v.begin(), v.end()); | |
519 ParseIfThenElse(str); | |
520 } | |
521 | |
522 void RunRandom(v8::base::RandomNumberGenerator* rng) { | |
523 // TODO(dcarney): permute inputs via model. | |
524 // TODO(dcarney): compute test_cases from n_ifs and n_vars. | |
525 int test_cases = 100; | |
526 for (int test = 0; test < test_cases; test++) { | |
527 Initialize(); | |
528 for (int i = 0; i < offset_; i++) { | |
529 inputs_[i] = rng->NextBool(); | |
530 } | |
531 DoCall(); | |
532 } | |
533 } | |
534 | |
535 void Run(const std::string& str, int32_t expected) { | |
536 Initialize(); | |
537 int offset = 0; | |
538 for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { | |
539 switch (*it) { | |
540 case 't': | |
541 inputs_[offset++] = 1; | |
542 break; | |
543 case 'f': | |
544 inputs_[offset++] = 0; | |
545 break; | |
546 default: | |
547 CHECK(false); | |
548 } | |
549 } | |
550 CHECK_EQ(offset_, offset); | |
551 // Call. | |
552 int32_t result = DoCall(); | |
553 CHECK_EQ(result, expected); | |
554 } | |
555 | |
556 private: | |
557 typedef std::vector<int32_t, zone_allocator<int32_t> > IOVector; | |
558 | |
559 void InitializeConstants(int n_vars) { | |
560 CHECK(inputs_.is_empty()); | |
561 inputs_.Reset(new int32_t[n_vars]); | |
562 outputs_.Reset(new int32_t[n_vars]); | |
563 } | |
564 | |
565 void Initialize() { | |
566 for (int i = 0; i < offset_; i++) { | |
567 inputs_[i] = 0; | |
568 outputs_[i] = kUninitializedOutput; | |
569 } | |
570 } | |
571 | |
572 int32_t DoCall() { | |
573 int32_t result = Call(inputs_.get(), outputs_.get()); | |
574 int32_t expected = m_.Verify(offset_, inputs_.get(), outputs_.get()); | |
575 CHECK_EQ(result, expected); | |
576 return result; | |
577 } | |
578 | |
579 const v8::internal::compiler::Variable var_; | |
580 IfBuilder c_; | |
581 IfBuilderModel m_; | |
582 Node* one_; | |
583 int32_t offset_; | |
584 SmartArrayPointer<int32_t> inputs_; | |
585 SmartArrayPointer<int32_t> outputs_; | |
586 }; | |
587 | |
588 | |
589 TEST(RunExpressionString) { | |
590 IfBuilderGenerator m; | |
591 m.ParseExpression("((v|v)|v)"); | |
592 m.Run("ttt", kInitalVar + 1 * kIfInc + kThenInc); | |
593 m.Run("ftt", kInitalVar + 2 * kIfInc + kDisjunctionInc + kThenInc); | |
594 m.Run("fft", kInitalVar + 3 * kIfInc + 2 * kDisjunctionInc + kThenInc); | |
595 m.Run("fff", kInitalVar + 3 * kIfInc + 2 * kDisjunctionInc + kElseInc); | |
596 } | |
597 | |
598 | |
599 TEST(RunExpressionStrings) { | |
600 const char* strings[] = { | |
601 "v", "(v)", "((v))", "v|v", | |
602 "(v|v)", "((v|v))", "v&v", "(v&v)", | |
603 "((v&v))", "v&(v)", "v&(v|v)", "v&(v|v)&v", | |
604 "v|(v)", "v|(v&v)", "v|(v&v)|v", "v|(((v)|(v&v)|(v)|v)&(v))|v", | |
605 }; | |
606 v8::base::RandomNumberGenerator rng; | |
607 for (size_t i = 0; i < arraysize(strings); i++) { | |
608 IfBuilderGenerator m; | |
609 m.ParseExpression(strings[i]); | |
610 m.RunRandom(&rng); | |
611 } | |
612 } | |
613 | |
614 | |
615 TEST(RunSimpleIfElseTester) { | |
616 const char* tests[] = { | |
617 "i(v)", "i(v)t", "i(v)te", | |
618 "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"}; | |
619 v8::base::RandomNumberGenerator rng; | |
620 for (size_t i = 0; i < arraysize(tests); ++i) { | |
621 IfBuilderGenerator m; | |
622 m.ParseIfThenElse(tests[i]); | |
623 m.RunRandom(&rng); | |
624 } | |
625 } | |
626 | |
627 | |
628 TEST(RunRandomExpressions) { | |
629 v8::base::RandomNumberGenerator rng; | |
630 for (int n_vars = 1; n_vars < 12; n_vars++) { | |
631 for (int i = 0; i < n_vars * n_vars + 10; i++) { | |
632 IfBuilderGenerator m; | |
633 m.ParseRandomIfThenElse(&rng, 1, n_vars); | |
634 m.RunRandom(&rng); | |
635 } | |
636 } | |
637 } | |
638 | |
639 | |
640 TEST(RunRandomIfElse) { | |
641 v8::base::RandomNumberGenerator rng; | |
642 for (int n_ifs = 1; n_ifs < 12; n_ifs++) { | |
643 for (int i = 0; i < n_ifs * n_ifs + 10; i++) { | |
644 IfBuilderGenerator m; | |
645 m.ParseRandomIfThenElse(&rng, n_ifs, 1); | |
646 m.RunRandom(&rng); | |
647 } | |
648 } | |
649 } | |
650 | |
651 | |
652 TEST(RunRandomIfElseExpressions) { | |
653 v8::base::RandomNumberGenerator rng; | |
654 for (int n_vars = 2; n_vars < 6; n_vars++) { | |
655 for (int n_ifs = 2; n_ifs < 7; n_ifs++) { | |
656 for (int i = 0; i < n_ifs * n_vars + 10; i++) { | |
657 IfBuilderGenerator m; | |
658 m.ParseRandomIfThenElse(&rng, n_ifs, n_vars); | |
659 m.RunRandom(&rng); | |
660 } | |
661 } | |
662 } | |
663 } | |
664 | |
665 #endif | |
OLD | NEW |