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 45namespace v8 { 46namespace internal { 47 48bool CpuFeatures::SupportsCrankshaft() { return true; } 49 50 51static const byte kCallOpcode = 0xE8; 52static const int kNoCodeAgeSequenceLength = 5; 53 54 55// The modes possibly affected by apply must be in kApplyMask. 56void 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 89Address RelocInfo::target_address() { 90 DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)); 91 return Assembler::target_address_at(pc_, host_); 92} 93 94 95Address 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 103Address RelocInfo::constant_pool_entry_address() { 104 UNREACHABLE(); 105 return NULL; 106} 107 108 109int RelocInfo::target_address_size() { 110 return Assembler::kSpecialTargetSize; 111} 112 113 114void 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 128Object* RelocInfo::target_object() { 129 DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); 130 return Memory::Object_at(pc_); 131} 132 133 134Handle<Object> RelocInfo::target_object_handle(Assembler* origin) { 135 DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); 136 return Memory::Object_Handle_at(pc_); 137} 138 139 140void 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 157Address RelocInfo::target_reference() { 158 DCHECK(rmode_ == RelocInfo::EXTERNAL_REFERENCE); 159 return Memory::Address_at(pc_); 160} 161 162 163Address RelocInfo::target_runtime_entry(Assembler* origin) { 164 DCHECK(IsRuntimeEntry(rmode_)); 165 return reinterpret_cast<Address>(*reinterpret_cast<int32_t*>(pc_)); 166} 167 168 169void 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 179Handle<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 186Cell* RelocInfo::target_cell() { 187 DCHECK(rmode_ == RelocInfo::CELL); 188 return Cell::FromValueAddress(Memory::Address_at(pc_)); 189} 190 191 192void 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 210Handle<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 217Code* 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 225void 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 234Address RelocInfo::call_address() { 235 DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) || 236 (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence())); 237 return Assembler::target_address_at(pc_ + 1, host_); 238} 239 240 241void 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 253Object* RelocInfo::call_object() { 254 return *call_object_address(); 255} 256 257 258void RelocInfo::set_call_object(Object* target) { 259 *call_object_address() = target; 260} 261 262 263Object** RelocInfo::call_object_address() { 264 DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) || 265 (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence())); 266 return reinterpret_cast<Object**>(pc_ + 1); 267} 268 269 270void 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 282bool RelocInfo::IsPatchedReturnSequence() { 283 return *pc_ == kCallOpcode; 284} 285 286 287bool RelocInfo::IsPatchedDebugBreakSlotSequence() { 288 return !Assembler::IsNop(pc()); 289} 290 291 292void 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 318template<typename StaticVisitor> 319void 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 346Immediate::Immediate(int x) { 347 x_ = x; 348 rmode_ = RelocInfo::NONE32; 349} 350 351 352Immediate::Immediate(const ExternalReference& ext) { 353 x_ = reinterpret_cast<int32_t>(ext.address()); 354 rmode_ = RelocInfo::EXTERNAL_REFERENCE; 355} 356 357 358Immediate::Immediate(Label* internal_offset) { 359 x_ = reinterpret_cast<int32_t>(internal_offset); 360 rmode_ = RelocInfo::INTERNAL_REFERENCE; 361} 362 363 364Immediate::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 380Immediate::Immediate(Smi* value) { 381 x_ = reinterpret_cast<intptr_t>(value); 382 rmode_ = RelocInfo::NONE32; 383} 384 385 386Immediate::Immediate(Address addr) { 387 x_ = reinterpret_cast<int32_t>(addr); 388 rmode_ = RelocInfo::NONE32; 389} 390 391 392void Assembler::emit(uint32_t x) { 393 *reinterpret_cast<uint32_t*>(pc_) = x; 394 pc_ += sizeof(uint32_t); 395} 396 397 398void 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 413void 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 424void 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 432void 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 443void 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 454void 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 462Address Assembler::target_address_at(Address pc, 463 ConstantPoolArray* constant_pool) { 464 return pc + sizeof(int32_t) + *reinterpret_cast<int32_t*>(pc); 465} 466 467 468void 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 480Address Assembler::target_address_from_return_address(Address pc) { 481 return pc - kCallTargetAddressOffset; 482} 483 484 485Address Assembler::break_address_from_return_address(Address pc) { 486 return pc - Assembler::kPatchDebugBreakSlotReturnOffset; 487} 488 489 490Displacement Assembler::disp_at(Label* L) { 491 return Displacement(long_at(L->pos())); 492} 493 494 495void Assembler::disp_at_put(Label* L, Displacement disp) { 496 long_at_put(L->pos(), disp.data()); 497} 498 499 500void 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 507void 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 519void 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 526void 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 536void Operand::set_disp8(int8_t disp) { 537 DCHECK(len_ == 1 || len_ == 2); 538 *reinterpret_cast<int8_t*>(&buf_[len_++]) = disp; 539} 540 541 542void 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 550Operand::Operand(Register reg) { 551 // reg 552 set_modrm(3, reg); 553} 554 555 556Operand::Operand(XMMRegister xmm_reg) { 557 Register reg = { xmm_reg.code() }; 558 set_modrm(3, reg); 559} 560 561 562Operand::Operand(int32_t disp, RelocInfo::Mode rmode) { 563 // [disp/r] 564 set_modrm(0, ebp); 565 set_dispr(disp, rmode); 566} 567 568 569Operand::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