#include "src/ppc/assembler-ppc.h"
#include "src/assembler.h"
-#include "src/debug.h"
+#include "src/debug/debug.h"
namespace v8 {
bool CpuFeatures::SupportsCrankshaft() { return true; }
-void RelocInfo::apply(intptr_t delta, ICacheFlushMode icache_flush_mode) {
- if (IsInternalReference(rmode_) || IsInternalReferenceEncoded(rmode_)) {
- // absolute code pointer inside code object moves with the code object.
- Assembler::RelocateInternalReference(pc_, delta, 0, rmode_,
- icache_flush_mode);
+void RelocInfo::apply(intptr_t delta) {
+ // absolute code pointer inside code object moves with the code object.
+ if (IsInternalReference(rmode_)) {
+ // Jump table entry
+ Address target = Memory::Address_at(pc_);
+ Memory::Address_at(pc_) = target + delta;
+ } else {
+ // mov sequence
+ DCHECK(IsInternalReferenceEncoded(rmode_));
+ Address target = Assembler::target_address_at(pc_, host_);
+ Assembler::set_target_address_at(pc_, host_, target + delta,
+ SKIP_ICACHE_FLUSH);
}
}
+Address RelocInfo::target_internal_reference() {
+ if (IsInternalReference(rmode_)) {
+ // Jump table entry
+ return Memory::Address_at(pc_);
+ } else {
+ // mov sequence
+ DCHECK(IsInternalReferenceEncoded(rmode_));
+ return Assembler::target_address_at(pc_, host_);
+ }
+}
+
+
+Address RelocInfo::target_internal_reference_address() {
+ DCHECK(IsInternalReference(rmode_) || IsInternalReferenceEncoded(rmode_));
+ return reinterpret_cast<Address>(pc_);
+}
+
+
Address RelocInfo::target_address() {
DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_));
return Assembler::target_address_at(pc_, host_);
DCHECK(IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_) ||
rmode_ == EMBEDDED_OBJECT || rmode_ == EXTERNAL_REFERENCE);
+ if (FLAG_enable_embedded_constant_pool &&
+ Assembler::IsConstantPoolLoadStart(pc_)) {
+ // We return the PC for embedded constant pool since this function is used
+ // by the serializer and expects the address to reside within the code
+ // object.
+ return reinterpret_cast<Address>(pc_);
+ }
+
// Read the address of the word containing the target_address in an
// instruction stream.
// The only architecture-independent user of this function is the serializer.
Address RelocInfo::constant_pool_entry_address() {
+ if (FLAG_enable_embedded_constant_pool) {
+ Address constant_pool = host_->constant_pool();
+ DCHECK(constant_pool);
+ ConstantPoolEntry::Access access;
+ if (Assembler::IsConstantPoolLoadStart(pc_, &access))
+ return Assembler::target_constant_pool_address_at(
+ pc_, constant_pool, access, ConstantPoolEntry::INTPTR);
+ }
UNREACHABLE();
return NULL;
}
}
-Address Assembler::break_address_from_return_address(Address pc) {
- return target_address_from_return_address(pc);
-}
-
-
Address Assembler::target_address_from_return_address(Address pc) {
// Returns the address of the call target from the return address that will
// be returned to after a call.
// mtlr ip
// blrl
// @ return address
- return pc - (kMovInstructions + 2) * kInstrSize;
+ int len;
+ ConstantPoolEntry::Access access;
+ if (FLAG_enable_embedded_constant_pool &&
+ IsConstantPoolLoadEnd(pc - 3 * kInstrSize, &access)) {
+ len = (access == ConstantPoolEntry::OVERFLOWED) ? 2 : 1;
+ } else {
+ len = kMovInstructionsNoConstantPool;
+ }
+ return pc - (len + 2) * kInstrSize;
}
Address Assembler::return_address_from_call_start(Address pc) {
- return pc + (kMovInstructions + 2) * kInstrSize;
+ int len;
+ ConstantPoolEntry::Access access;
+ if (FLAG_enable_embedded_constant_pool &&
+ IsConstantPoolLoadStart(pc, &access)) {
+ len = (access == ConstantPoolEntry::OVERFLOWED) ? 2 : 1;
+ } else {
+ len = kMovInstructionsNoConstantPool;
+ }
+ return pc + (len + 2) * kInstrSize;
}
}
-Address RelocInfo::target_reference() {
+Address RelocInfo::target_external_reference() {
DCHECK(rmode_ == EXTERNAL_REFERENCE);
return Assembler::target_address_at(pc_, host_);
}
}
-static const int kNoCodeAgeInstructions = 6;
-static const int kCodeAgingInstructions = Assembler::kMovInstructions + 3;
+static const int kNoCodeAgeInstructions =
+ FLAG_enable_embedded_constant_pool ? 7 : 6;
+static const int kCodeAgingInstructions =
+ Assembler::kMovInstructionsNoConstantPool + 3;
static const int kNoCodeAgeSequenceInstructions =
((kNoCodeAgeInstructions >= kCodeAgingInstructions)
? kNoCodeAgeInstructions
}
-Address RelocInfo::call_address() {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
- // The pc_ offset of 0 assumes patched return sequence per
- // BreakLocationIterator::SetDebugBreakAtReturn(), or debug break
- // slot per BreakLocationIterator::SetDebugBreakAtSlot().
+Address RelocInfo::debug_call_address() {
+ DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence());
return Assembler::target_address_at(pc_, host_);
}
-void RelocInfo::set_call_address(Address target) {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
+void RelocInfo::set_debug_call_address(Address target) {
+ DCHECK(IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence());
Assembler::set_target_address_at(pc_, host_, target);
if (host() != NULL) {
Object* target_code = Code::GetCodeFromTargetAddress(target);
}
-Object* RelocInfo::call_object() { return *call_object_address(); }
-
-
-void RelocInfo::set_call_object(Object* target) {
- *call_object_address() = target;
-}
-
-
-Object** RelocInfo::call_object_address() {
- DCHECK((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
- (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
- return reinterpret_cast<Object**>(pc_ + 2 * Assembler::kInstrSize);
-}
-
-
void RelocInfo::WipeOut() {
DCHECK(IsEmbeddedObject(rmode_) || IsCodeTarget(rmode_) ||
- IsRuntimeEntry(rmode_) || IsExternalReference(rmode_));
- Assembler::set_target_address_at(pc_, host_, NULL);
+ IsRuntimeEntry(rmode_) || IsExternalReference(rmode_) ||
+ IsInternalReference(rmode_) || IsInternalReferenceEncoded(rmode_));
+ if (IsInternalReference(rmode_)) {
+ // Jump table entry
+ Memory::Address_at(pc_) = NULL;
+ } else if (IsInternalReferenceEncoded(rmode_)) {
+ // mov sequence
+ // Currently used only by deserializer, no need to flush.
+ Assembler::set_target_address_at(pc_, host_, NULL, SKIP_ICACHE_FLUSH);
+ } else {
+ Assembler::set_target_address_at(pc_, host_, NULL);
+ }
}
bool RelocInfo::IsPatchedReturnSequence() {
//
// The patched return sequence is defined by
- // BreakLocationIterator::SetDebugBreakAtReturn()
+ // BreakLocation::SetDebugBreakAtReturn()
// FIXED_SEQUENCE
Instr instr0 = Assembler::instr_at(pc_);
visitor->VisitCell(this);
} else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
visitor->VisitExternalReference(this);
+ } else if (mode == RelocInfo::INTERNAL_REFERENCE ||
+ mode == RelocInfo::INTERNAL_REFERENCE_ENCODED) {
+ visitor->VisitInternalReference(this);
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
visitor->VisitCodeAgeSequence(this);
- } else if (((RelocInfo::IsJSReturn(mode) && IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence())) &&
- isolate->debug()->has_break_points()) {
+ } else if (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence()) {
visitor->VisitDebugTarget(this);
} else if (IsRuntimeEntry(mode)) {
visitor->VisitRuntimeEntry(this);
StaticVisitor::VisitCell(heap, this);
} else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
StaticVisitor::VisitExternalReference(this);
+ } else if (mode == RelocInfo::INTERNAL_REFERENCE ||
+ mode == RelocInfo::INTERNAL_REFERENCE_ENCODED) {
+ StaticVisitor::VisitInternalReference(this);
} else if (RelocInfo::IsCodeAgeSequence(mode)) {
StaticVisitor::VisitCodeAgeSequence(heap, this);
- } else if (heap->isolate()->debug()->has_break_points() &&
- ((RelocInfo::IsJSReturn(mode) && IsPatchedReturnSequence()) ||
- (RelocInfo::IsDebugBreakSlot(mode) &&
- IsPatchedDebugBreakSlotSequence()))) {
+ } else if (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence()) {
StaticVisitor::VisitDebugTarget(heap, this);
} else if (IsRuntimeEntry(mode)) {
StaticVisitor::VisitRuntimeEntry(this);
}
}
+void Assembler::TrackBranch() {
+ DCHECK(!trampoline_emitted_);
+ int count = tracked_branch_count_++;
+ if (count == 0) {
+ // We leave space (kMaxBlockTrampolineSectionSize)
+ // for BlockTrampolinePoolScope buffer.
+ next_trampoline_check_ =
+ pc_offset() + kMaxCondBranchReach - kMaxBlockTrampolineSectionSize;
+ } else {
+ next_trampoline_check_ -= kTrampolineSlotsSize;
+ }
+}
+
+void Assembler::UntrackBranch() {
+ DCHECK(!trampoline_emitted_);
+ DCHECK(tracked_branch_count_ > 0);
+ int count = --tracked_branch_count_;
+ if (count == 0) {
+ // Reset
+ next_trampoline_check_ = kMaxInt;
+ } else {
+ next_trampoline_check_ += kTrampolineSlotsSize;
+ }
+}
+
void Assembler::CheckTrampolinePoolQuick() {
- if (pc_offset() >= next_buffer_check_) {
+ if (pc_offset() >= next_trampoline_check_) {
CheckTrampolinePool();
}
}
// Fetch the 32bit value from the FIXED_SEQUENCE lis/ori
-Address Assembler::target_address_at(Address pc,
- ConstantPoolArray* constant_pool) {
+Address Assembler::target_address_at(Address pc, Address constant_pool) {
+ if (FLAG_enable_embedded_constant_pool && constant_pool) {
+ ConstantPoolEntry::Access access;
+ if (IsConstantPoolLoadStart(pc, &access))
+ return Memory::Address_at(target_constant_pool_address_at(
+ pc, constant_pool, access, ConstantPoolEntry::INTPTR));
+ }
+
Instr instr1 = instr_at(pc);
Instr instr2 = instr_at(pc + kInstrSize);
// Interpret 2 instructions generated by lis/ori
}
+#if V8_TARGET_ARCH_PPC64
+const int kLoadIntptrOpcode = LD;
+#else
+const int kLoadIntptrOpcode = LWZ;
+#endif
+
+// Constant pool load sequence detection:
+// 1) REGULAR access:
+// load <dst>, kConstantPoolRegister + <offset>
+//
+// 2) OVERFLOWED access:
+// addis <scratch>, kConstantPoolRegister, <offset_high>
+// load <dst>, <scratch> + <offset_low>
+bool Assembler::IsConstantPoolLoadStart(Address pc,
+ ConstantPoolEntry::Access* access) {
+ Instr instr = instr_at(pc);
+ int opcode = instr & kOpcodeMask;
+ if (!GetRA(instr).is(kConstantPoolRegister)) return false;
+ bool overflowed = (opcode == ADDIS);
+#ifdef DEBUG
+ if (overflowed) {
+ opcode = instr_at(pc + kInstrSize) & kOpcodeMask;
+ }
+ DCHECK(opcode == kLoadIntptrOpcode || opcode == LFD);
+#endif
+ if (access) {
+ *access = (overflowed ? ConstantPoolEntry::OVERFLOWED
+ : ConstantPoolEntry::REGULAR);
+ }
+ return true;
+}
+
+
+bool Assembler::IsConstantPoolLoadEnd(Address pc,
+ ConstantPoolEntry::Access* access) {
+ Instr instr = instr_at(pc);
+ int opcode = instr & kOpcodeMask;
+ bool overflowed = false;
+ if (!(opcode == kLoadIntptrOpcode || opcode == LFD)) return false;
+ if (!GetRA(instr).is(kConstantPoolRegister)) {
+ instr = instr_at(pc - kInstrSize);
+ opcode = instr & kOpcodeMask;
+ if ((opcode != ADDIS) || !GetRA(instr).is(kConstantPoolRegister)) {
+ return false;
+ }
+ overflowed = true;
+ }
+ if (access) {
+ *access = (overflowed ? ConstantPoolEntry::OVERFLOWED
+ : ConstantPoolEntry::REGULAR);
+ }
+ return true;
+}
+
+
+int Assembler::GetConstantPoolOffset(Address pc,
+ ConstantPoolEntry::Access access,
+ ConstantPoolEntry::Type type) {
+ bool overflowed = (access == ConstantPoolEntry::OVERFLOWED);
+#ifdef DEBUG
+ ConstantPoolEntry::Access access_check =
+ static_cast<ConstantPoolEntry::Access>(-1);
+ DCHECK(IsConstantPoolLoadStart(pc, &access_check));
+ DCHECK(access_check == access);
+#endif
+ int offset;
+ if (overflowed) {
+ offset = (instr_at(pc) & kImm16Mask) << 16;
+ offset += SIGN_EXT_IMM16(instr_at(pc + kInstrSize) & kImm16Mask);
+ DCHECK(!is_int16(offset));
+ } else {
+ offset = SIGN_EXT_IMM16((instr_at(pc) & kImm16Mask));
+ }
+ return offset;
+}
+
+
+void Assembler::PatchConstantPoolAccessInstruction(
+ int pc_offset, int offset, ConstantPoolEntry::Access access,
+ ConstantPoolEntry::Type type) {
+ Address pc = buffer_ + pc_offset;
+ bool overflowed = (access == ConstantPoolEntry::OVERFLOWED);
+ CHECK(overflowed != is_int16(offset));
+#ifdef DEBUG
+ ConstantPoolEntry::Access access_check =
+ static_cast<ConstantPoolEntry::Access>(-1);
+ DCHECK(IsConstantPoolLoadStart(pc, &access_check));
+ DCHECK(access_check == access);
+#endif
+ if (overflowed) {
+ int hi_word = static_cast<int>(offset >> 16);
+ int lo_word = static_cast<int>(offset & 0xffff);
+ if (lo_word & 0x8000) hi_word++;
+
+ Instr instr1 = instr_at(pc);
+ Instr instr2 = instr_at(pc + kInstrSize);
+ instr1 &= ~kImm16Mask;
+ instr1 |= (hi_word & kImm16Mask);
+ instr2 &= ~kImm16Mask;
+ instr2 |= (lo_word & kImm16Mask);
+ instr_at_put(pc, instr1);
+ instr_at_put(pc + kInstrSize, instr2);
+ } else {
+ Instr instr = instr_at(pc);
+ instr &= ~kImm16Mask;
+ instr |= (offset & kImm16Mask);
+ instr_at_put(pc, instr);
+ }
+}
+
+
+Address Assembler::target_constant_pool_address_at(
+ Address pc, Address constant_pool, ConstantPoolEntry::Access access,
+ ConstantPoolEntry::Type type) {
+ Address addr = constant_pool;
+ DCHECK(addr);
+ addr += GetConstantPoolOffset(pc, access, type);
+ return addr;
+}
+
+
// This sets the branch destination (which gets loaded at the call address).
// This is for calls and branches within generated code. The serializer
// has already deserialized the mov instructions etc.
set_target_address_at(instruction_payload, code, target);
}
+
+void Assembler::deserialization_set_target_internal_reference_at(
+ Address pc, Address target, RelocInfo::Mode mode) {
+ if (RelocInfo::IsInternalReferenceEncoded(mode)) {
+ Code* code = NULL;
+ set_target_address_at(pc, code, target, SKIP_ICACHE_FLUSH);
+ } else {
+ Memory::Address_at(pc) = target;
+ }
+}
+
+
// This code assumes the FIXED_SEQUENCE of lis/ori
-void Assembler::set_target_address_at(Address pc,
- ConstantPoolArray* constant_pool,
+void Assembler::set_target_address_at(Address pc, Address constant_pool,
Address target,
ICacheFlushMode icache_flush_mode) {
+ if (FLAG_enable_embedded_constant_pool && constant_pool) {
+ ConstantPoolEntry::Access access;
+ if (IsConstantPoolLoadStart(pc, &access)) {
+ Memory::Address_at(target_constant_pool_address_at(
+ pc, constant_pool, access, ConstantPoolEntry::INTPTR)) = target;
+ return;
+ }
+ }
+
Instr instr1 = instr_at(pc);
Instr instr2 = instr_at(pc + kInstrSize);
// Interpret 2 instructions generated by lis/ori
*(p + 3) = instr4;
*(p + 4) = instr5;
if (icache_flush_mode != SKIP_ICACHE_FLUSH) {
- CpuFeatures::FlushICache(p, 5 * kInstrSize);
+ Assembler::FlushICacheWithoutIsolate(p, 5 * kInstrSize);
}
#else
uint32_t* p = reinterpret_cast<uint32_t*>(pc);
*p = instr1;
*(p + 1) = instr2;
if (icache_flush_mode != SKIP_ICACHE_FLUSH) {
- CpuFeatures::FlushICache(p, 2 * kInstrSize);
+ Assembler::FlushICacheWithoutIsolate(p, 2 * kInstrSize);
}
#endif
return;
}
UNREACHABLE();
}
-}
-} // namespace v8::internal
+} // namespace internal
+} // namespace v8
#endif // V8_PPC_ASSEMBLER_PPC_INL_H_