1// Copyright 2012 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_X64_ASSEMBLER_X64_INL_H_ 6#define V8_X64_ASSEMBLER_X64_INL_H_ 7 8#include "src/x64/assembler-x64.h" 9 10#include "src/base/cpu.h" 11#include "src/debug.h" 12#include "src/v8memory.h" 13 14namespace v8 { 15namespace internal { 16 17bool CpuFeatures::SupportsCrankshaft() { return true; } 18 19 20// ----------------------------------------------------------------------------- 21// Implementation of Assembler 22 23 24static const byte kCallOpcode = 0xE8; 25// The length of pushq(rbp), movp(rbp, rsp), Push(rsi) and Push(rdi). 26static const int kNoCodeAgeSequenceLength = kPointerSize == kInt64Size ? 6 : 17; 27 28 29void Assembler::emitl(uint32_t x) { 30 Memory::uint32_at(pc_) = x; 31 pc_ += sizeof(uint32_t); 32} 33 34 35void Assembler::emitp(void* x, RelocInfo::Mode rmode) { 36 uintptr_t value = reinterpret_cast<uintptr_t>(x); 37 Memory::uintptr_at(pc_) = value; 38 if (!RelocInfo::IsNone(rmode)) { 39 RecordRelocInfo(rmode, value); 40 } 41 pc_ += sizeof(uintptr_t); 42} 43 44 45void Assembler::emitq(uint64_t x) { 46 Memory::uint64_at(pc_) = x; 47 pc_ += sizeof(uint64_t); 48} 49 50 51void Assembler::emitw(uint16_t x) { 52 Memory::uint16_at(pc_) = x; 53 pc_ += sizeof(uint16_t); 54} 55 56 57void Assembler::emit_code_target(Handle<Code> target, 58 RelocInfo::Mode rmode, 59 TypeFeedbackId ast_id) { 60 DCHECK(RelocInfo::IsCodeTarget(rmode) || 61 rmode == RelocInfo::CODE_AGE_SEQUENCE); 62 if (rmode == RelocInfo::CODE_TARGET && !ast_id.IsNone()) { 63 RecordRelocInfo(RelocInfo::CODE_TARGET_WITH_ID, ast_id.ToInt()); 64 } else { 65 RecordRelocInfo(rmode); 66 } 67 int current = code_targets_.length(); 68 if (current > 0 && code_targets_.last().is_identical_to(target)) { 69 // Optimization if we keep jumping to the same code target. 70 emitl(current - 1); 71 } else { 72 code_targets_.Add(target); 73 emitl(current); 74 } 75} 76 77 78void Assembler::emit_runtime_entry(Address entry, RelocInfo::Mode rmode) { 79 DCHECK(RelocInfo::IsRuntimeEntry(rmode)); 80 RecordRelocInfo(rmode); 81 emitl(static_cast<uint32_t>(entry - isolate()->code_range()->start())); 82} 83 84 85void Assembler::emit_rex_64(Register reg, Register rm_reg) { 86 emit(0x48 | reg.high_bit() << 2 | rm_reg.high_bit()); 87} 88 89 90void Assembler::emit_rex_64(XMMRegister reg, Register rm_reg) { 91 emit(0x48 | (reg.code() & 0x8) >> 1 | rm_reg.code() >> 3); 92} 93 94 95void Assembler::emit_rex_64(Register reg, XMMRegister rm_reg) { 96 emit(0x48 | (reg.code() & 0x8) >> 1 | rm_reg.code() >> 3); 97} 98 99 100void Assembler::emit_rex_64(Register reg, const Operand& op) { 101 emit(0x48 | reg.high_bit() << 2 | op.rex_); 102} 103 104 105void Assembler::emit_rex_64(XMMRegister reg, const Operand& op) { 106 emit(0x48 | (reg.code() & 0x8) >> 1 | op.rex_); 107} 108 109 110void Assembler::emit_rex_64(Register rm_reg) { 111 DCHECK_EQ(rm_reg.code() & 0xf, rm_reg.code()); 112 emit(0x48 | rm_reg.high_bit()); 113} 114 115 116void Assembler::emit_rex_64(const Operand& op) { 117 emit(0x48 | op.rex_); 118} 119 120 121void Assembler::emit_rex_32(Register reg, Register rm_reg) { 122 emit(0x40 | reg.high_bit() << 2 | rm_reg.high_bit()); 123} 124 125 126void Assembler::emit_rex_32(Register reg, const Operand& op) { 127 emit(0x40 | reg.high_bit() << 2 | op.rex_); 128} 129 130 131void Assembler::emit_rex_32(Register rm_reg) { 132 emit(0x40 | rm_reg.high_bit()); 133} 134 135 136void Assembler::emit_rex_32(const Operand& op) { 137 emit(0x40 | op.rex_); 138} 139 140 141void Assembler::emit_optional_rex_32(Register reg, Register rm_reg) { 142 byte rex_bits = reg.high_bit() << 2 | rm_reg.high_bit(); 143 if (rex_bits != 0) emit(0x40 | rex_bits); 144} 145 146 147void Assembler::emit_optional_rex_32(Register reg, const Operand& op) { 148 byte rex_bits = reg.high_bit() << 2 | op.rex_; 149 if (rex_bits != 0) emit(0x40 | rex_bits); 150} 151 152 153void Assembler::emit_optional_rex_32(XMMRegister reg, const Operand& op) { 154 byte rex_bits = (reg.code() & 0x8) >> 1 | op.rex_; 155 if (rex_bits != 0) emit(0x40 | rex_bits); 156} 157 158 159void Assembler::emit_optional_rex_32(XMMRegister reg, XMMRegister base) { 160 byte rex_bits = (reg.code() & 0x8) >> 1 | (base.code() & 0x8) >> 3; 161 if (rex_bits != 0) emit(0x40 | rex_bits); 162} 163 164 165void Assembler::emit_optional_rex_32(XMMRegister reg, Register base) { 166 byte rex_bits = (reg.code() & 0x8) >> 1 | (base.code() & 0x8) >> 3; 167 if (rex_bits != 0) emit(0x40 | rex_bits); 168} 169 170 171void Assembler::emit_optional_rex_32(Register reg, XMMRegister base) { 172 byte rex_bits = (reg.code() & 0x8) >> 1 | (base.code() & 0x8) >> 3; 173 if (rex_bits != 0) emit(0x40 | rex_bits); 174} 175 176 177void Assembler::emit_optional_rex_32(Register rm_reg) { 178 if (rm_reg.high_bit()) emit(0x41); 179} 180 181 182void Assembler::emit_optional_rex_32(const Operand& op) { 183 if (op.rex_ != 0) emit(0x40 | op.rex_); 184} 185 186 187Address Assembler::target_address_at(Address pc, 188 ConstantPoolArray* constant_pool) { 189 return Memory::int32_at(pc) + pc + 4; 190} 191 192 193void Assembler::set_target_address_at(Address pc, 194 ConstantPoolArray* constant_pool, 195 Address target, 196 ICacheFlushMode icache_flush_mode) { 197 Memory::int32_at(pc) = static_cast<int32_t>(target - pc - 4); 198 if (icache_flush_mode != SKIP_ICACHE_FLUSH) { 199 CpuFeatures::FlushICache(pc, sizeof(int32_t)); 200 } 201} 202 203 204Address Assembler::target_address_from_return_address(Address pc) { 205 return pc - kCallTargetAddressOffset; 206} 207 208 209Address Assembler::break_address_from_return_address(Address pc) { 210 return pc - Assembler::kPatchDebugBreakSlotReturnOffset; 211} 212 213 214Handle<Object> Assembler::code_target_object_handle_at(Address pc) { 215 return code_targets_[Memory::int32_at(pc)]; 216} 217 218 219Address Assembler::runtime_entry_at(Address pc) { 220 return Memory::int32_at(pc) + isolate()->code_range()->start(); 221} 222 223// ----------------------------------------------------------------------------- 224// Implementation of RelocInfo 225 226// The modes possibly affected by apply must be in kApplyMask. 227void RelocInfo::apply(intptr_t delta, ICacheFlushMode icache_flush_mode) { 228 bool flush_icache = icache_flush_mode != SKIP_ICACHE_FLUSH; 229 if (IsInternalReference(rmode_)) { 230 // absolute code pointer inside code object moves with the code object. 231 Memory::Address_at(pc_) += static_cast<int32_t>(delta); 232 if (flush_icache) CpuFeatures::FlushICache(pc_, sizeof(Address)); 233 } else if (IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)) { 234 Memory::int32_at(pc_) -= static_cast<int32_t>(delta); 235 if (flush_icache) CpuFeatures::FlushICache(pc_, sizeof(int32_t)); 236 } else if (rmode_ == CODE_AGE_SEQUENCE) { 237 if (*pc_ == kCallOpcode) { 238 int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1); 239 *p -= static_cast<int32_t>(delta); // Relocate entry. 240 if (flush_icache) CpuFeatures::FlushICache(p, sizeof(uint32_t)); 241 } 242 } 243} 244 245 246Address RelocInfo::target_address() { 247 DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)); 248 return Assembler::target_address_at(pc_, host_); 249} 250 251 252Address RelocInfo::target_address_address() { 253 DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) 254 || rmode_ == EMBEDDED_OBJECT 255 || rmode_ == EXTERNAL_REFERENCE); 256 return reinterpret_cast<Address>(pc_); 257} 258 259 260Address RelocInfo::constant_pool_entry_address() { 261 UNREACHABLE(); 262 return NULL; 263} 264 265 266int RelocInfo::target_address_size() { 267 if (IsCodedSpecially()) { 268 return Assembler::kSpecialTargetSize; 269 } else { 270 return kPointerSize; 271 } 272} 273 274 275void RelocInfo::set_target_address(Address target, 276 WriteBarrierMode write_barrier_mode, 277 ICacheFlushMode icache_flush_mode) { 278 DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)); 279 Assembler::set_target_address_at(pc_, host_, target, icache_flush_mode); 280 if (write_barrier_mode == UPDATE_WRITE_BARRIER && host() != NULL && 281 IsCodeTarget(rmode_)) { 282 Object* target_code = Code::GetCodeFromTargetAddress(target); 283 host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( 284 host(), this, HeapObject::cast(target_code)); 285 } 286} 287 288 289Object* RelocInfo::target_object() { 290 DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); 291 return Memory::Object_at(pc_); 292} 293 294 295Handle<Object> RelocInfo::target_object_handle(Assembler* origin) { 296 DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); 297 if (rmode_ == EMBEDDED_OBJECT) { 298 return Memory::Object_Handle_at(pc_); 299 } else { 300 return origin->code_target_object_handle_at(pc_); 301 } 302} 303 304 305Address RelocInfo::target_reference() { 306 DCHECK(rmode_ == RelocInfo::EXTERNAL_REFERENCE); 307 return Memory::Address_at(pc_); 308} 309 310 311void RelocInfo::set_target_object(Object* target, 312 WriteBarrierMode write_barrier_mode, 313 ICacheFlushMode icache_flush_mode) { 314 DCHECK(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); 315 Memory::Object_at(pc_) = target; 316 if (icache_flush_mode != SKIP_ICACHE_FLUSH) { 317 CpuFeatures::FlushICache(pc_, sizeof(Address)); 318 } 319 if (write_barrier_mode == UPDATE_WRITE_BARRIER && 320 host() != NULL && 321 target->IsHeapObject()) { 322 host()->GetHeap()->incremental_marking()->RecordWrite( 323 host(), &Memory::Object_at(pc_), HeapObject::cast(target)); 324 } 325} 326 327 328Address RelocInfo::target_runtime_entry(Assembler* origin) { 329 DCHECK(IsRuntimeEntry(rmode_)); 330 return origin->runtime_entry_at(pc_); 331} 332 333 334void RelocInfo::set_target_runtime_entry(Address target, 335 WriteBarrierMode write_barrier_mode, 336 ICacheFlushMode icache_flush_mode) { 337 DCHECK(IsRuntimeEntry(rmode_)); 338 if (target_address() != target) { 339 set_target_address(target, write_barrier_mode, icache_flush_mode); 340 } 341} 342 343 344Handle<Cell> RelocInfo::target_cell_handle() { 345 DCHECK(rmode_ == RelocInfo::CELL); 346 Address address = Memory::Address_at(pc_); 347 return Handle<Cell>(reinterpret_cast<Cell**>(address)); 348} 349 350 351Cell* RelocInfo::target_cell() { 352 DCHECK(rmode_ == RelocInfo::CELL); 353 return Cell::FromValueAddress(Memory::Address_at(pc_)); 354} 355 356 357void RelocInfo::set_target_cell(Cell* cell, 358 WriteBarrierMode write_barrier_mode, 359 ICacheFlushMode icache_flush_mode) { 360 DCHECK(rmode_ == RelocInfo::CELL); 361 Address address = cell->address() + Cell::kValueOffset; 362 Memory::Address_at(pc_) = address; 363 if (icache_flush_mode != SKIP_ICACHE_FLUSH) { 364 CpuFeatures::FlushICache(pc_, sizeof(Address)); 365 } 366 if (write_barrier_mode == UPDATE_WRITE_BARRIER && 367 host() != NULL) { 368 // TODO(1550) We are passing NULL as a slot because cell can never be on 369 // evacuation candidate. 370 host()->GetHeap()->incremental_marking()->RecordWrite( 371 host(), NULL, cell); 372 } 373} 374 375 376void RelocInfo::WipeOut() { 377 if (IsEmbeddedObject(rmode_) || IsExternalReference(rmode_)) { 378 Memory::Address_at(pc_) = NULL; 379 } else if (IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)) { 380 // Effectively write zero into the relocation. 381 Assembler::set_target_address_at(pc_, host_, pc_ + sizeof(int32_t)); 382 } else { 383 UNREACHABLE(); 384 } 385} 386 387 388bool RelocInfo::IsPatchedReturnSequence() { 389 // The recognized call sequence is: 390 // movq(kScratchRegister, address); call(kScratchRegister); 391 // It only needs to be distinguished from a return sequence 392 // movq(rsp, rbp); pop(rbp); ret(n); int3 *6 393 // The 11th byte is int3 (0xCC) in the return sequence and 394 // REX.WB (0x48+register bit) for the call sequence. 395 return pc_[Assembler::kMoveAddressIntoScratchRegisterInstructionLength] != 396 0xCC; 397} 398 399 400bool RelocInfo::IsPatchedDebugBreakSlotSequence() { 401 return !Assembler::IsNop(pc()); 402} 403 404 405Handle<Object> RelocInfo::code_age_stub_handle(Assembler* origin) { 406 DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); 407 DCHECK(*pc_ == kCallOpcode); 408 return origin->code_target_object_handle_at(pc_ + 1); 409} 410 411 412Code* RelocInfo::code_age_stub() { 413 DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); 414 DCHECK(*pc_ == kCallOpcode); 415 return Code::GetCodeFromTargetAddress( 416 Assembler::target_address_at(pc_ + 1, host_)); 417} 418 419 420void RelocInfo::set_code_age_stub(Code* stub, 421 ICacheFlushMode icache_flush_mode) { 422 DCHECK(*pc_ == kCallOpcode); 423 DCHECK(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); 424 Assembler::set_target_address_at(pc_ + 1, host_, stub->instruction_start(), 425 icache_flush_mode); 426} 427 428 429Address RelocInfo::call_address() { 430 DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) || 431 (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence())); 432 return Memory::Address_at( 433 pc_ + Assembler::kRealPatchReturnSequenceAddressOffset); 434} 435 436 437void RelocInfo::set_call_address(Address target) { 438 DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) || 439 (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence())); 440 Memory::Address_at(pc_ + Assembler::kRealPatchReturnSequenceAddressOffset) = 441 target; 442 CpuFeatures::FlushICache( 443 pc_ + Assembler::kRealPatchReturnSequenceAddressOffset, sizeof(Address)); 444 if (host() != NULL) { 445 Object* target_code = Code::GetCodeFromTargetAddress(target); 446 host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( 447 host(), this, HeapObject::cast(target_code)); 448 } 449} 450 451 452Object* RelocInfo::call_object() { 453 return *call_object_address(); 454} 455 456 457void RelocInfo::set_call_object(Object* target) { 458 *call_object_address() = target; 459} 460 461 462Object** RelocInfo::call_object_address() { 463 DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) || 464 (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence())); 465 return reinterpret_cast<Object**>( 466 pc_ + Assembler::kPatchReturnSequenceAddressOffset); 467} 468 469 470void RelocInfo::Visit(Isolate* isolate, ObjectVisitor* visitor) { 471 RelocInfo::Mode mode = rmode(); 472 if (mode == RelocInfo::EMBEDDED_OBJECT) { 473 visitor->VisitEmbeddedPointer(this); 474 CpuFeatures::FlushICache(pc_, sizeof(Address)); 475 } else if (RelocInfo::IsCodeTarget(mode)) { 476 visitor->VisitCodeTarget(this); 477 } else if (mode == RelocInfo::CELL) { 478 visitor->VisitCell(this); 479 } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { 480 visitor->VisitExternalReference(this); 481 CpuFeatures::FlushICache(pc_, sizeof(Address)); 482 } else if (RelocInfo::IsCodeAgeSequence(mode)) { 483 visitor->VisitCodeAgeSequence(this); 484 } else if (((RelocInfo::IsJSReturn(mode) && 485 IsPatchedReturnSequence()) || 486 (RelocInfo::IsDebugBreakSlot(mode) && 487 IsPatchedDebugBreakSlotSequence())) && 488 isolate->debug()->has_break_points()) { 489 visitor->VisitDebugTarget(this); 490 } else if (RelocInfo::IsRuntimeEntry(mode)) { 491 visitor->VisitRuntimeEntry(this); 492 } 493} 494 495 496template<typename StaticVisitor> 497void RelocInfo::Visit(Heap* heap) { 498 RelocInfo::Mode mode = rmode(); 499 if (mode == RelocInfo::EMBEDDED_OBJECT) { 500 StaticVisitor::VisitEmbeddedPointer(heap, this); 501 CpuFeatures::FlushICache(pc_, sizeof(Address)); 502 } else if (RelocInfo::IsCodeTarget(mode)) { 503 StaticVisitor::VisitCodeTarget(heap, this); 504 } else if (mode == RelocInfo::CELL) { 505 StaticVisitor::VisitCell(heap, this); 506 } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { 507 StaticVisitor::VisitExternalReference(this); 508 CpuFeatures::FlushICache(pc_, sizeof(Address)); 509 } else if (RelocInfo::IsCodeAgeSequence(mode)) { 510 StaticVisitor::VisitCodeAgeSequence(heap, this); 511 } else if (heap->isolate()->debug()->has_break_points() && 512 ((RelocInfo::IsJSReturn(mode) && 513 IsPatchedReturnSequence()) || 514 (RelocInfo::IsDebugBreakSlot(mode) && 515 IsPatchedDebugBreakSlotSequence()))) { 516 StaticVisitor::VisitDebugTarget(heap, this); 517 } else if (RelocInfo::IsRuntimeEntry(mode)) { 518 StaticVisitor::VisitRuntimeEntry(this); 519 } 520} 521 522 523// ----------------------------------------------------------------------------- 524// Implementation of Operand 525 526void Operand::set_modrm(int mod, Register rm_reg) { 527 DCHECK(is_uint2(mod)); 528 buf_[0] = mod << 6 | rm_reg.low_bits(); 529 // Set REX.B to the high bit of rm.code(). 530 rex_ |= rm_reg.high_bit(); 531} 532 533 534void Operand::set_sib(ScaleFactor scale, Register index, Register base) { 535 DCHECK(len_ == 1); 536 DCHECK(is_uint2(scale)); 537 // Use SIB with no index register only for base rsp or r12. Otherwise we 538 // would skip the SIB byte entirely. 539 DCHECK(!index.is(rsp) || base.is(rsp) || base.is(r12)); 540 buf_[1] = (scale << 6) | (index.low_bits() << 3) | base.low_bits(); 541 rex_ |= index.high_bit() << 1 | base.high_bit(); 542 len_ = 2; 543} 544 545void Operand::set_disp8(int disp) { 546 DCHECK(is_int8(disp)); 547 DCHECK(len_ == 1 || len_ == 2); 548 int8_t* p = reinterpret_cast<int8_t*>(&buf_[len_]); 549 *p = disp; 550 len_ += sizeof(int8_t); 551} 552 553void Operand::set_disp32(int disp) { 554 DCHECK(len_ == 1 || len_ == 2); 555 int32_t* p = reinterpret_cast<int32_t*>(&buf_[len_]); 556 *p = disp; 557 len_ += sizeof(int32_t); 558} 559 560 561} } // namespace v8::internal 562 563#endif // V8_X64_ASSEMBLER_X64_INL_H_ 564