| 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 653 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 664 __ add(sp, sp, Operand((scope_->num_parameters() + 1) * kPointerSize)); | 664 __ add(sp, sp, Operand((scope_->num_parameters() + 1) * kPointerSize)); |
| 665 __ mov(pc, lr); | 665 __ mov(pc, lr); |
| 666 | 666 |
| 667 // Code generation state must be reset. | 667 // Code generation state must be reset. |
| 668 scope_ = NULL; | 668 scope_ = NULL; |
| 669 ASSERT(!has_cc()); | 669 ASSERT(!has_cc()); |
| 670 ASSERT(state_ == NULL); | 670 ASSERT(state_ == NULL); |
| 671 } | 671 } |
| 672 | 672 |
| 673 | 673 |
| 674 #undef __ | |
| 675 #define __ masm-> | |
| 676 | |
| 677 MemOperand ArmCodeGenerator::SlotOperand(CodeGenerator* cgen, | |
| 678 Slot* slot, | |
| 679 Register tmp) { | |
| 680 // Currently, this assertion will fail if we try to assign to | |
| 681 // a constant variable that is constant because it is read-only | |
| 682 // (such as the variable referring to a named function expression). | |
| 683 // We need to implement assignments to read-only variables. | |
| 684 // Ideally, we should do this during AST generation (by converting | |
| 685 // such assignments into expression statements); however, in general | |
| 686 // we may not be able to make the decision until past AST generation, | |
| 687 // that is when the entire program is known. | |
| 688 ASSERT(slot != NULL); | |
| 689 int index = slot->index(); | |
| 690 switch (slot->type()) { | |
| 691 case Slot::PARAMETER: | |
| 692 return ParameterOperand(cgen, index); | |
| 693 | |
| 694 case Slot::LOCAL: { | |
| 695 ASSERT(0 <= index && | |
| 696 index < cgen->scope()->num_stack_slots() && | |
| 697 index >= 0); | |
| 698 int local_offset = JavaScriptFrameConstants::kLocal0Offset - | |
| 699 index * kPointerSize; | |
| 700 return MemOperand(fp, local_offset); | |
| 701 } | |
| 702 | |
| 703 case Slot::CONTEXT: { | |
| 704 MacroAssembler* masm = cgen->masm(); | |
| 705 // Follow the context chain if necessary. | |
| 706 ASSERT(!tmp.is(cp)); // do not overwrite context register | |
| 707 Register context = cp; | |
| 708 int chain_length = | |
| 709 cgen->scope()->ContextChainLength(slot->var()->scope()); | |
| 710 for (int i = chain_length; i-- > 0;) { | |
| 711 // Load the closure. | |
| 712 // (All contexts, even 'with' contexts, have a closure, | |
| 713 // and it is the same for all contexts inside a function. | |
| 714 // There is no need to go to the function context first.) | |
| 715 __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX)); | |
| 716 // Load the function context (which is the incoming, outer context). | |
| 717 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset)); | |
| 718 context = tmp; | |
| 719 } | |
| 720 // We may have a 'with' context now. Get the function context. | |
| 721 // (In fact this mov may never be the needed, since the scope analysis | |
| 722 // may not permit a direct context access in this case and thus we are | |
| 723 // always at a function context. However it is safe to dereference be- | |
| 724 // cause the function context of a function context is itself. Before | |
| 725 // deleting this mov we should try to create a counter-example first, | |
| 726 // though...) | |
| 727 __ ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX)); | |
| 728 return ContextOperand(tmp, index); | |
| 729 } | |
| 730 | |
| 731 default: | |
| 732 UNREACHABLE(); | |
| 733 return MemOperand(r0, 0); | |
| 734 } | |
| 735 } | |
| 736 | |
| 737 | |
| 738 #undef __ | |
| 739 #define __ masm_-> | |
| 740 | |
| 741 // Loads a value on the stack. If it is a boolean value, the result may have | 674 // Loads a value on the stack. If it is a boolean value, the result may have |
| 742 // been (partially) translated into branches, or it may have set the condition | 675 // been (partially) translated into branches, or it may have set the condition |
| 743 // code register. If force_cc is set, the value is forced to set the condition | 676 // code register. If force_cc is set, the value is forced to set the condition |
| 744 // code register and no value is pushed. If the condition code register was set, | 677 // code register and no value is pushed. If the condition code register was set, |
| 745 // has_cc() is true and cc_reg_ contains the condition to test for 'true'. | 678 // has_cc() is true and cc_reg_ contains the condition to test for 'true'. |
| 746 void ArmCodeGenerator::LoadCondition(Expression* x, | 679 void ArmCodeGenerator::LoadCondition(Expression* x, |
| 747 CodeGenState::AccessType access, | 680 CodeGenState::AccessType access, |
| 748 Label* true_target, | 681 Label* true_target, |
| 749 Label* false_target, | 682 Label* false_target, |
| 750 bool force_cc) { | 683 bool force_cc) { |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 895 if (size <= 0) { | 828 if (size <= 0) { |
| 896 // Do nothing. No popping is necessary. | 829 // Do nothing. No popping is necessary. |
| 897 } else { | 830 } else { |
| 898 __ pop(r0); | 831 __ pop(r0); |
| 899 __ add(sp, sp, Operand(size * kPointerSize)); | 832 __ add(sp, sp, Operand(size * kPointerSize)); |
| 900 __ push(r0); | 833 __ push(r0); |
| 901 } | 834 } |
| 902 } | 835 } |
| 903 | 836 |
| 904 | 837 |
| 905 #undef __ | |
| 906 #define __ masm-> | |
| 907 | |
| 908 void Property::GenerateStoreCode(CodeGenerator* cgen, | |
| 909 Reference* ref, | |
| 910 InitState init_state) { | |
| 911 MacroAssembler* masm = cgen->masm(); | |
| 912 Comment cmnt(masm, "[ Store to Property"); | |
| 913 __ RecordPosition(position()); | |
| 914 ArmCodeGenerator::SetReferenceProperty(cgen, ref, key()); | |
| 915 } | |
| 916 | |
| 917 | |
| 918 void VariableProxy::GenerateStoreCode(CodeGenerator* cgen, | |
| 919 Reference* ref, | |
| 920 InitState init_state) { | |
| 921 MacroAssembler* masm = cgen->masm(); | |
| 922 Comment cmnt(masm, "[ Store to VariableProxy"); | |
| 923 Variable* node = var(); | |
| 924 | |
| 925 Expression* expr = node->rewrite(); | |
| 926 if (expr != NULL) { | |
| 927 expr->GenerateStoreCode(cgen, ref, init_state); | |
| 928 } else { | |
| 929 ASSERT(node->is_global()); | |
| 930 if (node->AsProperty() != NULL) { | |
| 931 __ RecordPosition(node->AsProperty()->position()); | |
| 932 } | |
| 933 Expression* key = new Literal(node->name()); | |
| 934 ArmCodeGenerator::SetReferenceProperty(cgen, ref, key); | |
| 935 } | |
| 936 } | |
| 937 | |
| 938 | |
| 939 void Slot::GenerateStoreCode(CodeGenerator* cgen, | |
| 940 Reference* ref, | |
| 941 InitState init_state) { | |
| 942 MacroAssembler* masm = cgen->masm(); | |
| 943 Comment cmnt(masm, "[ Store to Slot"); | |
| 944 | |
| 945 if (type() == Slot::LOOKUP) { | |
| 946 ASSERT(var()->mode() == Variable::DYNAMIC); | |
| 947 | |
| 948 // For now, just do a runtime call. | |
| 949 __ push(cp); | |
| 950 __ mov(r0, Operand(var()->name())); | |
| 951 __ push(r0); | |
| 952 | |
| 953 if (init_state == CONST_INIT) { | |
| 954 // Same as the case for a normal store, but ignores attribute | |
| 955 // (e.g. READ_ONLY) of context slot so that we can initialize const | |
| 956 // properties (introduced via eval("const foo = (some expr);")). Also, | |
| 957 // uses the current function context instead of the top context. | |
| 958 // | |
| 959 // Note that we must declare the foo upon entry of eval(), via a | |
| 960 // context slot declaration, but we cannot initialize it at the same | |
| 961 // time, because the const declaration may be at the end of the eval | |
| 962 // code (sigh...) and the const variable may have been used before | |
| 963 // (where its value is 'undefined'). Thus, we can only do the | |
| 964 // initialization when we actually encounter the expression and when | |
| 965 // the expression operands are defined and valid, and thus we need the | |
| 966 // split into 2 operations: declaration of the context slot followed | |
| 967 // by initialization. | |
| 968 __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); | |
| 969 } else { | |
| 970 __ CallRuntime(Runtime::kStoreContextSlot, 3); | |
| 971 } | |
| 972 // Storing a variable must keep the (new) value on the expression | |
| 973 // stack. This is necessary for compiling assignment expressions. | |
| 974 __ push(r0); | |
| 975 | |
| 976 } else { | |
| 977 ASSERT(var()->mode() != Variable::DYNAMIC); | |
| 978 | |
| 979 Label exit; | |
| 980 if (init_state == CONST_INIT) { | |
| 981 ASSERT(var()->mode() == Variable::CONST); | |
| 982 // Only the first const initialization must be executed (the slot | |
| 983 // still contains 'the hole' value). When the assignment is executed, | |
| 984 // the code is identical to a normal store (see below). | |
| 985 Comment cmnt(masm, "[ Init const"); | |
| 986 __ ldr(r2, ArmCodeGenerator::SlotOperand(cgen, this, r2)); | |
| 987 __ cmp(r2, Operand(Factory::the_hole_value())); | |
| 988 __ b(ne, &exit); | |
| 989 } | |
| 990 | |
| 991 // We must execute the store. | |
| 992 // r2 may be loaded with context; used below in RecordWrite. | |
| 993 // Storing a variable must keep the (new) value on the stack. This is | |
| 994 // necessary for compiling assignment expressions. | |
| 995 // | |
| 996 // Note: We will reach here even with var()->mode() == Variable::CONST | |
| 997 // because of const declarations which will initialize consts to 'the | |
| 998 // hole' value and by doing so, end up calling this code. r2 may be | |
| 999 // loaded with context; used below in RecordWrite. | |
| 1000 __ pop(r0); | |
| 1001 __ str(r0, ArmCodeGenerator::SlotOperand(cgen, this, r2)); | |
| 1002 __ push(r0); | |
| 1003 | |
| 1004 if (type() == Slot::CONTEXT) { | |
| 1005 // Skip write barrier if the written value is a smi. | |
| 1006 __ tst(r0, Operand(kSmiTagMask)); | |
| 1007 __ b(eq, &exit); | |
| 1008 // r2 is loaded with context when calling SlotOperand above. | |
| 1009 int offset = FixedArray::kHeaderSize + index() * kPointerSize; | |
| 1010 __ mov(r3, Operand(offset)); | |
| 1011 __ RecordWrite(r2, r3, r1); | |
| 1012 } | |
| 1013 // If we definitely did not jump over the assignment, we do not need to | |
| 1014 // bind the exit label. Doing so can defeat peephole optimization. | |
| 1015 if (init_state == CONST_INIT || type() == Slot::CONTEXT) { | |
| 1016 __ bind(&exit); | |
| 1017 } | |
| 1018 } | |
| 1019 } | |
| 1020 | |
| 1021 | |
| 1022 #undef __ | |
| 1023 #define __ masm_-> | |
| 1024 | |
| 1025 // ECMA-262, section 9.2, page 30: ToBoolean(). Convert the given | 838 // ECMA-262, section 9.2, page 30: ToBoolean(). Convert the given |
| 1026 // register to a boolean in the condition code register. The code | 839 // register to a boolean in the condition code register. The code |
| 1027 // may jump to 'false_target' in case the register converts to 'false'. | 840 // may jump to 'false_target' in case the register converts to 'false'. |
| 1028 void ArmCodeGenerator::ToBoolean(Label* true_target, | 841 void ArmCodeGenerator::ToBoolean(Label* true_target, |
| 1029 Label* false_target) { | 842 Label* false_target) { |
| 1030 // Note: The generated code snippet does not change stack variables. | 843 // Note: The generated code snippet does not change stack variables. |
| 1031 // Only the condition code should be set. | 844 // Only the condition code should be set. |
| 1032 __ pop(r0); | 845 __ pop(r0); |
| 1033 | 846 |
| 1034 // Fast case checks | 847 // Fast case checks |
| (...skipping 20 matching lines...) Expand all Loading... |
| 1055 __ push(r0); | 868 __ push(r0); |
| 1056 __ CallRuntime(Runtime::kToBool, 1); | 869 __ CallRuntime(Runtime::kToBool, 1); |
| 1057 | 870 |
| 1058 // Convert result (r0) to condition code | 871 // Convert result (r0) to condition code |
| 1059 __ cmp(r0, Operand(Factory::false_value())); | 872 __ cmp(r0, Operand(Factory::false_value())); |
| 1060 | 873 |
| 1061 cc_reg_ = ne; | 874 cc_reg_ = ne; |
| 1062 } | 875 } |
| 1063 | 876 |
| 1064 | 877 |
| 1065 #undef __ | |
| 1066 #define __ masm-> | |
| 1067 | |
| 1068 class GetPropertyStub : public CodeStub { | 878 class GetPropertyStub : public CodeStub { |
| 1069 public: | 879 public: |
| 1070 GetPropertyStub() { } | 880 GetPropertyStub() { } |
| 1071 | 881 |
| 1072 private: | 882 private: |
| 1073 Major MajorKey() { return GetProperty; } | 883 Major MajorKey() { return GetProperty; } |
| 1074 int MinorKey() { return 0; } | 884 int MinorKey() { return 0; } |
| 1075 void Generate(MacroAssembler* masm); | 885 void Generate(MacroAssembler* masm); |
| 1076 | 886 |
| 1077 const char* GetName() { return "GetPropertyStub"; } | 887 const char* GetName() { return "GetPropertyStub"; } |
| 1078 }; | 888 }; |
| 1079 | 889 |
| 1080 | 890 |
| 1081 void GetPropertyStub::Generate(MacroAssembler* masm) { | |
| 1082 // sp[0]: key | |
| 1083 // sp[1]: receiver | |
| 1084 Label slow, fast; | |
| 1085 // Get the key and receiver object from the stack. | |
| 1086 __ ldm(ia, sp, r0.bit() | r1.bit()); | |
| 1087 // Check that the key is a smi. | |
| 1088 __ tst(r0, Operand(kSmiTagMask)); | |
| 1089 __ b(ne, &slow); | |
| 1090 __ mov(r0, Operand(r0, ASR, kSmiTagSize)); | |
| 1091 // Check that the object isn't a smi. | |
| 1092 __ tst(r1, Operand(kSmiTagMask)); | |
| 1093 __ b(eq, &slow); | |
| 1094 | |
| 1095 // Check that the object is some kind of JS object EXCEPT JS Value type. | |
| 1096 // In the case that the object is a value-wrapper object, | |
| 1097 // we enter the runtime system to make sure that indexing into string | |
| 1098 // objects work as intended. | |
| 1099 ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE); | |
| 1100 __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); | |
| 1101 __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); | |
| 1102 __ cmp(r2, Operand(JS_OBJECT_TYPE)); | |
| 1103 __ b(lt, &slow); | |
| 1104 | |
| 1105 // Get the elements array of the object. | |
| 1106 __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset)); | |
| 1107 // Check that the object is in fast mode (not dictionary). | |
| 1108 __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); | |
| 1109 __ cmp(r3, Operand(Factory::hash_table_map())); | |
| 1110 __ b(eq, &slow); | |
| 1111 // Check that the key (index) is within bounds. | |
| 1112 __ ldr(r3, FieldMemOperand(r1, Array::kLengthOffset)); | |
| 1113 __ cmp(r0, Operand(r3)); | |
| 1114 __ b(lo, &fast); | |
| 1115 | |
| 1116 // Slow case: Push extra copies of the arguments (2). | |
| 1117 __ bind(&slow); | |
| 1118 __ ldm(ia, sp, r0.bit() | r1.bit()); | |
| 1119 __ stm(db_w, sp, r0.bit() | r1.bit()); | |
| 1120 // Do tail-call to runtime routine. | |
| 1121 __ TailCallRuntime(ExternalReference(Runtime::kGetProperty), 2); | |
| 1122 | |
| 1123 // Fast case: Do the load. | |
| 1124 __ bind(&fast); | |
| 1125 __ add(r3, r1, Operand(Array::kHeaderSize - kHeapObjectTag)); | |
| 1126 __ ldr(r0, MemOperand(r3, r0, LSL, kPointerSizeLog2)); | |
| 1127 __ cmp(r0, Operand(Factory::the_hole_value())); | |
| 1128 // In case the loaded value is the_hole we have to consult GetProperty | |
| 1129 // to ensure the prototype chain is searched. | |
| 1130 __ b(eq, &slow); | |
| 1131 | |
| 1132 masm->StubReturn(1); | |
| 1133 } | |
| 1134 | |
| 1135 | |
| 1136 class SetPropertyStub : public CodeStub { | 891 class SetPropertyStub : public CodeStub { |
| 1137 public: | 892 public: |
| 1138 SetPropertyStub() { } | 893 SetPropertyStub() { } |
| 1139 | 894 |
| 1140 private: | 895 private: |
| 1141 Major MajorKey() { return SetProperty; } | 896 Major MajorKey() { return SetProperty; } |
| 1142 int MinorKey() { return 0; } | 897 int MinorKey() { return 0; } |
| 1143 void Generate(MacroAssembler* masm); | 898 void Generate(MacroAssembler* masm); |
| 1144 | 899 |
| 1145 const char* GetName() { return "GetPropertyStub"; } | 900 const char* GetName() { return "GetPropertyStub"; } |
| 1146 }; | 901 }; |
| 1147 | 902 |
| 1148 | 903 |
| 1149 | |
| 1150 void SetPropertyStub::Generate(MacroAssembler* masm) { | |
| 1151 // r0 : value | |
| 1152 // sp[0] : key | |
| 1153 // sp[1] : receiver | |
| 1154 | |
| 1155 Label slow, fast, array, extra, exit; | |
| 1156 // Get the key and the object from the stack. | |
| 1157 __ ldm(ia, sp, r1.bit() | r3.bit()); // r1 = key, r3 = receiver | |
| 1158 // Check that the key is a smi. | |
| 1159 __ tst(r1, Operand(kSmiTagMask)); | |
| 1160 __ b(ne, &slow); | |
| 1161 // Check that the object isn't a smi. | |
| 1162 __ tst(r3, Operand(kSmiTagMask)); | |
| 1163 __ b(eq, &slow); | |
| 1164 // Get the type of the object from its map. | |
| 1165 __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset)); | |
| 1166 __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); | |
| 1167 // Check if the object is a JS array or not. | |
| 1168 __ cmp(r2, Operand(JS_ARRAY_TYPE)); | |
| 1169 __ b(eq, &array); | |
| 1170 // Check that the object is some kind of JS object. | |
| 1171 __ cmp(r2, Operand(FIRST_JS_OBJECT_TYPE)); | |
| 1172 __ b(lt, &slow); | |
| 1173 | |
| 1174 | |
| 1175 // Object case: Check key against length in the elements array. | |
| 1176 __ ldr(r3, FieldMemOperand(r3, JSObject::kElementsOffset)); | |
| 1177 // Check that the object is in fast mode (not dictionary). | |
| 1178 __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset)); | |
| 1179 __ cmp(r2, Operand(Factory::hash_table_map())); | |
| 1180 __ b(eq, &slow); | |
| 1181 // Untag the key (for checking against untagged length in the fixed array). | |
| 1182 __ mov(r1, Operand(r1, ASR, kSmiTagSize)); | |
| 1183 // Compute address to store into and check array bounds. | |
| 1184 __ add(r2, r3, Operand(Array::kHeaderSize - kHeapObjectTag)); | |
| 1185 __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2)); | |
| 1186 __ ldr(ip, FieldMemOperand(r3, Array::kLengthOffset)); | |
| 1187 __ cmp(r1, Operand(ip)); | |
| 1188 __ b(lo, &fast); | |
| 1189 | |
| 1190 | |
| 1191 // Slow case: Push extra copies of the arguments (3). | |
| 1192 __ bind(&slow); | |
| 1193 __ ldm(ia, sp, r1.bit() | r3.bit()); // r0 == value, r1 == key, r3 == object | |
| 1194 __ stm(db_w, sp, r0.bit() | r1.bit() | r3.bit()); | |
| 1195 // Do tail-call to runtime routine. | |
| 1196 __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3); | |
| 1197 | |
| 1198 | |
| 1199 // Extra capacity case: Check if there is extra capacity to | |
| 1200 // perform the store and update the length. Used for adding one | |
| 1201 // element to the array by writing to array[array.length]. | |
| 1202 // r0 == value, r1 == key, r2 == elements, r3 == object | |
| 1203 __ bind(&extra); | |
| 1204 __ b(ne, &slow); // do not leave holes in the array | |
| 1205 __ mov(r1, Operand(r1, ASR, kSmiTagSize)); // untag | |
| 1206 __ ldr(ip, FieldMemOperand(r2, Array::kLengthOffset)); | |
| 1207 __ cmp(r1, Operand(ip)); | |
| 1208 __ b(hs, &slow); | |
| 1209 __ mov(r1, Operand(r1, LSL, kSmiTagSize)); // restore tag | |
| 1210 __ add(r1, r1, Operand(1 << kSmiTagSize)); // and increment | |
| 1211 __ str(r1, FieldMemOperand(r3, JSArray::kLengthOffset)); | |
| 1212 __ mov(r3, Operand(r2)); | |
| 1213 // NOTE: Computing the address to store into must take the fact | |
| 1214 // that the key has been incremented into account. | |
| 1215 int displacement = Array::kHeaderSize - kHeapObjectTag - | |
| 1216 ((1 << kSmiTagSize) * 2); | |
| 1217 __ add(r2, r2, Operand(displacement)); | |
| 1218 __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize)); | |
| 1219 __ b(&fast); | |
| 1220 | |
| 1221 | |
| 1222 // Array case: Get the length and the elements array from the JS | |
| 1223 // array. Check that the array is in fast mode; if it is the | |
| 1224 // length is always a smi. | |
| 1225 // r0 == value, r3 == object | |
| 1226 __ bind(&array); | |
| 1227 __ ldr(r2, FieldMemOperand(r3, JSObject::kElementsOffset)); | |
| 1228 __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset)); | |
| 1229 __ cmp(r1, Operand(Factory::hash_table_map())); | |
| 1230 __ b(eq, &slow); | |
| 1231 | |
| 1232 // Check the key against the length in the array, compute the | |
| 1233 // address to store into and fall through to fast case. | |
| 1234 __ ldr(r1, MemOperand(sp)); | |
| 1235 // r0 == value, r1 == key, r2 == elements, r3 == object. | |
| 1236 __ ldr(ip, FieldMemOperand(r3, JSArray::kLengthOffset)); | |
| 1237 __ cmp(r1, Operand(ip)); | |
| 1238 __ b(hs, &extra); | |
| 1239 __ mov(r3, Operand(r2)); | |
| 1240 __ add(r2, r2, Operand(Array::kHeaderSize - kHeapObjectTag)); | |
| 1241 __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize)); | |
| 1242 | |
| 1243 | |
| 1244 // Fast case: Do the store. | |
| 1245 // r0 == value, r2 == address to store into, r3 == elements | |
| 1246 __ bind(&fast); | |
| 1247 __ str(r0, MemOperand(r2)); | |
| 1248 // Skip write barrier if the written value is a smi. | |
| 1249 __ tst(r0, Operand(kSmiTagMask)); | |
| 1250 __ b(eq, &exit); | |
| 1251 // Update write barrier for the elements array address. | |
| 1252 __ sub(r1, r2, Operand(r3)); | |
| 1253 __ RecordWrite(r3, r1, r2); | |
| 1254 __ bind(&exit); | |
| 1255 masm->StubReturn(1); | |
| 1256 } | |
| 1257 | |
| 1258 | |
| 1259 class GenericBinaryOpStub : public CodeStub { | 904 class GenericBinaryOpStub : public CodeStub { |
| 1260 public: | 905 public: |
| 1261 explicit GenericBinaryOpStub(Token::Value op) : op_(op) { } | 906 explicit GenericBinaryOpStub(Token::Value op) : op_(op) { } |
| 1262 | 907 |
| 1263 private: | 908 private: |
| 1264 Token::Value op_; | 909 Token::Value op_; |
| 1265 | 910 |
| 1266 Major MajorKey() { return GenericBinaryOp; } | 911 Major MajorKey() { return GenericBinaryOp; } |
| 1267 int MinorKey() { return static_cast<int>(op_); } | 912 int MinorKey() { return static_cast<int>(op_); } |
| 1268 void Generate(MacroAssembler* masm); | 913 void Generate(MacroAssembler* masm); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 1282 default: return "GenericBinaryOpStub"; | 927 default: return "GenericBinaryOpStub"; |
| 1283 } | 928 } |
| 1284 } | 929 } |
| 1285 | 930 |
| 1286 #ifdef DEBUG | 931 #ifdef DEBUG |
| 1287 void Print() { PrintF("GenericBinaryOpStub (%s)\n", Token::String(op_)); } | 932 void Print() { PrintF("GenericBinaryOpStub (%s)\n", Token::String(op_)); } |
| 1288 #endif | 933 #endif |
| 1289 }; | 934 }; |
| 1290 | 935 |
| 1291 | 936 |
| 1292 void GenericBinaryOpStub::Generate(MacroAssembler* masm) { | |
| 1293 // r1 : x | |
| 1294 // r0 : y | |
| 1295 // result : r0 | |
| 1296 | |
| 1297 switch (op_) { | |
| 1298 case Token::ADD: { | |
| 1299 Label slow, exit; | |
| 1300 // fast path | |
| 1301 __ orr(r2, r1, Operand(r0)); // r2 = x | y; | |
| 1302 __ add(r0, r1, Operand(r0), SetCC); // add y optimistically | |
| 1303 // go slow-path in case of overflow | |
| 1304 __ b(vs, &slow); | |
| 1305 // go slow-path in case of non-smi operands | |
| 1306 ASSERT(kSmiTag == 0); // adjust code below | |
| 1307 __ tst(r2, Operand(kSmiTagMask)); | |
| 1308 __ b(eq, &exit); | |
| 1309 // slow path | |
| 1310 __ bind(&slow); | |
| 1311 __ sub(r0, r0, Operand(r1)); // revert optimistic add | |
| 1312 __ push(r1); | |
| 1313 __ push(r0); | |
| 1314 __ mov(r0, Operand(1)); // set number of arguments | |
| 1315 __ InvokeBuiltin(Builtins::ADD, JUMP_JS); | |
| 1316 // done | |
| 1317 __ bind(&exit); | |
| 1318 break; | |
| 1319 } | |
| 1320 | |
| 1321 case Token::SUB: { | |
| 1322 Label slow, exit; | |
| 1323 // fast path | |
| 1324 __ orr(r2, r1, Operand(r0)); // r2 = x | y; | |
| 1325 __ sub(r3, r1, Operand(r0), SetCC); // subtract y optimistically | |
| 1326 // go slow-path in case of overflow | |
| 1327 __ b(vs, &slow); | |
| 1328 // go slow-path in case of non-smi operands | |
| 1329 ASSERT(kSmiTag == 0); // adjust code below | |
| 1330 __ tst(r2, Operand(kSmiTagMask)); | |
| 1331 __ mov(r0, Operand(r3), LeaveCC, eq); // conditionally set r0 to result | |
| 1332 __ b(eq, &exit); | |
| 1333 // slow path | |
| 1334 __ bind(&slow); | |
| 1335 __ push(r1); | |
| 1336 __ push(r0); | |
| 1337 __ mov(r0, Operand(1)); // set number of arguments | |
| 1338 __ InvokeBuiltin(Builtins::SUB, JUMP_JS); | |
| 1339 // done | |
| 1340 __ bind(&exit); | |
| 1341 break; | |
| 1342 } | |
| 1343 | |
| 1344 case Token::MUL: { | |
| 1345 Label slow, exit; | |
| 1346 // tag check | |
| 1347 __ orr(r2, r1, Operand(r0)); // r2 = x | y; | |
| 1348 ASSERT(kSmiTag == 0); // adjust code below | |
| 1349 __ tst(r2, Operand(kSmiTagMask)); | |
| 1350 __ b(ne, &slow); | |
| 1351 // remove tag from one operand (but keep sign), so that result is smi | |
| 1352 __ mov(ip, Operand(r0, ASR, kSmiTagSize)); | |
| 1353 // do multiplication | |
| 1354 __ smull(r3, r2, r1, ip); // r3 = lower 32 bits of ip*r1 | |
| 1355 // go slow on overflows (overflow bit is not set) | |
| 1356 __ mov(ip, Operand(r3, ASR, 31)); | |
| 1357 __ cmp(ip, Operand(r2)); // no overflow if higher 33 bits are identical | |
| 1358 __ b(ne, &slow); | |
| 1359 // go slow on zero result to handle -0 | |
| 1360 __ tst(r3, Operand(r3)); | |
| 1361 __ mov(r0, Operand(r3), LeaveCC, ne); | |
| 1362 __ b(ne, &exit); | |
| 1363 // slow case | |
| 1364 __ bind(&slow); | |
| 1365 __ push(r1); | |
| 1366 __ push(r0); | |
| 1367 __ mov(r0, Operand(1)); // set number of arguments | |
| 1368 __ InvokeBuiltin(Builtins::MUL, JUMP_JS); | |
| 1369 // done | |
| 1370 __ bind(&exit); | |
| 1371 break; | |
| 1372 } | |
| 1373 | |
| 1374 case Token::BIT_OR: | |
| 1375 case Token::BIT_AND: | |
| 1376 case Token::BIT_XOR: { | |
| 1377 Label slow, exit; | |
| 1378 // tag check | |
| 1379 __ orr(r2, r1, Operand(r0)); // r2 = x | y; | |
| 1380 ASSERT(kSmiTag == 0); // adjust code below | |
| 1381 __ tst(r2, Operand(kSmiTagMask)); | |
| 1382 __ b(ne, &slow); | |
| 1383 switch (op_) { | |
| 1384 case Token::BIT_OR: __ orr(r0, r0, Operand(r1)); break; | |
| 1385 case Token::BIT_AND: __ and_(r0, r0, Operand(r1)); break; | |
| 1386 case Token::BIT_XOR: __ eor(r0, r0, Operand(r1)); break; | |
| 1387 default: UNREACHABLE(); | |
| 1388 } | |
| 1389 __ b(&exit); | |
| 1390 __ bind(&slow); | |
| 1391 __ push(r1); // restore stack | |
| 1392 __ push(r0); | |
| 1393 __ mov(r0, Operand(1)); // 1 argument (not counting receiver). | |
| 1394 switch (op_) { | |
| 1395 case Token::BIT_OR: | |
| 1396 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS); | |
| 1397 break; | |
| 1398 case Token::BIT_AND: | |
| 1399 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS); | |
| 1400 break; | |
| 1401 case Token::BIT_XOR: | |
| 1402 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS); | |
| 1403 break; | |
| 1404 default: | |
| 1405 UNREACHABLE(); | |
| 1406 } | |
| 1407 __ bind(&exit); | |
| 1408 break; | |
| 1409 } | |
| 1410 | |
| 1411 case Token::SHL: | |
| 1412 case Token::SHR: | |
| 1413 case Token::SAR: { | |
| 1414 Label slow, exit; | |
| 1415 // tag check | |
| 1416 __ orr(r2, r1, Operand(r0)); // r2 = x | y; | |
| 1417 ASSERT(kSmiTag == 0); // adjust code below | |
| 1418 __ tst(r2, Operand(kSmiTagMask)); | |
| 1419 __ b(ne, &slow); | |
| 1420 // remove tags from operands (but keep sign) | |
| 1421 __ mov(r3, Operand(r1, ASR, kSmiTagSize)); // x | |
| 1422 __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // y | |
| 1423 // use only the 5 least significant bits of the shift count | |
| 1424 __ and_(r2, r2, Operand(0x1f)); | |
| 1425 // perform operation | |
| 1426 switch (op_) { | |
| 1427 case Token::SAR: | |
| 1428 __ mov(r3, Operand(r3, ASR, r2)); | |
| 1429 // no checks of result necessary | |
| 1430 break; | |
| 1431 | |
| 1432 case Token::SHR: | |
| 1433 __ mov(r3, Operand(r3, LSR, r2)); | |
| 1434 // check that the *unsigned* result fits in a smi | |
| 1435 // neither of the two high-order bits can be set: | |
| 1436 // - 0x80000000: high bit would be lost when smi tagging | |
| 1437 // - 0x40000000: this number would convert to negative when | |
| 1438 // smi tagging these two cases can only happen with shifts | |
| 1439 // by 0 or 1 when handed a valid smi | |
| 1440 __ and_(r2, r3, Operand(0xc0000000), SetCC); | |
| 1441 __ b(ne, &slow); | |
| 1442 break; | |
| 1443 | |
| 1444 case Token::SHL: | |
| 1445 __ mov(r3, Operand(r3, LSL, r2)); | |
| 1446 // check that the *signed* result fits in a smi | |
| 1447 __ add(r2, r3, Operand(0x40000000), SetCC); | |
| 1448 __ b(mi, &slow); | |
| 1449 break; | |
| 1450 | |
| 1451 default: UNREACHABLE(); | |
| 1452 } | |
| 1453 // tag result and store it in r0 | |
| 1454 ASSERT(kSmiTag == 0); // adjust code below | |
| 1455 __ mov(r0, Operand(r3, LSL, kSmiTagSize)); | |
| 1456 __ b(&exit); | |
| 1457 // slow case | |
| 1458 __ bind(&slow); | |
| 1459 __ push(r1); // restore stack | |
| 1460 __ push(r0); | |
| 1461 __ mov(r0, Operand(1)); // 1 argument (not counting receiver). | |
| 1462 switch (op_) { | |
| 1463 case Token::SAR: __ InvokeBuiltin(Builtins::SAR, JUMP_JS); break; | |
| 1464 case Token::SHR: __ InvokeBuiltin(Builtins::SHR, JUMP_JS); break; | |
| 1465 case Token::SHL: __ InvokeBuiltin(Builtins::SHL, JUMP_JS); break; | |
| 1466 default: UNREACHABLE(); | |
| 1467 } | |
| 1468 __ bind(&exit); | |
| 1469 break; | |
| 1470 } | |
| 1471 | |
| 1472 default: UNREACHABLE(); | |
| 1473 } | |
| 1474 __ Ret(); | |
| 1475 } | |
| 1476 | |
| 1477 | |
| 1478 void StackCheckStub::Generate(MacroAssembler* masm) { | |
| 1479 Label within_limit; | |
| 1480 __ mov(ip, Operand(ExternalReference::address_of_stack_guard_limit())); | |
| 1481 __ ldr(ip, MemOperand(ip)); | |
| 1482 __ cmp(sp, Operand(ip)); | |
| 1483 __ b(hs, &within_limit); | |
| 1484 // Do tail-call to runtime routine. | |
| 1485 __ push(r0); | |
| 1486 __ TailCallRuntime(ExternalReference(Runtime::kStackGuard), 1); | |
| 1487 __ bind(&within_limit); | |
| 1488 | |
| 1489 masm->StubReturn(1); | |
| 1490 } | |
| 1491 | |
| 1492 | |
| 1493 void UnarySubStub::Generate(MacroAssembler* masm) { | |
| 1494 Label undo; | |
| 1495 Label slow; | |
| 1496 Label done; | |
| 1497 | |
| 1498 // Enter runtime system if the value is not a smi. | |
| 1499 __ tst(r0, Operand(kSmiTagMask)); | |
| 1500 __ b(ne, &slow); | |
| 1501 | |
| 1502 // Enter runtime system if the value of the expression is zero | |
| 1503 // to make sure that we switch between 0 and -0. | |
| 1504 __ cmp(r0, Operand(0)); | |
| 1505 __ b(eq, &slow); | |
| 1506 | |
| 1507 // The value of the expression is a smi that is not zero. Try | |
| 1508 // optimistic subtraction '0 - value'. | |
| 1509 __ rsb(r1, r0, Operand(0), SetCC); | |
| 1510 __ b(vs, &slow); | |
| 1511 | |
| 1512 // If result is a smi we are done. | |
| 1513 __ tst(r1, Operand(kSmiTagMask)); | |
| 1514 __ mov(r0, Operand(r1), LeaveCC, eq); // conditionally set r0 to result | |
| 1515 __ b(eq, &done); | |
| 1516 | |
| 1517 // Enter runtime system. | |
| 1518 __ bind(&slow); | |
| 1519 __ push(r0); | |
| 1520 __ mov(r0, Operand(0)); // set number of arguments | |
| 1521 __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_JS); | |
| 1522 | |
| 1523 __ bind(&done); | |
| 1524 masm->StubReturn(1); | |
| 1525 } | |
| 1526 | |
| 1527 | |
| 1528 class InvokeBuiltinStub : public CodeStub { | 937 class InvokeBuiltinStub : public CodeStub { |
| 1529 public: | 938 public: |
| 1530 enum Kind { Inc, Dec, ToNumber }; | 939 enum Kind { Inc, Dec, ToNumber }; |
| 1531 InvokeBuiltinStub(Kind kind, int argc) : kind_(kind), argc_(argc) { } | 940 InvokeBuiltinStub(Kind kind, int argc) : kind_(kind), argc_(argc) { } |
| 1532 | 941 |
| 1533 private: | 942 private: |
| 1534 Kind kind_; | 943 Kind kind_; |
| 1535 int argc_; | 944 int argc_; |
| 1536 | 945 |
| 1537 Major MajorKey() { return InvokeBuiltin; } | 946 Major MajorKey() { return InvokeBuiltin; } |
| 1538 int MinorKey() { return (argc_ << 3) | static_cast<int>(kind_); } | 947 int MinorKey() { return (argc_ << 3) | static_cast<int>(kind_); } |
| 1539 void Generate(MacroAssembler* masm); | 948 void Generate(MacroAssembler* masm); |
| 1540 | 949 |
| 1541 const char* GetName() { return "InvokeBuiltinStub"; } | 950 const char* GetName() { return "InvokeBuiltinStub"; } |
| 1542 | 951 |
| 1543 #ifdef DEBUG | 952 #ifdef DEBUG |
| 1544 void Print() { | 953 void Print() { |
| 1545 PrintF("InvokeBuiltinStub (kind %d, argc, %d)\n", | 954 PrintF("InvokeBuiltinStub (kind %d, argc, %d)\n", |
| 1546 static_cast<int>(kind_), | 955 static_cast<int>(kind_), |
| 1547 argc_); | 956 argc_); |
| 1548 } | 957 } |
| 1549 #endif | 958 #endif |
| 1550 }; | 959 }; |
| 1551 | 960 |
| 1552 | 961 |
| 1553 void InvokeBuiltinStub::Generate(MacroAssembler* masm) { | |
| 1554 __ push(r0); | |
| 1555 __ mov(r0, Operand(0)); // set number of arguments | |
| 1556 switch (kind_) { | |
| 1557 case ToNumber: __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_JS); break; | |
| 1558 case Inc: __ InvokeBuiltin(Builtins::INC, JUMP_JS); break; | |
| 1559 case Dec: __ InvokeBuiltin(Builtins::DEC, JUMP_JS); break; | |
| 1560 default: UNREACHABLE(); | |
| 1561 } | |
| 1562 masm->StubReturn(argc_); | |
| 1563 } | |
| 1564 | |
| 1565 | |
| 1566 void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { | |
| 1567 // r0 holds exception | |
| 1568 ASSERT(StackHandlerConstants::kSize == 6 * kPointerSize); // adjust this code | |
| 1569 __ mov(r3, Operand(ExternalReference(Top::k_handler_address))); | |
| 1570 __ ldr(sp, MemOperand(r3)); | |
| 1571 __ pop(r2); // pop next in chain | |
| 1572 __ str(r2, MemOperand(r3)); | |
| 1573 // restore parameter- and frame-pointer and pop state. | |
| 1574 __ ldm(ia_w, sp, r3.bit() | pp.bit() | fp.bit()); | |
| 1575 // Before returning we restore the context from the frame pointer if not NULL. | |
| 1576 // The frame pointer is NULL in the exception handler of a JS entry frame. | |
| 1577 __ cmp(fp, Operand(0)); | |
| 1578 // Set cp to NULL if fp is NULL. | |
| 1579 __ mov(cp, Operand(0), LeaveCC, eq); | |
| 1580 // Restore cp otherwise. | |
| 1581 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne); | |
| 1582 if (kDebug && FLAG_debug_code) __ mov(lr, Operand(pc)); | |
| 1583 __ pop(pc); | |
| 1584 } | |
| 1585 | |
| 1586 | |
| 1587 void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { | |
| 1588 // Fetch top stack handler. | |
| 1589 __ mov(r3, Operand(ExternalReference(Top::k_handler_address))); | |
| 1590 __ ldr(r3, MemOperand(r3)); | |
| 1591 | |
| 1592 // Unwind the handlers until the ENTRY handler is found. | |
| 1593 Label loop, done; | |
| 1594 __ bind(&loop); | |
| 1595 // Load the type of the current stack handler. | |
| 1596 const int kStateOffset = StackHandlerConstants::kAddressDisplacement + | |
| 1597 StackHandlerConstants::kStateOffset; | |
| 1598 __ ldr(r2, MemOperand(r3, kStateOffset)); | |
| 1599 __ cmp(r2, Operand(StackHandler::ENTRY)); | |
| 1600 __ b(eq, &done); | |
| 1601 // Fetch the next handler in the list. | |
| 1602 const int kNextOffset = StackHandlerConstants::kAddressDisplacement + | |
| 1603 StackHandlerConstants::kNextOffset; | |
| 1604 __ ldr(r3, MemOperand(r3, kNextOffset)); | |
| 1605 __ jmp(&loop); | |
| 1606 __ bind(&done); | |
| 1607 | |
| 1608 // Set the top handler address to next handler past the current ENTRY handler. | |
| 1609 __ ldr(r0, MemOperand(r3, kNextOffset)); | |
| 1610 __ mov(r2, Operand(ExternalReference(Top::k_handler_address))); | |
| 1611 __ str(r0, MemOperand(r2)); | |
| 1612 | |
| 1613 // Set external caught exception to false. | |
| 1614 __ mov(r0, Operand(false)); | |
| 1615 ExternalReference external_caught(Top::k_external_caught_exception_address); | |
| 1616 __ mov(r2, Operand(external_caught)); | |
| 1617 __ str(r0, MemOperand(r2)); | |
| 1618 | |
| 1619 // Set pending exception and r0 to out of memory exception. | |
| 1620 Failure* out_of_memory = Failure::OutOfMemoryException(); | |
| 1621 __ mov(r0, Operand(reinterpret_cast<int32_t>(out_of_memory))); | |
| 1622 __ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address))); | |
| 1623 __ str(r0, MemOperand(r2)); | |
| 1624 | |
| 1625 // Restore the stack to the address of the ENTRY handler | |
| 1626 __ mov(sp, Operand(r3)); | |
| 1627 | |
| 1628 // restore parameter- and frame-pointer and pop state. | |
| 1629 __ ldm(ia_w, sp, r3.bit() | pp.bit() | fp.bit()); | |
| 1630 // Before returning we restore the context from the frame pointer if not NULL. | |
| 1631 // The frame pointer is NULL in the exception handler of a JS entry frame. | |
| 1632 __ cmp(fp, Operand(0)); | |
| 1633 // Set cp to NULL if fp is NULL. | |
| 1634 __ mov(cp, Operand(0), LeaveCC, eq); | |
| 1635 // Restore cp otherwise. | |
| 1636 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne); | |
| 1637 if (kDebug && FLAG_debug_code) __ mov(lr, Operand(pc)); | |
| 1638 __ pop(pc); | |
| 1639 } | |
| 1640 | |
| 1641 | |
| 1642 void CEntryStub::GenerateCore(MacroAssembler* masm, | |
| 1643 Label* throw_normal_exception, | |
| 1644 Label* throw_out_of_memory_exception, | |
| 1645 StackFrame::Type frame_type, | |
| 1646 bool do_gc) { | |
| 1647 // r0: result parameter for PerformGC, if any | |
| 1648 // r4: number of arguments including receiver (C callee-saved) | |
| 1649 // r5: pointer to builtin function (C callee-saved) | |
| 1650 // r6: pointer to the first argument (C callee-saved) | |
| 1651 | |
| 1652 if (do_gc) { | |
| 1653 // Passing r0. | |
| 1654 __ Call(FUNCTION_ADDR(Runtime::PerformGC), RelocInfo::RUNTIME_ENTRY); | |
| 1655 } | |
| 1656 | |
| 1657 // Call C built-in. | |
| 1658 // r0 = argc, r1 = argv | |
| 1659 __ mov(r0, Operand(r4)); | |
| 1660 __ mov(r1, Operand(r6)); | |
| 1661 | |
| 1662 // TODO(1242173): To let the GC traverse the return address of the exit | |
| 1663 // frames, we need to know where the return address is. Right now, | |
| 1664 // we push it on the stack to be able to find it again, but we never | |
| 1665 // restore from it in case of changes, which makes it impossible to | |
| 1666 // support moving the C entry code stub. This should be fixed, but currently | |
| 1667 // this is OK because the CEntryStub gets generated so early in the V8 boot | |
| 1668 // sequence that it is not moving ever. | |
| 1669 __ add(lr, pc, Operand(4)); // compute return address: (pc + 8) + 4 | |
| 1670 __ push(lr); | |
| 1671 #if !defined(__arm__) | |
| 1672 // Notify the simulator of the transition to C code. | |
| 1673 __ swi(assembler::arm::call_rt_r5); | |
| 1674 #else /* !defined(__arm__) */ | |
| 1675 __ mov(pc, Operand(r5)); | |
| 1676 #endif /* !defined(__arm__) */ | |
| 1677 // result is in r0 or r0:r1 - do not destroy these registers! | |
| 1678 | |
| 1679 // check for failure result | |
| 1680 Label failure_returned; | |
| 1681 ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0); | |
| 1682 // Lower 2 bits of r2 are 0 iff r0 has failure tag. | |
| 1683 __ add(r2, r0, Operand(1)); | |
| 1684 __ tst(r2, Operand(kFailureTagMask)); | |
| 1685 __ b(eq, &failure_returned); | |
| 1686 | |
| 1687 // Exit C frame and return. | |
| 1688 // r0:r1: result | |
| 1689 // sp: stack pointer | |
| 1690 // fp: frame pointer | |
| 1691 // pp: caller's parameter pointer pp (restored as C callee-saved) | |
| 1692 __ LeaveExitFrame(frame_type); | |
| 1693 | |
| 1694 // check if we should retry or throw exception | |
| 1695 Label retry; | |
| 1696 __ bind(&failure_returned); | |
| 1697 ASSERT(Failure::RETRY_AFTER_GC == 0); | |
| 1698 __ tst(r0, Operand(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize)); | |
| 1699 __ b(eq, &retry); | |
| 1700 | |
| 1701 Label continue_exception; | |
| 1702 // If the returned failure is EXCEPTION then promote Top::pending_exception(). | |
| 1703 __ cmp(r0, Operand(reinterpret_cast<int32_t>(Failure::Exception()))); | |
| 1704 __ b(ne, &continue_exception); | |
| 1705 | |
| 1706 // Retrieve the pending exception and clear the variable. | |
| 1707 __ mov(ip, Operand(Factory::the_hole_value().location())); | |
| 1708 __ ldr(r3, MemOperand(ip)); | |
| 1709 __ mov(ip, Operand(Top::pending_exception_address())); | |
| 1710 __ ldr(r0, MemOperand(ip)); | |
| 1711 __ str(r3, MemOperand(ip)); | |
| 1712 | |
| 1713 __ bind(&continue_exception); | |
| 1714 // Special handling of out of memory exception. | |
| 1715 Failure* out_of_memory = Failure::OutOfMemoryException(); | |
| 1716 __ cmp(r0, Operand(reinterpret_cast<int32_t>(out_of_memory))); | |
| 1717 __ b(eq, throw_out_of_memory_exception); | |
| 1718 | |
| 1719 // Handle normal exception. | |
| 1720 __ jmp(throw_normal_exception); | |
| 1721 | |
| 1722 __ bind(&retry); // pass last failure (r0) as parameter (r0) when retrying | |
| 1723 } | |
| 1724 | |
| 1725 | |
| 1726 void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { | |
| 1727 // Called from JavaScript; parameters are on stack as if calling JS function | |
| 1728 // r0: number of arguments including receiver | |
| 1729 // r1: pointer to builtin function | |
| 1730 // fp: frame pointer (restored after C call) | |
| 1731 // sp: stack pointer (restored as callee's pp after C call) | |
| 1732 // cp: current context (C callee-saved) | |
| 1733 // pp: caller's parameter pointer pp (C callee-saved) | |
| 1734 | |
| 1735 // NOTE: Invocations of builtins may return failure objects | |
| 1736 // instead of a proper result. The builtin entry handles | |
| 1737 // this by performing a garbage collection and retrying the | |
| 1738 // builtin once. | |
| 1739 | |
| 1740 StackFrame::Type frame_type = is_debug_break | |
| 1741 ? StackFrame::EXIT_DEBUG | |
| 1742 : StackFrame::EXIT; | |
| 1743 | |
| 1744 // Enter the exit frame that transitions from JavaScript to C++. | |
| 1745 __ EnterExitFrame(frame_type); | |
| 1746 | |
| 1747 // r4: number of arguments (C callee-saved) | |
| 1748 // r5: pointer to builtin function (C callee-saved) | |
| 1749 // r6: pointer to first argument (C callee-saved) | |
| 1750 | |
| 1751 Label throw_out_of_memory_exception; | |
| 1752 Label throw_normal_exception; | |
| 1753 | |
| 1754 #ifdef DEBUG | |
| 1755 if (FLAG_gc_greedy) { | |
| 1756 Failure* failure = Failure::RetryAfterGC(0, NEW_SPACE); | |
| 1757 __ mov(r0, Operand(reinterpret_cast<intptr_t>(failure))); | |
| 1758 } | |
| 1759 GenerateCore(masm, | |
| 1760 &throw_normal_exception, | |
| 1761 &throw_out_of_memory_exception, | |
| 1762 frame_type, | |
| 1763 FLAG_gc_greedy); | |
| 1764 #else | |
| 1765 GenerateCore(masm, | |
| 1766 &throw_normal_exception, | |
| 1767 &throw_out_of_memory_exception, | |
| 1768 frame_type, | |
| 1769 false); | |
| 1770 #endif | |
| 1771 GenerateCore(masm, | |
| 1772 &throw_normal_exception, | |
| 1773 &throw_out_of_memory_exception, | |
| 1774 frame_type, | |
| 1775 true); | |
| 1776 | |
| 1777 __ bind(&throw_out_of_memory_exception); | |
| 1778 GenerateThrowOutOfMemory(masm); | |
| 1779 // control flow for generated will not return. | |
| 1780 | |
| 1781 __ bind(&throw_normal_exception); | |
| 1782 GenerateThrowTOS(masm); | |
| 1783 } | |
| 1784 | |
| 1785 | |
| 1786 void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { | |
| 1787 // r0: code entry | |
| 1788 // r1: function | |
| 1789 // r2: receiver | |
| 1790 // r3: argc | |
| 1791 // [sp+0]: argv | |
| 1792 | |
| 1793 Label invoke, exit; | |
| 1794 | |
| 1795 // Called from C, so do not pop argc and args on exit (preserve sp) | |
| 1796 // No need to save register-passed args | |
| 1797 // Save callee-saved registers (incl. cp, pp, and fp), sp, and lr | |
| 1798 __ stm(db_w, sp, kCalleeSaved | lr.bit()); | |
| 1799 | |
| 1800 // Get address of argv, see stm above. | |
| 1801 // r0: code entry | |
| 1802 // r1: function | |
| 1803 // r2: receiver | |
| 1804 // r3: argc | |
| 1805 __ add(r4, sp, Operand((kNumCalleeSaved + 1)*kPointerSize)); | |
| 1806 __ ldr(r4, MemOperand(r4)); // argv | |
| 1807 | |
| 1808 // Push a frame with special values setup to mark it as an entry frame. | |
| 1809 // r0: code entry | |
| 1810 // r1: function | |
| 1811 // r2: receiver | |
| 1812 // r3: argc | |
| 1813 // r4: argv | |
| 1814 int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY; | |
| 1815 __ mov(r8, Operand(-1)); // Push a bad frame pointer to fail if it is used. | |
| 1816 __ mov(r7, Operand(~ArgumentsAdaptorFrame::SENTINEL)); | |
| 1817 __ mov(r6, Operand(Smi::FromInt(marker))); | |
| 1818 __ mov(r5, Operand(ExternalReference(Top::k_c_entry_fp_address))); | |
| 1819 __ ldr(r5, MemOperand(r5)); | |
| 1820 __ stm(db_w, sp, r5.bit() | r6.bit() | r7.bit() | r8.bit()); | |
| 1821 | |
| 1822 // Setup frame pointer for the frame to be pushed. | |
| 1823 __ add(fp, sp, Operand(-EntryFrameConstants::kCallerFPOffset)); | |
| 1824 | |
| 1825 // Call a faked try-block that does the invoke. | |
| 1826 __ bl(&invoke); | |
| 1827 | |
| 1828 // Caught exception: Store result (exception) in the pending | |
| 1829 // exception field in the JSEnv and return a failure sentinel. | |
| 1830 // Coming in here the fp will be invalid because the PushTryHandler below | |
| 1831 // sets it to 0 to signal the existence of the JSEntry frame. | |
| 1832 __ mov(ip, Operand(Top::pending_exception_address())); | |
| 1833 __ str(r0, MemOperand(ip)); | |
| 1834 __ mov(r0, Operand(Handle<Failure>(Failure::Exception()))); | |
| 1835 __ b(&exit); | |
| 1836 | |
| 1837 // Invoke: Link this frame into the handler chain. | |
| 1838 __ bind(&invoke); | |
| 1839 // Must preserve r0-r4, r5-r7 are available. | |
| 1840 __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER); | |
| 1841 // If an exception not caught by another handler occurs, this handler returns | |
| 1842 // control to the code after the bl(&invoke) above, which restores all | |
| 1843 // kCalleeSaved registers (including cp, pp and fp) to their saved values | |
| 1844 // before returning a failure to C. | |
| 1845 | |
| 1846 // Clear any pending exceptions. | |
| 1847 __ mov(ip, Operand(ExternalReference::the_hole_value_location())); | |
| 1848 __ ldr(r5, MemOperand(ip)); | |
| 1849 __ mov(ip, Operand(Top::pending_exception_address())); | |
| 1850 __ str(r5, MemOperand(ip)); | |
| 1851 | |
| 1852 // Invoke the function by calling through JS entry trampoline builtin. | |
| 1853 // Notice that we cannot store a reference to the trampoline code directly in | |
| 1854 // this stub, because runtime stubs are not traversed when doing GC. | |
| 1855 | |
| 1856 // Expected registers by Builtins::JSEntryTrampoline | |
| 1857 // r0: code entry | |
| 1858 // r1: function | |
| 1859 // r2: receiver | |
| 1860 // r3: argc | |
| 1861 // r4: argv | |
| 1862 if (is_construct) { | |
| 1863 ExternalReference construct_entry(Builtins::JSConstructEntryTrampoline); | |
| 1864 __ mov(ip, Operand(construct_entry)); | |
| 1865 } else { | |
| 1866 ExternalReference entry(Builtins::JSEntryTrampoline); | |
| 1867 __ mov(ip, Operand(entry)); | |
| 1868 } | |
| 1869 __ ldr(ip, MemOperand(ip)); // deref address | |
| 1870 | |
| 1871 // Branch and link to JSEntryTrampoline | |
| 1872 __ mov(lr, Operand(pc)); | |
| 1873 __ add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag)); | |
| 1874 | |
| 1875 // Unlink this frame from the handler chain. When reading the | |
| 1876 // address of the next handler, there is no need to use the address | |
| 1877 // displacement since the current stack pointer (sp) points directly | |
| 1878 // to the stack handler. | |
| 1879 __ ldr(r3, MemOperand(sp, StackHandlerConstants::kNextOffset)); | |
| 1880 __ mov(ip, Operand(ExternalReference(Top::k_handler_address))); | |
| 1881 __ str(r3, MemOperand(ip)); | |
| 1882 // No need to restore registers | |
| 1883 __ add(sp, sp, Operand(StackHandlerConstants::kSize)); | |
| 1884 | |
| 1885 __ bind(&exit); // r0 holds result | |
| 1886 // Restore the top frame descriptors from the stack. | |
| 1887 __ pop(r3); | |
| 1888 __ mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address))); | |
| 1889 __ str(r3, MemOperand(ip)); | |
| 1890 | |
| 1891 // Reset the stack to the callee saved registers. | |
| 1892 __ add(sp, sp, Operand(-EntryFrameConstants::kCallerFPOffset)); | |
| 1893 | |
| 1894 // Restore callee-saved registers and return. | |
| 1895 #ifdef DEBUG | |
| 1896 if (FLAG_debug_code) __ mov(lr, Operand(pc)); | |
| 1897 #endif | |
| 1898 __ ldm(ia_w, sp, kCalleeSaved | pc.bit()); | |
| 1899 } | |
| 1900 | |
| 1901 | |
| 1902 class ArgumentsAccessStub: public CodeStub { | 962 class ArgumentsAccessStub: public CodeStub { |
| 1903 public: | 963 public: |
| 1904 explicit ArgumentsAccessStub(bool is_length) : is_length_(is_length) { } | 964 explicit ArgumentsAccessStub(bool is_length) : is_length_(is_length) { } |
| 1905 | 965 |
| 1906 private: | 966 private: |
| 1907 bool is_length_; | 967 bool is_length_; |
| 1908 | 968 |
| 1909 Major MajorKey() { return ArgumentsAccess; } | 969 Major MajorKey() { return ArgumentsAccess; } |
| 1910 int MinorKey() { return is_length_ ? 1 : 0; } | 970 int MinorKey() { return is_length_ ? 1 : 0; } |
| 1911 void Generate(MacroAssembler* masm); | 971 void Generate(MacroAssembler* masm); |
| 1912 | 972 |
| 1913 const char* GetName() { return "ArgumentsAccessStub"; } | 973 const char* GetName() { return "ArgumentsAccessStub"; } |
| 1914 | 974 |
| 1915 #ifdef DEBUG | 975 #ifdef DEBUG |
| 1916 void Print() { | 976 void Print() { |
| 1917 PrintF("ArgumentsAccessStub (is_length %s)\n", | 977 PrintF("ArgumentsAccessStub (is_length %s)\n", |
| 1918 is_length_ ? "true" : "false"); | 978 is_length_ ? "true" : "false"); |
| 1919 } | 979 } |
| 1920 #endif | 980 #endif |
| 1921 }; | 981 }; |
| 1922 | 982 |
| 1923 | 983 |
| 1924 void ArgumentsAccessStub::Generate(MacroAssembler* masm) { | |
| 1925 // ----------- S t a t e ------------- | |
| 1926 // -- r0: formal number of parameters for the calling function | |
| 1927 // -- r1: key (if value access) | |
| 1928 // -- lr: return address | |
| 1929 // ----------------------------------- | |
| 1930 | |
| 1931 // Check that the key is a smi for non-length accesses. | |
| 1932 Label slow; | |
| 1933 if (!is_length_) { | |
| 1934 __ tst(r1, Operand(kSmiTagMask)); | |
| 1935 __ b(ne, &slow); | |
| 1936 } | |
| 1937 | |
| 1938 // Check if the calling frame is an arguments adaptor frame. | |
| 1939 // r0: formal number of parameters | |
| 1940 // r1: key (if access) | |
| 1941 Label adaptor; | |
| 1942 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); | |
| 1943 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset)); | |
| 1944 __ cmp(r3, Operand(ArgumentsAdaptorFrame::SENTINEL)); | |
| 1945 __ b(eq, &adaptor); | |
| 1946 | |
| 1947 static const int kParamDisplacement = | |
| 1948 StandardFrameConstants::kCallerSPOffset - kPointerSize; | |
| 1949 | |
| 1950 if (is_length_) { | |
| 1951 // Nothing to do: the formal length of parameters has been passed in r0 | |
| 1952 // by the calling function. | |
| 1953 } else { | |
| 1954 // Check index against formal parameter count. Use unsigned comparison to | |
| 1955 // get the negative check for free. | |
| 1956 // r0: formal number of parameters | |
| 1957 // r1: index | |
| 1958 __ cmp(r1, r0); | |
| 1959 __ b(cs, &slow); | |
| 1960 | |
| 1961 // Read the argument from the current frame. | |
| 1962 __ sub(r3, r0, r1); | |
| 1963 __ add(r3, fp, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize)); | |
| 1964 __ ldr(r0, MemOperand(r3, kParamDisplacement)); | |
| 1965 } | |
| 1966 | |
| 1967 // Return to the calling function. | |
| 1968 __ mov(pc, lr); | |
| 1969 | |
| 1970 // An arguments adaptor frame is present. Find the length or the actual | |
| 1971 // argument in the calling frame. | |
| 1972 // r0: formal number of parameters | |
| 1973 // r1: key | |
| 1974 // r2: adaptor frame pointer | |
| 1975 __ bind(&adaptor); | |
| 1976 // Read the arguments length from the adaptor frame. This is the result if | |
| 1977 // only accessing the length, otherwise it is used in accessing the value | |
| 1978 __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset)); | |
| 1979 | |
| 1980 if (!is_length_) { | |
| 1981 // Check index against actual arguments count. Use unsigned comparison to | |
| 1982 // get the negative check for free. | |
| 1983 // r0: actual number of parameter | |
| 1984 // r1: index | |
| 1985 // r2: adaptor frame point | |
| 1986 __ cmp(r1, r0); | |
| 1987 __ b(cs, &slow); | |
| 1988 | |
| 1989 // Read the argument from the adaptor frame. | |
| 1990 __ sub(r3, r0, r1); | |
| 1991 __ add(r3, r2, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize)); | |
| 1992 __ ldr(r0, MemOperand(r3, kParamDisplacement)); | |
| 1993 } | |
| 1994 | |
| 1995 // Return to the calling function. | |
| 1996 __ mov(pc, lr); | |
| 1997 | |
| 1998 if (!is_length_) { | |
| 1999 __ bind(&slow); | |
| 2000 __ push(r1); | |
| 2001 __ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1); | |
| 2002 } | |
| 2003 } | |
| 2004 | |
| 2005 | |
| 2006 #undef __ | |
| 2007 #define __ masm_-> | |
| 2008 | |
| 2009 void ArmCodeGenerator::GetReferenceProperty(Expression* key) { | 984 void ArmCodeGenerator::GetReferenceProperty(Expression* key) { |
| 2010 ASSERT(!ref()->is_illegal()); | 985 ASSERT(!ref()->is_illegal()); |
| 2011 Reference::Type type = ref()->type(); | 986 Reference::Type type = ref()->type(); |
| 2012 | 987 |
| 2013 // TODO(1241834): Make sure that this it is safe to ignore the distinction | 988 // TODO(1241834): Make sure that this it is safe to ignore the distinction |
| 2014 // between access types LOAD and LOAD_TYPEOF_EXPR. If there is a chance | 989 // between access types LOAD and LOAD_TYPEOF_EXPR. If there is a chance |
| 2015 // that reference errors can be thrown below, we must distinguish between | 990 // that reference errors can be thrown below, we must distinguish between |
| 2016 // the two kinds of loads (typeof expression loads must not throw a | 991 // the two kinds of loads (typeof expression loads must not throw a |
| 2017 // reference error). | 992 // reference error). |
| 2018 if (type == Reference::NAMED) { | 993 if (type == Reference::NAMED) { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 2037 ASSERT(type == Reference::KEYED); | 1012 ASSERT(type == Reference::KEYED); |
| 2038 | 1013 |
| 2039 // TODO(1224671): Implement inline caching for keyed loads as on ia32. | 1014 // TODO(1224671): Implement inline caching for keyed loads as on ia32. |
| 2040 GetPropertyStub stub; | 1015 GetPropertyStub stub; |
| 2041 __ CallStub(&stub); | 1016 __ CallStub(&stub); |
| 2042 } | 1017 } |
| 2043 __ push(r0); | 1018 __ push(r0); |
| 2044 } | 1019 } |
| 2045 | 1020 |
| 2046 | 1021 |
| 2047 #undef __ | |
| 2048 #define __ masm-> | |
| 2049 | |
| 2050 void ArmCodeGenerator::SetReferenceProperty(CodeGenerator* cgen, | |
| 2051 Reference* ref, | |
| 2052 Expression* key) { | |
| 2053 ASSERT(!ref->is_illegal()); | |
| 2054 MacroAssembler* masm = cgen->masm(); | |
| 2055 | |
| 2056 if (ref->type() == Reference::NAMED) { | |
| 2057 // Compute the name of the property. | |
| 2058 Literal* literal = key->AsLiteral(); | |
| 2059 Handle<String> name(String::cast(*literal->handle())); | |
| 2060 | |
| 2061 // Call the appropriate IC code. | |
| 2062 masm->pop(r0); // value | |
| 2063 // Setup the name register. | |
| 2064 masm->mov(r2, Operand(name)); | |
| 2065 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); | |
| 2066 masm->Call(ic, RelocInfo::CODE_TARGET); | |
| 2067 | |
| 2068 } else { | |
| 2069 // Access keyed property. | |
| 2070 ASSERT(ref->type() == Reference::KEYED); | |
| 2071 | |
| 2072 masm->pop(r0); // value | |
| 2073 SetPropertyStub stub; | |
| 2074 masm->CallStub(&stub); | |
| 2075 } | |
| 2076 masm->push(r0); | |
| 2077 } | |
| 2078 | |
| 2079 | |
| 2080 #undef __ | |
| 2081 #define __ masm_-> | |
| 2082 | |
| 2083 void ArmCodeGenerator::GenericBinaryOperation(Token::Value op) { | 1022 void ArmCodeGenerator::GenericBinaryOperation(Token::Value op) { |
| 2084 // sp[0] : y | 1023 // sp[0] : y |
| 2085 // sp[1] : x | 1024 // sp[1] : x |
| 2086 // result : r0 | 1025 // result : r0 |
| 2087 | 1026 |
| 2088 // Stub is entered with a call: 'return address' is in lr. | 1027 // Stub is entered with a call: 'return address' is in lr. |
| 2089 switch (op) { | 1028 switch (op) { |
| 2090 case Token::ADD: // fall through. | 1029 case Token::ADD: // fall through. |
| 2091 case Token::SUB: // fall through. | 1030 case Token::SUB: // fall through. |
| 2092 case Token::MUL: | 1031 case Token::MUL: |
| (...skipping 322 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2415 | 1354 |
| 2416 #if defined(DEBUG) | 1355 #if defined(DEBUG) |
| 2417 void Print() { PrintF("CallFunctionStub (argc %d)\n", argc_); } | 1356 void Print() { PrintF("CallFunctionStub (argc %d)\n", argc_); } |
| 2418 #endif // defined(DEBUG) | 1357 #endif // defined(DEBUG) |
| 2419 | 1358 |
| 2420 Major MajorKey() { return CallFunction; } | 1359 Major MajorKey() { return CallFunction; } |
| 2421 int MinorKey() { return argc_; } | 1360 int MinorKey() { return argc_; } |
| 2422 }; | 1361 }; |
| 2423 | 1362 |
| 2424 | 1363 |
| 2425 void CallFunctionStub::Generate(MacroAssembler* masm) { | |
| 2426 Label slow; | |
| 2427 // Get the function to call from the stack. | |
| 2428 // function, receiver [, arguments] | |
| 2429 masm->ldr(r1, MemOperand(sp, (argc_ + 1) * kPointerSize)); | |
| 2430 | |
| 2431 // Check that the function is really a JavaScript function. | |
| 2432 // r1: pushed function (to be verified) | |
| 2433 masm->tst(r1, Operand(kSmiTagMask)); | |
| 2434 masm->b(eq, &slow); | |
| 2435 // Get the map of the function object. | |
| 2436 masm->ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); | |
| 2437 masm->ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); | |
| 2438 masm->cmp(r2, Operand(JS_FUNCTION_TYPE)); | |
| 2439 masm->b(ne, &slow); | |
| 2440 | |
| 2441 // Fast-case: Invoke the function now. | |
| 2442 // r1: pushed function | |
| 2443 ParameterCount actual(argc_); | |
| 2444 masm->InvokeFunction(r1, actual, JUMP_FUNCTION); | |
| 2445 | |
| 2446 // Slow-case: Non-function called. | |
| 2447 masm->bind(&slow); | |
| 2448 masm->mov(r0, Operand(argc_)); // Setup the number of arguments. | |
| 2449 masm->InvokeBuiltin(Builtins::CALL_NON_FUNCTION, JUMP_JS); | |
| 2450 } | |
| 2451 | |
| 2452 | |
| 2453 // Call the function on the stack with the given arguments. | 1364 // Call the function on the stack with the given arguments. |
| 2454 void ArmCodeGenerator::CallWithArguments(ZoneList<Expression*>* args, | 1365 void ArmCodeGenerator::CallWithArguments(ZoneList<Expression*>* args, |
| 2455 int position) { | 1366 int position) { |
| 2456 // Push the arguments ("left-to-right") on the stack. | 1367 // Push the arguments ("left-to-right") on the stack. |
| 2457 for (int i = 0; i < args->length(); i++) { | 1368 for (int i = 0; i < args->length(); i++) { |
| 2458 Load(args->at(i)); | 1369 Load(args->at(i)); |
| 2459 } | 1370 } |
| 2460 | 1371 |
| 2461 // Record the position for debugging purposes. | 1372 // Record the position for debugging purposes. |
| 2462 __ RecordPosition(position); | 1373 __ RecordPosition(position); |
| (...skipping 2062 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4525 | 3436 |
| 4526 void ArmCodeGenerator::ExitJSFrame() { | 3437 void ArmCodeGenerator::ExitJSFrame() { |
| 4527 // Drop the execution stack down to the frame pointer and restore the caller | 3438 // Drop the execution stack down to the frame pointer and restore the caller |
| 4528 // frame pointer and return address. | 3439 // frame pointer and return address. |
| 4529 __ mov(sp, fp); | 3440 __ mov(sp, fp); |
| 4530 __ ldm(ia_w, sp, fp.bit() | lr.bit()); | 3441 __ ldm(ia_w, sp, fp.bit() | lr.bit()); |
| 4531 } | 3442 } |
| 4532 | 3443 |
| 4533 | 3444 |
| 4534 #undef __ | 3445 #undef __ |
| 4535 | 3446 #define __ masm-> |
| 3447 |
| 3448 MemOperand ArmCodeGenerator::SlotOperand(CodeGenerator* cgen, |
| 3449 Slot* slot, |
| 3450 Register tmp) { |
| 3451 // Currently, this assertion will fail if we try to assign to |
| 3452 // a constant variable that is constant because it is read-only |
| 3453 // (such as the variable referring to a named function expression). |
| 3454 // We need to implement assignments to read-only variables. |
| 3455 // Ideally, we should do this during AST generation (by converting |
| 3456 // such assignments into expression statements); however, in general |
| 3457 // we may not be able to make the decision until past AST generation, |
| 3458 // that is when the entire program is known. |
| 3459 ASSERT(slot != NULL); |
| 3460 int index = slot->index(); |
| 3461 switch (slot->type()) { |
| 3462 case Slot::PARAMETER: |
| 3463 return ParameterOperand(cgen, index); |
| 3464 |
| 3465 case Slot::LOCAL: { |
| 3466 ASSERT(0 <= index && |
| 3467 index < cgen->scope()->num_stack_slots() && |
| 3468 index >= 0); |
| 3469 int local_offset = JavaScriptFrameConstants::kLocal0Offset - |
| 3470 index * kPointerSize; |
| 3471 return MemOperand(fp, local_offset); |
| 3472 } |
| 3473 |
| 3474 case Slot::CONTEXT: { |
| 3475 MacroAssembler* masm = cgen->masm(); |
| 3476 // Follow the context chain if necessary. |
| 3477 ASSERT(!tmp.is(cp)); // do not overwrite context register |
| 3478 Register context = cp; |
| 3479 int chain_length = |
| 3480 cgen->scope()->ContextChainLength(slot->var()->scope()); |
| 3481 for (int i = chain_length; i-- > 0;) { |
| 3482 // Load the closure. |
| 3483 // (All contexts, even 'with' contexts, have a closure, |
| 3484 // and it is the same for all contexts inside a function. |
| 3485 // There is no need to go to the function context first.) |
| 3486 __ ldr(tmp, ContextOperand(context, Context::CLOSURE_INDEX)); |
| 3487 // Load the function context (which is the incoming, outer context). |
| 3488 __ ldr(tmp, FieldMemOperand(tmp, JSFunction::kContextOffset)); |
| 3489 context = tmp; |
| 3490 } |
| 3491 // We may have a 'with' context now. Get the function context. |
| 3492 // (In fact this mov may never be the needed, since the scope analysis |
| 3493 // may not permit a direct context access in this case and thus we are |
| 3494 // always at a function context. However it is safe to dereference be- |
| 3495 // cause the function context of a function context is itself. Before |
| 3496 // deleting this mov we should try to create a counter-example first, |
| 3497 // though...) |
| 3498 __ ldr(tmp, ContextOperand(context, Context::FCONTEXT_INDEX)); |
| 3499 return ContextOperand(tmp, index); |
| 3500 } |
| 3501 |
| 3502 default: |
| 3503 UNREACHABLE(); |
| 3504 return MemOperand(r0, 0); |
| 3505 } |
| 3506 } |
| 3507 |
| 3508 |
| 3509 void Property::GenerateStoreCode(CodeGenerator* cgen, |
| 3510 Reference* ref, |
| 3511 InitState init_state) { |
| 3512 MacroAssembler* masm = cgen->masm(); |
| 3513 Comment cmnt(masm, "[ Store to Property"); |
| 3514 __ RecordPosition(position()); |
| 3515 ArmCodeGenerator::SetReferenceProperty(cgen, ref, key()); |
| 3516 } |
| 3517 |
| 3518 |
| 3519 void VariableProxy::GenerateStoreCode(CodeGenerator* cgen, |
| 3520 Reference* ref, |
| 3521 InitState init_state) { |
| 3522 MacroAssembler* masm = cgen->masm(); |
| 3523 Comment cmnt(masm, "[ Store to VariableProxy"); |
| 3524 Variable* node = var(); |
| 3525 |
| 3526 Expression* expr = node->rewrite(); |
| 3527 if (expr != NULL) { |
| 3528 expr->GenerateStoreCode(cgen, ref, init_state); |
| 3529 } else { |
| 3530 ASSERT(node->is_global()); |
| 3531 if (node->AsProperty() != NULL) { |
| 3532 __ RecordPosition(node->AsProperty()->position()); |
| 3533 } |
| 3534 Expression* key = new Literal(node->name()); |
| 3535 ArmCodeGenerator::SetReferenceProperty(cgen, ref, key); |
| 3536 } |
| 3537 } |
| 3538 |
| 3539 |
| 3540 void Slot::GenerateStoreCode(CodeGenerator* cgen, |
| 3541 Reference* ref, |
| 3542 InitState init_state) { |
| 3543 MacroAssembler* masm = cgen->masm(); |
| 3544 Comment cmnt(masm, "[ Store to Slot"); |
| 3545 |
| 3546 if (type() == Slot::LOOKUP) { |
| 3547 ASSERT(var()->mode() == Variable::DYNAMIC); |
| 3548 |
| 3549 // For now, just do a runtime call. |
| 3550 __ push(cp); |
| 3551 __ mov(r0, Operand(var()->name())); |
| 3552 __ push(r0); |
| 3553 |
| 3554 if (init_state == CONST_INIT) { |
| 3555 // Same as the case for a normal store, but ignores attribute |
| 3556 // (e.g. READ_ONLY) of context slot so that we can initialize const |
| 3557 // properties (introduced via eval("const foo = (some expr);")). Also, |
| 3558 // uses the current function context instead of the top context. |
| 3559 // |
| 3560 // Note that we must declare the foo upon entry of eval(), via a |
| 3561 // context slot declaration, but we cannot initialize it at the same |
| 3562 // time, because the const declaration may be at the end of the eval |
| 3563 // code (sigh...) and the const variable may have been used before |
| 3564 // (where its value is 'undefined'). Thus, we can only do the |
| 3565 // initialization when we actually encounter the expression and when |
| 3566 // the expression operands are defined and valid, and thus we need the |
| 3567 // split into 2 operations: declaration of the context slot followed |
| 3568 // by initialization. |
| 3569 __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); |
| 3570 } else { |
| 3571 __ CallRuntime(Runtime::kStoreContextSlot, 3); |
| 3572 } |
| 3573 // Storing a variable must keep the (new) value on the expression |
| 3574 // stack. This is necessary for compiling assignment expressions. |
| 3575 __ push(r0); |
| 3576 |
| 3577 } else { |
| 3578 ASSERT(var()->mode() != Variable::DYNAMIC); |
| 3579 |
| 3580 Label exit; |
| 3581 if (init_state == CONST_INIT) { |
| 3582 ASSERT(var()->mode() == Variable::CONST); |
| 3583 // Only the first const initialization must be executed (the slot |
| 3584 // still contains 'the hole' value). When the assignment is executed, |
| 3585 // the code is identical to a normal store (see below). |
| 3586 Comment cmnt(masm, "[ Init const"); |
| 3587 __ ldr(r2, ArmCodeGenerator::SlotOperand(cgen, this, r2)); |
| 3588 __ cmp(r2, Operand(Factory::the_hole_value())); |
| 3589 __ b(ne, &exit); |
| 3590 } |
| 3591 |
| 3592 // We must execute the store. |
| 3593 // r2 may be loaded with context; used below in RecordWrite. |
| 3594 // Storing a variable must keep the (new) value on the stack. This is |
| 3595 // necessary for compiling assignment expressions. |
| 3596 // |
| 3597 // Note: We will reach here even with var()->mode() == Variable::CONST |
| 3598 // because of const declarations which will initialize consts to 'the |
| 3599 // hole' value and by doing so, end up calling this code. r2 may be |
| 3600 // loaded with context; used below in RecordWrite. |
| 3601 __ pop(r0); |
| 3602 __ str(r0, ArmCodeGenerator::SlotOperand(cgen, this, r2)); |
| 3603 __ push(r0); |
| 3604 |
| 3605 if (type() == Slot::CONTEXT) { |
| 3606 // Skip write barrier if the written value is a smi. |
| 3607 __ tst(r0, Operand(kSmiTagMask)); |
| 3608 __ b(eq, &exit); |
| 3609 // r2 is loaded with context when calling SlotOperand above. |
| 3610 int offset = FixedArray::kHeaderSize + index() * kPointerSize; |
| 3611 __ mov(r3, Operand(offset)); |
| 3612 __ RecordWrite(r2, r3, r1); |
| 3613 } |
| 3614 // If we definitely did not jump over the assignment, we do not need to |
| 3615 // bind the exit label. Doing so can defeat peephole optimization. |
| 3616 if (init_state == CONST_INIT || type() == Slot::CONTEXT) { |
| 3617 __ bind(&exit); |
| 3618 } |
| 3619 } |
| 3620 } |
| 3621 |
| 3622 |
| 3623 void GetPropertyStub::Generate(MacroAssembler* masm) { |
| 3624 // sp[0]: key |
| 3625 // sp[1]: receiver |
| 3626 Label slow, fast; |
| 3627 // Get the key and receiver object from the stack. |
| 3628 __ ldm(ia, sp, r0.bit() | r1.bit()); |
| 3629 // Check that the key is a smi. |
| 3630 __ tst(r0, Operand(kSmiTagMask)); |
| 3631 __ b(ne, &slow); |
| 3632 __ mov(r0, Operand(r0, ASR, kSmiTagSize)); |
| 3633 // Check that the object isn't a smi. |
| 3634 __ tst(r1, Operand(kSmiTagMask)); |
| 3635 __ b(eq, &slow); |
| 3636 |
| 3637 // Check that the object is some kind of JS object EXCEPT JS Value type. |
| 3638 // In the case that the object is a value-wrapper object, |
| 3639 // we enter the runtime system to make sure that indexing into string |
| 3640 // objects work as intended. |
| 3641 ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE); |
| 3642 __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); |
| 3643 __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); |
| 3644 __ cmp(r2, Operand(JS_OBJECT_TYPE)); |
| 3645 __ b(lt, &slow); |
| 3646 |
| 3647 // Get the elements array of the object. |
| 3648 __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset)); |
| 3649 // Check that the object is in fast mode (not dictionary). |
| 3650 __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); |
| 3651 __ cmp(r3, Operand(Factory::hash_table_map())); |
| 3652 __ b(eq, &slow); |
| 3653 // Check that the key (index) is within bounds. |
| 3654 __ ldr(r3, FieldMemOperand(r1, Array::kLengthOffset)); |
| 3655 __ cmp(r0, Operand(r3)); |
| 3656 __ b(lo, &fast); |
| 3657 |
| 3658 // Slow case: Push extra copies of the arguments (2). |
| 3659 __ bind(&slow); |
| 3660 __ ldm(ia, sp, r0.bit() | r1.bit()); |
| 3661 __ stm(db_w, sp, r0.bit() | r1.bit()); |
| 3662 // Do tail-call to runtime routine. |
| 3663 __ TailCallRuntime(ExternalReference(Runtime::kGetProperty), 2); |
| 3664 |
| 3665 // Fast case: Do the load. |
| 3666 __ bind(&fast); |
| 3667 __ add(r3, r1, Operand(Array::kHeaderSize - kHeapObjectTag)); |
| 3668 __ ldr(r0, MemOperand(r3, r0, LSL, kPointerSizeLog2)); |
| 3669 __ cmp(r0, Operand(Factory::the_hole_value())); |
| 3670 // In case the loaded value is the_hole we have to consult GetProperty |
| 3671 // to ensure the prototype chain is searched. |
| 3672 __ b(eq, &slow); |
| 3673 |
| 3674 __ StubReturn(1); |
| 3675 } |
| 3676 |
| 3677 |
| 3678 void SetPropertyStub::Generate(MacroAssembler* masm) { |
| 3679 // r0 : value |
| 3680 // sp[0] : key |
| 3681 // sp[1] : receiver |
| 3682 |
| 3683 Label slow, fast, array, extra, exit; |
| 3684 // Get the key and the object from the stack. |
| 3685 __ ldm(ia, sp, r1.bit() | r3.bit()); // r1 = key, r3 = receiver |
| 3686 // Check that the key is a smi. |
| 3687 __ tst(r1, Operand(kSmiTagMask)); |
| 3688 __ b(ne, &slow); |
| 3689 // Check that the object isn't a smi. |
| 3690 __ tst(r3, Operand(kSmiTagMask)); |
| 3691 __ b(eq, &slow); |
| 3692 // Get the type of the object from its map. |
| 3693 __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset)); |
| 3694 __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); |
| 3695 // Check if the object is a JS array or not. |
| 3696 __ cmp(r2, Operand(JS_ARRAY_TYPE)); |
| 3697 __ b(eq, &array); |
| 3698 // Check that the object is some kind of JS object. |
| 3699 __ cmp(r2, Operand(FIRST_JS_OBJECT_TYPE)); |
| 3700 __ b(lt, &slow); |
| 3701 |
| 3702 |
| 3703 // Object case: Check key against length in the elements array. |
| 3704 __ ldr(r3, FieldMemOperand(r3, JSObject::kElementsOffset)); |
| 3705 // Check that the object is in fast mode (not dictionary). |
| 3706 __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset)); |
| 3707 __ cmp(r2, Operand(Factory::hash_table_map())); |
| 3708 __ b(eq, &slow); |
| 3709 // Untag the key (for checking against untagged length in the fixed array). |
| 3710 __ mov(r1, Operand(r1, ASR, kSmiTagSize)); |
| 3711 // Compute address to store into and check array bounds. |
| 3712 __ add(r2, r3, Operand(Array::kHeaderSize - kHeapObjectTag)); |
| 3713 __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2)); |
| 3714 __ ldr(ip, FieldMemOperand(r3, Array::kLengthOffset)); |
| 3715 __ cmp(r1, Operand(ip)); |
| 3716 __ b(lo, &fast); |
| 3717 |
| 3718 |
| 3719 // Slow case: Push extra copies of the arguments (3). |
| 3720 __ bind(&slow); |
| 3721 __ ldm(ia, sp, r1.bit() | r3.bit()); // r0 == value, r1 == key, r3 == object |
| 3722 __ stm(db_w, sp, r0.bit() | r1.bit() | r3.bit()); |
| 3723 // Do tail-call to runtime routine. |
| 3724 __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3); |
| 3725 |
| 3726 |
| 3727 // Extra capacity case: Check if there is extra capacity to |
| 3728 // perform the store and update the length. Used for adding one |
| 3729 // element to the array by writing to array[array.length]. |
| 3730 // r0 == value, r1 == key, r2 == elements, r3 == object |
| 3731 __ bind(&extra); |
| 3732 __ b(ne, &slow); // do not leave holes in the array |
| 3733 __ mov(r1, Operand(r1, ASR, kSmiTagSize)); // untag |
| 3734 __ ldr(ip, FieldMemOperand(r2, Array::kLengthOffset)); |
| 3735 __ cmp(r1, Operand(ip)); |
| 3736 __ b(hs, &slow); |
| 3737 __ mov(r1, Operand(r1, LSL, kSmiTagSize)); // restore tag |
| 3738 __ add(r1, r1, Operand(1 << kSmiTagSize)); // and increment |
| 3739 __ str(r1, FieldMemOperand(r3, JSArray::kLengthOffset)); |
| 3740 __ mov(r3, Operand(r2)); |
| 3741 // NOTE: Computing the address to store into must take the fact |
| 3742 // that the key has been incremented into account. |
| 3743 int displacement = Array::kHeaderSize - kHeapObjectTag - |
| 3744 ((1 << kSmiTagSize) * 2); |
| 3745 __ add(r2, r2, Operand(displacement)); |
| 3746 __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize)); |
| 3747 __ b(&fast); |
| 3748 |
| 3749 |
| 3750 // Array case: Get the length and the elements array from the JS |
| 3751 // array. Check that the array is in fast mode; if it is the |
| 3752 // length is always a smi. |
| 3753 // r0 == value, r3 == object |
| 3754 __ bind(&array); |
| 3755 __ ldr(r2, FieldMemOperand(r3, JSObject::kElementsOffset)); |
| 3756 __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset)); |
| 3757 __ cmp(r1, Operand(Factory::hash_table_map())); |
| 3758 __ b(eq, &slow); |
| 3759 |
| 3760 // Check the key against the length in the array, compute the |
| 3761 // address to store into and fall through to fast case. |
| 3762 __ ldr(r1, MemOperand(sp)); |
| 3763 // r0 == value, r1 == key, r2 == elements, r3 == object. |
| 3764 __ ldr(ip, FieldMemOperand(r3, JSArray::kLengthOffset)); |
| 3765 __ cmp(r1, Operand(ip)); |
| 3766 __ b(hs, &extra); |
| 3767 __ mov(r3, Operand(r2)); |
| 3768 __ add(r2, r2, Operand(Array::kHeaderSize - kHeapObjectTag)); |
| 3769 __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize)); |
| 3770 |
| 3771 |
| 3772 // Fast case: Do the store. |
| 3773 // r0 == value, r2 == address to store into, r3 == elements |
| 3774 __ bind(&fast); |
| 3775 __ str(r0, MemOperand(r2)); |
| 3776 // Skip write barrier if the written value is a smi. |
| 3777 __ tst(r0, Operand(kSmiTagMask)); |
| 3778 __ b(eq, &exit); |
| 3779 // Update write barrier for the elements array address. |
| 3780 __ sub(r1, r2, Operand(r3)); |
| 3781 __ RecordWrite(r3, r1, r2); |
| 3782 __ bind(&exit); |
| 3783 __ StubReturn(1); |
| 3784 } |
| 3785 |
| 3786 |
| 3787 void GenericBinaryOpStub::Generate(MacroAssembler* masm) { |
| 3788 // r1 : x |
| 3789 // r0 : y |
| 3790 // result : r0 |
| 3791 |
| 3792 switch (op_) { |
| 3793 case Token::ADD: { |
| 3794 Label slow, exit; |
| 3795 // fast path |
| 3796 __ orr(r2, r1, Operand(r0)); // r2 = x | y; |
| 3797 __ add(r0, r1, Operand(r0), SetCC); // add y optimistically |
| 3798 // go slow-path in case of overflow |
| 3799 __ b(vs, &slow); |
| 3800 // go slow-path in case of non-smi operands |
| 3801 ASSERT(kSmiTag == 0); // adjust code below |
| 3802 __ tst(r2, Operand(kSmiTagMask)); |
| 3803 __ b(eq, &exit); |
| 3804 // slow path |
| 3805 __ bind(&slow); |
| 3806 __ sub(r0, r0, Operand(r1)); // revert optimistic add |
| 3807 __ push(r1); |
| 3808 __ push(r0); |
| 3809 __ mov(r0, Operand(1)); // set number of arguments |
| 3810 __ InvokeBuiltin(Builtins::ADD, JUMP_JS); |
| 3811 // done |
| 3812 __ bind(&exit); |
| 3813 break; |
| 3814 } |
| 3815 |
| 3816 case Token::SUB: { |
| 3817 Label slow, exit; |
| 3818 // fast path |
| 3819 __ orr(r2, r1, Operand(r0)); // r2 = x | y; |
| 3820 __ sub(r3, r1, Operand(r0), SetCC); // subtract y optimistically |
| 3821 // go slow-path in case of overflow |
| 3822 __ b(vs, &slow); |
| 3823 // go slow-path in case of non-smi operands |
| 3824 ASSERT(kSmiTag == 0); // adjust code below |
| 3825 __ tst(r2, Operand(kSmiTagMask)); |
| 3826 __ mov(r0, Operand(r3), LeaveCC, eq); // conditionally set r0 to result |
| 3827 __ b(eq, &exit); |
| 3828 // slow path |
| 3829 __ bind(&slow); |
| 3830 __ push(r1); |
| 3831 __ push(r0); |
| 3832 __ mov(r0, Operand(1)); // set number of arguments |
| 3833 __ InvokeBuiltin(Builtins::SUB, JUMP_JS); |
| 3834 // done |
| 3835 __ bind(&exit); |
| 3836 break; |
| 3837 } |
| 3838 |
| 3839 case Token::MUL: { |
| 3840 Label slow, exit; |
| 3841 // tag check |
| 3842 __ orr(r2, r1, Operand(r0)); // r2 = x | y; |
| 3843 ASSERT(kSmiTag == 0); // adjust code below |
| 3844 __ tst(r2, Operand(kSmiTagMask)); |
| 3845 __ b(ne, &slow); |
| 3846 // remove tag from one operand (but keep sign), so that result is smi |
| 3847 __ mov(ip, Operand(r0, ASR, kSmiTagSize)); |
| 3848 // do multiplication |
| 3849 __ smull(r3, r2, r1, ip); // r3 = lower 32 bits of ip*r1 |
| 3850 // go slow on overflows (overflow bit is not set) |
| 3851 __ mov(ip, Operand(r3, ASR, 31)); |
| 3852 __ cmp(ip, Operand(r2)); // no overflow if higher 33 bits are identical |
| 3853 __ b(ne, &slow); |
| 3854 // go slow on zero result to handle -0 |
| 3855 __ tst(r3, Operand(r3)); |
| 3856 __ mov(r0, Operand(r3), LeaveCC, ne); |
| 3857 __ b(ne, &exit); |
| 3858 // slow case |
| 3859 __ bind(&slow); |
| 3860 __ push(r1); |
| 3861 __ push(r0); |
| 3862 __ mov(r0, Operand(1)); // set number of arguments |
| 3863 __ InvokeBuiltin(Builtins::MUL, JUMP_JS); |
| 3864 // done |
| 3865 __ bind(&exit); |
| 3866 break; |
| 3867 } |
| 3868 |
| 3869 case Token::BIT_OR: |
| 3870 case Token::BIT_AND: |
| 3871 case Token::BIT_XOR: { |
| 3872 Label slow, exit; |
| 3873 // tag check |
| 3874 __ orr(r2, r1, Operand(r0)); // r2 = x | y; |
| 3875 ASSERT(kSmiTag == 0); // adjust code below |
| 3876 __ tst(r2, Operand(kSmiTagMask)); |
| 3877 __ b(ne, &slow); |
| 3878 switch (op_) { |
| 3879 case Token::BIT_OR: __ orr(r0, r0, Operand(r1)); break; |
| 3880 case Token::BIT_AND: __ and_(r0, r0, Operand(r1)); break; |
| 3881 case Token::BIT_XOR: __ eor(r0, r0, Operand(r1)); break; |
| 3882 default: UNREACHABLE(); |
| 3883 } |
| 3884 __ b(&exit); |
| 3885 __ bind(&slow); |
| 3886 __ push(r1); // restore stack |
| 3887 __ push(r0); |
| 3888 __ mov(r0, Operand(1)); // 1 argument (not counting receiver). |
| 3889 switch (op_) { |
| 3890 case Token::BIT_OR: |
| 3891 __ InvokeBuiltin(Builtins::BIT_OR, JUMP_JS); |
| 3892 break; |
| 3893 case Token::BIT_AND: |
| 3894 __ InvokeBuiltin(Builtins::BIT_AND, JUMP_JS); |
| 3895 break; |
| 3896 case Token::BIT_XOR: |
| 3897 __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_JS); |
| 3898 break; |
| 3899 default: |
| 3900 UNREACHABLE(); |
| 3901 } |
| 3902 __ bind(&exit); |
| 3903 break; |
| 3904 } |
| 3905 |
| 3906 case Token::SHL: |
| 3907 case Token::SHR: |
| 3908 case Token::SAR: { |
| 3909 Label slow, exit; |
| 3910 // tag check |
| 3911 __ orr(r2, r1, Operand(r0)); // r2 = x | y; |
| 3912 ASSERT(kSmiTag == 0); // adjust code below |
| 3913 __ tst(r2, Operand(kSmiTagMask)); |
| 3914 __ b(ne, &slow); |
| 3915 // remove tags from operands (but keep sign) |
| 3916 __ mov(r3, Operand(r1, ASR, kSmiTagSize)); // x |
| 3917 __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // y |
| 3918 // use only the 5 least significant bits of the shift count |
| 3919 __ and_(r2, r2, Operand(0x1f)); |
| 3920 // perform operation |
| 3921 switch (op_) { |
| 3922 case Token::SAR: |
| 3923 __ mov(r3, Operand(r3, ASR, r2)); |
| 3924 // no checks of result necessary |
| 3925 break; |
| 3926 |
| 3927 case Token::SHR: |
| 3928 __ mov(r3, Operand(r3, LSR, r2)); |
| 3929 // check that the *unsigned* result fits in a smi |
| 3930 // neither of the two high-order bits can be set: |
| 3931 // - 0x80000000: high bit would be lost when smi tagging |
| 3932 // - 0x40000000: this number would convert to negative when |
| 3933 // smi tagging these two cases can only happen with shifts |
| 3934 // by 0 or 1 when handed a valid smi |
| 3935 __ and_(r2, r3, Operand(0xc0000000), SetCC); |
| 3936 __ b(ne, &slow); |
| 3937 break; |
| 3938 |
| 3939 case Token::SHL: |
| 3940 __ mov(r3, Operand(r3, LSL, r2)); |
| 3941 // check that the *signed* result fits in a smi |
| 3942 __ add(r2, r3, Operand(0x40000000), SetCC); |
| 3943 __ b(mi, &slow); |
| 3944 break; |
| 3945 |
| 3946 default: UNREACHABLE(); |
| 3947 } |
| 3948 // tag result and store it in r0 |
| 3949 ASSERT(kSmiTag == 0); // adjust code below |
| 3950 __ mov(r0, Operand(r3, LSL, kSmiTagSize)); |
| 3951 __ b(&exit); |
| 3952 // slow case |
| 3953 __ bind(&slow); |
| 3954 __ push(r1); // restore stack |
| 3955 __ push(r0); |
| 3956 __ mov(r0, Operand(1)); // 1 argument (not counting receiver). |
| 3957 switch (op_) { |
| 3958 case Token::SAR: __ InvokeBuiltin(Builtins::SAR, JUMP_JS); break; |
| 3959 case Token::SHR: __ InvokeBuiltin(Builtins::SHR, JUMP_JS); break; |
| 3960 case Token::SHL: __ InvokeBuiltin(Builtins::SHL, JUMP_JS); break; |
| 3961 default: UNREACHABLE(); |
| 3962 } |
| 3963 __ bind(&exit); |
| 3964 break; |
| 3965 } |
| 3966 |
| 3967 default: UNREACHABLE(); |
| 3968 } |
| 3969 __ Ret(); |
| 3970 } |
| 3971 |
| 3972 |
| 3973 void StackCheckStub::Generate(MacroAssembler* masm) { |
| 3974 Label within_limit; |
| 3975 __ mov(ip, Operand(ExternalReference::address_of_stack_guard_limit())); |
| 3976 __ ldr(ip, MemOperand(ip)); |
| 3977 __ cmp(sp, Operand(ip)); |
| 3978 __ b(hs, &within_limit); |
| 3979 // Do tail-call to runtime routine. |
| 3980 __ push(r0); |
| 3981 __ TailCallRuntime(ExternalReference(Runtime::kStackGuard), 1); |
| 3982 __ bind(&within_limit); |
| 3983 |
| 3984 __ StubReturn(1); |
| 3985 } |
| 3986 |
| 3987 |
| 3988 void UnarySubStub::Generate(MacroAssembler* masm) { |
| 3989 Label undo; |
| 3990 Label slow; |
| 3991 Label done; |
| 3992 |
| 3993 // Enter runtime system if the value is not a smi. |
| 3994 __ tst(r0, Operand(kSmiTagMask)); |
| 3995 __ b(ne, &slow); |
| 3996 |
| 3997 // Enter runtime system if the value of the expression is zero |
| 3998 // to make sure that we switch between 0 and -0. |
| 3999 __ cmp(r0, Operand(0)); |
| 4000 __ b(eq, &slow); |
| 4001 |
| 4002 // The value of the expression is a smi that is not zero. Try |
| 4003 // optimistic subtraction '0 - value'. |
| 4004 __ rsb(r1, r0, Operand(0), SetCC); |
| 4005 __ b(vs, &slow); |
| 4006 |
| 4007 // If result is a smi we are done. |
| 4008 __ tst(r1, Operand(kSmiTagMask)); |
| 4009 __ mov(r0, Operand(r1), LeaveCC, eq); // conditionally set r0 to result |
| 4010 __ b(eq, &done); |
| 4011 |
| 4012 // Enter runtime system. |
| 4013 __ bind(&slow); |
| 4014 __ push(r0); |
| 4015 __ mov(r0, Operand(0)); // set number of arguments |
| 4016 __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_JS); |
| 4017 |
| 4018 __ bind(&done); |
| 4019 __ StubReturn(1); |
| 4020 } |
| 4021 |
| 4022 |
| 4023 void InvokeBuiltinStub::Generate(MacroAssembler* masm) { |
| 4024 __ push(r0); |
| 4025 __ mov(r0, Operand(0)); // set number of arguments |
| 4026 switch (kind_) { |
| 4027 case ToNumber: __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_JS); break; |
| 4028 case Inc: __ InvokeBuiltin(Builtins::INC, JUMP_JS); break; |
| 4029 case Dec: __ InvokeBuiltin(Builtins::DEC, JUMP_JS); break; |
| 4030 default: UNREACHABLE(); |
| 4031 } |
| 4032 __ StubReturn(argc_); |
| 4033 } |
| 4034 |
| 4035 |
| 4036 void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { |
| 4037 // r0 holds exception |
| 4038 ASSERT(StackHandlerConstants::kSize == 6 * kPointerSize); // adjust this code |
| 4039 __ mov(r3, Operand(ExternalReference(Top::k_handler_address))); |
| 4040 __ ldr(sp, MemOperand(r3)); |
| 4041 __ pop(r2); // pop next in chain |
| 4042 __ str(r2, MemOperand(r3)); |
| 4043 // restore parameter- and frame-pointer and pop state. |
| 4044 __ ldm(ia_w, sp, r3.bit() | pp.bit() | fp.bit()); |
| 4045 // Before returning we restore the context from the frame pointer if not NULL. |
| 4046 // The frame pointer is NULL in the exception handler of a JS entry frame. |
| 4047 __ cmp(fp, Operand(0)); |
| 4048 // Set cp to NULL if fp is NULL. |
| 4049 __ mov(cp, Operand(0), LeaveCC, eq); |
| 4050 // Restore cp otherwise. |
| 4051 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne); |
| 4052 if (kDebug && FLAG_debug_code) __ mov(lr, Operand(pc)); |
| 4053 __ pop(pc); |
| 4054 } |
| 4055 |
| 4056 |
| 4057 void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) { |
| 4058 // Fetch top stack handler. |
| 4059 __ mov(r3, Operand(ExternalReference(Top::k_handler_address))); |
| 4060 __ ldr(r3, MemOperand(r3)); |
| 4061 |
| 4062 // Unwind the handlers until the ENTRY handler is found. |
| 4063 Label loop, done; |
| 4064 __ bind(&loop); |
| 4065 // Load the type of the current stack handler. |
| 4066 const int kStateOffset = StackHandlerConstants::kAddressDisplacement + |
| 4067 StackHandlerConstants::kStateOffset; |
| 4068 __ ldr(r2, MemOperand(r3, kStateOffset)); |
| 4069 __ cmp(r2, Operand(StackHandler::ENTRY)); |
| 4070 __ b(eq, &done); |
| 4071 // Fetch the next handler in the list. |
| 4072 const int kNextOffset = StackHandlerConstants::kAddressDisplacement + |
| 4073 StackHandlerConstants::kNextOffset; |
| 4074 __ ldr(r3, MemOperand(r3, kNextOffset)); |
| 4075 __ jmp(&loop); |
| 4076 __ bind(&done); |
| 4077 |
| 4078 // Set the top handler address to next handler past the current ENTRY handler. |
| 4079 __ ldr(r0, MemOperand(r3, kNextOffset)); |
| 4080 __ mov(r2, Operand(ExternalReference(Top::k_handler_address))); |
| 4081 __ str(r0, MemOperand(r2)); |
| 4082 |
| 4083 // Set external caught exception to false. |
| 4084 __ mov(r0, Operand(false)); |
| 4085 ExternalReference external_caught(Top::k_external_caught_exception_address); |
| 4086 __ mov(r2, Operand(external_caught)); |
| 4087 __ str(r0, MemOperand(r2)); |
| 4088 |
| 4089 // Set pending exception and r0 to out of memory exception. |
| 4090 Failure* out_of_memory = Failure::OutOfMemoryException(); |
| 4091 __ mov(r0, Operand(reinterpret_cast<int32_t>(out_of_memory))); |
| 4092 __ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address))); |
| 4093 __ str(r0, MemOperand(r2)); |
| 4094 |
| 4095 // Restore the stack to the address of the ENTRY handler |
| 4096 __ mov(sp, Operand(r3)); |
| 4097 |
| 4098 // restore parameter- and frame-pointer and pop state. |
| 4099 __ ldm(ia_w, sp, r3.bit() | pp.bit() | fp.bit()); |
| 4100 // Before returning we restore the context from the frame pointer if not NULL. |
| 4101 // The frame pointer is NULL in the exception handler of a JS entry frame. |
| 4102 __ cmp(fp, Operand(0)); |
| 4103 // Set cp to NULL if fp is NULL. |
| 4104 __ mov(cp, Operand(0), LeaveCC, eq); |
| 4105 // Restore cp otherwise. |
| 4106 __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne); |
| 4107 if (kDebug && FLAG_debug_code) __ mov(lr, Operand(pc)); |
| 4108 __ pop(pc); |
| 4109 } |
| 4110 |
| 4111 |
| 4112 void CEntryStub::GenerateCore(MacroAssembler* masm, |
| 4113 Label* throw_normal_exception, |
| 4114 Label* throw_out_of_memory_exception, |
| 4115 StackFrame::Type frame_type, |
| 4116 bool do_gc) { |
| 4117 // r0: result parameter for PerformGC, if any |
| 4118 // r4: number of arguments including receiver (C callee-saved) |
| 4119 // r5: pointer to builtin function (C callee-saved) |
| 4120 // r6: pointer to the first argument (C callee-saved) |
| 4121 |
| 4122 if (do_gc) { |
| 4123 // Passing r0. |
| 4124 __ Call(FUNCTION_ADDR(Runtime::PerformGC), RelocInfo::RUNTIME_ENTRY); |
| 4125 } |
| 4126 |
| 4127 // Call C built-in. |
| 4128 // r0 = argc, r1 = argv |
| 4129 __ mov(r0, Operand(r4)); |
| 4130 __ mov(r1, Operand(r6)); |
| 4131 |
| 4132 // TODO(1242173): To let the GC traverse the return address of the exit |
| 4133 // frames, we need to know where the return address is. Right now, |
| 4134 // we push it on the stack to be able to find it again, but we never |
| 4135 // restore from it in case of changes, which makes it impossible to |
| 4136 // support moving the C entry code stub. This should be fixed, but currently |
| 4137 // this is OK because the CEntryStub gets generated so early in the V8 boot |
| 4138 // sequence that it is not moving ever. |
| 4139 __ add(lr, pc, Operand(4)); // compute return address: (pc + 8) + 4 |
| 4140 __ push(lr); |
| 4141 #if !defined(__arm__) |
| 4142 // Notify the simulator of the transition to C code. |
| 4143 __ swi(assembler::arm::call_rt_r5); |
| 4144 #else /* !defined(__arm__) */ |
| 4145 __ mov(pc, Operand(r5)); |
| 4146 #endif /* !defined(__arm__) */ |
| 4147 // result is in r0 or r0:r1 - do not destroy these registers! |
| 4148 |
| 4149 // check for failure result |
| 4150 Label failure_returned; |
| 4151 ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0); |
| 4152 // Lower 2 bits of r2 are 0 iff r0 has failure tag. |
| 4153 __ add(r2, r0, Operand(1)); |
| 4154 __ tst(r2, Operand(kFailureTagMask)); |
| 4155 __ b(eq, &failure_returned); |
| 4156 |
| 4157 // Exit C frame and return. |
| 4158 // r0:r1: result |
| 4159 // sp: stack pointer |
| 4160 // fp: frame pointer |
| 4161 // pp: caller's parameter pointer pp (restored as C callee-saved) |
| 4162 __ LeaveExitFrame(frame_type); |
| 4163 |
| 4164 // check if we should retry or throw exception |
| 4165 Label retry; |
| 4166 __ bind(&failure_returned); |
| 4167 ASSERT(Failure::RETRY_AFTER_GC == 0); |
| 4168 __ tst(r0, Operand(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize)); |
| 4169 __ b(eq, &retry); |
| 4170 |
| 4171 Label continue_exception; |
| 4172 // If the returned failure is EXCEPTION then promote Top::pending_exception(). |
| 4173 __ cmp(r0, Operand(reinterpret_cast<int32_t>(Failure::Exception()))); |
| 4174 __ b(ne, &continue_exception); |
| 4175 |
| 4176 // Retrieve the pending exception and clear the variable. |
| 4177 __ mov(ip, Operand(Factory::the_hole_value().location())); |
| 4178 __ ldr(r3, MemOperand(ip)); |
| 4179 __ mov(ip, Operand(Top::pending_exception_address())); |
| 4180 __ ldr(r0, MemOperand(ip)); |
| 4181 __ str(r3, MemOperand(ip)); |
| 4182 |
| 4183 __ bind(&continue_exception); |
| 4184 // Special handling of out of memory exception. |
| 4185 Failure* out_of_memory = Failure::OutOfMemoryException(); |
| 4186 __ cmp(r0, Operand(reinterpret_cast<int32_t>(out_of_memory))); |
| 4187 __ b(eq, throw_out_of_memory_exception); |
| 4188 |
| 4189 // Handle normal exception. |
| 4190 __ jmp(throw_normal_exception); |
| 4191 |
| 4192 __ bind(&retry); // pass last failure (r0) as parameter (r0) when retrying |
| 4193 } |
| 4194 |
| 4195 |
| 4196 void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) { |
| 4197 // Called from JavaScript; parameters are on stack as if calling JS function |
| 4198 // r0: number of arguments including receiver |
| 4199 // r1: pointer to builtin function |
| 4200 // fp: frame pointer (restored after C call) |
| 4201 // sp: stack pointer (restored as callee's pp after C call) |
| 4202 // cp: current context (C callee-saved) |
| 4203 // pp: caller's parameter pointer pp (C callee-saved) |
| 4204 |
| 4205 // NOTE: Invocations of builtins may return failure objects |
| 4206 // instead of a proper result. The builtin entry handles |
| 4207 // this by performing a garbage collection and retrying the |
| 4208 // builtin once. |
| 4209 |
| 4210 StackFrame::Type frame_type = is_debug_break |
| 4211 ? StackFrame::EXIT_DEBUG |
| 4212 : StackFrame::EXIT; |
| 4213 |
| 4214 // Enter the exit frame that transitions from JavaScript to C++. |
| 4215 __ EnterExitFrame(frame_type); |
| 4216 |
| 4217 // r4: number of arguments (C callee-saved) |
| 4218 // r5: pointer to builtin function (C callee-saved) |
| 4219 // r6: pointer to first argument (C callee-saved) |
| 4220 |
| 4221 Label throw_out_of_memory_exception; |
| 4222 Label throw_normal_exception; |
| 4223 |
| 4224 #ifdef DEBUG |
| 4225 if (FLAG_gc_greedy) { |
| 4226 Failure* failure = Failure::RetryAfterGC(0, NEW_SPACE); |
| 4227 __ mov(r0, Operand(reinterpret_cast<intptr_t>(failure))); |
| 4228 } |
| 4229 GenerateCore(masm, |
| 4230 &throw_normal_exception, |
| 4231 &throw_out_of_memory_exception, |
| 4232 frame_type, |
| 4233 FLAG_gc_greedy); |
| 4234 #else |
| 4235 GenerateCore(masm, |
| 4236 &throw_normal_exception, |
| 4237 &throw_out_of_memory_exception, |
| 4238 frame_type, |
| 4239 false); |
| 4240 #endif |
| 4241 GenerateCore(masm, |
| 4242 &throw_normal_exception, |
| 4243 &throw_out_of_memory_exception, |
| 4244 frame_type, |
| 4245 true); |
| 4246 |
| 4247 __ bind(&throw_out_of_memory_exception); |
| 4248 GenerateThrowOutOfMemory(masm); |
| 4249 // control flow for generated will not return. |
| 4250 |
| 4251 __ bind(&throw_normal_exception); |
| 4252 GenerateThrowTOS(masm); |
| 4253 } |
| 4254 |
| 4255 |
| 4256 void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { |
| 4257 // r0: code entry |
| 4258 // r1: function |
| 4259 // r2: receiver |
| 4260 // r3: argc |
| 4261 // [sp+0]: argv |
| 4262 |
| 4263 Label invoke, exit; |
| 4264 |
| 4265 // Called from C, so do not pop argc and args on exit (preserve sp) |
| 4266 // No need to save register-passed args |
| 4267 // Save callee-saved registers (incl. cp, pp, and fp), sp, and lr |
| 4268 __ stm(db_w, sp, kCalleeSaved | lr.bit()); |
| 4269 |
| 4270 // Get address of argv, see stm above. |
| 4271 // r0: code entry |
| 4272 // r1: function |
| 4273 // r2: receiver |
| 4274 // r3: argc |
| 4275 __ add(r4, sp, Operand((kNumCalleeSaved + 1)*kPointerSize)); |
| 4276 __ ldr(r4, MemOperand(r4)); // argv |
| 4277 |
| 4278 // Push a frame with special values setup to mark it as an entry frame. |
| 4279 // r0: code entry |
| 4280 // r1: function |
| 4281 // r2: receiver |
| 4282 // r3: argc |
| 4283 // r4: argv |
| 4284 int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY; |
| 4285 __ mov(r8, Operand(-1)); // Push a bad frame pointer to fail if it is used. |
| 4286 __ mov(r7, Operand(~ArgumentsAdaptorFrame::SENTINEL)); |
| 4287 __ mov(r6, Operand(Smi::FromInt(marker))); |
| 4288 __ mov(r5, Operand(ExternalReference(Top::k_c_entry_fp_address))); |
| 4289 __ ldr(r5, MemOperand(r5)); |
| 4290 __ stm(db_w, sp, r5.bit() | r6.bit() | r7.bit() | r8.bit()); |
| 4291 |
| 4292 // Setup frame pointer for the frame to be pushed. |
| 4293 __ add(fp, sp, Operand(-EntryFrameConstants::kCallerFPOffset)); |
| 4294 |
| 4295 // Call a faked try-block that does the invoke. |
| 4296 __ bl(&invoke); |
| 4297 |
| 4298 // Caught exception: Store result (exception) in the pending |
| 4299 // exception field in the JSEnv and return a failure sentinel. |
| 4300 // Coming in here the fp will be invalid because the PushTryHandler below |
| 4301 // sets it to 0 to signal the existence of the JSEntry frame. |
| 4302 __ mov(ip, Operand(Top::pending_exception_address())); |
| 4303 __ str(r0, MemOperand(ip)); |
| 4304 __ mov(r0, Operand(Handle<Failure>(Failure::Exception()))); |
| 4305 __ b(&exit); |
| 4306 |
| 4307 // Invoke: Link this frame into the handler chain. |
| 4308 __ bind(&invoke); |
| 4309 // Must preserve r0-r4, r5-r7 are available. |
| 4310 __ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER); |
| 4311 // If an exception not caught by another handler occurs, this handler returns |
| 4312 // control to the code after the bl(&invoke) above, which restores all |
| 4313 // kCalleeSaved registers (including cp, pp and fp) to their saved values |
| 4314 // before returning a failure to C. |
| 4315 |
| 4316 // Clear any pending exceptions. |
| 4317 __ mov(ip, Operand(ExternalReference::the_hole_value_location())); |
| 4318 __ ldr(r5, MemOperand(ip)); |
| 4319 __ mov(ip, Operand(Top::pending_exception_address())); |
| 4320 __ str(r5, MemOperand(ip)); |
| 4321 |
| 4322 // Invoke the function by calling through JS entry trampoline builtin. |
| 4323 // Notice that we cannot store a reference to the trampoline code directly in |
| 4324 // this stub, because runtime stubs are not traversed when doing GC. |
| 4325 |
| 4326 // Expected registers by Builtins::JSEntryTrampoline |
| 4327 // r0: code entry |
| 4328 // r1: function |
| 4329 // r2: receiver |
| 4330 // r3: argc |
| 4331 // r4: argv |
| 4332 if (is_construct) { |
| 4333 ExternalReference construct_entry(Builtins::JSConstructEntryTrampoline); |
| 4334 __ mov(ip, Operand(construct_entry)); |
| 4335 } else { |
| 4336 ExternalReference entry(Builtins::JSEntryTrampoline); |
| 4337 __ mov(ip, Operand(entry)); |
| 4338 } |
| 4339 __ ldr(ip, MemOperand(ip)); // deref address |
| 4340 |
| 4341 // Branch and link to JSEntryTrampoline |
| 4342 __ mov(lr, Operand(pc)); |
| 4343 __ add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag)); |
| 4344 |
| 4345 // Unlink this frame from the handler chain. When reading the |
| 4346 // address of the next handler, there is no need to use the address |
| 4347 // displacement since the current stack pointer (sp) points directly |
| 4348 // to the stack handler. |
| 4349 __ ldr(r3, MemOperand(sp, StackHandlerConstants::kNextOffset)); |
| 4350 __ mov(ip, Operand(ExternalReference(Top::k_handler_address))); |
| 4351 __ str(r3, MemOperand(ip)); |
| 4352 // No need to restore registers |
| 4353 __ add(sp, sp, Operand(StackHandlerConstants::kSize)); |
| 4354 |
| 4355 __ bind(&exit); // r0 holds result |
| 4356 // Restore the top frame descriptors from the stack. |
| 4357 __ pop(r3); |
| 4358 __ mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address))); |
| 4359 __ str(r3, MemOperand(ip)); |
| 4360 |
| 4361 // Reset the stack to the callee saved registers. |
| 4362 __ add(sp, sp, Operand(-EntryFrameConstants::kCallerFPOffset)); |
| 4363 |
| 4364 // Restore callee-saved registers and return. |
| 4365 #ifdef DEBUG |
| 4366 if (FLAG_debug_code) __ mov(lr, Operand(pc)); |
| 4367 #endif |
| 4368 __ ldm(ia_w, sp, kCalleeSaved | pc.bit()); |
| 4369 } |
| 4370 |
| 4371 |
| 4372 void ArgumentsAccessStub::Generate(MacroAssembler* masm) { |
| 4373 // ----------- S t a t e ------------- |
| 4374 // -- r0: formal number of parameters for the calling function |
| 4375 // -- r1: key (if value access) |
| 4376 // -- lr: return address |
| 4377 // ----------------------------------- |
| 4378 |
| 4379 // Check that the key is a smi for non-length accesses. |
| 4380 Label slow; |
| 4381 if (!is_length_) { |
| 4382 __ tst(r1, Operand(kSmiTagMask)); |
| 4383 __ b(ne, &slow); |
| 4384 } |
| 4385 |
| 4386 // Check if the calling frame is an arguments adaptor frame. |
| 4387 // r0: formal number of parameters |
| 4388 // r1: key (if access) |
| 4389 Label adaptor; |
| 4390 __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); |
| 4391 __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset)); |
| 4392 __ cmp(r3, Operand(ArgumentsAdaptorFrame::SENTINEL)); |
| 4393 __ b(eq, &adaptor); |
| 4394 |
| 4395 static const int kParamDisplacement = |
| 4396 StandardFrameConstants::kCallerSPOffset - kPointerSize; |
| 4397 |
| 4398 if (is_length_) { |
| 4399 // Nothing to do: the formal length of parameters has been passed in r0 |
| 4400 // by the calling function. |
| 4401 } else { |
| 4402 // Check index against formal parameter count. Use unsigned comparison to |
| 4403 // get the negative check for free. |
| 4404 // r0: formal number of parameters |
| 4405 // r1: index |
| 4406 __ cmp(r1, r0); |
| 4407 __ b(cs, &slow); |
| 4408 |
| 4409 // Read the argument from the current frame. |
| 4410 __ sub(r3, r0, r1); |
| 4411 __ add(r3, fp, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize)); |
| 4412 __ ldr(r0, MemOperand(r3, kParamDisplacement)); |
| 4413 } |
| 4414 |
| 4415 // Return to the calling function. |
| 4416 __ mov(pc, lr); |
| 4417 |
| 4418 // An arguments adaptor frame is present. Find the length or the actual |
| 4419 // argument in the calling frame. |
| 4420 // r0: formal number of parameters |
| 4421 // r1: key |
| 4422 // r2: adaptor frame pointer |
| 4423 __ bind(&adaptor); |
| 4424 // Read the arguments length from the adaptor frame. This is the result if |
| 4425 // only accessing the length, otherwise it is used in accessing the value |
| 4426 __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset)); |
| 4427 |
| 4428 if (!is_length_) { |
| 4429 // Check index against actual arguments count. Use unsigned comparison to |
| 4430 // get the negative check for free. |
| 4431 // r0: actual number of parameter |
| 4432 // r1: index |
| 4433 // r2: adaptor frame point |
| 4434 __ cmp(r1, r0); |
| 4435 __ b(cs, &slow); |
| 4436 |
| 4437 // Read the argument from the adaptor frame. |
| 4438 __ sub(r3, r0, r1); |
| 4439 __ add(r3, r2, Operand(r3, LSL, kPointerSizeLog2 - kSmiTagSize)); |
| 4440 __ ldr(r0, MemOperand(r3, kParamDisplacement)); |
| 4441 } |
| 4442 |
| 4443 // Return to the calling function. |
| 4444 __ mov(pc, lr); |
| 4445 |
| 4446 if (!is_length_) { |
| 4447 __ bind(&slow); |
| 4448 __ push(r1); |
| 4449 __ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1); |
| 4450 } |
| 4451 } |
| 4452 |
| 4453 |
| 4454 void ArmCodeGenerator::SetReferenceProperty(CodeGenerator* cgen, |
| 4455 Reference* ref, |
| 4456 Expression* key) { |
| 4457 ASSERT(!ref->is_illegal()); |
| 4458 MacroAssembler* masm = cgen->masm(); |
| 4459 |
| 4460 if (ref->type() == Reference::NAMED) { |
| 4461 // Compute the name of the property. |
| 4462 Literal* literal = key->AsLiteral(); |
| 4463 Handle<String> name(String::cast(*literal->handle())); |
| 4464 |
| 4465 // Call the appropriate IC code. |
| 4466 __ pop(r0); // value |
| 4467 // Setup the name register. |
| 4468 __ mov(r2, Operand(name)); |
| 4469 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); |
| 4470 __ Call(ic, RelocInfo::CODE_TARGET); |
| 4471 |
| 4472 } else { |
| 4473 // Access keyed property. |
| 4474 ASSERT(ref->type() == Reference::KEYED); |
| 4475 |
| 4476 __ pop(r0); // value |
| 4477 SetPropertyStub stub; |
| 4478 __ CallStub(&stub); |
| 4479 } |
| 4480 __ push(r0); |
| 4481 } |
| 4482 |
| 4483 |
| 4484 void CallFunctionStub::Generate(MacroAssembler* masm) { |
| 4485 Label slow; |
| 4486 // Get the function to call from the stack. |
| 4487 // function, receiver [, arguments] |
| 4488 __ ldr(r1, MemOperand(sp, (argc_ + 1) * kPointerSize)); |
| 4489 |
| 4490 // Check that the function is really a JavaScript function. |
| 4491 // r1: pushed function (to be verified) |
| 4492 __ tst(r1, Operand(kSmiTagMask)); |
| 4493 __ b(eq, &slow); |
| 4494 // Get the map of the function object. |
| 4495 __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); |
| 4496 __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset)); |
| 4497 __ cmp(r2, Operand(JS_FUNCTION_TYPE)); |
| 4498 __ b(ne, &slow); |
| 4499 |
| 4500 // Fast-case: Invoke the function now. |
| 4501 // r1: pushed function |
| 4502 ParameterCount actual(argc_); |
| 4503 __ InvokeFunction(r1, actual, JUMP_FUNCTION); |
| 4504 |
| 4505 // Slow-case: Non-function called. |
| 4506 __ bind(&slow); |
| 4507 __ mov(r0, Operand(argc_)); // Setup the number of arguments. |
| 4508 __ InvokeBuiltin(Builtins::CALL_NON_FUNCTION, JUMP_JS); |
| 4509 } |
| 4510 |
| 4511 |
| 4512 #undef __ |
| 4536 | 4513 |
| 4537 // ----------------------------------------------------------------------------- | 4514 // ----------------------------------------------------------------------------- |
| 4538 // CodeGenerator interface | 4515 // CodeGenerator interface |
| 4539 | 4516 |
| 4540 // MakeCode() is just a wrapper for CodeGenerator::MakeCode() | 4517 // MakeCode() is just a wrapper for CodeGenerator::MakeCode() |
| 4541 // so we don't have to expose the entire CodeGenerator class in | 4518 // so we don't have to expose the entire CodeGenerator class in |
| 4542 // the .h file. | 4519 // the .h file. |
| 4543 Handle<Code> CodeGenerator::MakeCode(FunctionLiteral* fun, | 4520 Handle<Code> CodeGenerator::MakeCode(FunctionLiteral* fun, |
| 4544 Handle<Script> script, | 4521 Handle<Script> script, |
| 4545 bool is_eval) { | 4522 bool is_eval) { |
| 4546 Handle<Code> code = ArmCodeGenerator::MakeCode(fun, script, is_eval); | 4523 Handle<Code> code = ArmCodeGenerator::MakeCode(fun, script, is_eval); |
| 4547 if (!code.is_null()) { | 4524 if (!code.is_null()) { |
| 4548 Counters::total_compiled_code_size.Increment(code->instruction_size()); | 4525 Counters::total_compiled_code_size.Increment(code->instruction_size()); |
| 4549 } | 4526 } |
| 4550 return code; | 4527 return code; |
| 4551 } | 4528 } |
| 4552 | 4529 |
| 4553 | 4530 |
| 4554 } } // namespace v8::internal | 4531 } } // namespace v8::internal |
| OLD | NEW |