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