Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(268)

Side by Side Diff: src/typing-asm.cc

Issue 1322773002: Add asm.js typer / validator. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: based on landing others Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/typing-asm.h ('k') | test/cctest/cctest.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 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 "src/v8.h"
6
7 #include "src/typing-asm.h"
8
9 #include "src/ast.h"
10 #include "src/codegen.h"
11 #include "src/scopes.h"
12 #include "src/zone-type-cache.h"
13
14 namespace v8 {
15 namespace internal {
16 namespace {
17
18 base::LazyInstance<ZoneTypeCache>::type kCache = LAZY_INSTANCE_INITIALIZER;
19
20 } // namespace
21
22
23 #ifdef DEBUG
24 #define PRINT_TYPE(label, type) \
25 { \
26 PrintF("%s", label); \
27 type->Print(); \
28 }
29 #else
30 #define PRINT_TYPE(label, type) \
31 {}
32 #endif
33
34
35 #define FAIL(node, msg) \
36 do { \
37 valid_ = false; \
38 int line = node->position() == RelocInfo::kNoPosition \
39 ? -1 \
40 : info_->script()->GetLineNumber(node->position()); \
41 base::OS::SNPrintF(error_message_, sizeof(error_message_), \
42 "asm: line %d: %s\n", line + 1, msg); \
43 return; \
44 } while (false)
45
46
47 #define RECURSE(call) \
48 do { \
49 DCHECK(!HasStackOverflow()); \
50 call; \
51 if (HasStackOverflow()) return; \
52 if (!valid_) return; \
53 } while (false)
54
55
56 #define RECURSE_STMT(stmt) RECURSE(Visit(stmt));
57
58
59 #define RECURSE_EXPR(expr, expected_type, msg) \
titzer 2015/08/31 09:13:36 Can these four macros be made into helper function
bradn 2015/08/31 17:34:44 Done.
60 do { \
61 Type* save = expected_type_; \
62 expected_type_ = expected_type; \
63 RECURSE(Visit(expr)); \
64 Type* bounded_type = \
65 Type::Intersect(computed_type_, expected_type_, zone()); \
66 if (bounded_type->Is(Type::None(zone()))) { \
67 PRINT_TYPE("Computed type: ", computed_type_); \
68 PRINT_TYPE("Expected type: ", expected_type_); \
69 FAIL(expr, msg); \
70 } \
71 expected_type_ = save; \
72 } while (false)
73
74
75 #define RETURN(expr, type) \
76 do { \
77 computed_type_ = type; \
78 Type* bounded_type = \
79 Type::Intersect(computed_type_, expected_type_, zone()); \
80 expr->set_bounds(Bounds(bounded_type)); \
81 return; \
82 } while (false)
83
84
85 #define RETURN_COMPUTED(expr) \
86 do { \
87 Type* bounded_type = \
88 Type::Intersect(computed_type_, expected_type_, zone()); \
89 expr->set_bounds(Bounds(bounded_type)); \
90 return; \
91 } while (false)
92
93
94 #define RETURN_ANNOT(expr, type) \
95 do { \
96 computed_type_ = type; \
97 expr->set_bounds(Bounds(computed_type_)); \
98 return; \
99 } while (false)
100
101
102 AsmTyper::AsmTyper(CompilationInfo* info)
103 : info_(info),
104 valid_(true),
105 stdlib_types_(info->zone()),
106 stdlib_heap_types_(info->zone()),
107 stdlib_math_types_(info->zone()),
108 global_variable_type_(HashMap::PointersMatch,
109 ZoneHashMap::kDefaultHashMapCapacity,
110 ZoneAllocationPolicy(info->zone())),
111 local_variable_type_(HashMap::PointersMatch,
112 ZoneHashMap::kDefaultHashMapCapacity,
113 ZoneAllocationPolicy(info->zone())),
114 in_function_(false),
115 building_function_tables_(false),
116 cache_(kCache.Get()) {
117 InitializeAstVisitor(info->isolate(), info->zone());
118 InitializeStdlib();
119 }
120
121
122 bool AsmTyper::Validate() {
123 VisitAsmModule(info_->literal());
124 return valid_ && !HasStackOverflow();
125 }
126
127
128 void AsmTyper::VisitAsmModule(FunctionLiteral* fun) {
129 Scope* scope = fun->scope();
130 if (!scope->is_function_scope()) FAIL(fun, "not at function scope");
131
132 // Module parameters.
133 for (int i = 0; i < scope->num_parameters(); ++i) {
134 Variable* param = scope->parameter(i);
135 DCHECK(GetType(param) == NULL);
136 SetType(param, Type::None(zone()));
137 }
138
139 ZoneList<Declaration*>* decls = scope->declarations();
140
141 // Set all globals to type Any.
142 VariableDeclaration* decl = scope->function();
143 if (decl != NULL) SetType(decl->proxy()->var(), Type::Any(zone()));
titzer 2015/08/31 09:13:36 Shouldn't globals start at type none?
bradn 2015/08/31 17:34:44 Oops, yep. Done.
144 RECURSE(VisitDeclarations(scope->declarations()));
145
146 // Validate global variables.
147 RECURSE(VisitStatements(fun->body()));
148
149 // Validate function annotations.
150 for (int i = 0; i < decls->length(); ++i) {
151 FunctionDeclaration* decl = decls->at(i)->AsFunctionDeclaration();
152 if (decl != NULL) {
153 RECURSE(VisitFunctionAnnotation(decl->fun()));
154 Variable* var = decl->proxy()->var();
155 DCHECK(GetType(var) == NULL);
156 SetType(var, computed_type_);
157 DCHECK(GetType(var) != NULL);
158 }
159 }
160
161 // Build function tables.
162 building_function_tables_ = true;
163 RECURSE(VisitStatements(fun->body()));
164 building_function_tables_ = false;
165
166 // Validate function bodies.
167 for (int i = 0; i < decls->length(); ++i) {
168 FunctionDeclaration* decl = decls->at(i)->AsFunctionDeclaration();
169 if (decl != NULL) {
170 RECURSE_EXPR(decl->fun(), Type::Any(zone()), "UNREACHABLE");
171 if (!computed_type_->IsFunction()) {
172 FAIL(decl->fun(), "function literal expected to be a function");
173 }
174 }
175 }
176
177 // Validate exports.
178 ReturnStatement* stmt = fun->body()->last()->AsReturnStatement();
179 RECURSE_EXPR(stmt->expression(), Type::Object(), "expected object export");
180 }
181
182
183 void AsmTyper::VisitVariableDeclaration(VariableDeclaration* decl) {
184 Variable* var = decl->proxy()->var();
185 if (var->location() != VariableLocation::PARAMETER) {
186 if (GetType(var) == NULL) {
187 SetType(var, Type::Any(zone()));
188 } else {
189 DCHECK(!GetType(var)->IsFunction());
190 }
191 }
192 DCHECK(GetType(var) != NULL);
193 intish_ = 0;
194 }
195
196
197 void AsmTyper::VisitFunctionDeclaration(FunctionDeclaration* decl) {
198 if (in_function_) {
199 FAIL(decl, "function declared inside another");
200 }
201 }
202
203
204 void AsmTyper::VisitFunctionAnnotation(FunctionLiteral* fun) {
205 // Extract result type.
206 ZoneList<Statement*>* body = fun->body();
207 Type* result_type = Type::Undefined(zone());
208 if (body->length() > 0) {
209 ReturnStatement* stmt = body->last()->AsReturnStatement();
210 if (stmt != NULL) {
211 RECURSE(VisitExpressionAnnotation(stmt->expression()));
212 result_type = computed_type_;
213 }
214 }
215 Type::FunctionType* type =
216 Type::Function(result_type, Type::Any(), fun->parameter_count(), zone())
217 ->AsFunction();
218
219 // Extract parameter types.
220 bool good = true;
221 for (int i = 0; i < fun->parameter_count(); ++i) {
222 good = false;
223 if (i >= body->length()) break;
224 ExpressionStatement* stmt = body->at(i)->AsExpressionStatement();
225 if (stmt == NULL) break;
226 Assignment* expr = stmt->expression()->AsAssignment();
227 if (expr == NULL || expr->is_compound()) break;
228 VariableProxy* proxy = expr->target()->AsVariableProxy();
229 if (proxy == NULL) break;
230 Variable* var = proxy->var();
231 if (var->location() != VariableLocation::PARAMETER || var->index() != i)
232 break;
233 RECURSE(VisitExpressionAnnotation(expr->value()));
234 SetType(var, computed_type_);
235 type->InitParameter(i, computed_type_);
236 good = true;
237 }
238 if (!good) FAIL(fun, "missing parameter type annotations");
239
240 RETURN_ANNOT(fun, type);
241 }
242
243
244 void AsmTyper::VisitExpressionAnnotation(Expression* expr) {
245 // Normal +x or x|0 annotations.
246 BinaryOperation* bin = expr->AsBinaryOperation();
247 if (bin != NULL) {
248 Literal* right = bin->right()->AsLiteral();
249 if (right != NULL) {
250 switch (bin->op()) {
251 case Token::MUL: // We encode +x as 1*x
252 if (right->raw_value()->ContainsDot() &&
253 right->raw_value()->AsNumber() == 1.0) {
254 RETURN_ANNOT(expr, cache_.kFloat64);
255 }
256 break;
257 case Token::BIT_OR:
258 if (!right->raw_value()->ContainsDot() &&
259 right->raw_value()->AsNumber() == 0.0) {
260 RETURN_ANNOT(expr, cache_.kInt32);
261 }
262 break;
263 default:
264 break;
265 }
266 }
267 FAIL(expr, "invalid type annotation on binary op");
268 }
269
270 // Numbers or the undefined literal (for empty returns).
271 if (expr->IsLiteral()) {
272 RECURSE_EXPR(expr, Type::Any(), "invalid literal");
273 return;
274 }
275
276 Call* call = expr->AsCall();
277 if (call != NULL) {
278 if (call->expression()->IsVariableProxy()) {
279 RECURSE_EXPR(call->expression(), Type::Any(zone()),
280 "only fround allowed on expression annotations");
281 if (!computed_type_->Is(
282 Type::Function(cache_.kFloat32, Type::Number(zone()), zone()))) {
283 FAIL(call->expression(),
284 "only fround allowed on expression annotations");
285 }
286 if (call->arguments()->length() != 1) {
287 FAIL(call, "invalid argument count calling fround");
288 }
289 RETURN_ANNOT(expr, cache_.kFloat32);
290 }
291 }
292
293 FAIL(expr, "invalid type annotation");
294 }
295
296
297 void AsmTyper::VisitStatements(ZoneList<Statement*>* stmts) {
298 for (int i = 0; i < stmts->length(); ++i) {
299 Statement* stmt = stmts->at(i);
300 RECURSE_STMT(stmt);
301 }
302 }
303
304
305 void AsmTyper::VisitBlock(Block* stmt) {
306 RECURSE(VisitStatements(stmt->statements()));
307 }
308
309
310 void AsmTyper::VisitExpressionStatement(ExpressionStatement* stmt) {
311 RECURSE_EXPR(stmt->expression(), Type::Any(),
312 "expression statement expected to be any");
313 }
314
315
316 void AsmTyper::VisitEmptyStatement(EmptyStatement* stmt) {}
317
318
319 void AsmTyper::VisitEmptyParentheses(EmptyParentheses* expr) { UNREACHABLE(); }
320
321
322 void AsmTyper::VisitIfStatement(IfStatement* stmt) {
323 if (!in_function_) {
324 FAIL(stmt, "if statement inside module body");
325 }
326 RECURSE_EXPR(stmt->condition(), cache_.kInt32,
327 "if condition expected to be integer");
328 RECURSE_STMT(stmt->then_statement());
329 RECURSE_STMT(stmt->else_statement());
330 }
331
332
333 void AsmTyper::VisitContinueStatement(ContinueStatement* stmt) {
334 if (!in_function_) {
335 FAIL(stmt, "continue statement inside module body");
336 }
337 }
338
339
340 void AsmTyper::VisitBreakStatement(BreakStatement* stmt) {
341 if (!in_function_) {
342 FAIL(stmt, "continue statement inside module body");
343 }
344 }
345
346
347 void AsmTyper::VisitReturnStatement(ReturnStatement* stmt) {
348 // Handle module return statement in VisitAsmModule.
349 if (!in_function_) {
350 return;
351 }
352 RECURSE_EXPR(stmt->expression(), return_type_,
353 "return expression expected to have return type");
354 }
355
356
357 void AsmTyper::VisitWithStatement(WithStatement* stmt) {
358 FAIL(stmt, "bad with statement");
359 }
360
361
362 void AsmTyper::VisitSwitchStatement(SwitchStatement* stmt) {
363 if (!in_function_) {
364 FAIL(stmt, "switch statement inside module body");
365 }
366 RECURSE_EXPR(stmt->tag(), cache_.kInt32, "switch expression non-integer");
367 ZoneList<CaseClause*>* clauses = stmt->cases();
368 for (int i = 0; i < clauses->length(); ++i) {
369 CaseClause* clause = clauses->at(i);
370 if (clause->is_default()) continue;
371 Expression* label = clause->label();
372 RECURSE_EXPR(label, cache_.kInt32, "case label non-integer");
373 if (!label->IsLiteral()) FAIL(label, "non-literal case label");
374 Handle<Object> value = label->AsLiteral()->value();
375 int32_t value32;
376 if (!value->ToInt32(&value32)) FAIL(label, "illegal case label value");
377 // TODO(bradnelson): Detect duplicates.
378 ZoneList<Statement*>* stmts = clause->statements();
379 RECURSE(VisitStatements(stmts));
380 }
381 }
382
383
384 void AsmTyper::VisitCaseClause(CaseClause* clause) { UNREACHABLE(); }
385
386
387 void AsmTyper::VisitDoWhileStatement(DoWhileStatement* stmt) {
388 if (!in_function_) {
389 FAIL(stmt, "do statement inside module body");
390 }
391 RECURSE_STMT(stmt->body());
392 RECURSE_EXPR(stmt->cond(), cache_.kInt32,
393 "do condition expected to be integer");
394 }
395
396
397 void AsmTyper::VisitWhileStatement(WhileStatement* stmt) {
398 if (!in_function_) {
399 FAIL(stmt, "while statement inside module body");
400 }
401 RECURSE_EXPR(stmt->cond(), cache_.kInt32,
402 "while condition expected to be integer");
403 RECURSE_STMT(stmt->body());
404 }
405
406
407 void AsmTyper::VisitForStatement(ForStatement* stmt) {
408 if (!in_function_) {
409 FAIL(stmt, "for statement inside module body");
410 }
411 if (stmt->init() != NULL) {
412 RECURSE_STMT(stmt->init());
413 }
414 if (stmt->cond() != NULL) {
415 RECURSE_EXPR(stmt->cond(), cache_.kInt32,
416 "for condition expected to be integer");
417 }
418 if (stmt->next() != NULL) {
419 RECURSE_STMT(stmt->next());
420 }
421 RECURSE_STMT(stmt->body());
422 }
423
424
425 void AsmTyper::VisitForInStatement(ForInStatement* stmt) {
426 FAIL(stmt, "for-in statement encountered");
427 }
428
429
430 void AsmTyper::VisitForOfStatement(ForOfStatement* stmt) {
431 FAIL(stmt, "for-of statement encountered");
432 }
433
434
435 void AsmTyper::VisitTryCatchStatement(TryCatchStatement* stmt) {
436 FAIL(stmt, "try statement encountered");
437 }
438
439
440 void AsmTyper::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
441 FAIL(stmt, "try statement encountered");
442 }
443
444
445 void AsmTyper::VisitDebuggerStatement(DebuggerStatement* stmt) {
446 FAIL(stmt, "debugger statement encountered");
447 }
448
449
450 void AsmTyper::VisitFunctionLiteral(FunctionLiteral* expr) {
451 Scope* scope = expr->scope();
452 DCHECK(scope->is_function_scope());
453 if (in_function_) {
454 FAIL(expr, "invalid nested function");
455 }
456
457 if (!expr->bounds().upper->IsFunction()) {
458 FAIL(expr, "invalid function literal");
459 }
460
461 Type::FunctionType* type = expr->bounds().upper->AsFunction();
462 Type* save_return_type = return_type_;
463 return_type_ = type->Result();
464 in_function_ = true;
465 local_variable_type_.Clear();
466 RECURSE(VisitDeclarations(scope->declarations()));
467 RECURSE(VisitStatements(expr->body()));
468 in_function_ = false;
469 return_type_ = save_return_type;
470 RETURN(expr, type);
471 }
472
473
474 void AsmTyper::VisitNativeFunctionLiteral(NativeFunctionLiteral* expr) {
475 FAIL(expr, "function info literal encountered");
476 }
477
478
479 void AsmTyper::VisitConditional(Conditional* expr) {
480 RECURSE_EXPR(expr->condition(), cache_.kInt32,
481 "condition expected to be integer");
482 RECURSE_EXPR(expr->then_expression(), Type::Number(),
483 "conditional then branch expected to be integer");
484 Type* then_type = computed_type_;
485 RECURSE_EXPR(expr->else_expression(), Type::Number(),
486 "conditional else branch expected to be integer");
487 Type* else_type = computed_type_;
488 Type* type = Type::Intersect(then_type, else_type, zone());
489 if (!(type->Is(cache_.kInt32) || type->Is(cache_.kFloat64))) {
490 FAIL(expr, "ill-typed conditional");
491 }
492 RETURN(expr, type);
493 }
494
495
496 void AsmTyper::VisitVariableProxy(VariableProxy* expr) {
497 Variable* var = expr->var();
498 if (GetType(var) == NULL) {
499 FAIL(expr, "unbound variable");
500 }
501 Type* type = Type::Intersect(GetType(var), expected_type_, zone());
502 if (type->Is(cache_.kInt32)) {
503 type = cache_.kInt32;
504 }
505 SetType(var, type);
506 intish_ = 0;
507 RETURN(expr, type);
508 }
509
510
511 void AsmTyper::VisitLiteral(Literal* expr) {
512 intish_ = 0;
513 Handle<Object> value = expr->value();
514 if (value->IsNumber()) {
515 int32_t i;
516 uint32_t u;
517 if (expr->raw_value()->ContainsDot()) {
518 RETURN(expr, cache_.kFloat64);
519 } else if (value->ToUint32(&u)) {
520 RETURN(expr, cache_.kInt32);
521 } else if (value->ToInt32(&i)) {
522 RETURN(expr, cache_.kInt32);
523 } else {
524 FAIL(expr, "illegal number");
525 }
526 } else if (value->IsString()) {
527 RETURN(expr, Type::String());
528 } else if (value->IsUndefined()) {
529 RETURN(expr, Type::Undefined());
530 } else {
531 FAIL(expr, "illegal literal");
532 }
533 }
534
535
536 void AsmTyper::VisitRegExpLiteral(RegExpLiteral* expr) {
537 FAIL(expr, "regular expression encountered");
538 }
539
540
541 void AsmTyper::VisitObjectLiteral(ObjectLiteral* expr) {
542 if (in_function_) {
543 FAIL(expr, "object literal in function");
544 }
545 // Allowed for asm module's export declaration.
546 ZoneList<ObjectLiteralProperty*>* props = expr->properties();
547 for (int i = 0; i < props->length(); ++i) {
548 ObjectLiteralProperty* prop = props->at(i);
549 RECURSE_EXPR(prop->value(), Type::Any(zone()),
550 "object property expected to be a function");
551 if (!computed_type_->IsFunction()) {
552 FAIL(prop->value(), "non-function in function table");
553 }
554 }
555 RETURN(expr, Type::Object(zone()));
556 }
557
558
559 void AsmTyper::VisitArrayLiteral(ArrayLiteral* expr) {
560 if (in_function_) {
561 FAIL(expr, "array literal inside a function");
562 }
563 // Allowed for function tables.
564 ZoneList<Expression*>* values = expr->values();
565 Type* elem_type = Type::None(zone());
566 for (int i = 0; i < values->length(); ++i) {
567 Expression* value = values->at(i);
568 RECURSE_EXPR(value, Type::Any(), "UNREACHABLE");
569 if (!computed_type_->IsFunction()) {
570 FAIL(value, "array component expected to be a function");
571 }
572 elem_type = Type::Union(elem_type, computed_type_, zone());
573 }
574 array_size_ = values->length();
575 RETURN(expr, Type::Array(elem_type, zone()));
576 }
577
578
579 void AsmTyper::VisitAssignment(Assignment* expr) {
580 // Handle function tables and everything else in different passes.
581 if (!in_function_) {
582 if (expr->value()->IsArrayLiteral()) {
583 if (!building_function_tables_) {
584 return;
585 }
586 } else {
587 if (building_function_tables_) {
588 return;
589 }
590 }
591 }
592 if (expr->is_compound()) FAIL(expr, "compound assignment encountered");
593 Type* type = expected_type_;
594 RECURSE_EXPR(expr->value(), type,
595 "assignment value expected to match surrounding");
596 if (intish_ != 0) {
597 FAIL(expr, "value still an intish");
598 }
599 RECURSE_EXPR(expr->target(), computed_type_,
600 "assignment target expected to match value");
601 if (intish_ != 0) {
602 FAIL(expr, "value still an intish");
603 }
604 RETURN_COMPUTED(expr);
605 }
606
607
608 void AsmTyper::VisitYield(Yield* expr) {
609 FAIL(expr, "yield expression encountered");
610 }
611
612
613 void AsmTyper::VisitThrow(Throw* expr) {
614 FAIL(expr, "throw statement encountered");
615 }
616
617
618 int AsmTyper::ElementShiftSize(Type* type) {
619 if (type->Is(cache_.kInt8) || type->Is(cache_.kUint8)) return 0;
620 if (type->Is(cache_.kInt16) || type->Is(cache_.kUint16)) return 1;
621 if (type->Is(cache_.kInt32) || type->Is(cache_.kUint32) ||
622 type->Is(cache_.kFloat32))
623 return 2;
624 if (type->Is(cache_.kFloat64)) return 3;
625 return -1;
626 }
627
628
629 void AsmTyper::VisitProperty(Property* expr) {
630 // stdlib.Math.x
631 Property* inner_prop = expr->obj()->AsProperty();
632 if (inner_prop != NULL) {
633 // Get property name.
634 Literal* key = expr->key()->AsLiteral();
635 if (key == NULL || !key->IsPropertyName())
636 FAIL(expr, "invalid type annotation on property 2");
637 Handle<String> name = key->AsPropertyName();
638
639 // Check that inner property name is "Math".
640 Literal* math_key = inner_prop->key()->AsLiteral();
641 if (math_key == NULL || !math_key->IsPropertyName() ||
642 !math_key->AsPropertyName()->IsUtf8EqualTo(CStrVector("Math")))
643 FAIL(expr, "invalid type annotation on stdlib (a1)");
644
645 // Check that object is stdlib.
646 VariableProxy* proxy = inner_prop->obj()->AsVariableProxy();
647 if (proxy == NULL) FAIL(expr, "invalid type annotation on stdlib (a2)");
648 Variable* var = proxy->var();
649 if (var->location() != VariableLocation::PARAMETER || var->index() != 0)
650 FAIL(expr, "invalid type annotation on stdlib (a3)");
651
652 // Look up library type.
653 Type* type = LibType(stdlib_math_types_, name);
654 if (type == NULL) FAIL(expr, "unknown standard function 3 ");
655 RETURN_ANNOT(expr, type);
656 }
657
658 // Only recurse at this point so that we avoid needing
659 // stdlib.Math to have a real type.
660 RECURSE_EXPR(expr->obj(), Type::Any(),
661 "property holder expected to be object");
662
663 // For heap view or function table access.
664 if (computed_type_->IsArray()) {
titzer 2015/08/31 09:13:36 Can we factor this to a helper function for array
bradn 2015/08/31 17:34:44 Done.
665 Type::ArrayType* array_type = computed_type_->AsArray();
666 size_t size = array_size_;
667 Type* type = array_type->AsArray()->Element();
668 if (type->IsFunction()) {
669 BinaryOperation* bin = expr->key()->AsBinaryOperation();
670 if (bin == NULL || bin->op() != Token::BIT_AND) {
671 FAIL(expr->key(), "expected & in call");
672 }
673 RECURSE_EXPR(bin->left(), cache_.kInt32,
674 "array index expected to be integer");
675 Literal* right = bin->right()->AsLiteral();
676 if (right == NULL || right->raw_value()->ContainsDot()) {
677 FAIL(right, "call mask must be integer");
678 }
679 RECURSE_EXPR(bin->right(), cache_.kInt32,
680 "call mask expected to be integer");
681 if (static_cast<size_t>(right->raw_value()->AsNumber()) != size - 1) {
682 FAIL(right, "call mask must match function table");
683 }
684 bin->set_bounds(Bounds(cache_.kInt32));
685 } else {
686 BinaryOperation* bin = expr->key()->AsBinaryOperation();
687 if (bin == NULL || bin->op() != Token::SAR) {
688 FAIL(expr->key(), "expected >> in heap access");
689 }
690 RECURSE_EXPR(bin->left(), cache_.kInt32,
691 "array index expected to be integer");
692 Literal* right = bin->right()->AsLiteral();
693 if (right == NULL || right->raw_value()->ContainsDot()) {
694 FAIL(right, "heap access shift must be integer");
695 }
696 RECURSE_EXPR(bin->right(), cache_.kInt32,
697 "array shift expected to be integer");
698 int n = static_cast<int>(right->raw_value()->AsNumber());
699 int expected_shift = ElementShiftSize(type);
700 if (expected_shift < 0 || n != expected_shift) {
701 FAIL(right, "heap access shift must match element size");
702 }
703 bin->set_bounds(Bounds(cache_.kInt32));
704 }
705 RETURN(expr, type);
706 }
707
708 // Get property name.
709 Literal* key = expr->key()->AsLiteral();
710 if (key == NULL || !key->IsPropertyName())
711 FAIL(expr, "invalid type annotation on property 3");
712 Handle<String> name = key->AsPropertyName();
713
714 // stdlib.x or foreign.x
715 VariableProxy* proxy = expr->obj()->AsVariableProxy();
716 if (proxy != NULL) {
717 Variable* var = proxy->var();
718 if (var->location() != VariableLocation::PARAMETER) {
719 FAIL(expr, "invalid type annotation on variable");
720 }
721 switch (var->index()) {
722 case 0: {
723 // Object is stdlib, look up library type.
724 Type* type = LibType(stdlib_types_, name);
725 if (type == NULL) {
726 FAIL(expr, "unknown standard function 4");
727 }
728 RETURN_ANNOT(expr, type);
729 }
730 case 1:
731 // Object is foreign lib.
732 RETURN_ANNOT(expr, expected_type_);
733 default:
734 FAIL(expr, "invalid type annotation on parameter");
735 }
736 }
737
738 FAIL(expr, "invalid property access");
739 }
740
741
742 void AsmTyper::VisitCall(Call* expr) {
743 RECURSE_EXPR(expr->expression(), Type::Any(), "callee expected to be any");
744 if (computed_type_->IsFunction()) {
745 Type::FunctionType* fun_type = computed_type_->AsFunction();
746 ZoneList<Expression*>* args = expr->arguments();
747 if (fun_type->Arity() != args->length()) {
748 FAIL(expr, "call with wrong arity");
749 }
750 for (int i = 0; i < args->length(); ++i) {
751 Expression* arg = args->at(i);
752 RECURSE_EXPR(arg, fun_type->Parameter(i),
753 "call argument expected to match callee parameter");
754 }
755 RETURN(expr, fun_type->Result());
756 } else if (computed_type_->Is(Type::Any())) {
757 // For foreign calls.
758 ZoneList<Expression*>* args = expr->arguments();
759 for (int i = 0; i < args->length(); ++i) {
760 Expression* arg = args->at(i);
761 RECURSE_EXPR(arg, Type::Any(),
762 "foreign call argument expected to be any");
763 }
764 RETURN(expr, Type::Number());
765 } else {
766 FAIL(expr, "invalid callee");
767 }
768 }
769
770
771 void AsmTyper::VisitCallNew(CallNew* expr) {
772 if (in_function_) {
773 FAIL(expr, "new not allowed in module function");
774 }
775 RECURSE_EXPR(expr->expression(), Type::Any(), "expected stdlib function");
776 if (computed_type_->IsFunction()) {
777 Type::FunctionType* fun_type = computed_type_->AsFunction();
778 ZoneList<Expression*>* args = expr->arguments();
779 if (fun_type->Arity() != args->length())
780 FAIL(expr, "call with wrong arity");
781 for (int i = 0; i < args->length(); ++i) {
782 Expression* arg = args->at(i);
783 RECURSE_EXPR(arg, fun_type->Parameter(i),
784 "constructor argument expected to match callee parameter");
785 }
786 RETURN(expr, fun_type->Result());
787 }
788
789 FAIL(expr, "ill-typed new operator");
790 }
791
792
793 void AsmTyper::VisitCallRuntime(CallRuntime* expr) {
794 // Allow runtime calls for now.
795 }
796
797
798 void AsmTyper::VisitUnaryOperation(UnaryOperation* expr) {
799 switch (expr->op()) {
800 case Token::NOT: // Used to encode != and !==
801 RECURSE_EXPR(expr->expression(), cache_.kInt32,
802 "operand expected to be integer");
803 RETURN(expr, cache_.kInt32);
804 case Token::DELETE:
805 FAIL(expr, "delete operator encountered");
806 case Token::VOID:
807 FAIL(expr, "void operator encountered");
808 case Token::TYPEOF:
809 FAIL(expr, "typeof operator encountered");
810 default:
811 UNREACHABLE();
812 }
813 }
814
815
816 void AsmTyper::VisitCountOperation(CountOperation* expr) {
817 FAIL(expr, "increment or decrement operator encountered");
818 }
819
820
821 void AsmTyper::VisitBinaryOperation(BinaryOperation* expr) {
822 switch (expr->op()) {
823 case Token::COMMA: {
824 RECURSE_EXPR(expr->left(), Type::Any(),
825 "left comma operand expected to be any");
826 RECURSE_EXPR(expr->right(), Type::Any(),
827 "right comma operand expected to be any");
828 RETURN_COMPUTED(expr);
829 }
830 case Token::OR:
831 case Token::AND:
832 FAIL(expr, "logical operator encountered");
833 case Token::BIT_OR:
834 case Token::BIT_AND:
835 case Token::BIT_XOR:
836 case Token::SHL:
837 case Token::SHR:
838 case Token::SAR: {
839 // BIT_OR allows Any since it is used as a type coercion.
840 // BIT_XOR allows Number since it is used as a type coercion (encoding ~).
841 Type* expectation =
842 expr->op() == Token::BIT_OR
843 ? Type::Any()
844 : expr->op() == Token::BIT_XOR ? Type::Number() : cache_.kInt32;
845 Type* result =
846 expr->op() == Token::SHR ? Type::Unsigned32() : cache_.kInt32;
847 RECURSE_EXPR(expr->left(), expectation,
848 "left bit operand expected to be integer");
849 int left_intish = intish_;
850 RECURSE_EXPR(expr->right(), expectation,
851 "right bit operand expected to be integer");
852 int right_intish = intish_;
853 if (left_intish > (1 << 20)) {
854 FAIL(expr, "too many intishes");
855 }
856 if (right_intish > (1 << 20)) {
857 FAIL(expr, "too many intishes");
858 }
859 intish_ = 0;
860 RETURN(expr, result);
861 }
862 case Token::ADD:
863 case Token::SUB:
864 case Token::MUL:
865 case Token::DIV:
866 case Token::MOD: {
867 // MUL allows Any, since it is used as a type coercion (encoding unary +).
868 Type* expectation =
869 expr->op() == Token::MUL ? Type::Any() : Type::Number();
titzer 2015/08/31 09:13:36 I don't understand why MUL takes Type::Any, despit
bradn 2015/08/31 17:34:44 Ah, yeah. This was a carryover from Andreas' proty
870 RECURSE_EXPR(expr->left(), expectation,
871 "left arithmetic operand expected to be number");
872 Type* left_type = computed_type_;
873 int left_intish = intish_;
874 RECURSE_EXPR(expr->right(), expectation,
875 "right arithmetic operand expected to be number");
876 Type* right_type = computed_type_;
877 int right_intish = intish_;
878 Type* type = Type::Union(left_type, right_type, zone());
879 if (type->Is(cache_.kInt32)) {
880 if (expr->op() == Token::MUL) {
881 if (!expr->left()->IsLiteral() && !expr->right()->IsLiteral()) {
882 FAIL(expr, "direct integer multiply forbidden");
883 }
884 intish_ = 0;
885 RETURN(expr, cache_.kInt32);
886 } else {
887 intish_ = left_intish + right_intish + 1;
888 if (expr->op() == Token::ADD || expr->op() == Token::SUB) {
889 if (intish_ > (1 << 20)) {
titzer 2015/08/31 09:13:36 kMaxIntishness (or better name)
bradn 2015/08/31 17:34:44 Done.
890 FAIL(expr, "too many consecutive additive ops");
891 }
892 } else {
893 if (intish_ > 1) {
894 FAIL(expr, "too many consecutive multiplicative ops");
895 }
896 }
897 RETURN(expr, cache_.kInt32);
898 }
899 } else if (type->Is(Type::Number())) {
900 RETURN(expr, cache_.kFloat64);
901 } else {
902 FAIL(expr, "ill-typed arithmetic operation");
903 }
904 }
905 default:
906 UNREACHABLE();
907 }
908 }
909
910
911 void AsmTyper::VisitCompareOperation(CompareOperation* expr) {
912 RECURSE_EXPR(expr->left(), Type::Number(),
913 "left comparison operand expected to be number");
914 Type* left_type = computed_type_;
915 RECURSE_EXPR(expr->right(), Type::Number(),
916 "right comparison operand expected to be number");
917 Type* right_type = computed_type_;
918 Type* type = Type::Union(left_type, right_type, zone());
919 expr->set_combined_type(type);
920 if (type->Is(Type::Integral32()) || type->Is(Type::UntaggedFloat64())) {
921 RETURN(expr, cache_.kInt32);
922 } else {
923 FAIL(expr, "ill-typed comparison operation");
924 }
925 }
926
927
928 void AsmTyper::VisitThisFunction(ThisFunction* expr) {
929 FAIL(expr, "this function not allowed");
930 }
931
932
933 void AsmTyper::VisitDeclarations(ZoneList<Declaration*>* decls) {
934 for (int i = 0; i < decls->length(); ++i) {
935 Declaration* decl = decls->at(i);
936 RECURSE_STMT(decl);
937 }
938 }
939
940
941 void AsmTyper::VisitImportDeclaration(ImportDeclaration* decl) {
942 FAIL(decl, "import declaration encountered");
943 }
944
945
946 void AsmTyper::VisitExportDeclaration(ExportDeclaration* decl) {
947 FAIL(decl, "export declaration encountered");
948 }
949
950
951 void AsmTyper::VisitClassLiteral(ClassLiteral* expr) {
952 FAIL(expr, "class literal not allowed");
953 }
954
955
956 void AsmTyper::VisitSpread(Spread* expr) { FAIL(expr, "spread not allowed"); }
957
958
959 void AsmTyper::VisitSuperPropertyReference(SuperPropertyReference* expr) {
960 FAIL(expr, "super property reference not allowed");
961 }
962
963
964 void AsmTyper::VisitSuperCallReference(SuperCallReference* expr) {
965 FAIL(expr, "call reference not allowed");
966 }
967
968
969 void AsmTyper::InitializeStdlib() {
970 Type* number_type = Type::Number(zone());
971 Type* double_type = cache_.kFloat64;
972 Type* double_fn1_type = Type::Function(double_type, double_type, zone());
973 Type* double_fn2_type =
974 Type::Function(double_type, double_type, double_type, zone());
975
976 Type* fround_type = Type::Function(cache_.kFloat32, number_type, zone());
977 Type* imul_type =
978 Type::Function(cache_.kInt32, cache_.kInt32, cache_.kInt32, zone());
979 // TODO(bradnelson): currently only approximating the proper intersection type
980 // (which we cannot currently represent).
981 Type* abs_type = Type::Function(number_type, number_type, zone());
982
983 struct Assignment {
984 const char* name;
985 Type* type;
986 };
987
988 const Assignment math[] = {
989 {"PI", double_type}, {"E", double_type},
990 {"LN2", double_type}, {"LN10", double_type},
991 {"LOG2E", double_type}, {"LOG10E", double_type},
992 {"SQRT2", double_type}, {"SQRT1_2", double_type},
993 {"imul", imul_type}, {"abs", abs_type},
994 {"ceil", double_fn1_type}, {"floor", double_fn1_type},
995 {"fround", fround_type}, {"pow", double_fn2_type},
996 {"exp", double_fn1_type}, {"log", double_fn1_type},
997 {"min", double_fn2_type}, {"max", double_fn2_type},
998 {"sqrt", double_fn1_type}, {"cos", double_fn1_type},
999 {"sin", double_fn1_type}, {"tan", double_fn1_type},
1000 {"acos", double_fn1_type}, {"asin", double_fn1_type},
1001 {"atan", double_fn1_type}, {"atan2", double_fn2_type}};
1002 for (unsigned i = 0; i < arraysize(math); ++i) {
1003 stdlib_math_types_[math[i].name] = math[i].type;
1004 }
1005
1006 stdlib_types_["Infinity"] = double_type;
1007 stdlib_types_["NaN"] = double_type;
1008 Type* buffer_type = Type::Any(zone());
1009 #define TYPED_ARRAY(TypeName, type_name, TYPE_NAME, ctype, size) \
1010 stdlib_types_[#TypeName "Array"] = \
1011 Type::Function(cache_.k##TypeName##Array, buffer_type, zone());
1012 TYPED_ARRAYS(TYPED_ARRAY)
1013 #undef TYPED_ARRAY
1014
1015 #define TYPED_ARRAY(TypeName, type_name, TYPE_NAME, ctype, size) \
1016 stdlib_heap_types_[#TypeName "Array"] = \
1017 Type::Function(cache_.k##TypeName##Array, buffer_type, zone());
1018 TYPED_ARRAYS(TYPED_ARRAY)
1019 #undef TYPED_ARRAY
1020 }
1021
1022
1023 Type* AsmTyper::LibType(ObjectTypeMap map, Handle<String> name) {
1024 base::SmartArrayPointer<char> aname = name->ToCString();
1025 ObjectTypeMap::iterator i = map.find(std::string(aname.get()));
1026 if (i == map.end()) {
1027 return NULL;
1028 }
1029 return i->second;
1030 }
1031
1032
1033 void AsmTyper::SetType(Variable* variable, Type* type) {
1034 ZoneHashMap::Entry* entry;
1035 if (in_function_) {
1036 entry = local_variable_type_.LookupOrInsert(
1037 variable, ComputePointerHash(variable), ZoneAllocationPolicy(zone_));
1038 } else {
1039 entry = global_variable_type_.LookupOrInsert(
1040 variable, ComputePointerHash(variable), ZoneAllocationPolicy(zone_));
1041 }
1042 entry->value = reinterpret_cast<void*>(type);
1043 }
1044
1045
1046 Type* AsmTyper::GetType(Variable* variable) {
1047 i::ZoneHashMap::Entry* entry = NULL;
1048 if (in_function_) {
1049 entry = local_variable_type_.Lookup(variable, ComputePointerHash(variable));
1050 }
1051 if (entry == NULL) {
1052 entry =
1053 global_variable_type_.Lookup(variable, ComputePointerHash(variable));
1054 }
1055 if (entry == NULL) {
1056 return NULL;
1057 } else {
1058 return reinterpret_cast<Type*>(entry->value);
1059 }
1060 }
1061 }
1062 } // namespace v8::internal
OLDNEW
« no previous file with comments | « src/typing-asm.h ('k') | test/cctest/cctest.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698