1 // Copyright (c) 1994-2006 Sun Microsystems Inc. 2 // All Rights Reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are 6 // met: 7 // 8 // - Redistributions of source code must retain the above copyright notice, 9 // this list of conditions and the following disclaimer. 10 // 11 // - Redistribution in binary form must reproduce the above copyright 12 // notice, this list of conditions and the following disclaimer in the 13 // documentation and/or other materials provided with the distribution. 14 // 15 // - Neither the name of Sun Microsystems or the names of contributors may 16 // be used to endorse or promote products derived from this software without 17 // specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 23 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 // The original source code covered by the above license above has been 32 // modified significantly by Google Inc. 33 // Copyright 2012 the V8 project authors. All rights reserved. 34 35 // A light-weight IA32 Assembler. 36 37 #ifndef V8_X87_ASSEMBLER_X87_INL_H_ 38 #define V8_X87_ASSEMBLER_X87_INL_H_ 39 40 #include "src/x87/assembler-x87.h" 41 42 #include "src/assembler.h" 43 #include "src/debug/debug.h" 44 #include "src/objects-inl.h" 45 46 namespace v8 { 47 namespace internal { 48 49 bool CpuFeatures::SupportsCrankshaft() { return true; } 50 51 bool CpuFeatures::SupportsSimd128() { return false; } 52 53 static const byte kCallOpcode = 0xE8; 54 static const int kNoCodeAgeSequenceLength = 5; 55 56 57 // The modes possibly affected by apply must be in kApplyMask. 58 void RelocInfo::apply(intptr_t delta) { 59 if (IsRuntimeEntry(rmode_) || IsCodeTarget(rmode_)) { 60 int32_t* p = reinterpret_cast<int32_t*>(pc_); 61 *p -= delta; // Relocate entry. 62 } else if (IsCodeAgeSequence(rmode_)) { 63 if (*pc_ == kCallOpcode) { 64 int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1); 65 *p -= delta; // Relocate entry. 66 } 67 } else if (IsDebugBreakSlot(rmode_) && IsPatchedDebugBreakSlotSequence()) { 68 // Special handling of a debug break slot when a break point is set (call 69 // instruction has been inserted). 70 int32_t* p = reinterpret_cast<int32_t*>( 71 pc_ + Assembler::kPatchDebugBreakSlotAddressOffset); 72 *p -= delta; // Relocate entry. 73 } else if (IsInternalReference(rmode_)) { 74 // absolute code pointer inside code object moves with the code object. 75 int32_t* p = reinterpret_cast<int32_t*>(pc_); 76 *p += delta; // Relocate entry. 77 } 78 } 79 80 81 Address RelocInfo::target_address() { 82 DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)); 83 return Assembler::target_address_at(pc_, host_); 84 } 85 86 Address RelocInfo::target_address_address() { 87 DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) 88 || rmode_ == EMBEDDED_OBJECT 89 || rmode_ == EXTERNAL_REFERENCE); 90 return reinterpret_cast<Address>(pc_); 91 } 92 93 94 Address RelocInfo::constant_pool_entry_address() { 95 UNREACHABLE(); 96 return NULL; 97 } 98 99 100 int RelocInfo::target_address_size() { 101 return Assembler::kSpecialTargetSize; 102 } 103 104 105 Object* RelocInfo::target_object() { 106 DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); 107 return Memory::Object_at(pc_); 108 } 109 110 111 Handle<Object> RelocInfo::target_object_handle(Assembler* origin) { 112 DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); 113 return Memory::Object_Handle_at(pc_); 114 } 115 116 117 void RelocInfo::set_target_object(Object* target, 118 WriteBarrierMode write_barrier_mode, 119 ICacheFlushMode icache_flush_mode) { 120 DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); 121 Memory::Object_at(pc_) = target; 122 if (icache_flush_mode != SKIP_ICACHE_FLUSH) { 123 Assembler::FlushICache(isolate_, pc_, sizeof(Address)); 124 } 125 if (write_barrier_mode == UPDATE_WRITE_BARRIER && 126 host() != NULL && 127 target->IsHeapObject()) { 128 host()->GetHeap()->RecordWriteIntoCode(host(), this, target); 129 host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( 130 host(), this, HeapObject::cast(target)); 131 } 132 } 133 134 135 Address RelocInfo::target_external_reference() { 136 DCHECK(rmode_ == RelocInfo::EXTERNAL_REFERENCE); 137 return Memory::Address_at(pc_); 138 } 139 140 141 Address RelocInfo::target_internal_reference() { 142 DCHECK(rmode_ == INTERNAL_REFERENCE); 143 return Memory::Address_at(pc_); 144 } 145 146 147 Address RelocInfo::target_internal_reference_address() { 148 DCHECK(rmode_ == INTERNAL_REFERENCE); 149 return reinterpret_cast<Address>(pc_); 150 } 151 152 153 Address RelocInfo::target_runtime_entry(Assembler* origin) { 154 DCHECK(IsRuntimeEntry(rmode_)); 155 return reinterpret_cast<Address>(*reinterpret_cast<int32_t*>(pc_)); 156 } 157 158 159 void RelocInfo::set_target_runtime_entry(Address target, 160 WriteBarrierMode write_barrier_mode, 161 ICacheFlushMode icache_flush_mode) { 162 DCHECK(IsRuntimeEntry(rmode_)); 163 if (target_address() != target) { 164 set_target_address(target, write_barrier_mode, icache_flush_mode); 165 } 166 } 167 168 169 Handle<Cell> RelocInfo::target_cell_handle() { 170 DCHECK(rmode_ == RelocInfo::CELL); 171 Address address = Memory::Address_at(pc_); 172 return Handle<Cell>(reinterpret_cast<Cell**>(address)); 173 } 174 175 176 Cell* RelocInfo::target_cell() { 177 DCHECK(rmode_ == RelocInfo::CELL); 178 return Cell::FromValueAddress(Memory::Address_at(pc_)); 179 } 180 181 182 void RelocInfo::set_target_cell(Cell* cell, 183 WriteBarrierMode write_barrier_mode, 184 ICacheFlushMode icache_flush_mode) { 185 DCHECK(cell->IsCell()); 186 DCHECK(rmode_ == RelocInfo::CELL); 187 Address address = cell->address() + Cell::kValueOffset; 188 Memory::Address_at(pc_) = address; 189 if (icache_flush_mode != SKIP_ICACHE_FLUSH) { 190 Assembler::FlushICache(isolate_, pc_, sizeof(Address)); 191 } 192 if (write_barrier_mode == UPDATE_WRITE_BARRIER && host() != NULL) { 193 host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(host(), this, 194 cell); 195 } 196 } 197 198 199 Handle<Object> RelocInfo::code_age_stub_handle(Assembler* origin) { 200 DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); 201 DCHECK(*pc_ == kCallOpcode); 202 return Memory::Object_Handle_at(pc_ + 1); 203 } 204 205 206 Code* RelocInfo::code_age_stub() { 207 DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); 208 DCHECK(*pc_ == kCallOpcode); 209 return Code::GetCodeFromTargetAddress( 210 Assembler::target_address_at(pc_ + 1, host_)); 211 } 212 213 214 void RelocInfo::set_code_age_stub(Code* stub, 215 ICacheFlushMode icache_flush_mode) { 216 DCHECK(*pc_ == kCallOpcode); 217 DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); 218 Assembler::set_target_address_at( 219 isolate_, pc_ + 1, host_, stub->instruction_start(), icache_flush_mode); 220 } 221 222 223 Address RelocInfo::debug_call_address() { 224 DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()); 225 Address location = pc_ + Assembler::kPatchDebugBreakSlotAddressOffset; 226 return Assembler::target_address_at(location, host_); 227 } 228 229 230 void RelocInfo::set_debug_call_address(Address target) { 231 DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()); 232 Address location = pc_ + Assembler::kPatchDebugBreakSlotAddressOffset; 233 Assembler::set_target_address_at(isolate_, location, host_, target); 234 if (host() != NULL) { 235 Object* target_code = Code::GetCodeFromTargetAddress(target); 236 host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( 237 host(), this, HeapObject::cast(target_code)); 238 } 239 } 240 241 242 void RelocInfo::WipeOut() { 243 if (IsEmbeddedObject(rmode_) || IsExternalReference(rmode_) || 244 IsInternalReference(rmode_)) { 245 Memory::Address_at(pc_) = NULL; 246 } else if (IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)) { 247 // Effectively write zero into the relocation. 248 Assembler::set_target_address_at(isolate_, pc_, host_, 249 pc_ + sizeof(int32_t)); 250 } else { 251 UNREACHABLE(); 252 } 253 } 254 255 template <typename ObjectVisitor> 256 void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) { 257 RelocInfo::Mode mode = rmode(); 258 if (mode == RelocInfo::EMBEDDED_OBJECT) { 259 visitor->VisitEmbeddedPointer(this); 260 Assembler::FlushICache(isolate, pc_, sizeof(Address)); 261 } else if (RelocInfo::IsCodeTarget(mode)) { 262 visitor->VisitCodeTarget(this); 263 } else if (mode == RelocInfo::CELL) { 264 visitor->VisitCell(this); 265 } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { 266 visitor->VisitExternalReference(this); 267 } else if (mode == RelocInfo::INTERNAL_REFERENCE) { 268 visitor->VisitInternalReference(this); 269 } else if (RelocInfo::IsCodeAgeSequence(mode)) { 270 visitor->VisitCodeAgeSequence(this); 271 } else if (RelocInfo::IsDebugBreakSlot(mode) && 272 IsPatchedDebugBreakSlotSequence()) { 273 visitor->VisitDebugTarget(this); 274 } else if (IsRuntimeEntry(mode)) { 275 visitor->VisitRuntimeEntry(this); 276 } 277 } 278 279 280 template<typename StaticVisitor> 281 void RelocInfo::Visit(Heap* heap) { 282 RelocInfo::Mode mode = rmode(); 283 if (mode == RelocInfo::EMBEDDED_OBJECT) { 284 StaticVisitor::VisitEmbeddedPointer(heap, this); 285 Assembler::FlushICache(heap->isolate(), pc_, sizeof(Address)); 286 } else if (RelocInfo::IsCodeTarget(mode)) { 287 StaticVisitor::VisitCodeTarget(heap, this); 288 } else if (mode == RelocInfo::CELL) { 289 StaticVisitor::VisitCell(heap, this); 290 } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { 291 StaticVisitor::VisitExternalReference(this); 292 } else if (mode == RelocInfo::INTERNAL_REFERENCE) { 293 StaticVisitor::VisitInternalReference(this); 294 } else if (RelocInfo::IsCodeAgeSequence(mode)) { 295 StaticVisitor::VisitCodeAgeSequence(heap, this); 296 } else if (RelocInfo::IsDebugBreakSlot(mode) && 297 IsPatchedDebugBreakSlotSequence()) { 298 StaticVisitor::VisitDebugTarget(heap, this); 299 } else if (IsRuntimeEntry(mode)) { 300 StaticVisitor::VisitRuntimeEntry(this); 301 } 302 } 303 304 305 306 Immediate::Immediate(int x) { 307 x_ = x; 308 rmode_ = RelocInfo::NONE32; 309 } 310 311 Immediate::Immediate(Address x, RelocInfo::Mode rmode) { 312 x_ = reinterpret_cast<int32_t>(x); 313 rmode_ = rmode; 314 } 315 316 Immediate::Immediate(const ExternalReference& ext) { 317 x_ = reinterpret_cast<int32_t>(ext.address()); 318 rmode_ = RelocInfo::EXTERNAL_REFERENCE; 319 } 320 321 322 Immediate::Immediate(Label* internal_offset) { 323 x_ = reinterpret_cast<int32_t>(internal_offset); 324 rmode_ = RelocInfo::INTERNAL_REFERENCE; 325 } 326 327 328 Immediate::Immediate(Handle<Object> handle) { 329 AllowDeferredHandleDereference using_raw_address; 330 // Verify all Objects referred by code are NOT in new space. 331 Object* obj = *handle; 332 if (obj->IsHeapObject()) { 333 x_ = reinterpret_cast<intptr_t>(handle.location()); 334 rmode_ = RelocInfo::EMBEDDED_OBJECT; 335 } else { 336 // no relocation needed 337 x_ = reinterpret_cast<intptr_t>(obj); 338 rmode_ = RelocInfo::NONE32; 339 } 340 } 341 342 343 Immediate::Immediate(Smi* value) { 344 x_ = reinterpret_cast<intptr_t>(value); 345 rmode_ = RelocInfo::NONE32; 346 } 347 348 349 Immediate::Immediate(Address addr) { 350 x_ = reinterpret_cast<int32_t>(addr); 351 rmode_ = RelocInfo::NONE32; 352 } 353 354 355 void Assembler::emit(uint32_t x) { 356 *reinterpret_cast<uint32_t*>(pc_) = x; 357 pc_ += sizeof(uint32_t); 358 } 359 360 361 void Assembler::emit_q(uint64_t x) { 362 *reinterpret_cast<uint64_t*>(pc_) = x; 363 pc_ += sizeof(uint64_t); 364 } 365 366 367 void Assembler::emit(Handle<Object> handle) { 368 AllowDeferredHandleDereference heap_object_check; 369 // Verify all Objects referred by code are NOT in new space. 370 Object* obj = *handle; 371 if (obj->IsHeapObject()) { 372 emit(reinterpret_cast<intptr_t>(handle.location()), 373 RelocInfo::EMBEDDED_OBJECT); 374 } else { 375 // no relocation needed 376 emit(reinterpret_cast<intptr_t>(obj)); 377 } 378 } 379 380 381 void Assembler::emit(uint32_t x, RelocInfo::Mode rmode, TypeFeedbackId id) { 382 if (rmode == RelocInfo::CODE_TARGET && !id.IsNone()) { 383 RecordRelocInfo(RelocInfo::CODE_TARGET_WITH_ID, id.ToInt()); 384 } else if (!RelocInfo::IsNone(rmode) 385 && rmode != RelocInfo::CODE_AGE_SEQUENCE) { 386 RecordRelocInfo(rmode); 387 } 388 emit(x); 389 } 390 391 392 void Assembler::emit(Handle<Code> code, 393 RelocInfo::Mode rmode, 394 TypeFeedbackId id) { 395 AllowDeferredHandleDereference embedding_raw_address; 396 emit(reinterpret_cast<intptr_t>(code.location()), rmode, id); 397 } 398 399 400 void Assembler::emit(const Immediate& x) { 401 if (x.rmode_ == RelocInfo::INTERNAL_REFERENCE) { 402 Label* label = reinterpret_cast<Label*>(x.x_); 403 emit_code_relative_offset(label); 404 return; 405 } 406 if (!RelocInfo::IsNone(x.rmode_)) RecordRelocInfo(x.rmode_); 407 emit(x.x_); 408 } 409 410 411 void Assembler::emit_code_relative_offset(Label* label) { 412 if (label->is_bound()) { 413 int32_t pos; 414 pos = label->pos() + Code::kHeaderSize - kHeapObjectTag; 415 emit(pos); 416 } else { 417 emit_disp(label, Displacement::CODE_RELATIVE); 418 } 419 } 420 421 void Assembler::emit_b(Immediate x) { 422 DCHECK(x.is_int8() || x.is_uint8()); 423 uint8_t value = static_cast<uint8_t>(x.x_); 424 *pc_++ = value; 425 } 426 427 void Assembler::emit_w(const Immediate& x) { 428 DCHECK(RelocInfo::IsNone(x.rmode_)); 429 uint16_t value = static_cast<uint16_t>(x.x_); 430 reinterpret_cast<uint16_t*>(pc_)[0] = value; 431 pc_ += sizeof(uint16_t); 432 } 433 434 435 Address Assembler::target_address_at(Address pc, Address constant_pool) { 436 return pc + sizeof(int32_t) + *reinterpret_cast<int32_t*>(pc); 437 } 438 439 440 void Assembler::set_target_address_at(Isolate* isolate, Address pc, 441 Address constant_pool, Address target, 442 ICacheFlushMode icache_flush_mode) { 443 int32_t* p = reinterpret_cast<int32_t*>(pc); 444 *p = target - (pc + sizeof(int32_t)); 445 if (icache_flush_mode != SKIP_ICACHE_FLUSH) { 446 Assembler::FlushICache(isolate, p, sizeof(int32_t)); 447 } 448 } 449 450 Address Assembler::target_address_at(Address pc, Code* code) { 451 Address constant_pool = code ? code->constant_pool() : NULL; 452 return target_address_at(pc, constant_pool); 453 } 454 455 void Assembler::set_target_address_at(Isolate* isolate, Address pc, Code* code, 456 Address target, 457 ICacheFlushMode icache_flush_mode) { 458 Address constant_pool = code ? code->constant_pool() : NULL; 459 set_target_address_at(isolate, pc, constant_pool, target, icache_flush_mode); 460 } 461 462 Address Assembler::target_address_from_return_address(Address pc) { 463 return pc - kCallTargetAddressOffset; 464 } 465 466 467 Displacement Assembler::disp_at(Label* L) { 468 return Displacement(long_at(L->pos())); 469 } 470 471 472 void Assembler::disp_at_put(Label* L, Displacement disp) { 473 long_at_put(L->pos(), disp.data()); 474 } 475 476 477 void Assembler::emit_disp(Label* L, Displacement::Type type) { 478 Displacement disp(L, type); 479 L->link_to(pc_offset()); 480 emit(static_cast<int>(disp.data())); 481 } 482 483 484 void Assembler::emit_near_disp(Label* L) { 485 byte disp = 0x00; 486 if (L->is_near_linked()) { 487 int offset = L->near_link_pos() - pc_offset(); 488 DCHECK(is_int8(offset)); 489 disp = static_cast<byte>(offset & 0xFF); 490 } 491 L->link_to(pc_offset(), Label::kNear); 492 *pc_++ = disp; 493 } 494 495 496 void Assembler::deserialization_set_target_internal_reference_at( 497 Isolate* isolate, Address pc, Address target, RelocInfo::Mode mode) { 498 Memory::Address_at(pc) = target; 499 } 500 501 502 void Operand::set_modrm(int mod, Register rm) { 503 DCHECK((mod & -4) == 0); 504 buf_[0] = mod << 6 | rm.code(); 505 len_ = 1; 506 } 507 508 509 void Operand::set_sib(ScaleFactor scale, Register index, Register base) { 510 DCHECK(len_ == 1); 511 DCHECK((scale & -4) == 0); 512 // Use SIB with no index register only for base esp. 513 DCHECK(!index.is(esp) || base.is(esp)); 514 buf_[1] = scale << 6 | index.code() << 3 | base.code(); 515 len_ = 2; 516 } 517 518 519 void Operand::set_disp8(int8_t disp) { 520 DCHECK(len_ == 1 || len_ == 2); 521 *reinterpret_cast<int8_t*>(&buf_[len_++]) = disp; 522 } 523 524 525 void Operand::set_dispr(int32_t disp, RelocInfo::Mode rmode) { 526 DCHECK(len_ == 1 || len_ == 2); 527 int32_t* p = reinterpret_cast<int32_t*>(&buf_[len_]); 528 *p = disp; 529 len_ += sizeof(int32_t); 530 rmode_ = rmode; 531 } 532 533 Operand::Operand(Register reg) { 534 // reg 535 set_modrm(3, reg); 536 } 537 538 539 Operand::Operand(int32_t disp, RelocInfo::Mode rmode) { 540 // [disp/r] 541 set_modrm(0, ebp); 542 set_dispr(disp, rmode); 543 } 544 545 546 Operand::Operand(Immediate imm) { 547 // [disp/r] 548 set_modrm(0, ebp); 549 set_dispr(imm.x_, imm.rmode_); 550 } 551 } // namespace internal 552 } // namespace v8 553 554 #endif // V8_X87_ASSEMBLER_X87_INL_H_ 555