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