|  | /* | 
|  | * 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 "entrypoints/quick/quick_entrypoints.h" | 
|  | #include "heap_poisoning.h" | 
|  | #include "offsets.h" | 
|  | #include "thread.h" | 
|  |  | 
|  | using namespace vixl::aarch64;  // NOLINT(build/namespaces) | 
|  |  | 
|  | namespace art HIDDEN { | 
|  | 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<const uint8_t*>(); | 
|  | } | 
|  |  | 
|  | void Arm64Assembler::CopyInstructions(const MemoryRegion& region) { | 
|  | // Copy the instructions from the buffer. | 
|  | MemoryRegion from(vixl_masm_.GetBuffer()->GetStartAddress<void*>(), 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) { | 
|  | DCHECK(kReserveMarkingRegister); | 
|  |  | 
|  | 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<kArm64PointerSize>().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 |