/* * Copyright (C) 2014 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 "arch/arm64/instruction_set_features_arm64.h" #include "assembler_arm64.h" #include "base/bit_utils_iterator.h" #include "entrypoints/quick/quick_entrypoints.h" #include "heap_poisoning.h" #include "offsets.h" #include "thread.h" using namespace vixl::aarch64; // NOLINT(build/namespaces) namespace art { namespace arm64 { #ifdef ___ #error "ARM64 Assembler macro already defined." #else #define ___ vixl_masm_. #endif // Sets vixl::CPUFeatures according to ART instruction set features. static void SetVIXLCPUFeaturesFromART(vixl::aarch64::MacroAssembler* vixl_masm_, const Arm64InstructionSetFeatures* art_features) { // Retrieve already initialized default features of vixl. vixl::CPUFeatures* features = vixl_masm_->GetCPUFeatures(); DCHECK(features->Has(vixl::CPUFeatures::kFP)); DCHECK(features->Has(vixl::CPUFeatures::kNEON)); DCHECK(art_features != nullptr); if (art_features->HasCRC()) { features->Combine(vixl::CPUFeatures::kCRC32); } if (art_features->HasDotProd()) { features->Combine(vixl::CPUFeatures::kDotProduct); } if (art_features->HasFP16()) { features->Combine(vixl::CPUFeatures::kFPHalf); features->Combine(vixl::CPUFeatures::kNEONHalf); } if (art_features->HasLSE()) { features->Combine(vixl::CPUFeatures::kAtomics); } if (art_features->HasSVE()) { features->Combine(vixl::CPUFeatures::kSVE); } } Arm64Assembler::Arm64Assembler(ArenaAllocator* allocator, const Arm64InstructionSetFeatures* art_features) : Assembler(allocator) { if (art_features != nullptr) { SetVIXLCPUFeaturesFromART(&vixl_masm_, art_features); } } void Arm64Assembler::FinalizeCode() { ___ FinalizeCode(); } size_t Arm64Assembler::CodeSize() const { return vixl_masm_.GetSizeOfCodeGenerated(); } const uint8_t* Arm64Assembler::CodeBufferBaseAddress() const { return vixl_masm_.GetBuffer().GetStartAddress(); } void Arm64Assembler::FinalizeInstructions(const MemoryRegion& region) { // Copy the instructions from the buffer. MemoryRegion from(vixl_masm_.GetBuffer()->GetStartAddress(), CodeSize()); region.CopyFrom(0, from); } void Arm64Assembler::LoadRawPtr(ManagedRegister m_dst, ManagedRegister m_base, Offset offs) { Arm64ManagedRegister dst = m_dst.AsArm64(); Arm64ManagedRegister base = m_base.AsArm64(); CHECK(dst.IsXRegister() && base.IsXRegister()); // Remove dst and base form the temp list - higher level API uses IP1, IP0. UseScratchRegisterScope temps(&vixl_masm_); temps.Exclude(reg_x(dst.AsXRegister()), reg_x(base.AsXRegister())); ___ Ldr(reg_x(dst.AsXRegister()), MEM_OP(reg_x(base.AsXRegister()), offs.Int32Value())); } void Arm64Assembler::JumpTo(ManagedRegister m_base, Offset offs, ManagedRegister m_scratch) { Arm64ManagedRegister base = m_base.AsArm64(); Arm64ManagedRegister scratch = m_scratch.AsArm64(); CHECK(base.IsXRegister()) << base; CHECK(scratch.IsXRegister()) << scratch; // Remove base and scratch form the temp list - higher level API uses IP1, IP0. UseScratchRegisterScope temps(&vixl_masm_); temps.Exclude(reg_x(base.AsXRegister()), reg_x(scratch.AsXRegister())); ___ Ldr(reg_x(scratch.AsXRegister()), MEM_OP(reg_x(base.AsXRegister()), offs.Int32Value())); ___ Br(reg_x(scratch.AsXRegister())); } void Arm64Assembler::SpillRegisters(CPURegList registers, int offset) { int size = registers.GetRegisterSizeInBytes(); const Register sp = vixl_masm_.StackPointer(); // Since we are operating on register pairs, we would like to align on // double the standard size; on the other hand, we don't want to insert // an extra store, which will happen if the number of registers is even. if (!IsAlignedParam(offset, 2 * size) && registers.GetCount() % 2 != 0) { const CPURegister& dst0 = registers.PopLowestIndex(); ___ Str(dst0, MemOperand(sp, offset)); cfi_.RelOffset(DWARFReg(dst0), offset); offset += size; } while (registers.GetCount() >= 2) { const CPURegister& dst0 = registers.PopLowestIndex(); const CPURegister& dst1 = registers.PopLowestIndex(); ___ Stp(dst0, dst1, MemOperand(sp, offset)); cfi_.RelOffset(DWARFReg(dst0), offset); cfi_.RelOffset(DWARFReg(dst1), offset + size); offset += 2 * size; } if (!registers.IsEmpty()) { const CPURegister& dst0 = registers.PopLowestIndex(); ___ Str(dst0, MemOperand(sp, offset)); cfi_.RelOffset(DWARFReg(dst0), offset); } DCHECK(registers.IsEmpty()); } void Arm64Assembler::UnspillRegisters(CPURegList registers, int offset) { int size = registers.GetRegisterSizeInBytes(); const Register sp = vixl_masm_.StackPointer(); // Be consistent with the logic for spilling registers. if (!IsAlignedParam(offset, 2 * size) && registers.GetCount() % 2 != 0) { const CPURegister& dst0 = registers.PopLowestIndex(); ___ Ldr(dst0, MemOperand(sp, offset)); cfi_.Restore(DWARFReg(dst0)); offset += size; } while (registers.GetCount() >= 2) { const CPURegister& dst0 = registers.PopLowestIndex(); const CPURegister& dst1 = registers.PopLowestIndex(); ___ Ldp(dst0, dst1, MemOperand(sp, offset)); cfi_.Restore(DWARFReg(dst0)); cfi_.Restore(DWARFReg(dst1)); offset += 2 * size; } if (!registers.IsEmpty()) { const CPURegister& dst0 = registers.PopLowestIndex(); ___ Ldr(dst0, MemOperand(sp, offset)); cfi_.Restore(DWARFReg(dst0)); } DCHECK(registers.IsEmpty()); } void Arm64Assembler::PoisonHeapReference(Register reg) { DCHECK(reg.IsW()); // reg = -reg. ___ Neg(reg, Operand(reg)); } void Arm64Assembler::UnpoisonHeapReference(Register reg) { DCHECK(reg.IsW()); // reg = -reg. ___ Neg(reg, Operand(reg)); } void Arm64Assembler::MaybePoisonHeapReference(Register reg) { if (kPoisonHeapReferences) { PoisonHeapReference(reg); } } void Arm64Assembler::MaybeUnpoisonHeapReference(Register reg) { if (kPoisonHeapReferences) { UnpoisonHeapReference(reg); } } void Arm64Assembler::GenerateMarkingRegisterCheck(Register temp, int code) { // The Marking Register is only used in the Baker read barrier configuration. DCHECK(kEmitCompilerReadBarrier); DCHECK(kUseBakerReadBarrier); vixl::aarch64::Register mr = reg_x(MR); // Marking Register. vixl::aarch64::Register tr = reg_x(TR); // Thread Register. vixl::aarch64::Label mr_is_ok; // temp = self.tls32_.is.gc_marking ___ Ldr(temp, MemOperand(tr, Thread::IsGcMarkingOffset().Int32Value())); // Check that mr == self.tls32_.is.gc_marking. ___ Cmp(mr.W(), temp); ___ B(eq, &mr_is_ok); ___ Brk(code); ___ Bind(&mr_is_ok); } #undef ___ } // namespace arm64 } // namespace art