| /* |
| * 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 |