OLD | NEW |
---|---|
1 // Copyright 2006-2008 the V8 project authors. All rights reserved. | 1 // Copyright 2006-2008 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
49 // ----------------------------------------------------------------------------- | 49 // ----------------------------------------------------------------------------- |
50 // Reference support | 50 // Reference support |
51 | 51 |
52 // A reference is a C++ stack-allocated object that keeps an ECMA | 52 // A reference is a C++ stack-allocated object that keeps an ECMA |
53 // reference on the execution stack while in scope. For variables | 53 // reference on the execution stack while in scope. For variables |
54 // the reference is empty, indicating that it isn't necessary to | 54 // the reference is empty, indicating that it isn't necessary to |
55 // store state on the stack for keeping track of references to those. | 55 // store state on the stack for keeping track of references to those. |
56 // For properties, we keep either one (named) or two (indexed) values | 56 // For properties, we keep either one (named) or two (indexed) values |
57 // on the execution stack to represent the reference. | 57 // on the execution stack to represent the reference. |
58 | 58 |
59 enum InitState { CONST_INIT, NOT_CONST_INIT }; | |
60 | |
59 class Reference BASE_EMBEDDED { | 61 class Reference BASE_EMBEDDED { |
60 public: | 62 public: |
61 // The values of the types is important, see size(). | 63 // The values of the types is important, see size(). |
62 enum Type { ILLEGAL = -1, SLOT = 0, NAMED = 1, KEYED = 2 }; | 64 enum Type { ILLEGAL = -1, SLOT = 0, NAMED = 1, KEYED = 2 }; |
63 Reference(Ia32CodeGenerator* cgen, Expression* expression); | 65 Reference(Ia32CodeGenerator* cgen, Expression* expression); |
64 ~Reference(); | 66 ~Reference(); |
65 | 67 |
66 Expression* expression() const { return expression_; } | 68 Expression* expression() const { return expression_; } |
67 Type type() const { return type_; } | 69 Type type() const { return type_; } |
68 void set_type(Type value) { | 70 void set_type(Type value) { |
69 ASSERT(type_ == ILLEGAL); | 71 ASSERT(type_ == ILLEGAL); |
70 type_ = value; | 72 type_ = value; |
71 } | 73 } |
72 | 74 |
73 // The size of the reference or -1 if the reference is illegal. | 75 // The size of the reference or -1 if the reference is illegal. |
74 int size() const { return type_; } | 76 int size() const { return type_; } |
75 | 77 |
76 bool is_illegal() const { return type_ == ILLEGAL; } | 78 bool is_illegal() const { return type_ == ILLEGAL; } |
77 bool is_slot() const { return type_ == SLOT; } | 79 bool is_slot() const { return type_ == SLOT; } |
78 bool is_property() const { return type_ == NAMED || type_ == KEYED; } | 80 bool is_property() const { return type_ == NAMED || type_ == KEYED; } |
79 | 81 |
82 void SetValue(InitState init_state); | |
83 | |
80 private: | 84 private: |
81 Ia32CodeGenerator* cgen_; | 85 Ia32CodeGenerator* cgen_; |
82 Expression* expression_; | 86 Expression* expression_; |
83 Type type_; | 87 Type type_; |
84 }; | 88 }; |
85 | 89 |
86 | 90 |
87 // ------------------------------------------------------------------------- | 91 // ------------------------------------------------------------------------- |
88 // Code generation state | 92 // Code generation state |
89 | 93 |
90 // The state is passed down the AST by the code generator. It is passed | 94 // The state is passed down the AST by the code generator (and back up, in |
91 // implicitly (in a member variable) to the non-static code generator member | 95 // the form of the state of the label pair). It is threaded through the |
92 // functions, and explicitly (as an argument) to the static member functions | 96 // call stack. Constructing a state implicitly pushes it on the owning code |
93 // and the AST node member functions. | 97 // generator's stack of states, and destroying one implicitly pops it. |
94 // | |
95 // The state is threaded through the call stack. Constructing a state | |
96 // implicitly pushes it on the owning code generator's stack of states, and | |
97 // destroying one implicitly pops it. | |
98 | 98 |
99 class CodeGenState BASE_EMBEDDED { | 99 class CodeGenState BASE_EMBEDDED { |
100 public: | 100 public: |
101 enum AccessType { | 101 enum AccessType { |
102 UNDEFINED, | 102 UNDEFINED, |
103 LOAD, | 103 LOAD, |
104 LOAD_TYPEOF_EXPR | 104 LOAD_TYPEOF_EXPR |
105 }; | 105 }; |
106 | 106 |
107 // Create an initial code generator state. Destroying the initial state | 107 // Create an initial code generator state. Destroying the initial state |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
189 Reference* ref() const { return state_->ref(); } | 189 Reference* ref() const { return state_->ref(); } |
190 bool is_referenced() const { return state_->ref() != NULL; } | 190 bool is_referenced() const { return state_->ref() != NULL; } |
191 Label* true_target() const { return state_->true_target(); } | 191 Label* true_target() const { return state_->true_target(); } |
192 Label* false_target() const { return state_->false_target(); } | 192 Label* false_target() const { return state_->false_target(); } |
193 | 193 |
194 // Expressions | 194 // Expressions |
195 Operand GlobalObject() const { | 195 Operand GlobalObject() const { |
196 return ContextOperand(esi, Context::GLOBAL_INDEX); | 196 return ContextOperand(esi, Context::GLOBAL_INDEX); |
197 } | 197 } |
198 | 198 |
199 // Support functions for accessing parameters. Static versions can | 199 // Support functions for accessing parameters. |
200 // require some code generator state to be passed in as arguments. | 200 Operand ParameterOperand(int index) const { |
201 static Operand ParameterOperand(const CodeGenerator* cgen, int index) { | 201 int num_parameters = scope()->num_parameters(); |
202 int num_parameters = cgen->scope()->num_parameters(); | |
203 ASSERT(-2 <= index && index < num_parameters); | 202 ASSERT(-2 <= index && index < num_parameters); |
204 return Operand(ebp, (1 + num_parameters - index) * kPointerSize); | 203 return Operand(ebp, (1 + num_parameters - index) * kPointerSize); |
205 } | 204 } |
206 | 205 |
207 Operand ParameterOperand(int index) const { | |
208 return ParameterOperand(this, index); | |
209 } | |
210 | |
211 Operand ReceiverOperand() const { return ParameterOperand(-1); } | 206 Operand ReceiverOperand() const { return ParameterOperand(-1); } |
212 | 207 |
213 Operand FunctionOperand() const { | 208 Operand FunctionOperand() const { |
214 return Operand(ebp, JavaScriptFrameConstants::kFunctionOffset); | 209 return Operand(ebp, JavaScriptFrameConstants::kFunctionOffset); |
215 } | 210 } |
216 | 211 |
217 static Operand ContextOperand(Register context, int index) { | 212 Operand ContextOperand(Register context, int index) const { |
218 return Operand(context, Context::SlotOffset(index)); | 213 return Operand(context, Context::SlotOffset(index)); |
219 } | 214 } |
220 | 215 |
221 static Operand SlotOperand(CodeGenerator* cgen, | 216 Operand SlotOperand(Slot* slot, Register tmp); |
Kasper Lund
2008/10/07 11:02:11
I guess there's a good reason why this isn't const
| |
222 Slot* slot, | |
223 Register tmp); | |
224 | |
225 Operand SlotOperand(Slot* slot, Register tmp) { | |
226 return SlotOperand(this, slot, tmp); | |
227 } | |
228 | 217 |
229 void LoadCondition(Expression* x, | 218 void LoadCondition(Expression* x, |
230 CodeGenState::AccessType access, | 219 CodeGenState::AccessType access, |
231 Label* true_target, | 220 Label* true_target, |
232 Label* false_target, | 221 Label* false_target, |
233 bool force_cc); | 222 bool force_cc); |
234 void Load(Expression* x, | 223 void Load(Expression* x, |
235 CodeGenState::AccessType access = CodeGenState::LOAD); | 224 CodeGenState::AccessType access = CodeGenState::LOAD); |
236 void LoadGlobal(); | 225 void LoadGlobal(); |
237 | 226 |
(...skipping 10 matching lines...) Expand all Loading... | |
248 // Generate code to fetch the value of a reference. The reference is | 237 // Generate code to fetch the value of a reference. The reference is |
249 // expected to be on top of the expression stack. It is left in place and | 238 // expected to be on top of the expression stack. It is left in place and |
250 // its value is pushed on top of it. | 239 // its value is pushed on top of it. |
251 void GetValue(Reference* ref) { | 240 void GetValue(Reference* ref) { |
252 ASSERT(!has_cc()); | 241 ASSERT(!has_cc()); |
253 ASSERT(!ref->is_illegal()); | 242 ASSERT(!ref->is_illegal()); |
254 CodeGenState new_state(this, ref); | 243 CodeGenState new_state(this, ref); |
255 Visit(ref->expression()); | 244 Visit(ref->expression()); |
256 } | 245 } |
257 | 246 |
258 // Generate code to store a value in a reference. The stored value is | |
259 // expected on top of the expression stack, with the reference immediately | |
260 // below it. The expression stack is left unchanged. | |
261 void SetValue(Reference* ref) { | |
262 ASSERT(!has_cc()); | |
263 ASSERT(!ref->is_illegal()); | |
264 ref->expression()->GenerateStoreCode(this, ref, NOT_CONST_INIT); | |
265 } | |
266 | |
267 // Same as SetValue, used to set the initial value of a constant. | |
268 void InitConst(Reference* ref) { | |
269 ASSERT(!has_cc()); | |
270 ASSERT(!ref->is_illegal()); | |
271 ref->expression()->GenerateStoreCode(this, ref, CONST_INIT); | |
272 } | |
273 | |
274 // Generate code to fetch a value from a property of a reference. The | 247 // Generate code to fetch a value from a property of a reference. The |
275 // reference is expected on top of the expression stack. It is left in | 248 // reference is expected on top of the expression stack. It is left in |
276 // place and its value is pushed on top of it. | 249 // place and its value is pushed on top of it. |
277 void GetReferenceProperty(Expression* key); | 250 void GetReferenceProperty(Expression* key); |
278 | 251 |
279 // Generate code to store a value in a property of a reference. The | |
280 // stored value is expected on top of the expression stack, with the | |
281 // reference immediately below it. The expression stack is left | |
282 // unchanged. | |
283 static void SetReferenceProperty(CodeGenerator* cgen, | |
284 Reference* ref, | |
285 Expression* key); | |
286 | |
287 void ToBoolean(Label* true_target, Label* false_target); | 252 void ToBoolean(Label* true_target, Label* false_target); |
288 | 253 |
289 void GenericBinaryOperation( | 254 void GenericBinaryOperation( |
290 Token::Value op, | 255 Token::Value op, |
291 const OverwriteMode overwrite_mode = NO_OVERWRITE); | 256 const OverwriteMode overwrite_mode = NO_OVERWRITE); |
292 void Comparison(Condition cc, bool strict = false); | 257 void Comparison(Condition cc, bool strict = false); |
293 | 258 |
294 // Inline small integer literals. To prevent long attacker-controlled byte | 259 // Inline small integer literals. To prevent long attacker-controlled byte |
295 // sequences, we only inline small Smi:s. | 260 // sequences, we only inline small Smi:s. |
296 static const int kMaxSmiInlinedBits = 16; | 261 static const int kMaxSmiInlinedBits = 16; |
(...skipping 369 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
666 // representing slots take up no space on the expression stack | 631 // representing slots take up no space on the expression stack |
667 // (ie, it doesn't matter that the stored value is actually below | 632 // (ie, it doesn't matter that the stored value is actually below |
668 // the reference). | 633 // the reference). |
669 // | 634 // |
670 // If the newly-allocated argument object is not already on | 635 // If the newly-allocated argument object is not already on |
671 // the stack, we rely on the property that loading a | 636 // the stack, we rely on the property that loading a |
672 // zero-sized reference will not clobber the ecx register. | 637 // zero-sized reference will not clobber the ecx register. |
673 if (!arguments_object_saved) { | 638 if (!arguments_object_saved) { |
674 __ push(ecx); | 639 __ push(ecx); |
675 } | 640 } |
676 SetValue(&arguments_ref); | 641 arguments_ref.SetValue(NOT_CONST_INIT); |
677 } | 642 } |
678 SetValue(&shadow_ref); | 643 shadow_ref.SetValue(NOT_CONST_INIT); |
679 } | 644 } |
680 __ pop(eax); // Value is no longer needed. | 645 __ pop(eax); // Value is no longer needed. |
681 } | 646 } |
682 | 647 |
683 // Generate code to 'execute' declarations and initialize | 648 // Generate code to 'execute' declarations and initialize |
684 // functions (source elements). In case of an illegal | 649 // functions (source elements). In case of an illegal |
685 // redeclaration we need to handle that instead of processing the | 650 // redeclaration we need to handle that instead of processing the |
686 // declarations. | 651 // declarations. |
687 if (scope->HasIllegalRedeclaration()) { | 652 if (scope->HasIllegalRedeclaration()) { |
688 Comment cmnt(masm_, "[ illegal redeclarations"); | 653 Comment cmnt(masm_, "[ illegal redeclarations"); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
727 } | 692 } |
728 } | 693 } |
729 | 694 |
730 // Code generation state must be reset. | 695 // Code generation state must be reset. |
731 scope_ = NULL; | 696 scope_ = NULL; |
732 ASSERT(!has_cc()); | 697 ASSERT(!has_cc()); |
733 ASSERT(state_ == NULL); | 698 ASSERT(state_ == NULL); |
734 } | 699 } |
735 | 700 |
736 | 701 |
702 Operand Ia32CodeGenerator::SlotOperand(Slot* slot, Register tmp) { | |
703 // Currently, this assertion will fail if we try to assign to | |
704 // a constant variable that is constant because it is read-only | |
705 // (such as the variable referring to a named function expression). | |
706 // We need to implement assignments to read-only variables. | |
707 // Ideally, we should do this during AST generation (by converting | |
708 // such assignments into expression statements); however, in general | |
709 // we may not be able to make the decision until past AST generation, | |
710 // that is when the entire program is known. | |
711 ASSERT(slot != NULL); | |
712 int index = slot->index(); | |
713 switch (slot->type()) { | |
714 case Slot::PARAMETER: | |
715 return ParameterOperand(index); | |
716 | |
717 case Slot::LOCAL: { | |
718 ASSERT(0 <= index && index < scope()->num_stack_slots()); | |
719 const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset; | |
720 return Operand(ebp, kLocal0Offset - index * kPointerSize); | |
721 } | |
722 | |
723 case Slot::CONTEXT: { | |
724 // Follow the context chain if necessary. | |
725 ASSERT(!tmp.is(esi)); // do not overwrite context register | |
726 Register context = esi; | |
727 int chain_length = scope()->ContextChainLength(slot->var()->scope()); | |
728 for (int i = chain_length; i-- > 0;) { | |
729 // Load the closure. | |
730 // (All contexts, even 'with' contexts, have a closure, | |
731 // and it is the same for all contexts inside a function. | |
732 // There is no need to go to the function context first.) | |
733 __ mov(tmp, ContextOperand(context, Context::CLOSURE_INDEX)); | |
734 // Load the function context (which is the incoming, outer context). | |
735 __ mov(tmp, FieldOperand(tmp, JSFunction::kContextOffset)); | |
736 context = tmp; | |
737 } | |
738 // We may have a 'with' context now. Get the function context. | |
739 // (In fact this mov may never be the needed, since the scope analysis | |
740 // may not permit a direct context access in this case and thus we are | |
741 // always at a function context. However it is safe to dereference be- | |
742 // cause the function context of a function context is itself. Before | |
743 // deleting this mov we should try to create a counter-example first, | |
744 // though...) | |
745 __ mov(tmp, ContextOperand(context, Context::FCONTEXT_INDEX)); | |
746 return ContextOperand(tmp, index); | |
747 } | |
748 | |
749 default: | |
750 UNREACHABLE(); | |
751 return Operand(eax); | |
752 } | |
753 } | |
754 | |
755 | |
737 // Loads a value on TOS. If it is a boolean value, the result may have been | 756 // Loads a value on TOS. If it is a boolean value, the result may have been |
738 // (partially) translated into branches, or it may have set the condition code | 757 // (partially) translated into branches, or it may have set the condition code |
739 // register. If force_cc is set, the value is forced to set the condition code | 758 // register. If force_cc is set, the value is forced to set the condition code |
740 // register and no value is pushed. If the condition code register was set, | 759 // register and no value is pushed. If the condition code register was set, |
741 // has_cc() is true and cc_reg_ contains the condition to test for 'true'. | 760 // has_cc() is true and cc_reg_ contains the condition to test for 'true'. |
742 void Ia32CodeGenerator::LoadCondition(Expression* x, | 761 void Ia32CodeGenerator::LoadCondition(Expression* x, |
743 CodeGenState::AccessType access, | 762 CodeGenState::AccessType access, |
744 Label* true_target, | 763 Label* true_target, |
745 Label* false_target, | 764 Label* false_target, |
746 bool force_cc) { | 765 bool force_cc) { |
(...skipping 1009 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1756 val = new Literal(Factory::the_hole_value()); | 1775 val = new Literal(Factory::the_hole_value()); |
1757 } else { | 1776 } else { |
1758 val = node->fun(); // NULL if we don't have a function | 1777 val = node->fun(); // NULL if we don't have a function |
1759 } | 1778 } |
1760 | 1779 |
1761 if (val != NULL) { | 1780 if (val != NULL) { |
1762 // Set initial value. | 1781 // Set initial value. |
1763 Reference target(this, node->proxy()); | 1782 Reference target(this, node->proxy()); |
1764 ASSERT(target.is_slot()); | 1783 ASSERT(target.is_slot()); |
1765 Load(val); | 1784 Load(val); |
1766 SetValue(&target); | 1785 target.SetValue(NOT_CONST_INIT); |
1767 // Get rid of the assigned value (declarations are statements). It's | 1786 // Get rid of the assigned value (declarations are statements). It's |
1768 // safe to pop the value lying on top of the reference before unloading | 1787 // safe to pop the value lying on top of the reference before unloading |
1769 // the reference itself (which preserves the top of stack) because we | 1788 // the reference itself (which preserves the top of stack) because we |
1770 // know that it is a zero-sized reference. | 1789 // know that it is a zero-sized reference. |
1771 __ pop(eax); // Pop(no_reg); | 1790 __ pop(eax); // Pop(no_reg); |
1772 } | 1791 } |
1773 } | 1792 } |
1774 | 1793 |
1775 | 1794 |
1776 void Ia32CodeGenerator::VisitExpressionStatement(ExpressionStatement* node) { | 1795 void Ia32CodeGenerator::VisitExpressionStatement(ExpressionStatement* node) { |
(...skipping 506 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2283 // edx: i'th entry of the enum cache (or string there of) | 2302 // edx: i'th entry of the enum cache (or string there of) |
2284 __ push(ebx); | 2303 __ push(ebx); |
2285 { Reference each(this, node->each()); | 2304 { Reference each(this, node->each()); |
2286 if (!each.is_illegal()) { | 2305 if (!each.is_illegal()) { |
2287 if (each.size() > 0) { | 2306 if (each.size() > 0) { |
2288 __ push(Operand(esp, kPointerSize * each.size())); | 2307 __ push(Operand(esp, kPointerSize * each.size())); |
2289 } | 2308 } |
2290 // If the reference was to a slot we rely on the convenient property | 2309 // If the reference was to a slot we rely on the convenient property |
2291 // that it doesn't matter whether a value (eg, ebx pushed above) is | 2310 // that it doesn't matter whether a value (eg, ebx pushed above) is |
2292 // right on top of or right underneath a zero-sized reference. | 2311 // right on top of or right underneath a zero-sized reference. |
2293 SetValue(&each); | 2312 each.SetValue(NOT_CONST_INIT); |
2294 if (each.size() > 0) { | 2313 if (each.size() > 0) { |
2295 // It's safe to pop the value lying on top of the reference before | 2314 // It's safe to pop the value lying on top of the reference before |
2296 // unloading the reference itself (which preserves the top of stack, | 2315 // unloading the reference itself (which preserves the top of stack, |
2297 // ie, now the topmost value of the non-zero sized reference), since | 2316 // ie, now the topmost value of the non-zero sized reference), since |
2298 // we will discard the top of stack after unloading the reference | 2317 // we will discard the top of stack after unloading the reference |
2299 // anyway. | 2318 // anyway. |
2300 __ pop(eax); | 2319 __ pop(eax); |
2301 } | 2320 } |
2302 } | 2321 } |
2303 } | 2322 } |
(...skipping 23 matching lines...) Expand all Loading... | |
2327 __ call(&try_block); | 2346 __ call(&try_block); |
2328 // --- Catch block --- | 2347 // --- Catch block --- |
2329 __ push(eax); | 2348 __ push(eax); |
2330 | 2349 |
2331 // Store the caught exception in the catch variable. | 2350 // Store the caught exception in the catch variable. |
2332 { Reference ref(this, node->catch_var()); | 2351 { Reference ref(this, node->catch_var()); |
2333 ASSERT(ref.is_slot()); | 2352 ASSERT(ref.is_slot()); |
2334 // Load the exception to the top of the stack. Here we make use of the | 2353 // Load the exception to the top of the stack. Here we make use of the |
2335 // convenient property that it doesn't matter whether a value is | 2354 // convenient property that it doesn't matter whether a value is |
2336 // immediately on top of or underneath a zero-sized reference. | 2355 // immediately on top of or underneath a zero-sized reference. |
2337 SetValue(&ref); | 2356 ref.SetValue(NOT_CONST_INIT); |
2338 } | 2357 } |
2339 | 2358 |
2340 // Remove the exception from the stack. | 2359 // Remove the exception from the stack. |
2341 __ pop(edx); | 2360 __ pop(edx); |
2342 | 2361 |
2343 VisitStatements(node->catch_block()->statements()); | 2362 VisitStatements(node->catch_block()->statements()); |
2344 __ jmp(&exit); | 2363 __ jmp(&exit); |
2345 | 2364 |
2346 | 2365 |
2347 // --- Try block --- | 2366 // --- Try block --- |
(...skipping 589 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2937 if (var != NULL && | 2956 if (var != NULL && |
2938 var->mode() == Variable::CONST && | 2957 var->mode() == Variable::CONST && |
2939 node->op() != Token::INIT_VAR && node->op() != Token::INIT_CONST) { | 2958 node->op() != Token::INIT_VAR && node->op() != Token::INIT_CONST) { |
2940 // Assignment ignored - leave the value on the stack. | 2959 // Assignment ignored - leave the value on the stack. |
2941 } else { | 2960 } else { |
2942 __ RecordPosition(node->position()); | 2961 __ RecordPosition(node->position()); |
2943 if (node->op() == Token::INIT_CONST) { | 2962 if (node->op() == Token::INIT_CONST) { |
2944 // Dynamic constant initializations must use the function context | 2963 // Dynamic constant initializations must use the function context |
2945 // and initialize the actual constant declared. Dynamic variable | 2964 // and initialize the actual constant declared. Dynamic variable |
2946 // initializations are simply assignments and use SetValue. | 2965 // initializations are simply assignments and use SetValue. |
2947 InitConst(&target); | 2966 target.SetValue(CONST_INIT); |
2948 } else { | 2967 } else { |
2949 SetValue(&target); | 2968 target.SetValue(NOT_CONST_INIT); |
2950 } | 2969 } |
2951 } | 2970 } |
2952 } | 2971 } |
2953 | 2972 |
2954 | 2973 |
2955 void Ia32CodeGenerator::VisitThrow(Throw* node) { | 2974 void Ia32CodeGenerator::VisitThrow(Throw* node) { |
2956 Comment cmnt(masm_, "[ Throw"); | 2975 Comment cmnt(masm_, "[ Throw"); |
2957 | 2976 |
2958 Load(node->exception()); | 2977 Load(node->exception()); |
2959 __ RecordPosition(node->position()); | 2978 __ RecordPosition(node->position()); |
(...skipping 720 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3680 // If the count operation didn't overflow and the result is a | 3699 // If the count operation didn't overflow and the result is a |
3681 // valid smi, we're done. Otherwise, we jump to the deferred | 3700 // valid smi, we're done. Otherwise, we jump to the deferred |
3682 // slow-case code. | 3701 // slow-case code. |
3683 __ j(overflow, deferred->enter(), not_taken); | 3702 __ j(overflow, deferred->enter(), not_taken); |
3684 __ test(eax, Immediate(kSmiTagMask)); | 3703 __ test(eax, Immediate(kSmiTagMask)); |
3685 __ j(not_zero, deferred->enter(), not_taken); | 3704 __ j(not_zero, deferred->enter(), not_taken); |
3686 | 3705 |
3687 // Store the new value in the target if not const. | 3706 // Store the new value in the target if not const. |
3688 __ bind(deferred->exit()); | 3707 __ bind(deferred->exit()); |
3689 __ push(eax); // Push the new value to TOS | 3708 __ push(eax); // Push the new value to TOS |
3690 if (!is_const) SetValue(&target); | 3709 if (!is_const) target.SetValue(NOT_CONST_INIT); |
3691 } | 3710 } |
3692 | 3711 |
3693 // Postfix: Discard the new value and use the old. | 3712 // Postfix: Discard the new value and use the old. |
3694 if (is_postfix) __ pop(eax); | 3713 if (is_postfix) __ pop(eax); |
3695 } | 3714 } |
3696 | 3715 |
3697 | 3716 |
3698 void Ia32CodeGenerator::VisitBinaryOperation(BinaryOperation* node) { | 3717 void Ia32CodeGenerator::VisitBinaryOperation(BinaryOperation* node) { |
3699 Comment cmnt(masm_, "[ BinaryOperation"); | 3718 Comment cmnt(masm_, "[ BinaryOperation"); |
3700 Token::Value op = node->op(); | 3719 Token::Value op = node->op(); |
(...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4064 // call instruction to support patching the exit code in the | 4083 // call instruction to support patching the exit code in the |
4065 // debugger. See VisitReturnStatement for the full return sequence. | 4084 // debugger. See VisitReturnStatement for the full return sequence. |
4066 __ mov(esp, Operand(ebp)); | 4085 __ mov(esp, Operand(ebp)); |
4067 __ pop(ebp); | 4086 __ pop(ebp); |
4068 } | 4087 } |
4069 | 4088 |
4070 | 4089 |
4071 #undef __ | 4090 #undef __ |
4072 #define __ masm-> | 4091 #define __ masm-> |
4073 | 4092 |
4074 Operand Ia32CodeGenerator::SlotOperand(CodeGenerator* cgen, | 4093 void Reference::SetValue(InitState init_state) { |
4075 Slot* slot, | 4094 ASSERT(!is_illegal()); |
4076 Register tmp) { | 4095 ASSERT(!cgen_->has_cc()); |
4077 // Currently, this assertion will fail if we try to assign to | 4096 MacroAssembler* masm = cgen_->masm(); |
4078 // a constant variable that is constant because it is read-only | 4097 switch (type_) { |
4079 // (such as the variable referring to a named function expression). | 4098 case SLOT: { |
4080 // We need to implement assignments to read-only variables. | 4099 Comment cmnt(masm, "[ Store to Slot"); |
4081 // Ideally, we should do this during AST generation (by converting | 4100 Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot(); |
4082 // such assignments into expression statements); however, in general | 4101 ASSERT(slot != NULL); |
4083 // we may not be able to make the decision until past AST generation, | 4102 if (slot->type() == Slot::LOOKUP) { |
4084 // that is when the entire program is known. | 4103 ASSERT(slot->var()->mode() == Variable::DYNAMIC); |
4085 ASSERT(slot != NULL); | |
4086 int index = slot->index(); | |
4087 switch (slot->type()) { | |
4088 case Slot::PARAMETER: return ParameterOperand(cgen, index); | |
4089 | 4104 |
4090 case Slot::LOCAL: { | 4105 // For now, just do a runtime call. |
4091 ASSERT(0 <= index && index < cgen->scope()->num_stack_slots()); | 4106 __ push(esi); |
4092 const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset; | 4107 __ push(Immediate(slot->var()->name())); |
4093 return Operand(ebp, kLocal0Offset - index * kPointerSize); | 4108 |
4109 if (init_state == CONST_INIT) { | |
4110 // Same as the case for a normal store, but ignores attribute | |
4111 // (e.g. READ_ONLY) of context slot so that we can initialize | |
4112 // const properties (introduced via eval("const foo = (some | |
4113 // expr);")). Also, uses the current function context instead of | |
4114 // the top context. | |
4115 // | |
4116 // Note that we must declare the foo upon entry of eval(), via a | |
4117 // context slot declaration, but we cannot initialize it at the | |
4118 // same time, because the const declaration may be at the end of | |
4119 // the eval code (sigh...) and the const variable may have been | |
4120 // used before (where its value is 'undefined'). Thus, we can only | |
4121 // do the initialization when we actually encounter the expression | |
4122 // and when the expression operands are defined and valid, and | |
4123 // thus we need the split into 2 operations: declaration of the | |
4124 // context slot followed by initialization. | |
4125 __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); | |
4126 } else { | |
4127 __ CallRuntime(Runtime::kStoreContextSlot, 3); | |
4128 } | |
4129 // Storing a variable must keep the (new) value on the expression | |
4130 // stack. This is necessary for compiling chained assignment | |
4131 // expressions. | |
4132 __ push(eax); | |
4133 | |
4134 } else { | |
4135 ASSERT(slot->var()->mode() != Variable::DYNAMIC); | |
4136 | |
4137 Label exit; | |
4138 if (init_state == CONST_INIT) { | |
4139 ASSERT(slot->var()->mode() == Variable::CONST); | |
4140 // Only the first const initialization must be executed (the slot | |
4141 // still contains 'the hole' value). When the assignment is | |
4142 // executed, the code is identical to a normal store (see below). | |
4143 Comment cmnt(masm, "[ Init const"); | |
4144 __ mov(eax, cgen_->SlotOperand(slot, ecx)); | |
4145 __ cmp(eax, Factory::the_hole_value()); | |
4146 __ j(not_equal, &exit); | |
4147 } | |
4148 | |
4149 // We must execute the store. Storing a variable must keep the | |
4150 // (new) value on the stack. This is necessary for compiling | |
4151 // assignment expressions. | |
4152 // | |
4153 // Note: We will reach here even with slot->var()->mode() == | |
4154 // Variable::CONST because of const declarations which will | |
4155 // initialize consts to 'the hole' value and by doing so, end up | |
4156 // calling this code. | |
4157 __ pop(eax); | |
4158 __ mov(cgen_->SlotOperand(slot, ecx), eax); | |
4159 __ push(eax); // RecordWrite may destroy the value in eax. | |
4160 if (slot->type() == Slot::CONTEXT) { | |
4161 // ecx is loaded with context when calling SlotOperand above. | |
4162 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; | |
4163 __ RecordWrite(ecx, offset, eax, ebx); | |
4164 } | |
4165 // If we definitely did not jump over the assignment, we do not need | |
4166 // to bind the exit label. Doing so can defeat peephole | |
4167 // optimization. | |
4168 if (init_state == CONST_INIT) __ bind(&exit); | |
4169 } | |
4170 break; | |
4094 } | 4171 } |
4095 | 4172 |
4096 case Slot::CONTEXT: { | 4173 case NAMED: { |
4097 MacroAssembler* masm = cgen->masm(); | 4174 Comment cmnt(masm, "[ Store to named Property"); |
4098 // Follow the context chain if necessary. | 4175 Property* property = expression_->AsProperty(); |
4099 ASSERT(!tmp.is(esi)); // do not overwrite context register | 4176 Handle<String> name; |
4100 Register context = esi; | 4177 if (property == NULL) { |
4101 int chain_length = | 4178 // Global variable reference treated as named property access. |
4102 cgen->scope()->ContextChainLength(slot->var()->scope()); | 4179 VariableProxy* proxy = expression_->AsVariableProxy(); |
4103 for (int i = chain_length; i-- > 0;) { | 4180 ASSERT(proxy->AsVariable() != NULL); |
4104 // Load the closure. | 4181 ASSERT(proxy->AsVariable()->is_global()); |
4105 // (All contexts, even 'with' contexts, have a closure, | 4182 name = proxy->name(); |
4106 // and it is the same for all contexts inside a function. | 4183 } else { |
4107 // There is no need to go to the function context first.) | 4184 Literal* raw_name = property->key()->AsLiteral(); |
4108 __ mov(tmp, ContextOperand(context, Context::CLOSURE_INDEX)); | 4185 ASSERT(raw_name != NULL); |
4109 // Load the function context (which is the incoming, outer context). | 4186 name = Handle<String>(String::cast(*raw_name->handle())); |
4110 __ mov(tmp, FieldOperand(tmp, JSFunction::kContextOffset)); | 4187 __ RecordPosition(property->position()); |
4111 context = tmp; | |
4112 } | 4188 } |
4113 // We may have a 'with' context now. Get the function context. | 4189 |
4114 // (In fact this mov may never be the needed, since the scope analysis | 4190 // Call the appropriate IC code. |
4115 // may not permit a direct context access in this case and thus we are | 4191 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); |
4116 // always at a function context. However it is safe to dereference be- | 4192 // TODO(1222589): Make the IC grab the values from the stack. |
4117 // cause the function context of a function context is itself. Before | 4193 __ pop(eax); |
4118 // deleting this mov we should try to create a counter-example first, | 4194 // Setup the name register. |
4119 // though...) | 4195 __ mov(ecx, name); |
4120 __ mov(tmp, ContextOperand(context, Context::FCONTEXT_INDEX)); | 4196 __ call(ic, RelocInfo::CODE_TARGET); |
4121 return ContextOperand(tmp, index); | 4197 __ push(eax); // IC call leaves result in eax, push it out |
4198 break; | |
4199 } | |
4200 | |
4201 case KEYED: { | |
4202 Comment cmnt(masm, "[ Store to keyed Property"); | |
4203 Property* property = expression_->AsProperty(); | |
4204 ASSERT(property != NULL); | |
4205 __ RecordPosition(property->position()); | |
4206 // Call IC code. | |
4207 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); | |
4208 // TODO(1222589): Make the IC grab the values from the stack. | |
4209 __ pop(eax); | |
4210 __ call(ic, RelocInfo::CODE_TARGET); | |
4211 __ push(eax); // IC call leaves result in eax, push it out | |
4212 break; | |
4122 } | 4213 } |
4123 | 4214 |
4124 default: | 4215 default: |
4125 UNREACHABLE(); | 4216 UNREACHABLE(); |
4126 return Operand(eax); | |
4127 } | 4217 } |
4128 } | 4218 } |
4129 | 4219 |
4130 | |
4131 void Property::GenerateStoreCode(CodeGenerator* cgen, | |
4132 Reference* ref, | |
4133 InitState init_state) { | |
4134 MacroAssembler* masm = cgen->masm(); | |
4135 Comment cmnt(masm, "[ Store to Property"); | |
4136 __ RecordPosition(position()); | |
4137 Ia32CodeGenerator::SetReferenceProperty(cgen, ref, key()); | |
4138 } | |
4139 | |
4140 | |
4141 void VariableProxy::GenerateStoreCode(CodeGenerator* cgen, | |
4142 Reference* ref, | |
4143 InitState init_state) { | |
4144 MacroAssembler* masm = cgen->masm(); | |
4145 Comment cmnt(masm, "[ Store to VariableProxy"); | |
4146 Variable* node = var(); | |
4147 | |
4148 Expression* expr = node->rewrite(); | |
4149 if (expr != NULL) { | |
4150 expr->GenerateStoreCode(cgen, ref, init_state); | |
4151 } else { | |
4152 ASSERT(node->is_global()); | |
4153 if (node->AsProperty() != NULL) { | |
4154 __ RecordPosition(node->AsProperty()->position()); | |
4155 } | |
4156 Expression* key = new Literal(node->name()); | |
4157 Ia32CodeGenerator::SetReferenceProperty(cgen, ref, key); | |
4158 } | |
4159 } | |
4160 | |
4161 | |
4162 void Slot::GenerateStoreCode(CodeGenerator* cgen, | |
4163 Reference* ref, | |
4164 InitState init_state) { | |
4165 MacroAssembler* masm = cgen->masm(); | |
4166 Comment cmnt(masm, "[ Store to Slot"); | |
4167 | |
4168 if (type() == Slot::LOOKUP) { | |
4169 ASSERT(var()->mode() == Variable::DYNAMIC); | |
4170 | |
4171 // For now, just do a runtime call. | |
4172 __ push(esi); | |
4173 __ push(Immediate(var()->name())); | |
4174 | |
4175 if (init_state == CONST_INIT) { | |
4176 // Same as the case for a normal store, but ignores attribute | |
4177 // (e.g. READ_ONLY) of context slot so that we can initialize const | |
4178 // properties (introduced via eval("const foo = (some expr);")). Also, | |
4179 // uses the current function context instead of the top context. | |
4180 // | |
4181 // Note that we must declare the foo upon entry of eval(), via a | |
4182 // context slot declaration, but we cannot initialize it at the same | |
4183 // time, because the const declaration may be at the end of the eval | |
4184 // code (sigh...) and the const variable may have been used before | |
4185 // (where its value is 'undefined'). Thus, we can only do the | |
4186 // initialization when we actually encounter the expression and when | |
4187 // the expression operands are defined and valid, and thus we need the | |
4188 // split into 2 operations: declaration of the context slot followed | |
4189 // by initialization. | |
4190 __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); | |
4191 } else { | |
4192 __ CallRuntime(Runtime::kStoreContextSlot, 3); | |
4193 } | |
4194 // Storing a variable must keep the (new) value on the expression | |
4195 // stack. This is necessary for compiling assignment expressions. | |
4196 __ push(eax); | |
4197 | |
4198 } else { | |
4199 ASSERT(var()->mode() != Variable::DYNAMIC); | |
4200 | |
4201 Label exit; | |
4202 if (init_state == CONST_INIT) { | |
4203 ASSERT(var()->mode() == Variable::CONST); | |
4204 // Only the first const initialization must be executed (the slot | |
4205 // still contains 'the hole' value). When the assignment is executed, | |
4206 // the code is identical to a normal store (see below). | |
4207 Comment cmnt(masm, "[ Init const"); | |
4208 __ mov(eax, Ia32CodeGenerator::SlotOperand(cgen, this, ecx)); | |
4209 __ cmp(eax, Factory::the_hole_value()); | |
4210 __ j(not_equal, &exit); | |
4211 } | |
4212 | |
4213 // We must execute the store. | |
4214 // Storing a variable must keep the (new) value on the stack. This is | |
4215 // necessary for compiling assignment expressions. ecx may be loaded | |
4216 // with context; used below in RecordWrite. | |
4217 // | |
4218 // Note: We will reach here even with node->var()->mode() == | |
4219 // Variable::CONST because of const declarations which will initialize | |
4220 // consts to 'the hole' value and by doing so, end up calling this | |
4221 // code. | |
4222 __ pop(eax); | |
4223 __ mov(Ia32CodeGenerator::SlotOperand(cgen, this, ecx), eax); | |
4224 __ push(eax); // RecordWrite may destroy the value in eax. | |
4225 if (type() == Slot::CONTEXT) { | |
4226 // ecx is loaded with context when calling SlotOperand above. | |
4227 int offset = FixedArray::kHeaderSize + index() * kPointerSize; | |
4228 __ RecordWrite(ecx, offset, eax, ebx); | |
4229 } | |
4230 // If we definitely did not jump over the assignment, we do not need to | |
4231 // bind the exit label. Doing so can defeat peephole optimization. | |
4232 if (init_state == CONST_INIT) __ bind(&exit); | |
4233 } | |
4234 } | |
4235 | |
4236 | 4220 |
4237 // NOTE: The stub does not handle the inlined cases (Smis, Booleans, undefined). | 4221 // NOTE: The stub does not handle the inlined cases (Smis, Booleans, undefined). |
4238 void ToBooleanStub::Generate(MacroAssembler* masm) { | 4222 void ToBooleanStub::Generate(MacroAssembler* masm) { |
4239 Label false_result, true_result, not_string; | 4223 Label false_result, true_result, not_string; |
4240 __ mov(eax, Operand(esp, 1 * kPointerSize)); | 4224 __ mov(eax, Operand(esp, 1 * kPointerSize)); |
4241 | 4225 |
4242 // 'null' => false. | 4226 // 'null' => false. |
4243 __ cmp(eax, Factory::null_value()); | 4227 __ cmp(eax, Factory::null_value()); |
4244 __ j(equal, &false_result); | 4228 __ j(equal, &false_result); |
4245 | 4229 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4284 // Return 1/0 for true/false in eax. | 4268 // Return 1/0 for true/false in eax. |
4285 __ bind(&true_result); | 4269 __ bind(&true_result); |
4286 __ mov(eax, 1); | 4270 __ mov(eax, 1); |
4287 __ ret(1 * kPointerSize); | 4271 __ ret(1 * kPointerSize); |
4288 __ bind(&false_result); | 4272 __ bind(&false_result); |
4289 __ mov(eax, 0); | 4273 __ mov(eax, 0); |
4290 __ ret(1 * kPointerSize); | 4274 __ ret(1 * kPointerSize); |
4291 } | 4275 } |
4292 | 4276 |
4293 | 4277 |
4294 void Ia32CodeGenerator::SetReferenceProperty(CodeGenerator* cgen, | |
4295 Reference* ref, | |
4296 Expression* key) { | |
4297 ASSERT(!ref->is_illegal()); | |
4298 MacroAssembler* masm = cgen->masm(); | |
4299 | |
4300 if (ref->type() == Reference::NAMED) { | |
4301 // Compute the name of the property. | |
4302 Literal* literal = key->AsLiteral(); | |
4303 Handle<String> name(String::cast(*literal->handle())); | |
4304 | |
4305 // Call the appropriate IC code. | |
4306 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); | |
4307 // TODO(1222589): Make the IC grab the values from the stack. | |
4308 __ pop(eax); | |
4309 // Setup the name register. | |
4310 __ Set(ecx, Immediate(name)); | |
4311 __ call(ic, RelocInfo::CODE_TARGET); | |
4312 } else { | |
4313 // Access keyed property. | |
4314 ASSERT(ref->type() == Reference::KEYED); | |
4315 | |
4316 // Call IC code. | |
4317 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); | |
4318 // TODO(1222589): Make the IC grab the values from the stack. | |
4319 __ pop(eax); | |
4320 __ call(ic, RelocInfo::CODE_TARGET); | |
4321 } | |
4322 __ push(eax); // IC call leaves result in eax, push it out | |
4323 } | |
4324 | |
4325 | |
4326 void GenericBinaryOpStub::Generate(MacroAssembler* masm) { | 4278 void GenericBinaryOpStub::Generate(MacroAssembler* masm) { |
4327 Label call_runtime; | 4279 Label call_runtime; |
4328 __ mov(eax, Operand(esp, 1 * kPointerSize)); // Get y. | 4280 __ mov(eax, Operand(esp, 1 * kPointerSize)); // Get y. |
4329 __ mov(edx, Operand(esp, 2 * kPointerSize)); // Get x. | 4281 __ mov(edx, Operand(esp, 2 * kPointerSize)); // Get x. |
4330 | 4282 |
4331 // 1. Smi case. | 4283 // 1. Smi case. |
4332 switch (op_) { | 4284 switch (op_) { |
4333 case Token::ADD: { | 4285 case Token::ADD: { |
4334 // eax: y. | 4286 // eax: y. |
4335 // edx: x. | 4287 // edx: x. |
(...skipping 1034 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
5370 bool is_eval) { | 5322 bool is_eval) { |
5371 Handle<Code> code = Ia32CodeGenerator::MakeCode(fun, script, is_eval); | 5323 Handle<Code> code = Ia32CodeGenerator::MakeCode(fun, script, is_eval); |
5372 if (!code.is_null()) { | 5324 if (!code.is_null()) { |
5373 Counters::total_compiled_code_size.Increment(code->instruction_size()); | 5325 Counters::total_compiled_code_size.Increment(code->instruction_size()); |
5374 } | 5326 } |
5375 return code; | 5327 return code; |
5376 } | 5328 } |
5377 | 5329 |
5378 | 5330 |
5379 } } // namespace v8::internal | 5331 } } // namespace v8::internal |
OLD | NEW |