/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "jni_macro_assembler_arm_vixl.h"
#include <iostream>
#include <type_traits>
#include "entrypoints/quick/quick_entrypoints.h"
#include "thread.h"
using namespace vixl::aarch32; // NOLINT(build/namespaces)
namespace vixl32 = vixl::aarch32;
using vixl::ExactAssemblyScope;
using vixl::CodeBufferCheckScope;
namespace art {
namespace arm {
#ifdef ___
#error "ARM Assembler macro already defined."
#else
#define ___ asm_.GetVIXLAssembler()->
#endif
vixl::aarch32::Register AsVIXLRegister(ArmManagedRegister reg) {
CHECK(reg.IsCoreRegister());
return vixl::aarch32::Register(reg.RegId());
}
static inline vixl::aarch32::SRegister AsVIXLSRegister(ArmManagedRegister reg) {
CHECK(reg.IsSRegister());
return vixl::aarch32::SRegister(reg.RegId() - kNumberOfCoreRegIds);
}
static inline vixl::aarch32::DRegister AsVIXLDRegister(ArmManagedRegister reg) {
CHECK(reg.IsDRegister());
return vixl::aarch32::DRegister(reg.RegId() - kNumberOfCoreRegIds - kNumberOfSRegIds);
}
static inline vixl::aarch32::Register AsVIXLRegisterPairLow(ArmManagedRegister reg) {
return vixl::aarch32::Register(reg.AsRegisterPairLow());
}
static inline vixl::aarch32::Register AsVIXLRegisterPairHigh(ArmManagedRegister reg) {
return vixl::aarch32::Register(reg.AsRegisterPairHigh());
}
void ArmVIXLJNIMacroAssembler::FinalizeCode() {
for (const std::unique_ptr<
ArmVIXLJNIMacroAssembler::ArmException>& exception : exception_blocks_) {
EmitExceptionPoll(exception.get());
}
asm_.FinalizeCode();
}
static dwarf::Reg DWARFReg(vixl32::Register reg) {
return dwarf::Reg::ArmCore(static_cast<int>(reg.GetCode()));
}
static dwarf::Reg DWARFReg(vixl32::SRegister reg) {
return dwarf::Reg::ArmFp(static_cast<int>(reg.GetCode()));
}
static constexpr size_t kFramePointerSize = static_cast<size_t>(kArmPointerSize);
void ArmVIXLJNIMacroAssembler::BuildFrame(size_t frame_size,
ManagedRegister method_reg,
ArrayRef<const ManagedRegister> callee_save_regs,
const ManagedRegisterEntrySpills& entry_spills) {
CHECK_ALIGNED(frame_size, kStackAlignment);
CHECK(r0.Is(AsVIXLRegister(method_reg.AsArm())));
// Push callee saves and link register.
RegList core_spill_mask = 1 << LR;
uint32_t fp_spill_mask = 0;
for (const ManagedRegister& reg : callee_save_regs) {
if (reg.AsArm().IsCoreRegister()) {
core_spill_mask |= 1 << reg.AsArm().AsCoreRegister();
} else {
fp_spill_mask |= 1 << reg.AsArm().AsSRegister();
}
}
___ Push(RegisterList(core_spill_mask));
cfi().AdjustCFAOffset(POPCOUNT(core_spill_mask) * kFramePointerSize);
cfi().RelOffsetForMany(DWARFReg(r0), 0, core_spill_mask, kFramePointerSize);
if (fp_spill_mask != 0) {
uint32_t first = CTZ(fp_spill_mask);
// Check that list is contiguous.
DCHECK_EQ(fp_spill_mask >> CTZ(fp_spill_mask), ~0u >> (32 - POPCOUNT(fp_spill_mask)));
___ Vpush(SRegisterList(vixl32::SRegister(first), POPCOUNT(fp_spill_mask)));
cfi().AdjustCFAOffset(POPCOUNT(fp_spill_mask) * kFramePointerSize);
cfi().RelOffsetForMany(DWARFReg(s0), 0, fp_spill_mask, kFramePointerSize);
}
// Increase frame to required size.
int pushed_values = POPCOUNT(core_spill_mask) + POPCOUNT(fp_spill_mask);
// Must at least have space for Method*.
CHECK_GT(frame_size, pushed_values * kFramePointerSize);
IncreaseFrameSize(frame_size - pushed_values * kFramePointerSize); // handles CFI as well.
// Write out Method*.
asm_.StoreToOffset(kStoreWord, r0, sp, 0);
// Write out entry spills.
int32_t offset = frame_size + kFramePointerSize;
for (const ManagedRegisterSpill& spill : entry_spills) {
ArmManagedRegister reg = spill.AsArm();
if (reg.IsNoRegister()) {
// only increment stack offset.
offset += spill.getSize();
} else if (reg.IsCoreRegister()) {
asm_.StoreToOffset(kStoreWord, AsVIXLRegister(reg), sp, offset);
offset += 4;
} else if (reg.IsSRegister()) {
asm_.StoreSToOffset(AsVIXLSRegister(reg), sp, offset);
offset += 4;
} else if (reg.IsDRegister()) {
asm_.StoreDToOffset(AsVIXLDRegister(reg), sp, offset);
offset += 8;
}
}
}
void ArmVIXLJNIMacroAssembler::RemoveFrame(size_t frame_size,
ArrayRef<const ManagedRegister> callee_save_regs,
bool may_suspend) {
CHECK_ALIGNED(frame_size, kStackAlignment);
cfi().RememberState();
// Compute callee saves to pop and LR.
RegList core_spill_mask = 1 << LR;
uint32_t fp_spill_mask = 0;
for (const ManagedRegister& reg : callee_save_regs) {
if (reg.AsArm().IsCoreRegister()) {
core_spill_mask |= 1 << reg.AsArm().AsCoreRegister();
} else {
fp_spill_mask |= 1 << reg.AsArm().AsSRegister();
}
}
// Decrease frame to start of callee saves.
int pop_values = POPCOUNT(core_spill_mask) + POPCOUNT(fp_spill_mask);
CHECK_GT(frame_size, pop_values * kFramePointerSize);
DecreaseFrameSize(frame_size - (pop_values * kFramePointerSize)); // handles CFI as well.
// Pop FP callee saves.
if (fp_spill_mask != 0) {
uint32_t first = CTZ(fp_spill_mask);
// Check that list is contiguous.
DCHECK_EQ(fp_spill_mask >> CTZ(fp_spill_mask), ~0u >> (32 - POPCOUNT(fp_spill_mask)));
___ Vpop(SRegisterList(vixl32::SRegister(first), POPCOUNT(fp_spill_mask)));
cfi().AdjustCFAOffset(-kFramePointerSize * POPCOUNT(fp_spill_mask));
cfi().RestoreMany(DWARFReg(s0), fp_spill_mask);
}
// Pop core callee saves and LR.
___ Pop(RegisterList(core_spill_mask));
if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
if (may_suspend) {
// The method may be suspended; refresh the Marking Register.
___ Ldr(mr, MemOperand(tr, Thread::IsGcMarkingOffset<kArmPointerSize>().Int32Value()));
} else {
// The method shall not be suspended; no need to refresh the Marking Register.
// Check that the Marking Register is a callee-save register,
// and thus has been preserved by native code following the
// AAPCS calling convention.
DCHECK_NE(core_spill_mask & (1 << MR), 0)
<< "core_spill_mask should contain Marking Register R" << MR;
// The following condition is a compile-time one, so it does not have a run-time cost.
if (kIsDebugBuild) {
// The following condition is a run-time one; it is executed after the
// previous compile-time test, to avoid penalizing non-debug builds.
if (emit_run_time_checks_in_debug_mode_) {
// Emit a run-time check verifying that the Marking Register is up-to-date.
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
vixl32::Register temp = temps.Acquire();
// Ensure we are not clobbering a callee-save register that was restored before.
DCHECK_EQ(core_spill_mask & (1 << temp.GetCode()), 0)
<< "core_spill_mask hould not contain scratch register R" << temp.GetCode();
asm_.GenerateMarkingRegisterCheck(temp);
}
}
}
}
// Return to LR.
___ Bx(vixl32::lr);
// The CFI should be restored for any code that follows the exit block.
cfi().RestoreState();
cfi().DefCFAOffset(frame_size);
}
void ArmVIXLJNIMacroAssembler::IncreaseFrameSize(size_t adjust) {
asm_.AddConstant(sp, -adjust);
cfi().AdjustCFAOffset(adjust);
}
void ArmVIXLJNIMacroAssembler::DecreaseFrameSize(size_t adjust) {
asm_.AddConstant(sp, adjust);
cfi().AdjustCFAOffset(-adjust);
}
void ArmVIXLJNIMacroAssembler::Store(FrameOffset dest, ManagedRegister m_src, size_t size) {
ArmManagedRegister src = m_src.AsArm();
if (src.IsNoRegister()) {
CHECK_EQ(0u, size);
} else if (src.IsCoreRegister()) {
CHECK_EQ(4u, size);
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(AsVIXLRegister(src));
asm_.StoreToOffset(kStoreWord, AsVIXLRegister(src), sp, dest.Int32Value());
} else if (src.IsRegisterPair()) {
CHECK_EQ(8u, size);
asm_.StoreToOffset(kStoreWord, AsVIXLRegisterPairLow(src), sp, dest.Int32Value());
asm_.StoreToOffset(kStoreWord, AsVIXLRegisterPairHigh(src), sp, dest.Int32Value() + 4);
} else if (src.IsSRegister()) {
CHECK_EQ(4u, size);
asm_.StoreSToOffset(AsVIXLSRegister(src), sp, dest.Int32Value());
} else {
CHECK_EQ(8u, size);
CHECK(src.IsDRegister()) << src;
asm_.StoreDToOffset(AsVIXLDRegister(src), sp, dest.Int32Value());
}
}
void ArmVIXLJNIMacroAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
vixl::aarch32::Register src = AsVIXLRegister(msrc.AsArm());
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(src);
asm_.StoreToOffset(kStoreWord, src, sp, dest.Int32Value());
}
void ArmVIXLJNIMacroAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
vixl::aarch32::Register src = AsVIXLRegister(msrc.AsArm());
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(src);
asm_.StoreToOffset(kStoreWord, src, sp, dest.Int32Value());
}
void ArmVIXLJNIMacroAssembler::StoreSpanning(FrameOffset dest,
ManagedRegister msrc,
FrameOffset in_off,
ManagedRegister mscratch) {
vixl::aarch32::Register src = AsVIXLRegister(msrc.AsArm());
vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
asm_.StoreToOffset(kStoreWord, src, sp, dest.Int32Value());
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(scratch);
asm_.LoadFromOffset(kLoadWord, scratch, sp, in_off.Int32Value());
asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value() + 4);
}
void ArmVIXLJNIMacroAssembler::CopyRef(FrameOffset dest,
FrameOffset src,
ManagedRegister mscratch) {
vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(scratch);
asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value());
asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value());
}
void ArmVIXLJNIMacroAssembler::LoadRef(ManagedRegister mdest,
ManagedRegister mbase,
MemberOffset offs,
bool unpoison_reference) {
vixl::aarch32::Register dest = AsVIXLRegister(mdest.AsArm());
vixl::aarch32::Register base = AsVIXLRegister(mbase.AsArm());
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(dest, base);
asm_.LoadFromOffset(kLoadWord, dest, base, offs.Int32Value());
if (unpoison_reference) {
asm_.MaybeUnpoisonHeapReference(dest);
}
}
void ArmVIXLJNIMacroAssembler::LoadRef(ManagedRegister dest ATTRIBUTE_UNUSED,
FrameOffset src ATTRIBUTE_UNUSED) {
UNIMPLEMENTED(FATAL);
}
void ArmVIXLJNIMacroAssembler::LoadRawPtr(ManagedRegister dest ATTRIBUTE_UNUSED,
ManagedRegister base ATTRIBUTE_UNUSED,
Offset offs ATTRIBUTE_UNUSED) {
UNIMPLEMENTED(FATAL);
}
void ArmVIXLJNIMacroAssembler::StoreImmediateToFrame(FrameOffset dest,
uint32_t imm,
ManagedRegister mscratch) {
vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(scratch);
asm_.LoadImmediate(scratch, imm);
asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value());
}
void ArmVIXLJNIMacroAssembler::Load(ManagedRegister m_dst, FrameOffset src, size_t size) {
return Load(m_dst.AsArm(), sp, src.Int32Value(), size);
}
void ArmVIXLJNIMacroAssembler::LoadFromThread(ManagedRegister m_dst,
ThreadOffset32 src,
size_t size) {
return Load(m_dst.AsArm(), tr, src.Int32Value(), size);
}
void ArmVIXLJNIMacroAssembler::LoadRawPtrFromThread(ManagedRegister mdest, ThreadOffset32 offs) {
vixl::aarch32::Register dest = AsVIXLRegister(mdest.AsArm());
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(dest);
asm_.LoadFromOffset(kLoadWord, dest, tr, offs.Int32Value());
}
void ArmVIXLJNIMacroAssembler::CopyRawPtrFromThread(FrameOffset fr_offs,
ThreadOffset32 thr_offs,
ManagedRegister mscratch) {
vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(scratch);
asm_.LoadFromOffset(kLoadWord, scratch, tr, thr_offs.Int32Value());
asm_.StoreToOffset(kStoreWord, scratch, sp, fr_offs.Int32Value());
}
void ArmVIXLJNIMacroAssembler::CopyRawPtrToThread(ThreadOffset32 thr_offs ATTRIBUTE_UNUSED,
FrameOffset fr_offs ATTRIBUTE_UNUSED,
ManagedRegister mscratch ATTRIBUTE_UNUSED) {
UNIMPLEMENTED(FATAL);
}
void ArmVIXLJNIMacroAssembler::StoreStackOffsetToThread(ThreadOffset32 thr_offs,
FrameOffset fr_offs,
ManagedRegister mscratch) {
vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(scratch);
asm_.AddConstant(scratch, sp, fr_offs.Int32Value());
asm_.StoreToOffset(kStoreWord, scratch, tr, thr_offs.Int32Value());
}
void ArmVIXLJNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) {
asm_.StoreToOffset(kStoreWord, sp, tr, thr_offs.Int32Value());
}
void ArmVIXLJNIMacroAssembler::SignExtend(ManagedRegister mreg ATTRIBUTE_UNUSED,
size_t size ATTRIBUTE_UNUSED) {
UNIMPLEMENTED(FATAL) << "no sign extension necessary for arm";
}
void ArmVIXLJNIMacroAssembler::ZeroExtend(ManagedRegister mreg ATTRIBUTE_UNUSED,
size_t size ATTRIBUTE_UNUSED) {
UNIMPLEMENTED(FATAL) << "no zero extension necessary for arm";
}
void ArmVIXLJNIMacroAssembler::Move(ManagedRegister mdst,
ManagedRegister msrc,
size_t size ATTRIBUTE_UNUSED) {
ArmManagedRegister dst = mdst.AsArm();
ArmManagedRegister src = msrc.AsArm();
if (!dst.Equals(src)) {
if (dst.IsCoreRegister()) {
CHECK(src.IsCoreRegister()) << src;
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(AsVIXLRegister(dst));
___ Mov(AsVIXLRegister(dst), AsVIXLRegister(src));
} else if (dst.IsDRegister()) {
if (src.IsDRegister()) {
___ Vmov(F64, AsVIXLDRegister(dst), AsVIXLDRegister(src));
} else {
// VMOV Dn, Rlo, Rhi (Dn = {Rlo, Rhi})
CHECK(src.IsRegisterPair()) << src;
___ Vmov(AsVIXLDRegister(dst), AsVIXLRegisterPairLow(src), AsVIXLRegisterPairHigh(src));
}
} else if (dst.IsSRegister()) {
if (src.IsSRegister()) {
___ Vmov(F32, AsVIXLSRegister(dst), AsVIXLSRegister(src));
} else {
// VMOV Sn, Rn (Sn = Rn)
CHECK(src.IsCoreRegister()) << src;
___ Vmov(AsVIXLSRegister(dst), AsVIXLRegister(src));
}
} else {
CHECK(dst.IsRegisterPair()) << dst;
CHECK(src.IsRegisterPair()) << src;
// Ensure that the first move doesn't clobber the input of the second.
if (src.AsRegisterPairHigh() != dst.AsRegisterPairLow()) {
___ Mov(AsVIXLRegisterPairLow(dst), AsVIXLRegisterPairLow(src));
___ Mov(AsVIXLRegisterPairHigh(dst), AsVIXLRegisterPairHigh(src));
} else {
___ Mov(AsVIXLRegisterPairHigh(dst), AsVIXLRegisterPairHigh(src));
___ Mov(AsVIXLRegisterPairLow(dst), AsVIXLRegisterPairLow(src));
}
}
}
}
void ArmVIXLJNIMacroAssembler::Copy(FrameOffset dest,
FrameOffset src,
ManagedRegister mscratch,
size_t size) {
vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
CHECK(size == 4 || size == 8) << size;
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(scratch);
if (size == 4) {
asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value());
asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value());
} else if (size == 8) {
asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value());
asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value());
asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value() + 4);
asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value() + 4);
}
}
void ArmVIXLJNIMacroAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
ManagedRegister src_base ATTRIBUTE_UNUSED,
Offset src_offset ATTRIBUTE_UNUSED,
ManagedRegister mscratch ATTRIBUTE_UNUSED,
size_t size ATTRIBUTE_UNUSED) {
UNIMPLEMENTED(FATAL);
}
void ArmVIXLJNIMacroAssembler::Copy(ManagedRegister dest_base ATTRIBUTE_UNUSED,
Offset dest_offset ATTRIBUTE_UNUSED,
FrameOffset src ATTRIBUTE_UNUSED,
ManagedRegister mscratch ATTRIBUTE_UNUSED,
size_t size ATTRIBUTE_UNUSED) {
UNIMPLEMENTED(FATAL);
}
void ArmVIXLJNIMacroAssembler::Copy(FrameOffset dst ATTRIBUTE_UNUSED,
FrameOffset src_base ATTRIBUTE_UNUSED,
Offset src_offset ATTRIBUTE_UNUSED,
ManagedRegister mscratch ATTRIBUTE_UNUSED,
size_t size ATTRIBUTE_UNUSED) {
UNIMPLEMENTED(FATAL);
}
void ArmVIXLJNIMacroAssembler::Copy(ManagedRegister dest ATTRIBUTE_UNUSED,
Offset dest_offset ATTRIBUTE_UNUSED,
ManagedRegister src ATTRIBUTE_UNUSED,
Offset src_offset ATTRIBUTE_UNUSED,
ManagedRegister mscratch ATTRIBUTE_UNUSED,
size_t size ATTRIBUTE_UNUSED) {
UNIMPLEMENTED(FATAL);
}
void ArmVIXLJNIMacroAssembler::Copy(FrameOffset dst ATTRIBUTE_UNUSED,
Offset dest_offset ATTRIBUTE_UNUSED,
FrameOffset src ATTRIBUTE_UNUSED,
Offset src_offset ATTRIBUTE_UNUSED,
ManagedRegister scratch ATTRIBUTE_UNUSED,
size_t size ATTRIBUTE_UNUSED) {
UNIMPLEMENTED(FATAL);
}
void ArmVIXLJNIMacroAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
FrameOffset handle_scope_offset,
ManagedRegister min_reg,
bool null_allowed) {
vixl::aarch32::Register out_reg = AsVIXLRegister(mout_reg.AsArm());
vixl::aarch32::Register in_reg =
min_reg.AsArm().IsNoRegister() ? vixl::aarch32::Register() : AsVIXLRegister(min_reg.AsArm());
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(out_reg);
if (null_allowed) {
// Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
// the address in the handle scope holding the reference.
// e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset)
if (!in_reg.IsValid()) {
asm_.LoadFromOffset(kLoadWord, out_reg, sp, handle_scope_offset.Int32Value());
in_reg = out_reg;
}
temps.Exclude(in_reg);
___ Cmp(in_reg, 0);
if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value())) {
if (!out_reg.Is(in_reg)) {
ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
3 * vixl32::kMaxInstructionSizeInBytes,
CodeBufferCheckScope::kMaximumSize);
___ it(eq, 0xc);
___ mov(eq, out_reg, 0);
asm_.AddConstantInIt(out_reg, sp, handle_scope_offset.Int32Value(), ne);
} else {
ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
2 * vixl32::kMaxInstructionSizeInBytes,
CodeBufferCheckScope::kMaximumSize);
___ it(ne, 0x8);
asm_.AddConstantInIt(out_reg, sp, handle_scope_offset.Int32Value(), ne);
}
} else {
// TODO: Implement this (old arm assembler would have crashed here).
UNIMPLEMENTED(FATAL);
}
} else {
asm_.AddConstant(out_reg, sp, handle_scope_offset.Int32Value());
}
}
void ArmVIXLJNIMacroAssembler::CreateHandleScopeEntry(FrameOffset out_off,
FrameOffset handle_scope_offset,
ManagedRegister mscratch,
bool null_allowed) {
vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(scratch);
if (null_allowed) {
asm_.LoadFromOffset(kLoadWord, scratch, sp, handle_scope_offset.Int32Value());
// Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
// the address in the handle scope holding the reference.
// e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset)
___ Cmp(scratch, 0);
if (asm_.ShifterOperandCanHold(ADD, handle_scope_offset.Int32Value())) {
ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
2 * vixl32::kMaxInstructionSizeInBytes,
CodeBufferCheckScope::kMaximumSize);
___ it(ne, 0x8);
asm_.AddConstantInIt(scratch, sp, handle_scope_offset.Int32Value(), ne);
} else {
// TODO: Implement this (old arm assembler would have crashed here).
UNIMPLEMENTED(FATAL);
}
} else {
asm_.AddConstant(scratch, sp, handle_scope_offset.Int32Value());
}
asm_.StoreToOffset(kStoreWord, scratch, sp, out_off.Int32Value());
}
void ArmVIXLJNIMacroAssembler::LoadReferenceFromHandleScope(
ManagedRegister mout_reg ATTRIBUTE_UNUSED,
ManagedRegister min_reg ATTRIBUTE_UNUSED) {
UNIMPLEMENTED(FATAL);
}
void ArmVIXLJNIMacroAssembler::VerifyObject(ManagedRegister src ATTRIBUTE_UNUSED,
bool could_be_null ATTRIBUTE_UNUSED) {
// TODO: not validating references.
}
void ArmVIXLJNIMacroAssembler::VerifyObject(FrameOffset src ATTRIBUTE_UNUSED,
bool could_be_null ATTRIBUTE_UNUSED) {
// TODO: not validating references.
}
void ArmVIXLJNIMacroAssembler::Call(ManagedRegister mbase,
Offset offset,
ManagedRegister mscratch) {
vixl::aarch32::Register base = AsVIXLRegister(mbase.AsArm());
vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(scratch);
asm_.LoadFromOffset(kLoadWord, scratch, base, offset.Int32Value());
___ Blx(scratch);
// TODO: place reference map on call.
}
void ArmVIXLJNIMacroAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(scratch);
// Call *(*(SP + base) + offset)
asm_.LoadFromOffset(kLoadWord, scratch, sp, base.Int32Value());
asm_.LoadFromOffset(kLoadWord, scratch, scratch, offset.Int32Value());
___ Blx(scratch);
// TODO: place reference map on call
}
void ArmVIXLJNIMacroAssembler::CallFromThread(ThreadOffset32 offset ATTRIBUTE_UNUSED,
ManagedRegister scratch ATTRIBUTE_UNUSED) {
UNIMPLEMENTED(FATAL);
}
void ArmVIXLJNIMacroAssembler::GetCurrentThread(ManagedRegister mtr) {
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(AsVIXLRegister(mtr.AsArm()));
___ Mov(AsVIXLRegister(mtr.AsArm()), tr);
}
void ArmVIXLJNIMacroAssembler::GetCurrentThread(FrameOffset dest_offset,
ManagedRegister scratch ATTRIBUTE_UNUSED) {
asm_.StoreToOffset(kStoreWord, tr, sp, dest_offset.Int32Value());
}
void ArmVIXLJNIMacroAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) {
CHECK_ALIGNED(stack_adjust, kStackAlignment);
vixl::aarch32::Register scratch = AsVIXLRegister(mscratch.AsArm());
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(scratch);
exception_blocks_.emplace_back(
new ArmVIXLJNIMacroAssembler::ArmException(mscratch.AsArm(), stack_adjust));
asm_.LoadFromOffset(kLoadWord,
scratch,
tr,
Thread::ExceptionOffset<kArmPointerSize>().Int32Value());
___ Cmp(scratch, 0);
vixl32::Label* label = exception_blocks_.back()->Entry();
___ BPreferNear(ne, label);
// TODO: think about using CBNZ here.
}
std::unique_ptr<JNIMacroLabel> ArmVIXLJNIMacroAssembler::CreateLabel() {
return std::unique_ptr<JNIMacroLabel>(new ArmVIXLJNIMacroLabel());
}
void ArmVIXLJNIMacroAssembler::Jump(JNIMacroLabel* label) {
CHECK(label != nullptr);
___ B(ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
}
void ArmVIXLJNIMacroAssembler::Jump(JNIMacroLabel* label,
JNIMacroUnaryCondition condition,
ManagedRegister mtest) {
CHECK(label != nullptr);
vixl::aarch32::Register test = AsVIXLRegister(mtest.AsArm());
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(test);
switch (condition) {
case JNIMacroUnaryCondition::kZero:
___ CompareAndBranchIfZero(test, ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
break;
case JNIMacroUnaryCondition::kNotZero:
___ CompareAndBranchIfNonZero(test, ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
break;
default:
LOG(FATAL) << "Not implemented unary condition: " << static_cast<int>(condition);
UNREACHABLE();
}
}
void ArmVIXLJNIMacroAssembler::Bind(JNIMacroLabel* label) {
CHECK(label != nullptr);
___ Bind(ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
}
void ArmVIXLJNIMacroAssembler::EmitExceptionPoll(
ArmVIXLJNIMacroAssembler::ArmException* exception) {
___ Bind(exception->Entry());
if (exception->stack_adjust_ != 0) { // Fix up the frame.
DecreaseFrameSize(exception->stack_adjust_);
}
vixl::aarch32::Register scratch = AsVIXLRegister(exception->scratch_);
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(scratch);
// Pass exception object as argument.
// Don't care about preserving r0 as this won't return.
___ Mov(r0, scratch);
temps.Include(scratch);
// TODO: check that exception->scratch_ is dead by this point.
vixl32::Register temp = temps.Acquire();
___ Ldr(temp,
MemOperand(tr,
QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, pDeliverException).Int32Value()));
___ Blx(temp);
}
void ArmVIXLJNIMacroAssembler::MemoryBarrier(ManagedRegister scratch ATTRIBUTE_UNUSED) {
UNIMPLEMENTED(FATAL);
}
void ArmVIXLJNIMacroAssembler::Load(ArmManagedRegister
dest,
vixl32::Register base,
int32_t offset,
size_t size) {
if (dest.IsNoRegister()) {
CHECK_EQ(0u, size) << dest;
} else if (dest.IsCoreRegister()) {
vixl::aarch32::Register dst = AsVIXLRegister(dest);
CHECK(!dst.Is(sp)) << dest;
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
temps.Exclude(dst);
if (size == 1u) {
___ Ldrb(dst, MemOperand(base, offset));
} else {
CHECK_EQ(4u, size) << dest;
___ Ldr(dst, MemOperand(base, offset));
}
} else if (dest.IsRegisterPair()) {
CHECK_EQ(8u, size) << dest;
___ Ldr(AsVIXLRegisterPairLow(dest), MemOperand(base, offset));
___ Ldr(AsVIXLRegisterPairHigh(dest), MemOperand(base, offset + 4));
} else if (dest.IsSRegister()) {
___ Vldr(AsVIXLSRegister(dest), MemOperand(base, offset));
} else {
CHECK(dest.IsDRegister()) << dest;
___ Vldr(AsVIXLDRegister(dest), MemOperand(base, offset));
}
}
} // namespace arm
} // namespace art