1 // Copyright 2011 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_IA32_CODE_STUBS_IA32_H_ 6 #define V8_IA32_CODE_STUBS_IA32_H_ 7 8 namespace v8 { 9 namespace internal { 10 11 12 void ArrayNativeCode(MacroAssembler* masm, 13 bool construct_call, 14 Label* call_generic_code); 15 16 17 class StringHelper : public AllStatic { 18 public: 19 // Generate code for copying characters using the rep movs instruction. 20 // Copies ecx characters from esi to edi. Copying of overlapping regions is 21 // not supported. 22 static void GenerateCopyCharacters(MacroAssembler* masm, 23 Register dest, 24 Register src, 25 Register count, 26 Register scratch, 27 String::Encoding encoding); 28 29 // Compares two flat one byte strings and returns result in eax. 30 static void GenerateCompareFlatOneByteStrings(MacroAssembler* masm, 31 Register left, Register right, 32 Register scratch1, 33 Register scratch2, 34 Register scratch3); 35 36 // Compares two flat one byte strings for equality and returns result in eax. 37 static void GenerateFlatOneByteStringEquals(MacroAssembler* masm, 38 Register left, Register right, 39 Register scratch1, 40 Register scratch2); 41 42 private: 43 static void GenerateOneByteCharsCompareLoop( 44 MacroAssembler* masm, Register left, Register right, Register length, 45 Register scratch, Label* chars_not_equal, 46 Label::Distance chars_not_equal_near = Label::kFar); 47 48 DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper); 49 }; 50 51 52 class NameDictionaryLookupStub: public PlatformCodeStub { 53 public: 54 enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP }; 55 NameDictionaryLookupStub(Isolate * isolate,Register dictionary,Register result,Register index,LookupMode mode)56 NameDictionaryLookupStub(Isolate* isolate, Register dictionary, 57 Register result, Register index, LookupMode mode) 58 : PlatformCodeStub(isolate) { 59 minor_key_ = DictionaryBits::encode(dictionary.code()) | 60 ResultBits::encode(result.code()) | 61 IndexBits::encode(index.code()) | LookupModeBits::encode(mode); 62 } 63 64 static void GenerateNegativeLookup(MacroAssembler* masm, 65 Label* miss, 66 Label* done, 67 Register properties, 68 Handle<Name> name, 69 Register r0); 70 71 static void GeneratePositiveLookup(MacroAssembler* masm, 72 Label* miss, 73 Label* done, 74 Register elements, 75 Register name, 76 Register r0, 77 Register r1); 78 SometimesSetsUpAFrame()79 virtual bool SometimesSetsUpAFrame() { return false; } 80 81 private: 82 static const int kInlinedProbes = 4; 83 static const int kTotalProbes = 20; 84 85 static const int kCapacityOffset = 86 NameDictionary::kHeaderSize + 87 NameDictionary::kCapacityIndex * kPointerSize; 88 89 static const int kElementsStartOffset = 90 NameDictionary::kHeaderSize + 91 NameDictionary::kElementsStartIndex * kPointerSize; 92 dictionary()93 Register dictionary() const { 94 return Register::from_code(DictionaryBits::decode(minor_key_)); 95 } 96 result()97 Register result() const { 98 return Register::from_code(ResultBits::decode(minor_key_)); 99 } 100 index()101 Register index() const { 102 return Register::from_code(IndexBits::decode(minor_key_)); 103 } 104 mode()105 LookupMode mode() const { return LookupModeBits::decode(minor_key_); } 106 107 class DictionaryBits: public BitField<int, 0, 3> {}; 108 class ResultBits: public BitField<int, 3, 3> {}; 109 class IndexBits: public BitField<int, 6, 3> {}; 110 class LookupModeBits: public BitField<LookupMode, 9, 1> {}; 111 112 DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); 113 DEFINE_PLATFORM_CODE_STUB(NameDictionaryLookup, PlatformCodeStub); 114 }; 115 116 117 class RecordWriteStub: public PlatformCodeStub { 118 public: RecordWriteStub(Isolate * isolate,Register object,Register value,Register address,RememberedSetAction remembered_set_action,SaveFPRegsMode fp_mode)119 RecordWriteStub(Isolate* isolate, 120 Register object, 121 Register value, 122 Register address, 123 RememberedSetAction remembered_set_action, 124 SaveFPRegsMode fp_mode) 125 : PlatformCodeStub(isolate), 126 regs_(object, // An input reg. 127 address, // An input reg. 128 value) { // One scratch reg. 129 minor_key_ = ObjectBits::encode(object.code()) | 130 ValueBits::encode(value.code()) | 131 AddressBits::encode(address.code()) | 132 RememberedSetActionBits::encode(remembered_set_action) | 133 SaveFPRegsModeBits::encode(fp_mode); 134 } 135 RecordWriteStub(uint32_t key,Isolate * isolate)136 RecordWriteStub(uint32_t key, Isolate* isolate) 137 : PlatformCodeStub(key, isolate), regs_(object(), address(), value()) {} 138 139 enum Mode { 140 STORE_BUFFER_ONLY, 141 INCREMENTAL, 142 INCREMENTAL_COMPACTION 143 }; 144 SometimesSetsUpAFrame()145 virtual bool SometimesSetsUpAFrame() { return false; } 146 147 static const byte kTwoByteNopInstruction = 0x3c; // Cmpb al, #imm8. 148 static const byte kTwoByteJumpInstruction = 0xeb; // Jmp #imm8. 149 150 static const byte kFiveByteNopInstruction = 0x3d; // Cmpl eax, #imm32. 151 static const byte kFiveByteJumpInstruction = 0xe9; // Jmp #imm32. 152 GetMode(Code * stub)153 static Mode GetMode(Code* stub) { 154 byte first_instruction = stub->instruction_start()[0]; 155 byte second_instruction = stub->instruction_start()[2]; 156 157 if (first_instruction == kTwoByteJumpInstruction) { 158 return INCREMENTAL; 159 } 160 161 DCHECK(first_instruction == kTwoByteNopInstruction); 162 163 if (second_instruction == kFiveByteJumpInstruction) { 164 return INCREMENTAL_COMPACTION; 165 } 166 167 DCHECK(second_instruction == kFiveByteNopInstruction); 168 169 return STORE_BUFFER_ONLY; 170 } 171 Patch(Code * stub,Mode mode)172 static void Patch(Code* stub, Mode mode) { 173 switch (mode) { 174 case STORE_BUFFER_ONLY: 175 DCHECK(GetMode(stub) == INCREMENTAL || 176 GetMode(stub) == INCREMENTAL_COMPACTION); 177 stub->instruction_start()[0] = kTwoByteNopInstruction; 178 stub->instruction_start()[2] = kFiveByteNopInstruction; 179 break; 180 case INCREMENTAL: 181 DCHECK(GetMode(stub) == STORE_BUFFER_ONLY); 182 stub->instruction_start()[0] = kTwoByteJumpInstruction; 183 break; 184 case INCREMENTAL_COMPACTION: 185 DCHECK(GetMode(stub) == STORE_BUFFER_ONLY); 186 stub->instruction_start()[0] = kTwoByteNopInstruction; 187 stub->instruction_start()[2] = kFiveByteJumpInstruction; 188 break; 189 } 190 DCHECK(GetMode(stub) == mode); 191 CpuFeatures::FlushICache(stub->instruction_start(), 7); 192 } 193 194 DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); 195 196 private: 197 // This is a helper class for freeing up 3 scratch registers, where the third 198 // is always ecx (needed for shift operations). The input is two registers 199 // that must be preserved and one scratch register provided by the caller. 200 class RegisterAllocation { 201 public: RegisterAllocation(Register object,Register address,Register scratch0)202 RegisterAllocation(Register object, 203 Register address, 204 Register scratch0) 205 : object_orig_(object), 206 address_orig_(address), 207 scratch0_orig_(scratch0), 208 object_(object), 209 address_(address), 210 scratch0_(scratch0) { 211 DCHECK(!AreAliased(scratch0, object, address, no_reg)); 212 scratch1_ = GetRegThatIsNotEcxOr(object_, address_, scratch0_); 213 if (scratch0.is(ecx)) { 214 scratch0_ = GetRegThatIsNotEcxOr(object_, address_, scratch1_); 215 } 216 if (object.is(ecx)) { 217 object_ = GetRegThatIsNotEcxOr(address_, scratch0_, scratch1_); 218 } 219 if (address.is(ecx)) { 220 address_ = GetRegThatIsNotEcxOr(object_, scratch0_, scratch1_); 221 } 222 DCHECK(!AreAliased(scratch0_, object_, address_, ecx)); 223 } 224 Save(MacroAssembler * masm)225 void Save(MacroAssembler* masm) { 226 DCHECK(!address_orig_.is(object_)); 227 DCHECK(object_.is(object_orig_) || address_.is(address_orig_)); 228 DCHECK(!AreAliased(object_, address_, scratch1_, scratch0_)); 229 DCHECK(!AreAliased(object_orig_, address_, scratch1_, scratch0_)); 230 DCHECK(!AreAliased(object_, address_orig_, scratch1_, scratch0_)); 231 // We don't have to save scratch0_orig_ because it was given to us as 232 // a scratch register. But if we had to switch to a different reg then 233 // we should save the new scratch0_. 234 if (!scratch0_.is(scratch0_orig_)) masm->push(scratch0_); 235 if (!ecx.is(scratch0_orig_) && 236 !ecx.is(object_orig_) && 237 !ecx.is(address_orig_)) { 238 masm->push(ecx); 239 } 240 masm->push(scratch1_); 241 if (!address_.is(address_orig_)) { 242 masm->push(address_); 243 masm->mov(address_, address_orig_); 244 } 245 if (!object_.is(object_orig_)) { 246 masm->push(object_); 247 masm->mov(object_, object_orig_); 248 } 249 } 250 Restore(MacroAssembler * masm)251 void Restore(MacroAssembler* masm) { 252 // These will have been preserved the entire time, so we just need to move 253 // them back. Only in one case is the orig_ reg different from the plain 254 // one, since only one of them can alias with ecx. 255 if (!object_.is(object_orig_)) { 256 masm->mov(object_orig_, object_); 257 masm->pop(object_); 258 } 259 if (!address_.is(address_orig_)) { 260 masm->mov(address_orig_, address_); 261 masm->pop(address_); 262 } 263 masm->pop(scratch1_); 264 if (!ecx.is(scratch0_orig_) && 265 !ecx.is(object_orig_) && 266 !ecx.is(address_orig_)) { 267 masm->pop(ecx); 268 } 269 if (!scratch0_.is(scratch0_orig_)) masm->pop(scratch0_); 270 } 271 272 // If we have to call into C then we need to save and restore all caller- 273 // saved registers that were not already preserved. The caller saved 274 // registers are eax, ecx and edx. The three scratch registers (incl. ecx) 275 // will be restored by other means so we don't bother pushing them here. SaveCallerSaveRegisters(MacroAssembler * masm,SaveFPRegsMode mode)276 void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) { 277 if (!scratch0_.is(eax) && !scratch1_.is(eax)) masm->push(eax); 278 if (!scratch0_.is(edx) && !scratch1_.is(edx)) masm->push(edx); 279 if (mode == kSaveFPRegs) { 280 masm->sub(esp, 281 Immediate(kDoubleSize * (XMMRegister::kMaxNumRegisters - 1))); 282 // Save all XMM registers except XMM0. 283 for (int i = XMMRegister::kMaxNumRegisters - 1; i > 0; i--) { 284 XMMRegister reg = XMMRegister::from_code(i); 285 masm->movsd(Operand(esp, (i - 1) * kDoubleSize), reg); 286 } 287 } 288 } 289 RestoreCallerSaveRegisters(MacroAssembler * masm,SaveFPRegsMode mode)290 inline void RestoreCallerSaveRegisters(MacroAssembler*masm, 291 SaveFPRegsMode mode) { 292 if (mode == kSaveFPRegs) { 293 // Restore all XMM registers except XMM0. 294 for (int i = XMMRegister::kMaxNumRegisters - 1; i > 0; i--) { 295 XMMRegister reg = XMMRegister::from_code(i); 296 masm->movsd(reg, Operand(esp, (i - 1) * kDoubleSize)); 297 } 298 masm->add(esp, 299 Immediate(kDoubleSize * (XMMRegister::kMaxNumRegisters - 1))); 300 } 301 if (!scratch0_.is(edx) && !scratch1_.is(edx)) masm->pop(edx); 302 if (!scratch0_.is(eax) && !scratch1_.is(eax)) masm->pop(eax); 303 } 304 object()305 inline Register object() { return object_; } address()306 inline Register address() { return address_; } scratch0()307 inline Register scratch0() { return scratch0_; } scratch1()308 inline Register scratch1() { return scratch1_; } 309 310 private: 311 Register object_orig_; 312 Register address_orig_; 313 Register scratch0_orig_; 314 Register object_; 315 Register address_; 316 Register scratch0_; 317 Register scratch1_; 318 // Third scratch register is always ecx. 319 GetRegThatIsNotEcxOr(Register r1,Register r2,Register r3)320 Register GetRegThatIsNotEcxOr(Register r1, 321 Register r2, 322 Register r3) { 323 for (int i = 0; i < Register::NumAllocatableRegisters(); i++) { 324 Register candidate = Register::FromAllocationIndex(i); 325 if (candidate.is(ecx)) continue; 326 if (candidate.is(r1)) continue; 327 if (candidate.is(r2)) continue; 328 if (candidate.is(r3)) continue; 329 return candidate; 330 } 331 UNREACHABLE(); 332 return no_reg; 333 } 334 friend class RecordWriteStub; 335 }; 336 337 enum OnNoNeedToInformIncrementalMarker { 338 kReturnOnNoNeedToInformIncrementalMarker, 339 kUpdateRememberedSetOnNoNeedToInformIncrementalMarker 340 }; 341 MajorKey()342 virtual inline Major MajorKey() const FINAL OVERRIDE { return RecordWrite; } 343 344 virtual void Generate(MacroAssembler* masm) OVERRIDE; 345 void GenerateIncremental(MacroAssembler* masm, Mode mode); 346 void CheckNeedsToInformIncrementalMarker( 347 MacroAssembler* masm, 348 OnNoNeedToInformIncrementalMarker on_no_need, 349 Mode mode); 350 void InformIncrementalMarker(MacroAssembler* masm); 351 Activate(Code * code)352 void Activate(Code* code) { 353 code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code); 354 } 355 object()356 Register object() const { 357 return Register::from_code(ObjectBits::decode(minor_key_)); 358 } 359 value()360 Register value() const { 361 return Register::from_code(ValueBits::decode(minor_key_)); 362 } 363 address()364 Register address() const { 365 return Register::from_code(AddressBits::decode(minor_key_)); 366 } 367 remembered_set_action()368 RememberedSetAction remembered_set_action() const { 369 return RememberedSetActionBits::decode(minor_key_); 370 } 371 save_fp_regs_mode()372 SaveFPRegsMode save_fp_regs_mode() const { 373 return SaveFPRegsModeBits::decode(minor_key_); 374 } 375 376 class ObjectBits: public BitField<int, 0, 3> {}; 377 class ValueBits: public BitField<int, 3, 3> {}; 378 class AddressBits: public BitField<int, 6, 3> {}; 379 class RememberedSetActionBits: public BitField<RememberedSetAction, 9, 1> {}; 380 class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 10, 1> {}; 381 382 RegisterAllocation regs_; 383 384 DISALLOW_COPY_AND_ASSIGN(RecordWriteStub); 385 }; 386 387 388 } } // namespace v8::internal 389 390 #endif // V8_IA32_CODE_STUBS_IA32_H_ 391