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_IA32_ASSEMBLER_IA32_INL_H_ 38 #define V8_IA32_ASSEMBLER_IA32_INL_H_ 39 40 #include "src/ia32/assembler-ia32.h" 41 42 #include "src/assembler.h" 43 #include "src/debug.h" 44 45 namespace v8 { 46 namespace internal { 47 48 bool CpuFeatures::SupportsCrankshaft() { return true; } 49 50 51 static const byte kCallOpcode = 0xE8; 52 static const int kNoCodeAgeSequenceLength = 5; 53 54 55 // The modes possibly affected by apply must be in kApplyMask. 56 void RelocInfo::apply(intptr_t delta, ICacheFlushMode icache_flush_mode) { 57 bool flush_icache = icache_flush_mode != SKIP_ICACHE_FLUSH; 58 if (IsRuntimeEntry(rmode_) || IsCodeTarget(rmode_)) { 59 int32_t* p = reinterpret_cast<int32_t*>(pc_); 60 *p -= delta; // Relocate entry. 61 if (flush_icache) CpuFeatures::FlushICache(p, sizeof(uint32_t)); 62 } else if (rmode_ == CODE_AGE_SEQUENCE) { 63 if (*pc_ == kCallOpcode) { 64 int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1); 65 *p -= delta; // Relocate entry. 66 if (flush_icache) CpuFeatures::FlushICache(p, sizeof(uint32_t)); 67 } 68 } else if (rmode_ == JS_RETURN && IsPatchedReturnSequence()) { 69 // Special handling of js_return when a break point is set (call 70 // instruction has been inserted). 71 int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1); 72 *p -= delta; // Relocate entry. 73 if (flush_icache) CpuFeatures::FlushICache(p, sizeof(uint32_t)); 74 } else if (rmode_ == DEBUG_BREAK_SLOT && IsPatchedDebugBreakSlotSequence()) { 75 // Special handling of a debug break slot when a break point is set (call 76 // instruction has been inserted). 77 int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1); 78 *p -= delta; // Relocate entry. 79 if (flush_icache) CpuFeatures::FlushICache(p, sizeof(uint32_t)); 80 } else if (IsInternalReference(rmode_)) { 81 // absolute code pointer inside code object moves with the code object. 82 int32_t* p = reinterpret_cast<int32_t*>(pc_); 83 *p += delta; // Relocate entry. 84 if (flush_icache) CpuFeatures::FlushICache(p, sizeof(uint32_t)); 85 } 86 } 87 88 89 Address RelocInfo::target_address() { 90 DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)); 91 return Assembler::target_address_at(pc_, host_); 92 } 93 94 95 Address RelocInfo::target_address_address() { 96 DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) 97 || rmode_ == EMBEDDED_OBJECT 98 || rmode_ == EXTERNAL_REFERENCE); 99 return reinterpret_cast<Address>(pc_); 100 } 101 102 103 Address RelocInfo::constant_pool_entry_address() { 104 UNREACHABLE(); 105 return NULL; 106 } 107 108 109 int RelocInfo::target_address_size() { 110 return Assembler::kSpecialTargetSize; 111 } 112 113 114 void RelocInfo::set_target_address(Address target, 115 WriteBarrierMode write_barrier_mode, 116 ICacheFlushMode icache_flush_mode) { 117 Assembler::set_target_address_at(pc_, host_, target, icache_flush_mode); 118 DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)); 119 if (write_barrier_mode == UPDATE_WRITE_BARRIER && host() != NULL && 120 IsCodeTarget(rmode_)) { 121 Object* target_code = Code::GetCodeFromTargetAddress(target); 122 host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( 123 host(), this, HeapObject::cast(target_code)); 124 } 125 } 126 127 128 Object* RelocInfo::target_object() { 129 DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); 130 return Memory::Object_at(pc_); 131 } 132 133 134 Handle<Object> RelocInfo::target_object_handle(Assembler* origin) { 135 DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); 136 return Memory::Object_Handle_at(pc_); 137 } 138 139 140 void RelocInfo::set_target_object(Object* target, 141 WriteBarrierMode write_barrier_mode, 142 ICacheFlushMode icache_flush_mode) { 143 DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); 144 Memory::Object_at(pc_) = target; 145 if (icache_flush_mode != SKIP_ICACHE_FLUSH) { 146 CpuFeatures::FlushICache(pc_, sizeof(Address)); 147 } 148 if (write_barrier_mode == UPDATE_WRITE_BARRIER && 149 host() != NULL && 150 target->IsHeapObject()) { 151 host()->GetHeap()->incremental_marking()->RecordWrite( 152 host(), &Memory::Object_at(pc_), HeapObject::cast(target)); 153 } 154 } 155 156 157 Address RelocInfo::target_reference() { 158 DCHECK(rmode_ == RelocInfo::EXTERNAL_REFERENCE); 159 return Memory::Address_at(pc_); 160 } 161 162 163 Address RelocInfo::target_runtime_entry(Assembler* origin) { 164 DCHECK(IsRuntimeEntry(rmode_)); 165 return reinterpret_cast<Address>(*reinterpret_cast<int32_t*>(pc_)); 166 } 167 168 169 void RelocInfo::set_target_runtime_entry(Address target, 170 WriteBarrierMode write_barrier_mode, 171 ICacheFlushMode icache_flush_mode) { 172 DCHECK(IsRuntimeEntry(rmode_)); 173 if (target_address() != target) { 174 set_target_address(target, write_barrier_mode, icache_flush_mode); 175 } 176 } 177 178 179 Handle<Cell> RelocInfo::target_cell_handle() { 180 DCHECK(rmode_ == RelocInfo::CELL); 181 Address address = Memory::Address_at(pc_); 182 return Handle<Cell>(reinterpret_cast<Cell**>(address)); 183 } 184 185 186 Cell* RelocInfo::target_cell() { 187 DCHECK(rmode_ == RelocInfo::CELL); 188 return Cell::FromValueAddress(Memory::Address_at(pc_)); 189 } 190 191 192 void RelocInfo::set_target_cell(Cell* cell, 193 WriteBarrierMode write_barrier_mode, 194 ICacheFlushMode icache_flush_mode) { 195 DCHECK(rmode_ == RelocInfo::CELL); 196 Address address = cell->address() + Cell::kValueOffset; 197 Memory::Address_at(pc_) = address; 198 if (icache_flush_mode != SKIP_ICACHE_FLUSH) { 199 CpuFeatures::FlushICache(pc_, sizeof(Address)); 200 } 201 if (write_barrier_mode == UPDATE_WRITE_BARRIER && host() != NULL) { 202 // TODO(1550) We are passing NULL as a slot because cell can never be on 203 // evacuation candidate. 204 host()->GetHeap()->incremental_marking()->RecordWrite( 205 host(), NULL, cell); 206 } 207 } 208 209 210 Handle<Object> RelocInfo::code_age_stub_handle(Assembler* origin) { 211 DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); 212 DCHECK(*pc_ == kCallOpcode); 213 return Memory::Object_Handle_at(pc_ + 1); 214 } 215 216 217 Code* RelocInfo::code_age_stub() { 218 DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); 219 DCHECK(*pc_ == kCallOpcode); 220 return Code::GetCodeFromTargetAddress( 221 Assembler::target_address_at(pc_ + 1, host_)); 222 } 223 224 225 void RelocInfo::set_code_age_stub(Code* stub, 226 ICacheFlushMode icache_flush_mode) { 227 DCHECK(*pc_ == kCallOpcode); 228 DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); 229 Assembler::set_target_address_at(pc_ + 1, host_, stub->instruction_start(), 230 icache_flush_mode); 231 } 232 233 234 Address RelocInfo::call_address() { 235 DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) || 236 (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence())); 237 return Assembler::target_address_at(pc_ + 1, host_); 238 } 239 240 241 void RelocInfo::set_call_address(Address target) { 242 DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) || 243 (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence())); 244 Assembler::set_target_address_at(pc_ + 1, host_, target); 245 if (host() != NULL) { 246 Object* target_code = Code::GetCodeFromTargetAddress(target); 247 host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( 248 host(), this, HeapObject::cast(target_code)); 249 } 250 } 251 252 253 Object* RelocInfo::call_object() { 254 return *call_object_address(); 255 } 256 257 258 void RelocInfo::set_call_object(Object* target) { 259 *call_object_address() = target; 260 } 261 262 263 Object** RelocInfo::call_object_address() { 264 DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) || 265 (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence())); 266 return reinterpret_cast<Object**>(pc_ + 1); 267 } 268 269 270 void RelocInfo::WipeOut() { 271 if (IsEmbeddedObject(rmode_) || IsExternalReference(rmode_)) { 272 Memory::Address_at(pc_) = NULL; 273 } else if (IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)) { 274 // Effectively write zero into the relocation. 275 Assembler::set_target_address_at(pc_, host_, pc_ + sizeof(int32_t)); 276 } else { 277 UNREACHABLE(); 278 } 279 } 280 281 282 bool RelocInfo::IsPatchedReturnSequence() { 283 return *pc_ == kCallOpcode; 284 } 285 286 287 bool RelocInfo::IsPatchedDebugBreakSlotSequence() { 288 return !Assembler::IsNop(pc()); 289 } 290 291 292 void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) { 293 RelocInfo::Mode mode = rmode(); 294 if (mode == RelocInfo::EMBEDDED_OBJECT) { 295 visitor->VisitEmbeddedPointer(this); 296 CpuFeatures::FlushICache(pc_, sizeof(Address)); 297 } else if (RelocInfo::IsCodeTarget(mode)) { 298 visitor->VisitCodeTarget(this); 299 } else if (mode == RelocInfo::CELL) { 300 visitor->VisitCell(this); 301 } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { 302 visitor->VisitExternalReference(this); 303 CpuFeatures::FlushICache(pc_, sizeof(Address)); 304 } else if (RelocInfo::IsCodeAgeSequence(mode)) { 305 visitor->VisitCodeAgeSequence(this); 306 } else if (((RelocInfo::IsJSReturn(mode) && 307 IsPatchedReturnSequence()) || 308 (RelocInfo::IsDebugBreakSlot(mode) && 309 IsPatchedDebugBreakSlotSequence())) && 310 isolate->debug()->has_break_points()) { 311 visitor->VisitDebugTarget(this); 312 } else if (IsRuntimeEntry(mode)) { 313 visitor->VisitRuntimeEntry(this); 314 } 315 } 316 317 318 template<typename StaticVisitor> 319 void RelocInfo::Visit(Heap* heap) { 320 RelocInfo::Mode mode = rmode(); 321 if (mode == RelocInfo::EMBEDDED_OBJECT) { 322 StaticVisitor::VisitEmbeddedPointer(heap, this); 323 CpuFeatures::FlushICache(pc_, sizeof(Address)); 324 } else if (RelocInfo::IsCodeTarget(mode)) { 325 StaticVisitor::VisitCodeTarget(heap, this); 326 } else if (mode == RelocInfo::CELL) { 327 StaticVisitor::VisitCell(heap, this); 328 } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { 329 StaticVisitor::VisitExternalReference(this); 330 CpuFeatures::FlushICache(pc_, sizeof(Address)); 331 } else if (RelocInfo::IsCodeAgeSequence(mode)) { 332 StaticVisitor::VisitCodeAgeSequence(heap, this); 333 } else if (heap->isolate()->debug()->has_break_points() && 334 ((RelocInfo::IsJSReturn(mode) && 335 IsPatchedReturnSequence()) || 336 (RelocInfo::IsDebugBreakSlot(mode) && 337 IsPatchedDebugBreakSlotSequence()))) { 338 StaticVisitor::VisitDebugTarget(heap, this); 339 } else if (IsRuntimeEntry(mode)) { 340 StaticVisitor::VisitRuntimeEntry(this); 341 } 342 } 343 344 345 346 Immediate::Immediate(int x) { 347 x_ = x; 348 rmode_ = RelocInfo::NONE32; 349 } 350 351 352 Immediate::Immediate(const ExternalReference& ext) { 353 x_ = reinterpret_cast<int32_t>(ext.address()); 354 rmode_ = RelocInfo::EXTERNAL_REFERENCE; 355 } 356 357 358 Immediate::Immediate(Label* internal_offset) { 359 x_ = reinterpret_cast<int32_t>(internal_offset); 360 rmode_ = RelocInfo::INTERNAL_REFERENCE; 361 } 362 363 364 Immediate::Immediate(Handle<Object> handle) { 365 AllowDeferredHandleDereference using_raw_address; 366 // Verify all Objects referred by code are NOT in new space. 367 Object* obj = *handle; 368 if (obj->IsHeapObject()) { 369 DCHECK(!HeapObject::cast(obj)->GetHeap()->InNewSpace(obj)); 370 x_ = reinterpret_cast<intptr_t>(handle.location()); 371 rmode_ = RelocInfo::EMBEDDED_OBJECT; 372 } else { 373 // no relocation needed 374 x_ = reinterpret_cast<intptr_t>(obj); 375 rmode_ = RelocInfo::NONE32; 376 } 377 } 378 379 380 Immediate::Immediate(Smi* value) { 381 x_ = reinterpret_cast<intptr_t>(value); 382 rmode_ = RelocInfo::NONE32; 383 } 384 385 386 Immediate::Immediate(Address addr) { 387 x_ = reinterpret_cast<int32_t>(addr); 388 rmode_ = RelocInfo::NONE32; 389 } 390 391 392 void Assembler::emit(uint32_t x) { 393 *reinterpret_cast<uint32_t*>(pc_) = x; 394 pc_ += sizeof(uint32_t); 395 } 396 397 398 void Assembler::emit(Handle<Object> handle) { 399 AllowDeferredHandleDereference heap_object_check; 400 // Verify all Objects referred by code are NOT in new space. 401 Object* obj = *handle; 402 DCHECK(!isolate()->heap()->InNewSpace(obj)); 403 if (obj->IsHeapObject()) { 404 emit(reinterpret_cast<intptr_t>(handle.location()), 405 RelocInfo::EMBEDDED_OBJECT); 406 } else { 407 // no relocation needed 408 emit(reinterpret_cast<intptr_t>(obj)); 409 } 410 } 411 412 413 void Assembler::emit(uint32_t x, RelocInfo::Mode rmode, TypeFeedbackId id) { 414 if (rmode == RelocInfo::CODE_TARGET && !id.IsNone()) { 415 RecordRelocInfo(RelocInfo::CODE_TARGET_WITH_ID, id.ToInt()); 416 } else if (!RelocInfo::IsNone(rmode) 417 && rmode != RelocInfo::CODE_AGE_SEQUENCE) { 418 RecordRelocInfo(rmode); 419 } 420 emit(x); 421 } 422 423 424 void Assembler::emit(Handle<Code> code, 425 RelocInfo::Mode rmode, 426 TypeFeedbackId id) { 427 AllowDeferredHandleDereference embedding_raw_address; 428 emit(reinterpret_cast<intptr_t>(code.location()), rmode, id); 429 } 430 431 432 void Assembler::emit(const Immediate& x) { 433 if (x.rmode_ == RelocInfo::INTERNAL_REFERENCE) { 434 Label* label = reinterpret_cast<Label*>(x.x_); 435 emit_code_relative_offset(label); 436 return; 437 } 438 if (!RelocInfo::IsNone(x.rmode_)) RecordRelocInfo(x.rmode_); 439 emit(x.x_); 440 } 441 442 443 void Assembler::emit_code_relative_offset(Label* label) { 444 if (label->is_bound()) { 445 int32_t pos; 446 pos = label->pos() + Code::kHeaderSize - kHeapObjectTag; 447 emit(pos); 448 } else { 449 emit_disp(label, Displacement::CODE_RELATIVE); 450 } 451 } 452 453 454 void Assembler::emit_w(const Immediate& x) { 455 DCHECK(RelocInfo::IsNone(x.rmode_)); 456 uint16_t value = static_cast<uint16_t>(x.x_); 457 reinterpret_cast<uint16_t*>(pc_)[0] = value; 458 pc_ += sizeof(uint16_t); 459 } 460 461 462 Address Assembler::target_address_at(Address pc, 463 ConstantPoolArray* constant_pool) { 464 return pc + sizeof(int32_t) + *reinterpret_cast<int32_t*>(pc); 465 } 466 467 468 void Assembler::set_target_address_at(Address pc, 469 ConstantPoolArray* constant_pool, 470 Address target, 471 ICacheFlushMode icache_flush_mode) { 472 int32_t* p = reinterpret_cast<int32_t*>(pc); 473 *p = target - (pc + sizeof(int32_t)); 474 if (icache_flush_mode != SKIP_ICACHE_FLUSH) { 475 CpuFeatures::FlushICache(p, sizeof(int32_t)); 476 } 477 } 478 479 480 Address Assembler::target_address_from_return_address(Address pc) { 481 return pc - kCallTargetAddressOffset; 482 } 483 484 485 Address Assembler::break_address_from_return_address(Address pc) { 486 return pc - Assembler::kPatchDebugBreakSlotReturnOffset; 487 } 488 489 490 Displacement Assembler::disp_at(Label* L) { 491 return Displacement(long_at(L->pos())); 492 } 493 494 495 void Assembler::disp_at_put(Label* L, Displacement disp) { 496 long_at_put(L->pos(), disp.data()); 497 } 498 499 500 void Assembler::emit_disp(Label* L, Displacement::Type type) { 501 Displacement disp(L, type); 502 L->link_to(pc_offset()); 503 emit(static_cast<int>(disp.data())); 504 } 505 506 507 void Assembler::emit_near_disp(Label* L) { 508 byte disp = 0x00; 509 if (L->is_near_linked()) { 510 int offset = L->near_link_pos() - pc_offset(); 511 DCHECK(is_int8(offset)); 512 disp = static_cast<byte>(offset & 0xFF); 513 } 514 L->link_to(pc_offset(), Label::kNear); 515 *pc_++ = disp; 516 } 517 518 519 void Operand::set_modrm(int mod, Register rm) { 520 DCHECK((mod & -4) == 0); 521 buf_[0] = mod << 6 | rm.code(); 522 len_ = 1; 523 } 524 525 526 void Operand::set_sib(ScaleFactor scale, Register index, Register base) { 527 DCHECK(len_ == 1); 528 DCHECK((scale & -4) == 0); 529 // Use SIB with no index register only for base esp. 530 DCHECK(!index.is(esp) || base.is(esp)); 531 buf_[1] = scale << 6 | index.code() << 3 | base.code(); 532 len_ = 2; 533 } 534 535 536 void Operand::set_disp8(int8_t disp) { 537 DCHECK(len_ == 1 || len_ == 2); 538 *reinterpret_cast<int8_t*>(&buf_[len_++]) = disp; 539 } 540 541 542 void Operand::set_dispr(int32_t disp, RelocInfo::Mode rmode) { 543 DCHECK(len_ == 1 || len_ == 2); 544 int32_t* p = reinterpret_cast<int32_t*>(&buf_[len_]); 545 *p = disp; 546 len_ += sizeof(int32_t); 547 rmode_ = rmode; 548 } 549 550 Operand::Operand(Register reg) { 551 // reg 552 set_modrm(3, reg); 553 } 554 555 556 Operand::Operand(XMMRegister xmm_reg) { 557 Register reg = { xmm_reg.code() }; 558 set_modrm(3, reg); 559 } 560 561 562 Operand::Operand(int32_t disp, RelocInfo::Mode rmode) { 563 // [disp/r] 564 set_modrm(0, ebp); 565 set_dispr(disp, rmode); 566 } 567 568 569 Operand::Operand(Immediate imm) { 570 // [disp/r] 571 set_modrm(0, ebp); 572 set_dispr(imm.x_, imm.rmode_); 573 } 574 } } // namespace v8::internal 575 576 #endif // V8_IA32_ASSEMBLER_IA32_INL_H_ 577