1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_ARM_CODE_STUBS_ARM_H_ 6 #define V8_ARM_CODE_STUBS_ARM_H_ 7 8 #include "src/arm/frames-arm.h" 9 10 namespace v8 { 11 namespace internal { 12 13 14 void ArrayNativeCode(MacroAssembler* masm, Label* call_generic_code); 15 16 17 class StringHelper : public AllStatic { 18 public: 19 // Generate code for copying a large number of characters. This function 20 // is allowed to spend extra time setting up conditions to make copying 21 // faster. Copying of overlapping regions is not supported. 22 // Dest register ends at the position after the last character written. 23 static void GenerateCopyCharacters(MacroAssembler* masm, 24 Register dest, 25 Register src, 26 Register count, 27 Register scratch, 28 String::Encoding encoding); 29 30 // Compares two flat one-byte strings and returns result in r0. 31 static void GenerateCompareFlatOneByteStrings( 32 MacroAssembler* masm, Register left, Register right, Register scratch1, 33 Register scratch2, Register scratch3, Register scratch4); 34 35 // Compares two flat one-byte strings for equality and returns result in r0. 36 static void GenerateFlatOneByteStringEquals(MacroAssembler* masm, 37 Register left, Register right, 38 Register scratch1, 39 Register scratch2, 40 Register scratch3); 41 42 private: 43 static void GenerateOneByteCharsCompareLoop( 44 MacroAssembler* masm, Register left, Register right, Register length, 45 Register scratch1, Register scratch2, Label* chars_not_equal); 46 47 DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper); 48 }; 49 50 51 class RecordWriteStub: public PlatformCodeStub { 52 public: 53 RecordWriteStub(Isolate* isolate, 54 Register object, 55 Register value, 56 Register address, 57 RememberedSetAction remembered_set_action, 58 SaveFPRegsMode fp_mode) 59 : PlatformCodeStub(isolate), 60 regs_(object, // An input reg. 61 address, // An input reg. 62 value) { // One scratch reg. 63 minor_key_ = ObjectBits::encode(object.code()) | 64 ValueBits::encode(value.code()) | 65 AddressBits::encode(address.code()) | 66 RememberedSetActionBits::encode(remembered_set_action) | 67 SaveFPRegsModeBits::encode(fp_mode); 68 } 69 70 RecordWriteStub(uint32_t key, Isolate* isolate) 71 : PlatformCodeStub(key, isolate), regs_(object(), address(), value()) {} 72 73 enum Mode { 74 STORE_BUFFER_ONLY, 75 INCREMENTAL, 76 INCREMENTAL_COMPACTION 77 }; 78 79 bool SometimesSetsUpAFrame() override { return false; } 80 81 static void PatchBranchIntoNop(MacroAssembler* masm, int pos) { 82 masm->instr_at_put(pos, (masm->instr_at(pos) & ~B27) | (B24 | B20)); 83 DCHECK(Assembler::IsTstImmediate(masm->instr_at(pos))); 84 } 85 86 static void PatchNopIntoBranch(MacroAssembler* masm, int pos) { 87 masm->instr_at_put(pos, (masm->instr_at(pos) & ~(B24 | B20)) | B27); 88 DCHECK(Assembler::IsBranch(masm->instr_at(pos))); 89 } 90 91 static Mode GetMode(Code* stub) { 92 Instr first_instruction = Assembler::instr_at(stub->instruction_start()); 93 Instr second_instruction = Assembler::instr_at(stub->instruction_start() + 94 Assembler::kInstrSize); 95 96 if (Assembler::IsBranch(first_instruction)) { 97 return INCREMENTAL; 98 } 99 100 DCHECK(Assembler::IsTstImmediate(first_instruction)); 101 102 if (Assembler::IsBranch(second_instruction)) { 103 return INCREMENTAL_COMPACTION; 104 } 105 106 DCHECK(Assembler::IsTstImmediate(second_instruction)); 107 108 return STORE_BUFFER_ONLY; 109 } 110 111 static void Patch(Code* stub, Mode mode) { 112 MacroAssembler masm(stub->GetIsolate(), stub->instruction_start(), 113 stub->instruction_size(), CodeObjectRequired::kNo); 114 switch (mode) { 115 case STORE_BUFFER_ONLY: 116 DCHECK(GetMode(stub) == INCREMENTAL || 117 GetMode(stub) == INCREMENTAL_COMPACTION); 118 PatchBranchIntoNop(&masm, 0); 119 PatchBranchIntoNop(&masm, Assembler::kInstrSize); 120 break; 121 case INCREMENTAL: 122 DCHECK(GetMode(stub) == STORE_BUFFER_ONLY); 123 PatchNopIntoBranch(&masm, 0); 124 break; 125 case INCREMENTAL_COMPACTION: 126 DCHECK(GetMode(stub) == STORE_BUFFER_ONLY); 127 PatchNopIntoBranch(&masm, Assembler::kInstrSize); 128 break; 129 } 130 DCHECK(GetMode(stub) == mode); 131 Assembler::FlushICache(stub->GetIsolate(), stub->instruction_start(), 132 2 * Assembler::kInstrSize); 133 } 134 135 DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); 136 137 private: 138 // This is a helper class for freeing up 3 scratch registers. The input is 139 // two registers that must be preserved and one scratch register provided by 140 // the caller. 141 class RegisterAllocation { 142 public: 143 RegisterAllocation(Register object, 144 Register address, 145 Register scratch0) 146 : object_(object), 147 address_(address), 148 scratch0_(scratch0) { 149 DCHECK(!AreAliased(scratch0, object, address, no_reg)); 150 scratch1_ = GetRegisterThatIsNotOneOf(object_, address_, scratch0_); 151 } 152 153 void Save(MacroAssembler* masm) { 154 DCHECK(!AreAliased(object_, address_, scratch1_, scratch0_)); 155 // We don't have to save scratch0_ because it was given to us as 156 // a scratch register. 157 masm->push(scratch1_); 158 } 159 160 void Restore(MacroAssembler* masm) { 161 masm->pop(scratch1_); 162 } 163 164 // If we have to call into C then we need to save and restore all caller- 165 // saved registers that were not already preserved. The scratch registers 166 // will be restored by other means so we don't bother pushing them here. 167 void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) { 168 masm->stm(db_w, sp, (kCallerSaved | lr.bit()) & ~scratch1_.bit()); 169 if (mode == kSaveFPRegs) { 170 masm->SaveFPRegs(sp, scratch0_); 171 } 172 } 173 174 inline void RestoreCallerSaveRegisters(MacroAssembler*masm, 175 SaveFPRegsMode mode) { 176 if (mode == kSaveFPRegs) { 177 masm->RestoreFPRegs(sp, scratch0_); 178 } 179 masm->ldm(ia_w, sp, (kCallerSaved | lr.bit()) & ~scratch1_.bit()); 180 } 181 182 inline Register object() { return object_; } 183 inline Register address() { return address_; } 184 inline Register scratch0() { return scratch0_; } 185 inline Register scratch1() { return scratch1_; } 186 187 private: 188 Register object_; 189 Register address_; 190 Register scratch0_; 191 Register scratch1_; 192 193 friend class RecordWriteStub; 194 }; 195 196 enum OnNoNeedToInformIncrementalMarker { 197 kReturnOnNoNeedToInformIncrementalMarker, 198 kUpdateRememberedSetOnNoNeedToInformIncrementalMarker 199 }; 200 201 inline Major MajorKey() const final { return RecordWrite; } 202 203 void Generate(MacroAssembler* masm) override; 204 void GenerateIncremental(MacroAssembler* masm, Mode mode); 205 void CheckNeedsToInformIncrementalMarker( 206 MacroAssembler* masm, 207 OnNoNeedToInformIncrementalMarker on_no_need, 208 Mode mode); 209 void InformIncrementalMarker(MacroAssembler* masm); 210 211 void Activate(Code* code) override { 212 code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code); 213 } 214 215 Register object() const { 216 return Register::from_code(ObjectBits::decode(minor_key_)); 217 } 218 219 Register value() const { 220 return Register::from_code(ValueBits::decode(minor_key_)); 221 } 222 223 Register address() const { 224 return Register::from_code(AddressBits::decode(minor_key_)); 225 } 226 227 RememberedSetAction remembered_set_action() const { 228 return RememberedSetActionBits::decode(minor_key_); 229 } 230 231 SaveFPRegsMode save_fp_regs_mode() const { 232 return SaveFPRegsModeBits::decode(minor_key_); 233 } 234 235 class ObjectBits: public BitField<int, 0, 4> {}; 236 class ValueBits: public BitField<int, 4, 4> {}; 237 class AddressBits: public BitField<int, 8, 4> {}; 238 class RememberedSetActionBits: public BitField<RememberedSetAction, 12, 1> {}; 239 class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 13, 1> {}; 240 241 Label slow_; 242 RegisterAllocation regs_; 243 244 DISALLOW_COPY_AND_ASSIGN(RecordWriteStub); 245 }; 246 247 248 // Trampoline stub to call into native code. To call safely into native code 249 // in the presence of compacting GC (which can move code objects) we need to 250 // keep the code which called into native pinned in the memory. Currently the 251 // simplest approach is to generate such stub early enough so it can never be 252 // moved by GC 253 class DirectCEntryStub: public PlatformCodeStub { 254 public: 255 explicit DirectCEntryStub(Isolate* isolate) : PlatformCodeStub(isolate) {} 256 void GenerateCall(MacroAssembler* masm, Register target); 257 258 private: 259 bool NeedsImmovableCode() override { return true; } 260 261 DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); 262 DEFINE_PLATFORM_CODE_STUB(DirectCEntry, PlatformCodeStub); 263 }; 264 265 266 class NameDictionaryLookupStub: public PlatformCodeStub { 267 public: 268 enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP }; 269 270 NameDictionaryLookupStub(Isolate* isolate, LookupMode mode) 271 : PlatformCodeStub(isolate) { 272 minor_key_ = LookupModeBits::encode(mode); 273 } 274 275 static void GenerateNegativeLookup(MacroAssembler* masm, 276 Label* miss, 277 Label* done, 278 Register receiver, 279 Register properties, 280 Handle<Name> name, 281 Register scratch0); 282 283 static void GeneratePositiveLookup(MacroAssembler* masm, 284 Label* miss, 285 Label* done, 286 Register elements, 287 Register name, 288 Register r0, 289 Register r1); 290 291 bool SometimesSetsUpAFrame() override { return false; } 292 293 private: 294 static const int kInlinedProbes = 4; 295 static const int kTotalProbes = 20; 296 297 static const int kCapacityOffset = 298 NameDictionary::kHeaderSize + 299 NameDictionary::kCapacityIndex * kPointerSize; 300 301 static const int kElementsStartOffset = 302 NameDictionary::kHeaderSize + 303 NameDictionary::kElementsStartIndex * kPointerSize; 304 305 LookupMode mode() const { return LookupModeBits::decode(minor_key_); } 306 307 class LookupModeBits: public BitField<LookupMode, 0, 1> {}; 308 309 DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); 310 DEFINE_PLATFORM_CODE_STUB(NameDictionaryLookup, PlatformCodeStub); 311 }; 312 313 } // namespace internal 314 } // namespace v8 315 316 #endif // V8_ARM_CODE_STUBS_ARM_H_ 317