OLD | NEW |
1 // Copyright 2010 the V8 project authors. All rights reserved. | 1 // Copyright 2010 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 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
74 __ j(not_equal, &miss); | 74 __ j(not_equal, &miss); |
75 | 75 |
76 // Jump to the first instruction in the code stub. | 76 // Jump to the first instruction in the code stub. |
77 __ addq(kScratchRegister, Immediate(Code::kHeaderSize - kHeapObjectTag)); | 77 __ addq(kScratchRegister, Immediate(Code::kHeaderSize - kHeapObjectTag)); |
78 __ jmp(kScratchRegister); | 78 __ jmp(kScratchRegister); |
79 | 79 |
80 __ bind(&miss); | 80 __ bind(&miss); |
81 } | 81 } |
82 | 82 |
83 | 83 |
| 84 // Helper function used to check that the dictionary doesn't contain |
| 85 // the property. This function may return false negatives, so miss_label |
| 86 // must always call a backup property check that is complete. |
| 87 // This function is safe to call if the receiver has fast properties. |
| 88 // Name must be a symbol and receiver must be a heap object. |
| 89 static void GenerateDictionaryNegativeLookup(MacroAssembler* masm, |
| 90 Label* miss_label, |
| 91 Register receiver, |
| 92 String* name, |
| 93 Register r0, |
| 94 Register extra) { |
| 95 ASSERT(name->IsSymbol()); |
| 96 __ IncrementCounter(&Counters::negative_lookups, 1); |
| 97 __ IncrementCounter(&Counters::negative_lookups_miss, 1); |
| 98 |
| 99 Label done; |
| 100 __ movq(r0, FieldOperand(receiver, HeapObject::kMapOffset)); |
| 101 |
| 102 const int kInterceptorOrAccessCheckNeededMask = |
| 103 (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded); |
| 104 |
| 105 // Bail out if the receiver has a named interceptor or requires access checks. |
| 106 __ testb(FieldOperand(r0, Map::kBitFieldOffset), |
| 107 Immediate(kInterceptorOrAccessCheckNeededMask)); |
| 108 __ j(not_zero, miss_label); |
| 109 |
| 110 // Check that receiver is a JSObject. |
| 111 __ CmpInstanceType(r0, FIRST_JS_OBJECT_TYPE); |
| 112 __ j(below, miss_label); |
| 113 |
| 114 // Load properties array. |
| 115 Register properties = r0; |
| 116 __ movq(properties, FieldOperand(receiver, JSObject::kPropertiesOffset)); |
| 117 |
| 118 // Check that the properties array is a dictionary. |
| 119 __ CompareRoot(FieldOperand(properties, HeapObject::kMapOffset), |
| 120 Heap::kHashTableMapRootIndex); |
| 121 __ j(not_equal, miss_label); |
| 122 |
| 123 // Compute the capacity mask. |
| 124 const int kCapacityOffset = |
| 125 StringDictionary::kHeaderSize + |
| 126 StringDictionary::kCapacityIndex * kPointerSize; |
| 127 |
| 128 // Generate an unrolled loop that performs a few probes before |
| 129 // giving up. |
| 130 static const int kProbes = 4; |
| 131 const int kElementsStartOffset = |
| 132 StringDictionary::kHeaderSize + |
| 133 StringDictionary::kElementsStartIndex * kPointerSize; |
| 134 |
| 135 // If names of slots in range from 1 to kProbes - 1 for the hash value are |
| 136 // not equal to the name and kProbes-th slot is not used (its name is the |
| 137 // undefined value), it guarantees the hash table doesn't contain the |
| 138 // property. It's true even if some slots represent deleted properties |
| 139 // (their names are the null value). |
| 140 for (int i = 0; i < kProbes; i++) { |
| 141 // r0 points to properties hash. |
| 142 // Compute the masked index: (hash + i + i * i) & mask. |
| 143 if (extra.is(no_reg)) { |
| 144 __ push(receiver); |
| 145 } |
| 146 Register index = extra.is(no_reg) ? receiver : extra; |
| 147 // Capacity is smi 2^n. |
| 148 __ SmiToInteger32(index, FieldOperand(properties, kCapacityOffset)); |
| 149 __ decl(index); |
| 150 __ and_(index, |
| 151 Immediate(name->Hash() + StringDictionary::GetProbeOffset(i))); |
| 152 |
| 153 // Scale the index by multiplying by the entry size. |
| 154 ASSERT(StringDictionary::kEntrySize == 3); |
| 155 __ lea(index, Operand(index, index, times_2, 0)); // index *= 3. |
| 156 |
| 157 Register entity_name = extra.is(no_reg) ? properties : extra; |
| 158 // Having undefined at this place means the name is not contained. |
| 159 ASSERT_EQ(kSmiTagSize, 1); |
| 160 __ movq(entity_name, Operand(properties, index, times_pointer_size, |
| 161 kElementsStartOffset - kHeapObjectTag)); |
| 162 __ Cmp(entity_name, Factory::undefined_value()); |
| 163 if (extra.is(no_reg)) { |
| 164 // 'receiver' shares a register with 'entity_name'. |
| 165 __ pop(receiver); |
| 166 } |
| 167 // __ jmp(miss_label); |
| 168 if (i != kProbes - 1) { |
| 169 __ j(equal, &done); |
| 170 |
| 171 // Stop if found the property. |
| 172 __ Cmp(entity_name, Handle<String>(name)); |
| 173 __ j(equal, miss_label); |
| 174 |
| 175 if (extra.is(no_reg)) { |
| 176 // Restore the properties if their register was occupied by the name. |
| 177 __ movq(properties, |
| 178 FieldOperand(receiver, JSObject::kPropertiesOffset)); |
| 179 } |
| 180 } else { |
| 181 // Give up probing if still not found the undefined value. |
| 182 __ j(not_equal, miss_label); |
| 183 } |
| 184 } |
| 185 |
| 186 __ bind(&done); |
| 187 __ DecrementCounter(&Counters::negative_lookups_miss, 1); |
| 188 } |
| 189 |
| 190 |
84 void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) { | 191 void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) { |
85 ASSERT(kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC); | 192 ASSERT(kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC); |
86 Code* code = NULL; | 193 Code* code = NULL; |
87 if (kind == Code::LOAD_IC) { | 194 if (kind == Code::LOAD_IC) { |
88 code = Builtins::builtin(Builtins::LoadIC_Miss); | 195 code = Builtins::builtin(Builtins::LoadIC_Miss); |
89 } else { | 196 } else { |
90 code = Builtins::builtin(Builtins::KeyedLoadIC_Miss); | 197 code = Builtins::builtin(Builtins::KeyedLoadIC_Miss); |
91 } | 198 } |
92 | 199 |
93 Handle<Code> ic(code); | 200 Handle<Code> ic(code); |
(...skipping 683 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
777 JSObject::cast(object), holder); | 884 JSObject::cast(object), holder); |
778 } | 885 } |
779 | 886 |
780 if (depth != kInvalidProtoDepth) { | 887 if (depth != kInvalidProtoDepth) { |
781 __ IncrementCounter(&Counters::call_const_fast_api, 1); | 888 __ IncrementCounter(&Counters::call_const_fast_api, 1); |
782 ReserveSpaceForFastApiCall(masm(), rax); | 889 ReserveSpaceForFastApiCall(masm(), rax); |
783 } | 890 } |
784 | 891 |
785 // Check that the maps haven't changed. | 892 // Check that the maps haven't changed. |
786 CheckPrototypes(JSObject::cast(object), rdx, holder, | 893 CheckPrototypes(JSObject::cast(object), rdx, holder, |
787 rbx, rax, name, depth, &miss); | 894 rbx, rax, name, depth, &miss, rdi); |
788 | 895 |
789 // Patch the receiver on the stack with the global proxy if | 896 // Patch the receiver on the stack with the global proxy if |
790 // necessary. | 897 // necessary. |
791 if (object->IsGlobalObject()) { | 898 if (object->IsGlobalObject()) { |
792 ASSERT(depth == kInvalidProtoDepth); | 899 ASSERT(depth == kInvalidProtoDepth); |
793 __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset)); | 900 __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset)); |
794 __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx); | 901 __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx); |
795 } | 902 } |
796 break; | 903 break; |
797 | 904 |
798 case STRING_CHECK: | 905 case STRING_CHECK: |
799 if (!function->IsBuiltin()) { | 906 if (!function->IsBuiltin()) { |
800 // Calling non-builtins with a value as receiver requires boxing. | 907 // Calling non-builtins with a value as receiver requires boxing. |
801 __ jmp(&miss); | 908 __ jmp(&miss); |
802 } else { | 909 } else { |
803 // Check that the object is a two-byte string or a symbol. | 910 // Check that the object is a two-byte string or a symbol. |
804 __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rax); | 911 __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rax); |
805 __ j(above_equal, &miss); | 912 __ j(above_equal, &miss); |
806 // Check that the maps starting from the prototype haven't changed. | 913 // Check that the maps starting from the prototype haven't changed. |
807 GenerateDirectLoadGlobalFunctionPrototype( | 914 GenerateDirectLoadGlobalFunctionPrototype( |
808 masm(), Context::STRING_FUNCTION_INDEX, rax); | 915 masm(), Context::STRING_FUNCTION_INDEX, rax); |
809 CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, | 916 CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, |
810 rbx, rdx, name, &miss); | 917 rbx, rdx, name, &miss, rdi); |
811 } | 918 } |
812 break; | 919 break; |
813 | 920 |
814 case NUMBER_CHECK: { | 921 case NUMBER_CHECK: { |
815 if (!function->IsBuiltin()) { | 922 if (!function->IsBuiltin()) { |
816 // Calling non-builtins with a value as receiver requires boxing. | 923 // Calling non-builtins with a value as receiver requires boxing. |
817 __ jmp(&miss); | 924 __ jmp(&miss); |
818 } else { | 925 } else { |
819 Label fast; | 926 Label fast; |
820 // Check that the object is a smi or a heap number. | 927 // Check that the object is a smi or a heap number. |
821 __ JumpIfSmi(rdx, &fast); | 928 __ JumpIfSmi(rdx, &fast); |
822 __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rax); | 929 __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rax); |
823 __ j(not_equal, &miss); | 930 __ j(not_equal, &miss); |
824 __ bind(&fast); | 931 __ bind(&fast); |
825 // Check that the maps starting from the prototype haven't changed. | 932 // Check that the maps starting from the prototype haven't changed. |
826 GenerateDirectLoadGlobalFunctionPrototype( | 933 GenerateDirectLoadGlobalFunctionPrototype( |
827 masm(), Context::NUMBER_FUNCTION_INDEX, rax); | 934 masm(), Context::NUMBER_FUNCTION_INDEX, rax); |
828 CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, | 935 CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, |
829 rbx, rdx, name, &miss); | 936 rbx, rdx, name, &miss, rdi); |
830 } | 937 } |
831 break; | 938 break; |
832 } | 939 } |
833 | 940 |
834 case BOOLEAN_CHECK: { | 941 case BOOLEAN_CHECK: { |
835 if (!function->IsBuiltin()) { | 942 if (!function->IsBuiltin()) { |
836 // Calling non-builtins with a value as receiver requires boxing. | 943 // Calling non-builtins with a value as receiver requires boxing. |
837 __ jmp(&miss); | 944 __ jmp(&miss); |
838 } else { | 945 } else { |
839 Label fast; | 946 Label fast; |
840 // Check that the object is a boolean. | 947 // Check that the object is a boolean. |
841 __ CompareRoot(rdx, Heap::kTrueValueRootIndex); | 948 __ CompareRoot(rdx, Heap::kTrueValueRootIndex); |
842 __ j(equal, &fast); | 949 __ j(equal, &fast); |
843 __ CompareRoot(rdx, Heap::kFalseValueRootIndex); | 950 __ CompareRoot(rdx, Heap::kFalseValueRootIndex); |
844 __ j(not_equal, &miss); | 951 __ j(not_equal, &miss); |
845 __ bind(&fast); | 952 __ bind(&fast); |
846 // Check that the maps starting from the prototype haven't changed. | 953 // Check that the maps starting from the prototype haven't changed. |
847 GenerateDirectLoadGlobalFunctionPrototype( | 954 GenerateDirectLoadGlobalFunctionPrototype( |
848 masm(), Context::BOOLEAN_FUNCTION_INDEX, rax); | 955 masm(), Context::BOOLEAN_FUNCTION_INDEX, rax); |
849 CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, | 956 CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, |
850 rbx, rdx, name, &miss); | 957 rbx, rdx, name, &miss, rdi); |
851 } | 958 } |
852 break; | 959 break; |
853 } | 960 } |
854 | 961 |
855 default: | 962 default: |
856 UNREACHABLE(); | 963 UNREACHABLE(); |
857 } | 964 } |
858 | 965 |
859 if (depth != kInvalidProtoDepth) { | 966 if (depth != kInvalidProtoDepth) { |
860 GenerateFastApiCall(masm(), optimization, argc); | 967 GenerateFastApiCall(masm(), optimization, argc); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
895 GenerateNameCheck(name, &miss); | 1002 GenerateNameCheck(name, &miss); |
896 | 1003 |
897 // Get the receiver from the stack. | 1004 // Get the receiver from the stack. |
898 const int argc = arguments().immediate(); | 1005 const int argc = arguments().immediate(); |
899 __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); | 1006 __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); |
900 | 1007 |
901 // Check that the receiver isn't a smi. | 1008 // Check that the receiver isn't a smi. |
902 __ JumpIfSmi(rdx, &miss); | 1009 __ JumpIfSmi(rdx, &miss); |
903 | 1010 |
904 // Do the right check and compute the holder register. | 1011 // Do the right check and compute the holder register. |
905 Register reg = CheckPrototypes(object, rdx, holder, rbx, rax, name, &miss); | 1012 Register reg = CheckPrototypes(object, rdx, holder, rbx, rax, |
| 1013 name, &miss, rdi); |
906 | 1014 |
907 GenerateFastPropertyLoad(masm(), rdi, reg, holder, index); | 1015 GenerateFastPropertyLoad(masm(), rdi, reg, holder, index); |
908 | 1016 |
909 // Check that the function really is a function. | 1017 // Check that the function really is a function. |
910 __ JumpIfSmi(rdi, &miss); | 1018 __ JumpIfSmi(rdi, &miss); |
911 __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rbx); | 1019 __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rbx); |
912 __ j(not_equal, &miss); | 1020 __ j(not_equal, &miss); |
913 | 1021 |
914 // Patch the receiver on the stack with the global proxy if | 1022 // Patch the receiver on the stack with the global proxy if |
915 // necessary. | 1023 // necessary. |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
959 | 1067 |
960 // Check that the receiver isn't a smi. | 1068 // Check that the receiver isn't a smi. |
961 __ JumpIfSmi(rdx, &miss); | 1069 __ JumpIfSmi(rdx, &miss); |
962 | 1070 |
963 CheckPrototypes(JSObject::cast(object), | 1071 CheckPrototypes(JSObject::cast(object), |
964 rdx, | 1072 rdx, |
965 holder, | 1073 holder, |
966 rbx, | 1074 rbx, |
967 rax, | 1075 rax, |
968 name, | 1076 name, |
969 &miss); | 1077 &miss, |
| 1078 rdi); |
970 | 1079 |
971 if (argc == 0) { | 1080 if (argc == 0) { |
972 // Noop, return the length. | 1081 // Noop, return the length. |
973 __ movq(rax, FieldOperand(rdx, JSArray::kLengthOffset)); | 1082 __ movq(rax, FieldOperand(rdx, JSArray::kLengthOffset)); |
974 __ ret((argc + 1) * kPointerSize); | 1083 __ ret((argc + 1) * kPointerSize); |
975 } else { | 1084 } else { |
976 // Get the elements array of the object. | 1085 // Get the elements array of the object. |
977 __ movq(rbx, FieldOperand(rdx, JSArray::kElementsOffset)); | 1086 __ movq(rbx, FieldOperand(rdx, JSArray::kElementsOffset)); |
978 | 1087 |
979 // Check that the elements are in fast mode (not dictionary). | 1088 // Check that the elements are in fast mode (not dictionary). |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1112 | 1221 |
1113 // Get the receiver from the stack. | 1222 // Get the receiver from the stack. |
1114 const int argc = arguments().immediate(); | 1223 const int argc = arguments().immediate(); |
1115 __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); | 1224 __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); |
1116 | 1225 |
1117 // Check that the receiver isn't a smi. | 1226 // Check that the receiver isn't a smi. |
1118 __ JumpIfSmi(rdx, &miss); | 1227 __ JumpIfSmi(rdx, &miss); |
1119 | 1228 |
1120 CheckPrototypes(JSObject::cast(object), rdx, | 1229 CheckPrototypes(JSObject::cast(object), rdx, |
1121 holder, rbx, | 1230 holder, rbx, |
1122 rax, name, &miss); | 1231 rax, name, &miss, rdi); |
1123 | 1232 |
1124 // Get the elements array of the object. | 1233 // Get the elements array of the object. |
1125 __ movq(rbx, FieldOperand(rdx, JSArray::kElementsOffset)); | 1234 __ movq(rbx, FieldOperand(rdx, JSArray::kElementsOffset)); |
1126 | 1235 |
1127 // Check that the elements are in fast mode (not dictionary). | 1236 // Check that the elements are in fast mode (not dictionary). |
1128 __ Cmp(FieldOperand(rbx, HeapObject::kMapOffset), Factory::fixed_array_map()); | 1237 __ Cmp(FieldOperand(rbx, HeapObject::kMapOffset), Factory::fixed_array_map()); |
1129 __ j(not_equal, &miss); | 1238 __ j(not_equal, &miss); |
1130 | 1239 |
1131 // Get the array's length into rcx and calculate new length. | 1240 // Get the array's length into rcx and calculate new length. |
1132 __ SmiToInteger32(rcx, FieldOperand(rdx, JSArray::kLengthOffset)); | 1241 __ SmiToInteger32(rcx, FieldOperand(rdx, JSArray::kLengthOffset)); |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1281 __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); | 1390 __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); |
1282 | 1391 |
1283 // If the object is the holder then we know that it's a global | 1392 // If the object is the holder then we know that it's a global |
1284 // object which can only happen for contextual calls. In this case, | 1393 // object which can only happen for contextual calls. In this case, |
1285 // the receiver cannot be a smi. | 1394 // the receiver cannot be a smi. |
1286 if (object != holder) { | 1395 if (object != holder) { |
1287 __ JumpIfSmi(rdx, &miss); | 1396 __ JumpIfSmi(rdx, &miss); |
1288 } | 1397 } |
1289 | 1398 |
1290 // Check that the maps haven't changed. | 1399 // Check that the maps haven't changed. |
1291 CheckPrototypes(object, rdx, holder, rbx, rax, name, &miss); | 1400 CheckPrototypes(object, rdx, holder, rbx, rax, name, &miss, rdi); |
1292 | 1401 |
1293 // Get the value from the cell. | 1402 // Get the value from the cell. |
1294 __ Move(rdi, Handle<JSGlobalPropertyCell>(cell)); | 1403 __ Move(rdi, Handle<JSGlobalPropertyCell>(cell)); |
1295 __ movq(rdi, FieldOperand(rdi, JSGlobalPropertyCell::kValueOffset)); | 1404 __ movq(rdi, FieldOperand(rdi, JSGlobalPropertyCell::kValueOffset)); |
1296 | 1405 |
1297 // Check that the cell contains the same function. | 1406 // Check that the cell contains the same function. |
1298 if (Heap::InNewSpace(function)) { | 1407 if (Heap::InNewSpace(function)) { |
1299 // We can't embed a pointer to a function in new space so we have | 1408 // We can't embed a pointer to a function in new space so we have |
1300 // to verify that the shared function info is unchanged. This has | 1409 // to verify that the shared function info is unchanged. This has |
1301 // the nice side effect that multiple closures based on the same | 1410 // the nice side effect that multiple closures based on the same |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1394 // -- rsp[0] : return address | 1503 // -- rsp[0] : return address |
1395 // ----------------------------------- | 1504 // ----------------------------------- |
1396 Label miss; | 1505 Label miss; |
1397 | 1506 |
1398 // Chech that receiver is not a smi. | 1507 // Chech that receiver is not a smi. |
1399 __ JumpIfSmi(rax, &miss); | 1508 __ JumpIfSmi(rax, &miss); |
1400 | 1509 |
1401 // Check the maps of the full prototype chain. Also check that | 1510 // Check the maps of the full prototype chain. Also check that |
1402 // global property cells up to (but not including) the last object | 1511 // global property cells up to (but not including) the last object |
1403 // in the prototype chain are empty. | 1512 // in the prototype chain are empty. |
1404 CheckPrototypes(object, rax, last, rbx, rdx, name, &miss); | 1513 CheckPrototypes(object, rax, last, rbx, rdx, name, &miss, rdi); |
1405 | 1514 |
1406 // If the last object in the prototype chain is a global object, | 1515 // If the last object in the prototype chain is a global object, |
1407 // check that the global property cell is empty. | 1516 // check that the global property cell is empty. |
1408 if (last->IsGlobalObject()) { | 1517 if (last->IsGlobalObject()) { |
1409 Object* cell = GenerateCheckPropertyCell(masm(), | 1518 Object* cell = GenerateCheckPropertyCell(masm(), |
1410 GlobalObject::cast(last), | 1519 GlobalObject::cast(last), |
1411 name, | 1520 name, |
1412 rdx, | 1521 rdx, |
1413 &miss); | 1522 &miss); |
1414 if (cell->IsFailure()) return cell; | 1523 if (cell->IsFailure()) return cell; |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1493 Label miss; | 1602 Label miss; |
1494 | 1603 |
1495 // If the object is the holder then we know that it's a global | 1604 // If the object is the holder then we know that it's a global |
1496 // object which can only happen for contextual loads. In this case, | 1605 // object which can only happen for contextual loads. In this case, |
1497 // the receiver cannot be a smi. | 1606 // the receiver cannot be a smi. |
1498 if (object != holder) { | 1607 if (object != holder) { |
1499 __ JumpIfSmi(rax, &miss); | 1608 __ JumpIfSmi(rax, &miss); |
1500 } | 1609 } |
1501 | 1610 |
1502 // Check that the maps haven't changed. | 1611 // Check that the maps haven't changed. |
1503 CheckPrototypes(object, rax, holder, rbx, rdx, name, &miss); | 1612 CheckPrototypes(object, rax, holder, rbx, rdx, name, &miss, rdi); |
1504 | 1613 |
1505 // Get the value from the cell. | 1614 // Get the value from the cell. |
1506 __ Move(rbx, Handle<JSGlobalPropertyCell>(cell)); | 1615 __ Move(rbx, Handle<JSGlobalPropertyCell>(cell)); |
1507 __ movq(rbx, FieldOperand(rbx, JSGlobalPropertyCell::kValueOffset)); | 1616 __ movq(rbx, FieldOperand(rbx, JSGlobalPropertyCell::kValueOffset)); |
1508 | 1617 |
1509 // Check for deleted property if property can actually be deleted. | 1618 // Check for deleted property if property can actually be deleted. |
1510 if (!is_dont_delete) { | 1619 if (!is_dont_delete) { |
1511 __ CompareRoot(rbx, Heap::kTheHoleValueRootIndex); | 1620 __ CompareRoot(rbx, Heap::kTheHoleValueRootIndex); |
1512 __ j(equal, &miss); | 1621 __ j(equal, &miss); |
1513 } else if (FLAG_debug_code) { | 1622 } else if (FLAG_debug_code) { |
(...skipping 606 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2120 | 2229 |
2121 Register StubCompiler::CheckPrototypes(JSObject* object, | 2230 Register StubCompiler::CheckPrototypes(JSObject* object, |
2122 Register object_reg, | 2231 Register object_reg, |
2123 JSObject* holder, | 2232 JSObject* holder, |
2124 Register holder_reg, | 2233 Register holder_reg, |
2125 Register scratch, | 2234 Register scratch, |
2126 String* name, | 2235 String* name, |
2127 int save_at_depth, | 2236 int save_at_depth, |
2128 Label* miss, | 2237 Label* miss, |
2129 Register extra) { | 2238 Register extra) { |
2130 // Check that the maps haven't changed. | 2239 // Make sure there's no overlap between scratch and the other |
2131 Register result = | 2240 // registers. |
2132 masm()->CheckMaps(object, | 2241 ASSERT(!scratch.is(object_reg) && !scratch.is(holder_reg)); |
2133 object_reg, | 2242 |
2134 holder, | 2243 // Keep track of the current object in register reg. On the first |
2135 holder_reg, | 2244 // iteration, reg is an alias for object_reg, on later iterations, |
2136 scratch, | 2245 // it is an alias for holder_reg. |
2137 save_at_depth, | 2246 Register reg = object_reg; |
2138 miss); | 2247 int depth = 0; |
| 2248 |
| 2249 if (save_at_depth == depth) { |
| 2250 __ movq(Operand(rsp, kPointerSize), object_reg); |
| 2251 } |
| 2252 |
| 2253 // Check the maps in the prototype chain. |
| 2254 // Traverse the prototype chain from the object and do map checks. |
| 2255 JSObject* current = object; |
| 2256 while (current != holder) { |
| 2257 depth++; |
| 2258 |
| 2259 // Only global objects and objects that do not require access |
| 2260 // checks are allowed in stubs. |
| 2261 ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded()); |
| 2262 |
| 2263 JSObject* prototype = JSObject::cast(current->GetPrototype()); |
| 2264 if (!current->HasFastProperties() && |
| 2265 !current->IsJSGlobalObject() && |
| 2266 !current->IsJSGlobalProxy()) { |
| 2267 if (!name->IsSymbol()) { |
| 2268 Object* lookup_result = Heap::LookupSymbol(name); |
| 2269 if (lookup_result->IsFailure()) { |
| 2270 set_failure(Failure::cast(lookup_result)); |
| 2271 return reg; |
| 2272 } else { |
| 2273 name = String::cast(lookup_result); |
| 2274 } |
| 2275 } |
| 2276 ASSERT(current->property_dictionary()->FindEntry(name) == |
| 2277 StringDictionary::kNotFound); |
| 2278 |
| 2279 GenerateDictionaryNegativeLookup(masm(), |
| 2280 miss, |
| 2281 reg, |
| 2282 name, |
| 2283 scratch, |
| 2284 extra); |
| 2285 __ movq(scratch, FieldOperand(reg, HeapObject::kMapOffset)); |
| 2286 reg = holder_reg; // from now the object is in holder_reg |
| 2287 __ movq(reg, FieldOperand(scratch, Map::kPrototypeOffset)); |
| 2288 } else if (Heap::InNewSpace(prototype)) { |
| 2289 // Get the map of the current object. |
| 2290 __ movq(scratch, FieldOperand(reg, HeapObject::kMapOffset)); |
| 2291 __ Cmp(scratch, Handle<Map>(current->map())); |
| 2292 // Branch on the result of the map check. |
| 2293 __ j(not_equal, miss); |
| 2294 // Check access rights to the global object. This has to happen |
| 2295 // after the map check so that we know that the object is |
| 2296 // actually a global object. |
| 2297 if (current->IsJSGlobalProxy()) { |
| 2298 __ CheckAccessGlobalProxy(reg, scratch, miss); |
| 2299 |
| 2300 // Restore scratch register to be the map of the object. |
| 2301 // We load the prototype from the map in the scratch register. |
| 2302 __ movq(scratch, FieldOperand(reg, HeapObject::kMapOffset)); |
| 2303 } |
| 2304 // The prototype is in new space; we cannot store a reference |
| 2305 // to it in the code. Load it from the map. |
| 2306 reg = holder_reg; // from now the object is in holder_reg |
| 2307 __ movq(reg, FieldOperand(scratch, Map::kPrototypeOffset)); |
| 2308 |
| 2309 } else { |
| 2310 // Check the map of the current object. |
| 2311 __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), |
| 2312 Handle<Map>(current->map())); |
| 2313 // Branch on the result of the map check. |
| 2314 __ j(not_equal, miss); |
| 2315 // Check access rights to the global object. This has to happen |
| 2316 // after the map check so that we know that the object is |
| 2317 // actually a global object. |
| 2318 if (current->IsJSGlobalProxy()) { |
| 2319 __ CheckAccessGlobalProxy(reg, scratch, miss); |
| 2320 } |
| 2321 // The prototype is in old space; load it directly. |
| 2322 reg = holder_reg; // from now the object is in holder_reg |
| 2323 __ Move(reg, Handle<JSObject>(prototype)); |
| 2324 } |
| 2325 |
| 2326 if (save_at_depth == depth) { |
| 2327 __ movq(Operand(rsp, kPointerSize), reg); |
| 2328 } |
| 2329 |
| 2330 // Go to the next object in the prototype chain. |
| 2331 current = prototype; |
| 2332 } |
| 2333 |
| 2334 // Check the holder map. |
| 2335 __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), Handle<Map>(holder->map())); |
| 2336 __ j(not_equal, miss); |
| 2337 |
| 2338 // Log the check depth. |
| 2339 LOG(IntEvent("check-maps-depth", depth + 1)); |
| 2340 |
| 2341 // Perform security check for access to the global object and return |
| 2342 // the holder register. |
| 2343 ASSERT(current == holder); |
| 2344 ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded()); |
| 2345 if (current->IsJSGlobalProxy()) { |
| 2346 __ CheckAccessGlobalProxy(reg, scratch, miss); |
| 2347 } |
2139 | 2348 |
2140 // If we've skipped any global objects, it's not enough to verify | 2349 // If we've skipped any global objects, it's not enough to verify |
2141 // that their maps haven't changed. We also need to check that the | 2350 // that their maps haven't changed. We also need to check that the |
2142 // property cell for the property is still empty. | 2351 // property cell for the property is still empty. |
2143 while (object != holder) { | 2352 current = object; |
2144 if (object->IsGlobalObject()) { | 2353 while (current != holder) { |
| 2354 if (current->IsGlobalObject()) { |
2145 Object* cell = GenerateCheckPropertyCell(masm(), | 2355 Object* cell = GenerateCheckPropertyCell(masm(), |
2146 GlobalObject::cast(object), | 2356 GlobalObject::cast(current), |
2147 name, | 2357 name, |
2148 scratch, | 2358 scratch, |
2149 miss); | 2359 miss); |
2150 if (cell->IsFailure()) { | 2360 if (cell->IsFailure()) { |
2151 set_failure(Failure::cast(cell)); | 2361 set_failure(Failure::cast(cell)); |
2152 return result; | 2362 return reg; |
2153 } | 2363 } |
2154 } | 2364 } |
2155 object = JSObject::cast(object->GetPrototype()); | 2365 current = JSObject::cast(current->GetPrototype()); |
2156 } | 2366 } |
2157 | 2367 |
2158 // Return the register containing the holder. | 2368 // Return the register containing the holder. |
2159 return result; | 2369 return reg; |
2160 } | 2370 } |
2161 | 2371 |
2162 | 2372 |
2163 void StubCompiler::GenerateLoadField(JSObject* object, | 2373 void StubCompiler::GenerateLoadField(JSObject* object, |
2164 JSObject* holder, | 2374 JSObject* holder, |
2165 Register receiver, | 2375 Register receiver, |
2166 Register scratch1, | 2376 Register scratch1, |
2167 Register scratch2, | 2377 Register scratch2, |
2168 int index, | 2378 int index, |
2169 String* name, | 2379 String* name, |
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2332 // Return the generated code. | 2542 // Return the generated code. |
2333 return GetCode(); | 2543 return GetCode(); |
2334 } | 2544 } |
2335 | 2545 |
2336 | 2546 |
2337 #undef __ | 2547 #undef __ |
2338 | 2548 |
2339 } } // namespace v8::internal | 2549 } } // namespace v8::internal |
2340 | 2550 |
2341 #endif // V8_TARGET_ARCH_X64 | 2551 #endif // V8_TARGET_ARCH_X64 |
OLD | NEW |