| OLD | NEW |
| 1 // Copyright 2011 the V8 project authors. All rights reserved. | 1 // Copyright 2011 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 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 200 // stack frame was an arguments adapter frame. | 200 // stack frame was an arguments adapter frame. |
| 201 ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT); | 201 ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT); |
| 202 __ CallStub(&stub); | 202 __ CallStub(&stub); |
| 203 // Store new arguments object in both "arguments" and ".arguments" slots. | 203 // Store new arguments object in both "arguments" and ".arguments" slots. |
| 204 __ movq(rcx, rax); | 204 __ movq(rcx, rax); |
| 205 Move(arguments->AsSlot(), rax, rbx, rdx); | 205 Move(arguments->AsSlot(), rax, rbx, rdx); |
| 206 Slot* dot_arguments_slot = scope()->arguments_shadow()->AsSlot(); | 206 Slot* dot_arguments_slot = scope()->arguments_shadow()->AsSlot(); |
| 207 Move(dot_arguments_slot, rcx, rbx, rdx); | 207 Move(dot_arguments_slot, rcx, rbx, rdx); |
| 208 } | 208 } |
| 209 | 209 |
| 210 { Comment cmnt(masm_, "[ Declarations"); | |
| 211 // For named function expressions, declare the function name as a | |
| 212 // constant. | |
| 213 if (scope()->is_function_scope() && scope()->function() != NULL) { | |
| 214 EmitDeclaration(scope()->function(), Variable::CONST, NULL); | |
| 215 } | |
| 216 // Visit all the explicit declarations unless there is an illegal | |
| 217 // redeclaration. | |
| 218 if (scope()->HasIllegalRedeclaration()) { | |
| 219 scope()->VisitIllegalRedeclaration(this); | |
| 220 } else { | |
| 221 VisitDeclarations(scope()->declarations()); | |
| 222 } | |
| 223 } | |
| 224 | |
| 225 if (FLAG_trace) { | 210 if (FLAG_trace) { |
| 226 __ CallRuntime(Runtime::kTraceEnter, 0); | 211 __ CallRuntime(Runtime::kTraceEnter, 0); |
| 227 } | 212 } |
| 228 | 213 |
| 229 { Comment cmnt(masm_, "[ Stack check"); | 214 // Visit the declarations and body unless there is an illegal |
| 230 PrepareForBailout(info->function(), NO_REGISTERS); | 215 // redeclaration. |
| 231 NearLabel ok; | 216 if (scope()->HasIllegalRedeclaration()) { |
| 232 __ CompareRoot(rsp, Heap::kStackLimitRootIndex); | 217 Comment cmnt(masm_, "[ Declarations"); |
| 233 __ j(above_equal, &ok); | 218 scope()->VisitIllegalRedeclaration(this); |
| 234 StackCheckStub stub; | 219 } else { |
| 235 __ CallStub(&stub); | 220 { Comment cmnt(masm_, "[ Declarations"); |
| 236 __ bind(&ok); | 221 // For named function expressions, declare the function name as a |
| 222 // constant. |
| 223 if (scope()->is_function_scope() && scope()->function() != NULL) { |
| 224 EmitDeclaration(scope()->function(), Variable::CONST, NULL); |
| 225 } |
| 226 VisitDeclarations(scope()->declarations()); |
| 227 } |
| 228 |
| 229 { Comment cmnt(masm_, "[ Stack check"); |
| 230 PrepareForBailout(info->function(), NO_REGISTERS); |
| 231 NearLabel ok; |
| 232 __ CompareRoot(rsp, Heap::kStackLimitRootIndex); |
| 233 __ j(above_equal, &ok); |
| 234 StackCheckStub stub; |
| 235 __ CallStub(&stub); |
| 236 __ bind(&ok); |
| 237 } |
| 238 |
| 239 { Comment cmnt(masm_, "[ Body"); |
| 240 ASSERT(loop_depth() == 0); |
| 241 VisitStatements(function()->body()); |
| 242 ASSERT(loop_depth() == 0); |
| 243 } |
| 237 } | 244 } |
| 238 | 245 |
| 239 { Comment cmnt(masm_, "[ Body"); | 246 // Always emit a 'return undefined' in case control fell off the end of |
| 240 ASSERT(loop_depth() == 0); | 247 // the body. |
| 241 VisitStatements(function()->body()); | |
| 242 ASSERT(loop_depth() == 0); | |
| 243 } | |
| 244 | |
| 245 { Comment cmnt(masm_, "[ return <undefined>;"); | 248 { Comment cmnt(masm_, "[ return <undefined>;"); |
| 246 // Emit a 'return undefined' in case control fell off the end of the body. | |
| 247 __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); | 249 __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); |
| 248 EmitReturnSequence(); | 250 EmitReturnSequence(); |
| 249 } | 251 } |
| 250 } | 252 } |
| 251 | 253 |
| 252 | 254 |
| 253 void FullCodeGenerator::ClearAccumulator() { | 255 void FullCodeGenerator::ClearAccumulator() { |
| 254 __ Set(rax, 0); | 256 __ Set(rax, 0); |
| 255 } | 257 } |
| 256 | 258 |
| 257 | 259 |
| 258 void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt) { | 260 void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt) { |
| 259 Comment cmnt(masm_, "[ Stack check"); | 261 Comment cmnt(masm_, "[ Stack check"); |
| 260 NearLabel ok; | 262 NearLabel ok; |
| 261 __ CompareRoot(rsp, Heap::kStackLimitRootIndex); | 263 __ CompareRoot(rsp, Heap::kStackLimitRootIndex); |
| 262 __ j(above_equal, &ok); | 264 __ j(above_equal, &ok); |
| 263 StackCheckStub stub; | 265 StackCheckStub stub; |
| 264 __ CallStub(&stub); | 266 __ CallStub(&stub); |
| 265 // Record a mapping of this PC offset to the OSR id. This is used to find | 267 // Record a mapping of this PC offset to the OSR id. This is used to find |
| 266 // the AST id from the unoptimized code in order to use it as a key into | 268 // the AST id from the unoptimized code in order to use it as a key into |
| 267 // the deoptimization input data found in the optimized code. | 269 // the deoptimization input data found in the optimized code. |
| 268 RecordStackCheck(stmt->OsrEntryId()); | 270 RecordStackCheck(stmt->OsrEntryId()); |
| 269 | 271 |
| 272 // Loop stack checks can be patched to perform on-stack replacement. In |
| 273 // order to decide whether or not to perform OSR we embed the loop depth |
| 274 // in a test instruction after the call so we can extract it from the OSR |
| 275 // builtin. |
| 276 ASSERT(loop_depth() > 0); |
| 277 __ testl(rax, Immediate(Min(loop_depth(), Code::kMaxLoopNestingMarker))); |
| 278 |
| 270 __ bind(&ok); | 279 __ bind(&ok); |
| 271 PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); | 280 PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); |
| 272 // Record a mapping of the OSR id to this PC. This is used if the OSR | 281 // Record a mapping of the OSR id to this PC. This is used if the OSR |
| 273 // entry becomes the target of a bailout. We don't expect it to be, but | 282 // entry becomes the target of a bailout. We don't expect it to be, but |
| 274 // we want it to work if it is. | 283 // we want it to work if it is. |
| 275 PrepareForBailoutForId(stmt->OsrEntryId(), NO_REGISTERS); | 284 PrepareForBailoutForId(stmt->OsrEntryId(), NO_REGISTERS); |
| 276 } | 285 } |
| 277 | 286 |
| 278 | 287 |
| 279 void FullCodeGenerator::EmitReturnSequence() { | 288 void FullCodeGenerator::EmitReturnSequence() { |
| (...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 536 void FullCodeGenerator::DoTest(Label* if_true, | 545 void FullCodeGenerator::DoTest(Label* if_true, |
| 537 Label* if_false, | 546 Label* if_false, |
| 538 Label* fall_through) { | 547 Label* fall_through) { |
| 539 // Emit the inlined tests assumed by the stub. | 548 // Emit the inlined tests assumed by the stub. |
| 540 __ CompareRoot(result_register(), Heap::kUndefinedValueRootIndex); | 549 __ CompareRoot(result_register(), Heap::kUndefinedValueRootIndex); |
| 541 __ j(equal, if_false); | 550 __ j(equal, if_false); |
| 542 __ CompareRoot(result_register(), Heap::kTrueValueRootIndex); | 551 __ CompareRoot(result_register(), Heap::kTrueValueRootIndex); |
| 543 __ j(equal, if_true); | 552 __ j(equal, if_true); |
| 544 __ CompareRoot(result_register(), Heap::kFalseValueRootIndex); | 553 __ CompareRoot(result_register(), Heap::kFalseValueRootIndex); |
| 545 __ j(equal, if_false); | 554 __ j(equal, if_false); |
| 546 ASSERT_EQ(0, kSmiTag); | 555 STATIC_ASSERT(kSmiTag == 0); |
| 547 __ SmiCompare(result_register(), Smi::FromInt(0)); | 556 __ SmiCompare(result_register(), Smi::FromInt(0)); |
| 548 __ j(equal, if_false); | 557 __ j(equal, if_false); |
| 549 Condition is_smi = masm_->CheckSmi(result_register()); | 558 Condition is_smi = masm_->CheckSmi(result_register()); |
| 550 __ j(is_smi, if_true); | 559 __ j(is_smi, if_true); |
| 551 | 560 |
| 552 // Call the ToBoolean stub for all other cases. | 561 // Call the ToBoolean stub for all other cases. |
| 553 ToBooleanStub stub; | 562 ToBooleanStub stub; |
| 554 __ push(result_register()); | 563 __ push(result_register()); |
| 555 __ CallStub(&stub); | 564 __ CallStub(&stub); |
| 556 __ testq(rax, rax); | 565 __ testq(rax, rax); |
| (...skipping 287 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 844 Label loop, exit; | 853 Label loop, exit; |
| 845 ForIn loop_statement(this, stmt); | 854 ForIn loop_statement(this, stmt); |
| 846 increment_loop_depth(); | 855 increment_loop_depth(); |
| 847 | 856 |
| 848 // Get the object to enumerate over. Both SpiderMonkey and JSC | 857 // Get the object to enumerate over. Both SpiderMonkey and JSC |
| 849 // ignore null and undefined in contrast to the specification; see | 858 // ignore null and undefined in contrast to the specification; see |
| 850 // ECMA-262 section 12.6.4. | 859 // ECMA-262 section 12.6.4. |
| 851 VisitForAccumulatorValue(stmt->enumerable()); | 860 VisitForAccumulatorValue(stmt->enumerable()); |
| 852 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); | 861 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); |
| 853 __ j(equal, &exit); | 862 __ j(equal, &exit); |
| 854 __ CompareRoot(rax, Heap::kNullValueRootIndex); | 863 Register null_value = rdi; |
| 864 __ LoadRoot(null_value, Heap::kNullValueRootIndex); |
| 865 __ cmpq(rax, null_value); |
| 855 __ j(equal, &exit); | 866 __ j(equal, &exit); |
| 856 | 867 |
| 857 // Convert the object to a JS object. | 868 // Convert the object to a JS object. |
| 858 Label convert, done_convert; | 869 Label convert, done_convert; |
| 859 __ JumpIfSmi(rax, &convert); | 870 __ JumpIfSmi(rax, &convert); |
| 860 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx); | 871 __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx); |
| 861 __ j(above_equal, &done_convert); | 872 __ j(above_equal, &done_convert); |
| 862 __ bind(&convert); | 873 __ bind(&convert); |
| 863 __ push(rax); | 874 __ push(rax); |
| 864 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); | 875 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); |
| 865 __ bind(&done_convert); | 876 __ bind(&done_convert); |
| 866 __ push(rax); | 877 __ push(rax); |
| 867 | 878 |
| 868 // BUG(867): Check cache validity in generated code. This is a fast | 879 // Check cache validity in generated code. This is a fast case for |
| 869 // case for the JSObject::IsSimpleEnum cache validity checks. If we | 880 // the JSObject::IsSimpleEnum cache validity checks. If we cannot |
| 870 // cannot guarantee cache validity, call the runtime system to check | 881 // guarantee cache validity, call the runtime system to check cache |
| 871 // cache validity or get the property names in a fixed array. | 882 // validity or get the property names in a fixed array. |
| 883 Label next, call_runtime; |
| 884 Register empty_fixed_array_value = r8; |
| 885 __ LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex); |
| 886 Register empty_descriptor_array_value = r9; |
| 887 __ LoadRoot(empty_descriptor_array_value, |
| 888 Heap::kEmptyDescriptorArrayRootIndex); |
| 889 __ movq(rcx, rax); |
| 890 __ bind(&next); |
| 891 |
| 892 // Check that there are no elements. Register rcx contains the |
| 893 // current JS object we've reached through the prototype chain. |
| 894 __ cmpq(empty_fixed_array_value, |
| 895 FieldOperand(rcx, JSObject::kElementsOffset)); |
| 896 __ j(not_equal, &call_runtime); |
| 897 |
| 898 // Check that instance descriptors are not empty so that we can |
| 899 // check for an enum cache. Leave the map in rbx for the subsequent |
| 900 // prototype load. |
| 901 __ movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset)); |
| 902 __ movq(rdx, FieldOperand(rbx, Map::kInstanceDescriptorsOffset)); |
| 903 __ cmpq(rdx, empty_descriptor_array_value); |
| 904 __ j(equal, &call_runtime); |
| 905 |
| 906 // Check that there is an enum cache in the non-empty instance |
| 907 // descriptors (rdx). This is the case if the next enumeration |
| 908 // index field does not contain a smi. |
| 909 __ movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumerationIndexOffset)); |
| 910 __ JumpIfSmi(rdx, &call_runtime); |
| 911 |
| 912 // For all objects but the receiver, check that the cache is empty. |
| 913 NearLabel check_prototype; |
| 914 __ cmpq(rcx, rax); |
| 915 __ j(equal, &check_prototype); |
| 916 __ movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumCacheBridgeCacheOffset)); |
| 917 __ cmpq(rdx, empty_fixed_array_value); |
| 918 __ j(not_equal, &call_runtime); |
| 919 |
| 920 // Load the prototype from the map and loop if non-null. |
| 921 __ bind(&check_prototype); |
| 922 __ movq(rcx, FieldOperand(rbx, Map::kPrototypeOffset)); |
| 923 __ cmpq(rcx, null_value); |
| 924 __ j(not_equal, &next); |
| 925 |
| 926 // The enum cache is valid. Load the map of the object being |
| 927 // iterated over and use the cache for the iteration. |
| 928 NearLabel use_cache; |
| 929 __ movq(rax, FieldOperand(rax, HeapObject::kMapOffset)); |
| 930 __ jmp(&use_cache); |
| 872 | 931 |
| 873 // Get the set of properties to enumerate. | 932 // Get the set of properties to enumerate. |
| 933 __ bind(&call_runtime); |
| 874 __ push(rax); // Duplicate the enumerable object on the stack. | 934 __ push(rax); // Duplicate the enumerable object on the stack. |
| 875 __ CallRuntime(Runtime::kGetPropertyNamesFast, 1); | 935 __ CallRuntime(Runtime::kGetPropertyNamesFast, 1); |
| 876 | 936 |
| 877 // If we got a map from the runtime call, we can do a fast | 937 // If we got a map from the runtime call, we can do a fast |
| 878 // modification check. Otherwise, we got a fixed array, and we have | 938 // modification check. Otherwise, we got a fixed array, and we have |
| 879 // to do a slow check. | 939 // to do a slow check. |
| 880 NearLabel fixed_array; | 940 NearLabel fixed_array; |
| 881 __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset), | 941 __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset), |
| 882 Heap::kMetaMapRootIndex); | 942 Heap::kMetaMapRootIndex); |
| 883 __ j(not_equal, &fixed_array); | 943 __ j(not_equal, &fixed_array); |
| 884 | 944 |
| 885 // We got a map in register rax. Get the enumeration cache from it. | 945 // We got a map in register rax. Get the enumeration cache from it. |
| 946 __ bind(&use_cache); |
| 886 __ movq(rcx, FieldOperand(rax, Map::kInstanceDescriptorsOffset)); | 947 __ movq(rcx, FieldOperand(rax, Map::kInstanceDescriptorsOffset)); |
| 887 __ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumerationIndexOffset)); | 948 __ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumerationIndexOffset)); |
| 888 __ movq(rdx, FieldOperand(rcx, DescriptorArray::kEnumCacheBridgeCacheOffset)); | 949 __ movq(rdx, FieldOperand(rcx, DescriptorArray::kEnumCacheBridgeCacheOffset)); |
| 889 | 950 |
| 890 // Setup the four remaining stack slots. | 951 // Setup the four remaining stack slots. |
| 891 __ push(rax); // Map. | 952 __ push(rax); // Map. |
| 892 __ push(rdx); // Enumeration cache. | 953 __ push(rdx); // Enumeration cache. |
| 893 __ movq(rax, FieldOperand(rdx, FixedArray::kLengthOffset)); | 954 __ movq(rax, FieldOperand(rdx, FixedArray::kLengthOffset)); |
| 894 __ push(rax); // Enumeration cache length (as smi). | 955 __ push(rax); // Enumeration cache length (as smi). |
| 895 __ Push(Smi::FromInt(0)); // Initial index. | 956 __ Push(Smi::FromInt(0)); // Initial index. |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 964 | 1025 |
| 965 // Exit and decrement the loop depth. | 1026 // Exit and decrement the loop depth. |
| 966 __ bind(&exit); | 1027 __ bind(&exit); |
| 967 decrement_loop_depth(); | 1028 decrement_loop_depth(); |
| 968 } | 1029 } |
| 969 | 1030 |
| 970 | 1031 |
| 971 void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info, | 1032 void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info, |
| 972 bool pretenure) { | 1033 bool pretenure) { |
| 973 // Use the fast case closure allocation code that allocates in new | 1034 // Use the fast case closure allocation code that allocates in new |
| 974 // space for nested functions that don't need literals cloning. | 1035 // space for nested functions that don't need literals cloning. If |
| 975 if (scope()->is_function_scope() && | 1036 // we're running with the --always-opt or the --prepare-always-opt |
| 1037 // flag, we need to use the runtime function so that the new function |
| 1038 // we are creating here gets a chance to have its code optimized and |
| 1039 // doesn't just get a copy of the existing unoptimized code. |
| 1040 if (!FLAG_always_opt && |
| 1041 !FLAG_prepare_always_opt && |
| 1042 scope()->is_function_scope() && |
| 976 info->num_literals() == 0 && | 1043 info->num_literals() == 0 && |
| 977 !pretenure) { | 1044 !pretenure) { |
| 978 FastNewClosureStub stub; | 1045 FastNewClosureStub stub; |
| 979 __ Push(info); | 1046 __ Push(info); |
| 980 __ CallStub(&stub); | 1047 __ CallStub(&stub); |
| 981 } else { | 1048 } else { |
| 982 __ push(rsi); | 1049 __ push(rsi); |
| 983 __ Push(info); | 1050 __ Push(info); |
| 984 __ Push(pretenure ? Factory::true_value() : Factory::false_value()); | 1051 __ Push(pretenure ? Factory::true_value() : Factory::false_value()); |
| 985 __ CallRuntime(Runtime::kNewClosure, 3); | 1052 __ CallRuntime(Runtime::kNewClosure, 3); |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1075 } | 1142 } |
| 1076 __ movq(temp, ContextOperand(context, Context::CLOSURE_INDEX)); | 1143 __ movq(temp, ContextOperand(context, Context::CLOSURE_INDEX)); |
| 1077 __ movq(temp, FieldOperand(temp, JSFunction::kContextOffset)); | 1144 __ movq(temp, FieldOperand(temp, JSFunction::kContextOffset)); |
| 1078 // Walk the rest of the chain without clobbering rsi. | 1145 // Walk the rest of the chain without clobbering rsi. |
| 1079 context = temp; | 1146 context = temp; |
| 1080 } | 1147 } |
| 1081 } | 1148 } |
| 1082 // Check that last extension is NULL. | 1149 // Check that last extension is NULL. |
| 1083 __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0)); | 1150 __ cmpq(ContextOperand(context, Context::EXTENSION_INDEX), Immediate(0)); |
| 1084 __ j(not_equal, slow); | 1151 __ j(not_equal, slow); |
| 1085 __ movq(temp, ContextOperand(context, Context::FCONTEXT_INDEX)); | 1152 |
| 1086 return ContextOperand(temp, slot->index()); | 1153 // This function is used only for loads, not stores, so it's safe to |
| 1154 // return an rsi-based operand (the write barrier cannot be allowed to |
| 1155 // destroy the rsi register). |
| 1156 return ContextOperand(context, slot->index()); |
| 1087 } | 1157 } |
| 1088 | 1158 |
| 1089 | 1159 |
| 1090 void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase( | 1160 void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase( |
| 1091 Slot* slot, | 1161 Slot* slot, |
| 1092 TypeofState typeof_state, | 1162 TypeofState typeof_state, |
| 1093 Label* slow, | 1163 Label* slow, |
| 1094 Label* done) { | 1164 Label* done) { |
| 1095 // Generate fast-case code for variables that might be shadowed by | 1165 // Generate fast-case code for variables that might be shadowed by |
| 1096 // eval-introduced variables. Eval is used a lot without | 1166 // eval-introduced variables. Eval is used a lot without |
| (...skipping 626 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1723 // Assignment to a global variable. Use inline caching for the | 1793 // Assignment to a global variable. Use inline caching for the |
| 1724 // assignment. Right-hand-side value is passed in rax, variable name in | 1794 // assignment. Right-hand-side value is passed in rax, variable name in |
| 1725 // rcx, and the global object on the stack. | 1795 // rcx, and the global object on the stack. |
| 1726 __ Move(rcx, var->name()); | 1796 __ Move(rcx, var->name()); |
| 1727 __ movq(rdx, GlobalObjectOperand()); | 1797 __ movq(rdx, GlobalObjectOperand()); |
| 1728 Handle<Code> ic(Builtins::builtin(is_strict() | 1798 Handle<Code> ic(Builtins::builtin(is_strict() |
| 1729 ? Builtins::StoreIC_Initialize_Strict | 1799 ? Builtins::StoreIC_Initialize_Strict |
| 1730 : Builtins::StoreIC_Initialize)); | 1800 : Builtins::StoreIC_Initialize)); |
| 1731 EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); | 1801 EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); |
| 1732 | 1802 |
| 1733 } else if (var->mode() != Variable::CONST || op == Token::INIT_CONST) { | 1803 } else if (op == Token::INIT_CONST) { |
| 1734 // Perform the assignment for non-const variables and for initialization | 1804 // Like var declarations, const declarations are hoisted to function |
| 1735 // of const variables. Const assignments are simply skipped. | 1805 // scope. However, unlike var initializers, const initializers are able |
| 1736 Label done; | 1806 // to drill a hole to that function context, even from inside a 'with' |
| 1807 // context. We thus bypass the normal static scope lookup. |
| 1808 Slot* slot = var->AsSlot(); |
| 1809 Label skip; |
| 1810 switch (slot->type()) { |
| 1811 case Slot::PARAMETER: |
| 1812 // No const parameters. |
| 1813 UNREACHABLE(); |
| 1814 break; |
| 1815 case Slot::LOCAL: |
| 1816 __ movq(rdx, Operand(rbp, SlotOffset(slot))); |
| 1817 __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); |
| 1818 __ j(not_equal, &skip); |
| 1819 __ movq(Operand(rbp, SlotOffset(slot)), rax); |
| 1820 break; |
| 1821 case Slot::CONTEXT: { |
| 1822 __ movq(rcx, ContextOperand(rsi, Context::FCONTEXT_INDEX)); |
| 1823 __ movq(rdx, ContextOperand(rcx, slot->index())); |
| 1824 __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); |
| 1825 __ j(not_equal, &skip); |
| 1826 __ movq(ContextOperand(rcx, slot->index()), rax); |
| 1827 int offset = Context::SlotOffset(slot->index()); |
| 1828 __ movq(rdx, rax); // Preserve the stored value in eax. |
| 1829 __ RecordWrite(rcx, offset, rdx, rbx); |
| 1830 break; |
| 1831 } |
| 1832 case Slot::LOOKUP: |
| 1833 __ push(rax); |
| 1834 __ push(rsi); |
| 1835 __ Push(var->name()); |
| 1836 __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); |
| 1837 break; |
| 1838 } |
| 1839 __ bind(&skip); |
| 1840 |
| 1841 } else if (var->mode() != Variable::CONST) { |
| 1842 // Perform the assignment for non-const variables. Const assignments |
| 1843 // are simply skipped. |
| 1737 Slot* slot = var->AsSlot(); | 1844 Slot* slot = var->AsSlot(); |
| 1738 switch (slot->type()) { | 1845 switch (slot->type()) { |
| 1739 case Slot::PARAMETER: | 1846 case Slot::PARAMETER: |
| 1740 case Slot::LOCAL: | 1847 case Slot::LOCAL: |
| 1741 if (op == Token::INIT_CONST) { | |
| 1742 // Detect const reinitialization by checking for the hole value. | |
| 1743 __ movq(rdx, Operand(rbp, SlotOffset(slot))); | |
| 1744 __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); | |
| 1745 __ j(not_equal, &done); | |
| 1746 } | |
| 1747 // Perform the assignment. | 1848 // Perform the assignment. |
| 1748 __ movq(Operand(rbp, SlotOffset(slot)), rax); | 1849 __ movq(Operand(rbp, SlotOffset(slot)), rax); |
| 1749 break; | 1850 break; |
| 1750 | 1851 |
| 1751 case Slot::CONTEXT: { | 1852 case Slot::CONTEXT: { |
| 1752 MemOperand target = EmitSlotSearch(slot, rcx); | 1853 MemOperand target = EmitSlotSearch(slot, rcx); |
| 1753 if (op == Token::INIT_CONST) { | |
| 1754 // Detect const reinitialization by checking for the hole value. | |
| 1755 __ movq(rdx, target); | |
| 1756 __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); | |
| 1757 __ j(not_equal, &done); | |
| 1758 } | |
| 1759 // Perform the assignment and issue the write barrier. | 1854 // Perform the assignment and issue the write barrier. |
| 1760 __ movq(target, rax); | 1855 __ movq(target, rax); |
| 1761 // The value of the assignment is in rax. RecordWrite clobbers its | 1856 // The value of the assignment is in rax. RecordWrite clobbers its |
| 1762 // register arguments. | 1857 // register arguments. |
| 1763 __ movq(rdx, rax); | 1858 __ movq(rdx, rax); |
| 1764 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; | 1859 int offset = Context::SlotOffset(slot->index()); |
| 1765 __ RecordWrite(rcx, offset, rdx, rbx); | 1860 __ RecordWrite(rcx, offset, rdx, rbx); |
| 1766 break; | 1861 break; |
| 1767 } | 1862 } |
| 1768 | 1863 |
| 1769 case Slot::LOOKUP: | 1864 case Slot::LOOKUP: |
| 1770 // Call the runtime for the assignment. The runtime will ignore | 1865 // Call the runtime for the assignment. |
| 1771 // const reinitialization. | |
| 1772 __ push(rax); // Value. | 1866 __ push(rax); // Value. |
| 1773 __ push(rsi); // Context. | 1867 __ push(rsi); // Context. |
| 1774 __ Push(var->name()); | 1868 __ Push(var->name()); |
| 1775 if (op == Token::INIT_CONST) { | 1869 __ CallRuntime(Runtime::kStoreContextSlot, 3); |
| 1776 // The runtime will ignore const redeclaration. | |
| 1777 __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); | |
| 1778 } else { | |
| 1779 __ CallRuntime(Runtime::kStoreContextSlot, 3); | |
| 1780 } | |
| 1781 break; | 1870 break; |
| 1782 } | 1871 } |
| 1783 __ bind(&done); | |
| 1784 } | 1872 } |
| 1785 } | 1873 } |
| 1786 | 1874 |
| 1787 | 1875 |
| 1788 void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { | 1876 void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { |
| 1789 // Assignment to a property, using a named store IC. | 1877 // Assignment to a property, using a named store IC. |
| 1790 Property* prop = expr->target()->AsProperty(); | 1878 Property* prop = expr->target()->AsProperty(); |
| 1791 ASSERT(prop != NULL); | 1879 ASSERT(prop != NULL); |
| 1792 ASSERT(prop->key()->AsLiteral() != NULL); | 1880 ASSERT(prop->key()->AsLiteral() != NULL); |
| 1793 | 1881 |
| (...skipping 1004 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2798 VisitForStackValue(args->at(1)); | 2886 VisitForStackValue(args->at(1)); |
| 2799 | 2887 |
| 2800 StringCompareStub stub; | 2888 StringCompareStub stub; |
| 2801 __ CallStub(&stub); | 2889 __ CallStub(&stub); |
| 2802 context()->Plug(rax); | 2890 context()->Plug(rax); |
| 2803 } | 2891 } |
| 2804 | 2892 |
| 2805 | 2893 |
| 2806 void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { | 2894 void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { |
| 2807 // Load the argument on the stack and call the stub. | 2895 // Load the argument on the stack and call the stub. |
| 2808 TranscendentalCacheStub stub(TranscendentalCache::SIN); | 2896 TranscendentalCacheStub stub(TranscendentalCache::SIN, |
| 2897 TranscendentalCacheStub::TAGGED); |
| 2809 ASSERT(args->length() == 1); | 2898 ASSERT(args->length() == 1); |
| 2810 VisitForStackValue(args->at(0)); | 2899 VisitForStackValue(args->at(0)); |
| 2811 __ CallStub(&stub); | 2900 __ CallStub(&stub); |
| 2812 context()->Plug(rax); | 2901 context()->Plug(rax); |
| 2813 } | 2902 } |
| 2814 | 2903 |
| 2815 | 2904 |
| 2816 void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { | 2905 void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { |
| 2817 // Load the argument on the stack and call the stub. | 2906 // Load the argument on the stack and call the stub. |
| 2818 TranscendentalCacheStub stub(TranscendentalCache::COS); | 2907 TranscendentalCacheStub stub(TranscendentalCache::COS, |
| 2908 TranscendentalCacheStub::TAGGED); |
| 2819 ASSERT(args->length() == 1); | 2909 ASSERT(args->length() == 1); |
| 2820 VisitForStackValue(args->at(0)); | 2910 VisitForStackValue(args->at(0)); |
| 2821 __ CallStub(&stub); | 2911 __ CallStub(&stub); |
| 2822 context()->Plug(rax); | 2912 context()->Plug(rax); |
| 2823 } | 2913 } |
| 2824 | 2914 |
| 2825 | 2915 |
| 2826 void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) { | 2916 void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) { |
| 2827 // Load the argument on the stack and call the stub. | 2917 // Load the argument on the stack and call the stub. |
| 2828 TranscendentalCacheStub stub(TranscendentalCache::LOG); | 2918 TranscendentalCacheStub stub(TranscendentalCache::LOG, |
| 2919 TranscendentalCacheStub::TAGGED); |
| 2829 ASSERT(args->length() == 1); | 2920 ASSERT(args->length() == 1); |
| 2830 VisitForStackValue(args->at(0)); | 2921 VisitForStackValue(args->at(0)); |
| 2831 __ CallStub(&stub); | 2922 __ CallStub(&stub); |
| 2832 context()->Plug(rax); | 2923 context()->Plug(rax); |
| 2833 } | 2924 } |
| 2834 | 2925 |
| 2835 | 2926 |
| 2836 void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { | 2927 void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { |
| 2837 // Load the argument on the stack and call the runtime function. | 2928 // Load the argument on the stack and call the runtime function. |
| 2838 ASSERT(args->length() == 1); | 2929 ASSERT(args->length() == 1); |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2993 PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); | 3084 PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); |
| 2994 __ j(zero, if_true); | 3085 __ j(zero, if_true); |
| 2995 __ jmp(if_false); | 3086 __ jmp(if_false); |
| 2996 | 3087 |
| 2997 context()->Plug(if_true, if_false); | 3088 context()->Plug(if_true, if_false); |
| 2998 } | 3089 } |
| 2999 | 3090 |
| 3000 | 3091 |
| 3001 void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { | 3092 void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { |
| 3002 ASSERT(args->length() == 1); | 3093 ASSERT(args->length() == 1); |
| 3094 VisitForAccumulatorValue(args->at(0)); |
| 3003 | 3095 |
| 3004 VisitForAccumulatorValue(args->at(0)); | 3096 if (FLAG_debug_code) { |
| 3097 __ AbortIfNotString(rax); |
| 3098 } |
| 3005 | 3099 |
| 3006 __ movl(rax, FieldOperand(rax, String::kHashFieldOffset)); | 3100 __ movl(rax, FieldOperand(rax, String::kHashFieldOffset)); |
| 3007 ASSERT(String::kHashShift >= kSmiTagSize); | 3101 ASSERT(String::kHashShift >= kSmiTagSize); |
| 3008 __ IndexFromHash(rax, rax); | 3102 __ IndexFromHash(rax, rax); |
| 3009 | 3103 |
| 3010 context()->Plug(rax); | 3104 context()->Plug(rax); |
| 3011 } | 3105 } |
| 3012 | 3106 |
| 3013 | 3107 |
| 3014 void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { | 3108 void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { |
| (...skipping 693 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3708 __ nop(); // Signals no inlined code. | 3802 __ nop(); // Signals no inlined code. |
| 3709 break; | 3803 break; |
| 3710 default: | 3804 default: |
| 3711 // Do nothing. | 3805 // Do nothing. |
| 3712 break; | 3806 break; |
| 3713 } | 3807 } |
| 3714 } | 3808 } |
| 3715 | 3809 |
| 3716 | 3810 |
| 3717 void FullCodeGenerator::EmitCallIC(Handle<Code> ic, JumpPatchSite* patch_site) { | 3811 void FullCodeGenerator::EmitCallIC(Handle<Code> ic, JumpPatchSite* patch_site) { |
| 3812 switch (ic->kind()) { |
| 3813 case Code::LOAD_IC: |
| 3814 __ IncrementCounter(&Counters::named_load_full, 1); |
| 3815 break; |
| 3816 case Code::KEYED_LOAD_IC: |
| 3817 __ IncrementCounter(&Counters::keyed_load_full, 1); |
| 3818 break; |
| 3819 case Code::STORE_IC: |
| 3820 __ IncrementCounter(&Counters::named_store_full, 1); |
| 3821 break; |
| 3822 case Code::KEYED_STORE_IC: |
| 3823 __ IncrementCounter(&Counters::keyed_store_full, 1); |
| 3824 default: |
| 3825 break; |
| 3826 } |
| 3827 |
| 3718 __ call(ic, RelocInfo::CODE_TARGET); | 3828 __ call(ic, RelocInfo::CODE_TARGET); |
| 3719 if (patch_site != NULL && patch_site->is_bound()) { | 3829 if (patch_site != NULL && patch_site->is_bound()) { |
| 3720 patch_site->EmitPatchInfo(); | 3830 patch_site->EmitPatchInfo(); |
| 3721 } else { | 3831 } else { |
| 3722 __ nop(); // Signals no inlined code. | 3832 __ nop(); // Signals no inlined code. |
| 3723 } | 3833 } |
| 3724 } | 3834 } |
| 3725 | 3835 |
| 3726 | 3836 |
| 3727 void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) { | 3837 void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) { |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3768 __ ret(0); | 3878 __ ret(0); |
| 3769 } | 3879 } |
| 3770 | 3880 |
| 3771 | 3881 |
| 3772 #undef __ | 3882 #undef __ |
| 3773 | 3883 |
| 3774 | 3884 |
| 3775 } } // namespace v8::internal | 3885 } } // namespace v8::internal |
| 3776 | 3886 |
| 3777 #endif // V8_TARGET_ARCH_X64 | 3887 #endif // V8_TARGET_ARCH_X64 |
| OLD | NEW |