1// Copyright 2012 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28#ifndef V8_X64_ASSEMBLER_X64_INL_H_ 29#define V8_X64_ASSEMBLER_X64_INL_H_ 30 31#include "x64/assembler-x64.h" 32 33#include "cpu.h" 34#include "debug.h" 35#include "v8memory.h" 36 37namespace v8 { 38namespace internal { 39 40 41// ----------------------------------------------------------------------------- 42// Implementation of Assembler 43 44 45void Assembler::emitl(uint32_t x) { 46 Memory::uint32_at(pc_) = x; 47 pc_ += sizeof(uint32_t); 48} 49 50 51void Assembler::emitq(uint64_t x, RelocInfo::Mode rmode) { 52 Memory::uint64_at(pc_) = x; 53 if (rmode != RelocInfo::NONE) { 54 RecordRelocInfo(rmode, x); 55 } 56 pc_ += sizeof(uint64_t); 57} 58 59 60void Assembler::emitw(uint16_t x) { 61 Memory::uint16_at(pc_) = x; 62 pc_ += sizeof(uint16_t); 63} 64 65 66void Assembler::emit_code_target(Handle<Code> target, 67 RelocInfo::Mode rmode, 68 unsigned ast_id) { 69 ASSERT(RelocInfo::IsCodeTarget(rmode)); 70 if (rmode == RelocInfo::CODE_TARGET && ast_id != kNoASTId) { 71 RecordRelocInfo(RelocInfo::CODE_TARGET_WITH_ID, ast_id); 72 } else { 73 RecordRelocInfo(rmode); 74 } 75 int current = code_targets_.length(); 76 if (current > 0 && code_targets_.last().is_identical_to(target)) { 77 // Optimization if we keep jumping to the same code target. 78 emitl(current - 1); 79 } else { 80 code_targets_.Add(target); 81 emitl(current); 82 } 83} 84 85 86void Assembler::emit_rex_64(Register reg, Register rm_reg) { 87 emit(0x48 | reg.high_bit() << 2 | rm_reg.high_bit()); 88} 89 90 91void Assembler::emit_rex_64(XMMRegister reg, Register rm_reg) { 92 emit(0x48 | (reg.code() & 0x8) >> 1 | rm_reg.code() >> 3); 93} 94 95 96void Assembler::emit_rex_64(Register reg, XMMRegister rm_reg) { 97 emit(0x48 | (reg.code() & 0x8) >> 1 | rm_reg.code() >> 3); 98} 99 100 101void Assembler::emit_rex_64(Register reg, const Operand& op) { 102 emit(0x48 | reg.high_bit() << 2 | op.rex_); 103} 104 105 106void Assembler::emit_rex_64(XMMRegister reg, const Operand& op) { 107 emit(0x48 | (reg.code() & 0x8) >> 1 | op.rex_); 108} 109 110 111void Assembler::emit_rex_64(Register rm_reg) { 112 ASSERT_EQ(rm_reg.code() & 0xf, rm_reg.code()); 113 emit(0x48 | rm_reg.high_bit()); 114} 115 116 117void Assembler::emit_rex_64(const Operand& op) { 118 emit(0x48 | op.rex_); 119} 120 121 122void Assembler::emit_rex_32(Register reg, Register rm_reg) { 123 emit(0x40 | reg.high_bit() << 2 | rm_reg.high_bit()); 124} 125 126 127void Assembler::emit_rex_32(Register reg, const Operand& op) { 128 emit(0x40 | reg.high_bit() << 2 | op.rex_); 129} 130 131 132void Assembler::emit_rex_32(Register rm_reg) { 133 emit(0x40 | rm_reg.high_bit()); 134} 135 136 137void Assembler::emit_rex_32(const Operand& op) { 138 emit(0x40 | op.rex_); 139} 140 141 142void Assembler::emit_optional_rex_32(Register reg, Register rm_reg) { 143 byte rex_bits = reg.high_bit() << 2 | rm_reg.high_bit(); 144 if (rex_bits != 0) emit(0x40 | rex_bits); 145} 146 147 148void Assembler::emit_optional_rex_32(Register reg, const Operand& op) { 149 byte rex_bits = reg.high_bit() << 2 | op.rex_; 150 if (rex_bits != 0) emit(0x40 | rex_bits); 151} 152 153 154void Assembler::emit_optional_rex_32(XMMRegister reg, const Operand& op) { 155 byte rex_bits = (reg.code() & 0x8) >> 1 | op.rex_; 156 if (rex_bits != 0) emit(0x40 | rex_bits); 157} 158 159 160void Assembler::emit_optional_rex_32(XMMRegister reg, XMMRegister base) { 161 byte rex_bits = (reg.code() & 0x8) >> 1 | (base.code() & 0x8) >> 3; 162 if (rex_bits != 0) emit(0x40 | rex_bits); 163} 164 165 166void Assembler::emit_optional_rex_32(XMMRegister reg, Register base) { 167 byte rex_bits = (reg.code() & 0x8) >> 1 | (base.code() & 0x8) >> 3; 168 if (rex_bits != 0) emit(0x40 | rex_bits); 169} 170 171 172void Assembler::emit_optional_rex_32(Register reg, XMMRegister base) { 173 byte rex_bits = (reg.code() & 0x8) >> 1 | (base.code() & 0x8) >> 3; 174 if (rex_bits != 0) emit(0x40 | rex_bits); 175} 176 177 178void Assembler::emit_optional_rex_32(Register rm_reg) { 179 if (rm_reg.high_bit()) emit(0x41); 180} 181 182 183void Assembler::emit_optional_rex_32(const Operand& op) { 184 if (op.rex_ != 0) emit(0x40 | op.rex_); 185} 186 187 188Address Assembler::target_address_at(Address pc) { 189 return Memory::int32_at(pc) + pc + 4; 190} 191 192 193void Assembler::set_target_address_at(Address pc, Address target) { 194 Memory::int32_at(pc) = static_cast<int32_t>(target - pc - 4); 195 CPU::FlushICache(pc, sizeof(int32_t)); 196} 197 198Handle<Object> Assembler::code_target_object_handle_at(Address pc) { 199 return code_targets_[Memory::int32_at(pc)]; 200} 201 202// ----------------------------------------------------------------------------- 203// Implementation of RelocInfo 204 205// The modes possibly affected by apply must be in kApplyMask. 206void RelocInfo::apply(intptr_t delta) { 207 if (IsInternalReference(rmode_)) { 208 // absolute code pointer inside code object moves with the code object. 209 Memory::Address_at(pc_) += static_cast<int32_t>(delta); 210 CPU::FlushICache(pc_, sizeof(Address)); 211 } else if (IsCodeTarget(rmode_)) { 212 Memory::int32_at(pc_) -= static_cast<int32_t>(delta); 213 CPU::FlushICache(pc_, sizeof(int32_t)); 214 } 215} 216 217 218Address RelocInfo::target_address() { 219 ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY); 220 if (IsCodeTarget(rmode_)) { 221 return Assembler::target_address_at(pc_); 222 } else { 223 return Memory::Address_at(pc_); 224 } 225} 226 227 228Address RelocInfo::target_address_address() { 229 ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY 230 || rmode_ == EMBEDDED_OBJECT 231 || rmode_ == EXTERNAL_REFERENCE); 232 return reinterpret_cast<Address>(pc_); 233} 234 235 236int RelocInfo::target_address_size() { 237 if (IsCodedSpecially()) { 238 return Assembler::kSpecialTargetSize; 239 } else { 240 return kPointerSize; 241 } 242} 243 244 245void RelocInfo::set_target_address(Address target, WriteBarrierMode mode) { 246 ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY); 247 if (IsCodeTarget(rmode_)) { 248 Assembler::set_target_address_at(pc_, target); 249 Object* target_code = Code::GetCodeFromTargetAddress(target); 250 if (mode == UPDATE_WRITE_BARRIER && host() != NULL) { 251 host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( 252 host(), this, HeapObject::cast(target_code)); 253 } 254 } else { 255 Memory::Address_at(pc_) = target; 256 CPU::FlushICache(pc_, sizeof(Address)); 257 } 258} 259 260 261Object* RelocInfo::target_object() { 262 ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); 263 return Memory::Object_at(pc_); 264} 265 266 267Handle<Object> RelocInfo::target_object_handle(Assembler* origin) { 268 ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); 269 if (rmode_ == EMBEDDED_OBJECT) { 270 return Memory::Object_Handle_at(pc_); 271 } else { 272 return origin->code_target_object_handle_at(pc_); 273 } 274} 275 276 277Object** RelocInfo::target_object_address() { 278 ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); 279 return reinterpret_cast<Object**>(pc_); 280} 281 282 283Address* RelocInfo::target_reference_address() { 284 ASSERT(rmode_ == RelocInfo::EXTERNAL_REFERENCE); 285 return reinterpret_cast<Address*>(pc_); 286} 287 288 289void RelocInfo::set_target_object(Object* target, WriteBarrierMode mode) { 290 ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT); 291 Memory::Object_at(pc_) = target; 292 CPU::FlushICache(pc_, sizeof(Address)); 293 if (mode == UPDATE_WRITE_BARRIER && 294 host() != NULL && 295 target->IsHeapObject()) { 296 host()->GetHeap()->incremental_marking()->RecordWrite( 297 host(), &Memory::Object_at(pc_), HeapObject::cast(target)); 298 } 299} 300 301 302Handle<JSGlobalPropertyCell> RelocInfo::target_cell_handle() { 303 ASSERT(rmode_ == RelocInfo::GLOBAL_PROPERTY_CELL); 304 Address address = Memory::Address_at(pc_); 305 return Handle<JSGlobalPropertyCell>( 306 reinterpret_cast<JSGlobalPropertyCell**>(address)); 307} 308 309 310JSGlobalPropertyCell* RelocInfo::target_cell() { 311 ASSERT(rmode_ == RelocInfo::GLOBAL_PROPERTY_CELL); 312 Address address = Memory::Address_at(pc_); 313 Object* object = HeapObject::FromAddress( 314 address - JSGlobalPropertyCell::kValueOffset); 315 return reinterpret_cast<JSGlobalPropertyCell*>(object); 316} 317 318 319void RelocInfo::set_target_cell(JSGlobalPropertyCell* cell, 320 WriteBarrierMode mode) { 321 ASSERT(rmode_ == RelocInfo::GLOBAL_PROPERTY_CELL); 322 Address address = cell->address() + JSGlobalPropertyCell::kValueOffset; 323 Memory::Address_at(pc_) = address; 324 CPU::FlushICache(pc_, sizeof(Address)); 325 if (mode == UPDATE_WRITE_BARRIER && 326 host() != NULL) { 327 // TODO(1550) We are passing NULL as a slot because cell can never be on 328 // evacuation candidate. 329 host()->GetHeap()->incremental_marking()->RecordWrite( 330 host(), NULL, cell); 331 } 332} 333 334 335bool RelocInfo::IsPatchedReturnSequence() { 336 // The recognized call sequence is: 337 // movq(kScratchRegister, immediate64); call(kScratchRegister); 338 // It only needs to be distinguished from a return sequence 339 // movq(rsp, rbp); pop(rbp); ret(n); int3 *6 340 // The 11th byte is int3 (0xCC) in the return sequence and 341 // REX.WB (0x48+register bit) for the call sequence. 342#ifdef ENABLE_DEBUGGER_SUPPORT 343 return pc_[10] != 0xCC; 344#else 345 return false; 346#endif 347} 348 349 350bool RelocInfo::IsPatchedDebugBreakSlotSequence() { 351 return !Assembler::IsNop(pc()); 352} 353 354 355Address RelocInfo::call_address() { 356 ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) || 357 (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence())); 358 return Memory::Address_at( 359 pc_ + Assembler::kRealPatchReturnSequenceAddressOffset); 360} 361 362 363void RelocInfo::set_call_address(Address target) { 364 ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) || 365 (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence())); 366 Memory::Address_at(pc_ + Assembler::kRealPatchReturnSequenceAddressOffset) = 367 target; 368 CPU::FlushICache(pc_ + Assembler::kRealPatchReturnSequenceAddressOffset, 369 sizeof(Address)); 370 if (host() != NULL) { 371 Object* target_code = Code::GetCodeFromTargetAddress(target); 372 host()->GetHeap()->incremental_marking()->RecordWriteIntoCode( 373 host(), this, HeapObject::cast(target_code)); 374 } 375} 376 377 378Object* RelocInfo::call_object() { 379 return *call_object_address(); 380} 381 382 383void RelocInfo::set_call_object(Object* target) { 384 *call_object_address() = target; 385} 386 387 388Object** RelocInfo::call_object_address() { 389 ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) || 390 (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence())); 391 return reinterpret_cast<Object**>( 392 pc_ + Assembler::kPatchReturnSequenceAddressOffset); 393} 394 395 396void RelocInfo::Visit(ObjectVisitor* visitor) { 397 RelocInfo::Mode mode = rmode(); 398 if (mode == RelocInfo::EMBEDDED_OBJECT) { 399 visitor->VisitEmbeddedPointer(this); 400 CPU::FlushICache(pc_, sizeof(Address)); 401 } else if (RelocInfo::IsCodeTarget(mode)) { 402 visitor->VisitCodeTarget(this); 403 } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) { 404 visitor->VisitGlobalPropertyCell(this); 405 } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { 406 visitor->VisitExternalReference(this); 407 CPU::FlushICache(pc_, sizeof(Address)); 408#ifdef ENABLE_DEBUGGER_SUPPORT 409 // TODO(isolates): Get a cached isolate below. 410 } else if (((RelocInfo::IsJSReturn(mode) && 411 IsPatchedReturnSequence()) || 412 (RelocInfo::IsDebugBreakSlot(mode) && 413 IsPatchedDebugBreakSlotSequence())) && 414 Isolate::Current()->debug()->has_break_points()) { 415 visitor->VisitDebugTarget(this); 416#endif 417 } else if (mode == RelocInfo::RUNTIME_ENTRY) { 418 visitor->VisitRuntimeEntry(this); 419 } 420} 421 422 423template<typename StaticVisitor> 424void RelocInfo::Visit(Heap* heap) { 425 RelocInfo::Mode mode = rmode(); 426 if (mode == RelocInfo::EMBEDDED_OBJECT) { 427 StaticVisitor::VisitEmbeddedPointer(heap, this); 428 CPU::FlushICache(pc_, sizeof(Address)); 429 } else if (RelocInfo::IsCodeTarget(mode)) { 430 StaticVisitor::VisitCodeTarget(heap, this); 431 } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) { 432 StaticVisitor::VisitGlobalPropertyCell(heap, this); 433 } else if (mode == RelocInfo::EXTERNAL_REFERENCE) { 434 StaticVisitor::VisitExternalReference(this); 435 CPU::FlushICache(pc_, sizeof(Address)); 436#ifdef ENABLE_DEBUGGER_SUPPORT 437 } else if (heap->isolate()->debug()->has_break_points() && 438 ((RelocInfo::IsJSReturn(mode) && 439 IsPatchedReturnSequence()) || 440 (RelocInfo::IsDebugBreakSlot(mode) && 441 IsPatchedDebugBreakSlotSequence()))) { 442 StaticVisitor::VisitDebugTarget(heap, this); 443#endif 444 } else if (mode == RelocInfo::RUNTIME_ENTRY) { 445 StaticVisitor::VisitRuntimeEntry(this); 446 } 447} 448 449 450// ----------------------------------------------------------------------------- 451// Implementation of Operand 452 453void Operand::set_modrm(int mod, Register rm_reg) { 454 ASSERT(is_uint2(mod)); 455 buf_[0] = mod << 6 | rm_reg.low_bits(); 456 // Set REX.B to the high bit of rm.code(). 457 rex_ |= rm_reg.high_bit(); 458} 459 460 461void Operand::set_sib(ScaleFactor scale, Register index, Register base) { 462 ASSERT(len_ == 1); 463 ASSERT(is_uint2(scale)); 464 // Use SIB with no index register only for base rsp or r12. Otherwise we 465 // would skip the SIB byte entirely. 466 ASSERT(!index.is(rsp) || base.is(rsp) || base.is(r12)); 467 buf_[1] = (scale << 6) | (index.low_bits() << 3) | base.low_bits(); 468 rex_ |= index.high_bit() << 1 | base.high_bit(); 469 len_ = 2; 470} 471 472void Operand::set_disp8(int disp) { 473 ASSERT(is_int8(disp)); 474 ASSERT(len_ == 1 || len_ == 2); 475 int8_t* p = reinterpret_cast<int8_t*>(&buf_[len_]); 476 *p = disp; 477 len_ += sizeof(int8_t); 478} 479 480void Operand::set_disp32(int disp) { 481 ASSERT(len_ == 1 || len_ == 2); 482 int32_t* p = reinterpret_cast<int32_t*>(&buf_[len_]); 483 *p = disp; 484 len_ += sizeof(int32_t); 485} 486 487 488} } // namespace v8::internal 489 490#endif // V8_X64_ASSEMBLER_X64_INL_H_ 491