aboutsummaryrefslogtreecommitdiff
path: root/deps/v8/src/codegen/arm/assembler-arm-inl.h
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/codegen/arm/assembler-arm-inl.h')
-rw-r--r--deps/v8/src/codegen/arm/assembler-arm-inl.h361
1 files changed, 361 insertions, 0 deletions
diff --git a/deps/v8/src/codegen/arm/assembler-arm-inl.h b/deps/v8/src/codegen/arm/assembler-arm-inl.h
new file mode 100644
index 0000000000..3fbd679104
--- /dev/null
+++ b/deps/v8/src/codegen/arm/assembler-arm-inl.h
@@ -0,0 +1,361 @@
+// Copyright (c) 1994-2006 Sun Microsystems Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// - Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// - Redistribution in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the
+// distribution.
+//
+// - Neither the name of Sun Microsystems or the names of contributors may
+// be used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// The original source code covered by the above license above has been modified
+// significantly by Google Inc.
+// Copyright 2012 the V8 project authors. All rights reserved.
+
+#ifndef V8_CODEGEN_ARM_ASSEMBLER_ARM_INL_H_
+#define V8_CODEGEN_ARM_ASSEMBLER_ARM_INL_H_
+
+#include "src/codegen/arm/assembler-arm.h"
+
+#include "src/codegen/assembler.h"
+#include "src/debug/debug.h"
+#include "src/objects/objects-inl.h"
+#include "src/objects/smi.h"
+
+namespace v8 {
+namespace internal {
+
+bool CpuFeatures::SupportsOptimizer() { return true; }
+
+bool CpuFeatures::SupportsWasmSimd128() { return IsSupported(NEON); }
+
+int DoubleRegister::NumRegisters() {
+ return CpuFeatures::IsSupported(VFP32DREGS) ? 32 : 16;
+}
+
+void RelocInfo::apply(intptr_t delta) {
+ if (RelocInfo::IsInternalReference(rmode_)) {
+ // absolute code pointer inside code object moves with the code object.
+ int32_t* p = reinterpret_cast<int32_t*>(pc_);
+ *p += delta; // relocate entry
+ } else if (RelocInfo::IsRelativeCodeTarget(rmode_)) {
+ Instruction* branch = Instruction::At(pc_);
+ int32_t branch_offset = branch->GetBranchOffset() - delta;
+ branch->SetBranchOffset(branch_offset);
+ }
+}
+
+Address RelocInfo::target_address() {
+ DCHECK(IsCodeTargetMode(rmode_) || IsRuntimeEntry(rmode_) ||
+ IsWasmCall(rmode_));
+ return Assembler::target_address_at(pc_, constant_pool_);
+}
+
+Address RelocInfo::target_address_address() {
+ DCHECK(HasTargetAddressAddress());
+ if (Assembler::IsMovW(Memory<int32_t>(pc_))) {
+ return pc_;
+ } else if (Assembler::IsLdrPcImmediateOffset(Memory<int32_t>(pc_))) {
+ return constant_pool_entry_address();
+ } else {
+ DCHECK(Assembler::IsBOrBlPcImmediateOffset(Memory<int32_t>(pc_)));
+ DCHECK(IsRelativeCodeTarget(rmode_));
+ return pc_;
+ }
+}
+
+Address RelocInfo::constant_pool_entry_address() {
+ DCHECK(IsInConstantPool());
+ return Assembler::constant_pool_entry_address(pc_, constant_pool_);
+}
+
+int RelocInfo::target_address_size() { return kPointerSize; }
+
+HeapObject RelocInfo::target_object() {
+ DCHECK(IsCodeTarget(rmode_) || rmode_ == FULL_EMBEDDED_OBJECT);
+ return HeapObject::cast(
+ Object(Assembler::target_address_at(pc_, constant_pool_)));
+}
+
+HeapObject RelocInfo::target_object_no_host(Isolate* isolate) {
+ return target_object();
+}
+
+Handle<HeapObject> RelocInfo::target_object_handle(Assembler* origin) {
+ if (IsCodeTarget(rmode_) || rmode_ == FULL_EMBEDDED_OBJECT) {
+ return Handle<HeapObject>(reinterpret_cast<Address*>(
+ Assembler::target_address_at(pc_, constant_pool_)));
+ }
+ DCHECK(IsRelativeCodeTarget(rmode_));
+ return origin->relative_code_target_object_handle_at(pc_);
+}
+
+void RelocInfo::set_target_object(Heap* heap, HeapObject target,
+ WriteBarrierMode write_barrier_mode,
+ ICacheFlushMode icache_flush_mode) {
+ DCHECK(IsCodeTarget(rmode_) || rmode_ == FULL_EMBEDDED_OBJECT);
+ Assembler::set_target_address_at(pc_, constant_pool_, target.ptr(),
+ icache_flush_mode);
+ if (write_barrier_mode == UPDATE_WRITE_BARRIER && !host().is_null()) {
+ WriteBarrierForCode(host(), this, target);
+ }
+}
+
+Address RelocInfo::target_external_reference() {
+ DCHECK(rmode_ == EXTERNAL_REFERENCE);
+ return Assembler::target_address_at(pc_, constant_pool_);
+}
+
+void RelocInfo::set_target_external_reference(
+ Address target, ICacheFlushMode icache_flush_mode) {
+ DCHECK(rmode_ == RelocInfo::EXTERNAL_REFERENCE);
+ Assembler::set_target_address_at(pc_, constant_pool_, target,
+ icache_flush_mode);
+}
+
+Address RelocInfo::target_internal_reference() {
+ DCHECK(rmode_ == INTERNAL_REFERENCE);
+ return Memory<Address>(pc_);
+}
+
+Address RelocInfo::target_internal_reference_address() {
+ DCHECK(rmode_ == INTERNAL_REFERENCE);
+ return pc_;
+}
+
+Address RelocInfo::target_runtime_entry(Assembler* origin) {
+ DCHECK(IsRuntimeEntry(rmode_));
+ return target_address();
+}
+
+void RelocInfo::set_target_runtime_entry(Address target,
+ WriteBarrierMode write_barrier_mode,
+ ICacheFlushMode icache_flush_mode) {
+ DCHECK(IsRuntimeEntry(rmode_));
+ if (target_address() != target)
+ set_target_address(target, write_barrier_mode, icache_flush_mode);
+}
+
+Address RelocInfo::target_off_heap_target() {
+ DCHECK(IsOffHeapTarget(rmode_));
+ return Assembler::target_address_at(pc_, constant_pool_);
+}
+
+void RelocInfo::WipeOut() {
+ DCHECK(IsFullEmbeddedObject(rmode_) || IsCodeTarget(rmode_) ||
+ IsRuntimeEntry(rmode_) || IsExternalReference(rmode_) ||
+ IsInternalReference(rmode_) || IsOffHeapTarget(rmode_));
+ if (IsInternalReference(rmode_)) {
+ Memory<Address>(pc_) = kNullAddress;
+ } else {
+ Assembler::set_target_address_at(pc_, constant_pool_, kNullAddress);
+ }
+}
+
+Handle<Code> Assembler::relative_code_target_object_handle_at(
+ Address pc) const {
+ Instruction* branch = Instruction::At(pc);
+ int code_target_index = branch->GetBranchOffset() / kInstrSize;
+ return GetCodeTarget(code_target_index);
+}
+
+Operand Operand::Zero() { return Operand(static_cast<int32_t>(0)); }
+
+Operand::Operand(const ExternalReference& f)
+ : rmode_(RelocInfo::EXTERNAL_REFERENCE) {
+ value_.immediate = static_cast<int32_t>(f.address());
+}
+
+Operand::Operand(Smi value) : rmode_(RelocInfo::NONE) {
+ value_.immediate = static_cast<intptr_t>(value.ptr());
+}
+
+Operand::Operand(Register rm) : rm_(rm), shift_op_(LSL), shift_imm_(0) {}
+
+void Assembler::CheckBuffer() {
+ if (buffer_space() <= kGap) {
+ GrowBuffer();
+ }
+ MaybeCheckConstPool();
+}
+
+void Assembler::emit(Instr x) {
+ CheckBuffer();
+ *reinterpret_cast<Instr*>(pc_) = x;
+ pc_ += kInstrSize;
+}
+
+void Assembler::deserialization_set_special_target_at(
+ Address constant_pool_entry, Code code, Address target) {
+ DCHECK(!Builtins::IsIsolateIndependentBuiltin(code));
+ Memory<Address>(constant_pool_entry) = target;
+}
+
+int Assembler::deserialization_special_target_size(Address location) {
+ return kSpecialTargetSize;
+}
+
+void Assembler::deserialization_set_target_internal_reference_at(
+ Address pc, Address target, RelocInfo::Mode mode) {
+ Memory<Address>(pc) = target;
+}
+
+bool Assembler::is_constant_pool_load(Address pc) {
+ return IsLdrPcImmediateOffset(Memory<int32_t>(pc));
+}
+
+Address Assembler::constant_pool_entry_address(Address pc,
+ Address constant_pool) {
+ DCHECK(Assembler::IsLdrPcImmediateOffset(Memory<int32_t>(pc)));
+ Instr instr = Memory<int32_t>(pc);
+ return pc + GetLdrRegisterImmediateOffset(instr) + Instruction::kPcLoadDelta;
+}
+
+Address Assembler::target_address_at(Address pc, Address constant_pool) {
+ if (is_constant_pool_load(pc)) {
+ // This is a constant pool lookup. Return the value in the constant pool.
+ return Memory<Address>(constant_pool_entry_address(pc, constant_pool));
+ } else if (CpuFeatures::IsSupported(ARMv7) && IsMovW(Memory<int32_t>(pc))) {
+ // This is an movw / movt immediate load. Return the immediate.
+ DCHECK(IsMovW(Memory<int32_t>(pc)) &&
+ IsMovT(Memory<int32_t>(pc + kInstrSize)));
+ Instruction* movw_instr = Instruction::At(pc);
+ Instruction* movt_instr = Instruction::At(pc + kInstrSize);
+ return static_cast<Address>((movt_instr->ImmedMovwMovtValue() << 16) |
+ movw_instr->ImmedMovwMovtValue());
+ } else if (IsMovImmed(Memory<int32_t>(pc))) {
+ // This is an mov / orr immediate load. Return the immediate.
+ DCHECK(IsMovImmed(Memory<int32_t>(pc)) &&
+ IsOrrImmed(Memory<int32_t>(pc + kInstrSize)) &&
+ IsOrrImmed(Memory<int32_t>(pc + 2 * kInstrSize)) &&
+ IsOrrImmed(Memory<int32_t>(pc + 3 * kInstrSize)));
+ Instr mov_instr = instr_at(pc);
+ Instr orr_instr_1 = instr_at(pc + kInstrSize);
+ Instr orr_instr_2 = instr_at(pc + 2 * kInstrSize);
+ Instr orr_instr_3 = instr_at(pc + 3 * kInstrSize);
+ Address ret = static_cast<Address>(
+ DecodeShiftImm(mov_instr) | DecodeShiftImm(orr_instr_1) |
+ DecodeShiftImm(orr_instr_2) | DecodeShiftImm(orr_instr_3));
+ return ret;
+ } else {
+ Instruction* branch = Instruction::At(pc);
+ int32_t delta = branch->GetBranchOffset();
+ return pc + delta + Instruction::kPcLoadDelta;
+ }
+}
+
+void Assembler::set_target_address_at(Address pc, Address constant_pool,
+ Address target,
+ ICacheFlushMode icache_flush_mode) {
+ if (is_constant_pool_load(pc)) {
+ // This is a constant pool lookup. Update the entry in the constant pool.
+ Memory<Address>(constant_pool_entry_address(pc, constant_pool)) = target;
+ // Intuitively, we would think it is necessary to always flush the
+ // instruction cache after patching a target address in the code as follows:
+ // FlushInstructionCache(pc, sizeof(target));
+ // However, on ARM, no instruction is actually patched in the case
+ // of embedded constants of the form:
+ // ldr ip, [pp, #...]
+ // since the instruction accessing this address in the constant pool remains
+ // unchanged.
+ } else if (CpuFeatures::IsSupported(ARMv7) && IsMovW(Memory<int32_t>(pc))) {
+ // This is an movw / movt immediate load. Patch the immediate embedded in
+ // the instructions.
+ DCHECK(IsMovW(Memory<int32_t>(pc)));
+ DCHECK(IsMovT(Memory<int32_t>(pc + kInstrSize)));
+ uint32_t* instr_ptr = reinterpret_cast<uint32_t*>(pc);
+ uint32_t immediate = static_cast<uint32_t>(target);
+ instr_ptr[0] = PatchMovwImmediate(instr_ptr[0], immediate & 0xFFFF);
+ instr_ptr[1] = PatchMovwImmediate(instr_ptr[1], immediate >> 16);
+ DCHECK(IsMovW(Memory<int32_t>(pc)));
+ DCHECK(IsMovT(Memory<int32_t>(pc + kInstrSize)));
+ if (icache_flush_mode != SKIP_ICACHE_FLUSH) {
+ FlushInstructionCache(pc, 2 * kInstrSize);
+ }
+ } else if (IsMovImmed(Memory<int32_t>(pc))) {
+ // This is an mov / orr immediate load. Patch the immediate embedded in
+ // the instructions.
+ DCHECK(IsMovImmed(Memory<int32_t>(pc)) &&
+ IsOrrImmed(Memory<int32_t>(pc + kInstrSize)) &&
+ IsOrrImmed(Memory<int32_t>(pc + 2 * kInstrSize)) &&
+ IsOrrImmed(Memory<int32_t>(pc + 3 * kInstrSize)));
+ uint32_t* instr_ptr = reinterpret_cast<uint32_t*>(pc);
+ uint32_t immediate = static_cast<uint32_t>(target);
+ instr_ptr[0] = PatchShiftImm(instr_ptr[0], immediate & kImm8Mask);
+ instr_ptr[1] = PatchShiftImm(instr_ptr[1], immediate & (kImm8Mask << 8));
+ instr_ptr[2] = PatchShiftImm(instr_ptr[2], immediate & (kImm8Mask << 16));
+ instr_ptr[3] = PatchShiftImm(instr_ptr[3], immediate & (kImm8Mask << 24));
+ DCHECK(IsMovImmed(Memory<int32_t>(pc)) &&
+ IsOrrImmed(Memory<int32_t>(pc + kInstrSize)) &&
+ IsOrrImmed(Memory<int32_t>(pc + 2 * kInstrSize)) &&
+ IsOrrImmed(Memory<int32_t>(pc + 3 * kInstrSize)));
+ if (icache_flush_mode != SKIP_ICACHE_FLUSH) {
+ FlushInstructionCache(pc, 4 * kInstrSize);
+ }
+ } else {
+ intptr_t branch_offset = target - pc - Instruction::kPcLoadDelta;
+ Instruction* branch = Instruction::At(pc);
+ branch->SetBranchOffset(branch_offset);
+ if (icache_flush_mode != SKIP_ICACHE_FLUSH) {
+ FlushInstructionCache(pc, kInstrSize);
+ }
+ }
+}
+
+EnsureSpace::EnsureSpace(Assembler* assembler) { assembler->CheckBuffer(); }
+
+template <typename T>
+bool UseScratchRegisterScope::CanAcquireVfp() const {
+ VfpRegList* available = assembler_->GetScratchVfpRegisterList();
+ DCHECK_NOT_NULL(available);
+ for (int index = 0; index < T::kNumRegisters; index++) {
+ T reg = T::from_code(index);
+ uint64_t mask = reg.ToVfpRegList();
+ if ((*available & mask) == mask) {
+ return true;
+ }
+ }
+ return false;
+}
+
+template <typename T>
+T UseScratchRegisterScope::AcquireVfp() {
+ VfpRegList* available = assembler_->GetScratchVfpRegisterList();
+ DCHECK_NOT_NULL(available);
+ for (int index = 0; index < T::kNumRegisters; index++) {
+ T reg = T::from_code(index);
+ uint64_t mask = reg.ToVfpRegList();
+ if ((*available & mask) == mask) {
+ *available &= ~mask;
+ return reg;
+ }
+ }
+ UNREACHABLE();
+}
+
+} // namespace internal
+} // namespace v8
+
+#endif // V8_CODEGEN_ARM_ASSEMBLER_ARM_INL_H_