diff options
-rw-r--r-- | build/Android.common.mk | 1 | ||||
-rw-r--r-- | build/Android.gtest.mk | 1 | ||||
-rw-r--r-- | compiler/Android.mk | 6 | ||||
-rw-r--r-- | compiler/utils/arm64/assembler_arm64.cc | 616 | ||||
-rw-r--r-- | compiler/utils/arm64/assembler_arm64.h | 286 | ||||
-rw-r--r-- | compiler/utils/arm64/constants_arm64.h | 37 | ||||
-rw-r--r-- | compiler/utils/arm64/managed_register_arm64.cc | 116 | ||||
-rw-r--r-- | compiler/utils/arm64/managed_register_arm64.h | 224 | ||||
-rw-r--r-- | compiler/utils/arm64/managed_register_arm64_test.cc | 611 | ||||
-rw-r--r-- | compiler/utils/assembler.cc | 3 | ||||
-rw-r--r-- | compiler/utils/assembler.h | 6 | ||||
-rw-r--r-- | compiler/utils/managed_register.h | 4 | ||||
-rw-r--r-- | runtime/Android.mk | 1 | ||||
-rw-r--r-- | runtime/arch/arm64/registers_arm64.cc | 75 | ||||
-rw-r--r-- | runtime/arch/arm64/registers_arm64.h | 193 | ||||
-rw-r--r-- | runtime/instruction_set.h | 1 |
16 files changed, 2176 insertions, 5 deletions
diff --git a/build/Android.common.mk b/build/Android.common.mk index 28546e9283..05224569b3 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -119,6 +119,7 @@ ART_C_INCLUDES := \ external/gtest/include \ external/valgrind/main/include \ external/valgrind/main \ + external/vixl/src \ external/zlib \ frameworks/compile/mclinker/include diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 85ee9d5fb6..19748a867f 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -75,6 +75,7 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ compiler/utils/arena_allocator_test.cc \ compiler/utils/dedupe_set_test.cc \ compiler/utils/arm/managed_register_arm_test.cc \ + compiler/utils/arm64/managed_register_arm64_test.cc \ compiler/utils/x86/managed_register_x86_test.cc \ ifeq ($(ART_SEA_IR_MODE),true) diff --git a/compiler/Android.mk b/compiler/Android.mk index 4968ab5b41..499f23f6a5 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -79,6 +79,8 @@ LIBART_COMPILER_SRC_FILES := \ utils/arena_bit_vector.cc \ utils/arm/assembler_arm.cc \ utils/arm/managed_register_arm.cc \ + utils/arm64/assembler_arm64.cc \ + utils/arm64/managed_register_arm64.cc \ utils/assembler.cc \ utils/mips/assembler_mips.cc \ utils/mips/managed_register_mips.cc \ @@ -235,10 +237,10 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk ifeq ($$(art_target_or_host),target) - LOCAL_SHARED_LIBRARIES += libcutils + LOCAL_SHARED_LIBRARIES += libcutils libvixl include $(BUILD_SHARED_LIBRARY) else # host - LOCAL_STATIC_LIBRARIES += libcutils + LOCAL_STATIC_LIBRARIES += libcutils libvixl include $(BUILD_HOST_SHARED_LIBRARY) endif diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc new file mode 100644 index 0000000000..b364ba0f65 --- /dev/null +++ b/compiler/utils/arm64/assembler_arm64.cc @@ -0,0 +1,616 @@ +/* + * 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 "assembler_arm64.h" +#include "base/logging.h" +#include "entrypoints/quick/quick_entrypoints.h" +#include "offsets.h" +#include "thread.h" +#include "utils.h" + +namespace art { +namespace arm64 { + +#ifdef ___ +#error "ARM64 Assembler macro already defined." +#else +#define ___ vixl_masm_-> +#endif + +void Arm64Assembler::EmitSlowPaths() { + if (!exception_blocks_.empty()) { + for (size_t i = 0; i < exception_blocks_.size(); i++) { + EmitExceptionPoll(exception_blocks_.at(i)); + } + } + ___ FinalizeCode(); +} + +size_t Arm64Assembler::CodeSize() const { + return ___ SizeOfCodeGenerated(); +} + +void Arm64Assembler::FinalizeInstructions(const MemoryRegion& region) { + // Copy the instructions from the buffer. + MemoryRegion from(reinterpret_cast<void*>(vixl_buf_), CodeSize()); + region.CopyFrom(0, from); +} + +void Arm64Assembler::GetCurrentThread(ManagedRegister tr) { + ___ Mov(reg_x(tr.AsArm64().AsCoreRegister()), reg_x(TR)); +} + +void Arm64Assembler::GetCurrentThread(FrameOffset offset, ManagedRegister /* scratch */) { + StoreToOffset(TR, SP, offset.Int32Value()); +} + +// See Arm64 PCS Section 5.2.2.1. +void Arm64Assembler::IncreaseFrameSize(size_t adjust) { + CHECK_ALIGNED(adjust, kStackAlignment); + AddConstant(SP, -adjust); +} + +// See Arm64 PCS Section 5.2.2.1. +void Arm64Assembler::DecreaseFrameSize(size_t adjust) { + CHECK_ALIGNED(adjust, kStackAlignment); + AddConstant(SP, adjust); +} + +void Arm64Assembler::AddConstant(Register rd, int32_t value, Condition cond) { + AddConstant(rd, rd, value, cond); +} + +void Arm64Assembler::AddConstant(Register rd, Register rn, int32_t value, + Condition cond) { + if ((cond == AL) || (cond == NV)) { + // VIXL macro-assembler handles all variants. + ___ Add(reg_x(rd), reg_x(rn), value); + } else { + // ip1 = rd + value + // rd = cond ? ip1 : rn + CHECK_NE(rn, IP1); + ___ Add(reg_x(IP1), reg_x(rn), value); + ___ Csel(reg_x(rd), reg_x(IP1), reg_x(rd), COND_OP(cond)); + } +} + +void Arm64Assembler::StoreWToOffset(StoreOperandType type, WRegister source, + Register base, int32_t offset) { + switch (type) { + case kStoreByte: + ___ Strb(reg_w(source), MEM_OP(reg_x(base), offset)); + break; + case kStoreHalfword: + ___ Strh(reg_w(source), MEM_OP(reg_x(base), offset)); + break; + case kStoreWord: + ___ Str(reg_w(source), MEM_OP(reg_x(base), offset)); + break; + default: + LOG(FATAL) << "UNREACHABLE"; + } +} + +void Arm64Assembler::StoreToOffset(Register source, Register base, int32_t offset) { + CHECK_NE(source, SP); + ___ Str(reg_x(source), MEM_OP(reg_x(base), offset)); +} + +void Arm64Assembler::StoreSToOffset(SRegister source, Register base, int32_t offset) { + ___ Str(reg_s(source), MEM_OP(reg_x(base), offset)); +} + +void Arm64Assembler::StoreDToOffset(DRegister source, Register base, int32_t offset) { + ___ Str(reg_d(source), MEM_OP(reg_x(base), offset)); +} + +void Arm64Assembler::Store(FrameOffset offs, ManagedRegister m_src, size_t size) { + Arm64ManagedRegister src = m_src.AsArm64(); + if (src.IsNoRegister()) { + CHECK_EQ(0u, size); + } else if (src.IsWRegister()) { + CHECK_EQ(4u, size); + StoreWToOffset(kStoreWord, src.AsWRegister(), SP, offs.Int32Value()); + } else if (src.IsCoreRegister()) { + CHECK_EQ(8u, size); + StoreToOffset(src.AsCoreRegister(), SP, offs.Int32Value()); + } else if (src.IsSRegister()) { + StoreSToOffset(src.AsSRegister(), SP, offs.Int32Value()); + } else { + CHECK(src.IsDRegister()) << src; + StoreDToOffset(src.AsDRegister(), SP, offs.Int32Value()); + } +} + +void Arm64Assembler::StoreRef(FrameOffset offs, ManagedRegister m_src) { + Arm64ManagedRegister src = m_src.AsArm64(); + CHECK(src.IsCoreRegister()) << src; + StoreToOffset(src.AsCoreRegister(), SP, offs.Int32Value()); +} + +void Arm64Assembler::StoreRawPtr(FrameOffset offs, ManagedRegister m_src) { + Arm64ManagedRegister src = m_src.AsArm64(); + CHECK(src.IsCoreRegister()) << src; + StoreToOffset(src.AsCoreRegister(), SP, offs.Int32Value()); +} + +void Arm64Assembler::StoreImmediateToFrame(FrameOffset offs, uint32_t imm, + ManagedRegister m_scratch) { + Arm64ManagedRegister scratch = m_scratch.AsArm64(); + CHECK(scratch.IsCoreRegister()) << scratch; + LoadImmediate(scratch.AsCoreRegister(), imm); + StoreToOffset(scratch.AsCoreRegister(), SP, offs.Int32Value()); +} + +void Arm64Assembler::StoreImmediateToThread(ThreadOffset offs, uint32_t imm, + ManagedRegister m_scratch) { + Arm64ManagedRegister scratch = m_scratch.AsArm64(); + CHECK(scratch.IsCoreRegister()) << scratch; + LoadImmediate(scratch.AsCoreRegister(), imm); + StoreToOffset(scratch.AsCoreRegister(), TR, offs.Int32Value()); +} + +void Arm64Assembler::StoreStackOffsetToThread(ThreadOffset tr_offs, + FrameOffset fr_offs, + ManagedRegister m_scratch) { + Arm64ManagedRegister scratch = m_scratch.AsArm64(); + CHECK(scratch.IsCoreRegister()) << scratch; + AddConstant(scratch.AsCoreRegister(), SP, fr_offs.Int32Value()); + StoreToOffset(scratch.AsCoreRegister(), TR, tr_offs.Int32Value()); +} + +void Arm64Assembler::StoreStackPointerToThread(ThreadOffset tr_offs) { + // Arm64 does not support: "str sp, [dest]" therefore we use IP1 as a temp reg. + ___ Mov(reg_x(IP1), reg_x(SP)); + StoreToOffset(IP1, TR, tr_offs.Int32Value()); +} + +void Arm64Assembler::StoreSpanning(FrameOffset dest_off, ManagedRegister m_source, + FrameOffset in_off, ManagedRegister m_scratch) { + Arm64ManagedRegister source = m_source.AsArm64(); + Arm64ManagedRegister scratch = m_scratch.AsArm64(); + StoreToOffset(source.AsCoreRegister(), SP, dest_off.Int32Value()); + LoadFromOffset(scratch.AsCoreRegister(), SP, in_off.Int32Value()); + StoreToOffset(scratch.AsCoreRegister(), SP, dest_off.Int32Value() + 8); +} + +// Load routines. +void Arm64Assembler::LoadImmediate(Register dest, int32_t value, + Condition cond) { + if ((cond == AL) || (cond == NV)) { + ___ Mov(reg_x(dest), value); + } else { + // ip1 = value + // rd = cond ? ip1 : rd + if (value != 0) { + CHECK_NE(dest, IP1); + ___ Mov(reg_x(IP1), value); + ___ Csel(reg_x(dest), reg_x(IP1), reg_x(dest), COND_OP(cond)); + } else { + ___ Csel(reg_x(dest), reg_x(XZR), reg_x(dest), COND_OP(cond)); + } + } +} + +void Arm64Assembler::LoadWFromOffset(LoadOperandType type, WRegister dest, + Register base, int32_t offset) { + switch (type) { + case kLoadSignedByte: + ___ Ldrsb(reg_w(dest), MEM_OP(reg_x(base), offset)); + break; + case kLoadSignedHalfword: + ___ Ldrsh(reg_w(dest), MEM_OP(reg_x(base), offset)); + break; + case kLoadUnsignedByte: + ___ Ldrb(reg_w(dest), MEM_OP(reg_x(base), offset)); + break; + case kLoadUnsignedHalfword: + ___ Ldrh(reg_w(dest), MEM_OP(reg_x(base), offset)); + break; + case kLoadWord: + ___ Ldr(reg_w(dest), MEM_OP(reg_x(base), offset)); + break; + default: + LOG(FATAL) << "UNREACHABLE"; + } +} + +// Note: We can extend this member by adding load type info - see +// sign extended A64 load variants. +void Arm64Assembler::LoadFromOffset(Register dest, Register base, + int32_t offset) { + CHECK_NE(dest, SP); + ___ Ldr(reg_x(dest), MEM_OP(reg_x(base), offset)); +} + +void Arm64Assembler::LoadSFromOffset(SRegister dest, Register base, + int32_t offset) { + ___ Ldr(reg_s(dest), MEM_OP(reg_x(base), offset)); +} + +void Arm64Assembler::LoadDFromOffset(DRegister dest, Register base, + int32_t offset) { + ___ Ldr(reg_d(dest), MEM_OP(reg_x(base), offset)); +} + +void Arm64Assembler::Load(Arm64ManagedRegister dest, Register base, + int32_t offset, size_t size) { + if (dest.IsNoRegister()) { + CHECK_EQ(0u, size) << dest; + } else if (dest.IsWRegister()) { + CHECK_EQ(4u, size) << dest; + ___ Ldr(reg_w(dest.AsWRegister()), MEM_OP(reg_x(base), offset)); + } else if (dest.IsCoreRegister()) { + CHECK_EQ(8u, size) << dest; + CHECK_NE(dest.AsCoreRegister(), SP) << dest; + ___ Ldr(reg_x(dest.AsCoreRegister()), MEM_OP(reg_x(base), offset)); + } else if (dest.IsSRegister()) { + ___ Ldr(reg_s(dest.AsSRegister()), MEM_OP(reg_x(base), offset)); + } else { + CHECK(dest.IsDRegister()) << dest; + ___ Ldr(reg_d(dest.AsDRegister()), MEM_OP(reg_x(base), offset)); + } +} + +void Arm64Assembler::Load(ManagedRegister m_dst, FrameOffset src, size_t size) { + return Load(m_dst.AsArm64(), SP, src.Int32Value(), size); +} + +void Arm64Assembler::Load(ManagedRegister m_dst, ThreadOffset src, size_t size) { + return Load(m_dst.AsArm64(), TR, src.Int32Value(), size); +} + +void Arm64Assembler::LoadRef(ManagedRegister m_dst, FrameOffset offs) { + Arm64ManagedRegister dst = m_dst.AsArm64(); + CHECK(dst.IsCoreRegister()) << dst; + LoadFromOffset(dst.AsCoreRegister(), SP, offs.Int32Value()); +} + +void Arm64Assembler::LoadRef(ManagedRegister m_dst, ManagedRegister m_base, + MemberOffset offs) { + Arm64ManagedRegister dst = m_dst.AsArm64(); + Arm64ManagedRegister base = m_base.AsArm64(); + CHECK(dst.IsCoreRegister() && base.IsCoreRegister()); + LoadFromOffset(dst.AsCoreRegister(), base.AsCoreRegister(), offs.Int32Value()); +} + +void Arm64Assembler::LoadRawPtr(ManagedRegister m_dst, ManagedRegister m_base, Offset offs) { + Arm64ManagedRegister dst = m_dst.AsArm64(); + Arm64ManagedRegister base = m_base.AsArm64(); + CHECK(dst.IsCoreRegister() && base.IsCoreRegister()); + LoadFromOffset(dst.AsCoreRegister(), base.AsCoreRegister(), offs.Int32Value()); +} + +void Arm64Assembler::LoadRawPtrFromThread(ManagedRegister m_dst, ThreadOffset offs) { + Arm64ManagedRegister dst = m_dst.AsArm64(); + CHECK(dst.IsCoreRegister()) << dst; + LoadFromOffset(dst.AsCoreRegister(), TR, offs.Int32Value()); +} + +// Copying routines. +void Arm64Assembler::Move(ManagedRegister m_dst, ManagedRegister m_src, size_t size) { + Arm64ManagedRegister dst = m_dst.AsArm64(); + Arm64ManagedRegister src = m_src.AsArm64(); + if (!dst.Equals(src)) { + if (dst.IsCoreRegister()) { + CHECK(src.IsCoreRegister()) << src; + ___ Mov(reg_x(dst.AsCoreRegister()), reg_x(src.AsCoreRegister())); + } else if (dst.IsWRegister()) { + CHECK(src.IsWRegister()) << src; + ___ Mov(reg_w(dst.AsWRegister()), reg_w(src.AsWRegister())); + } else if (dst.IsSRegister()) { + CHECK(src.IsSRegister()) << src; + ___ Fmov(reg_s(dst.AsSRegister()), reg_s(src.AsSRegister())); + } else { + CHECK(dst.IsDRegister()) << dst; + CHECK(src.IsDRegister()) << src; + ___ Fmov(reg_d(dst.AsDRegister()), reg_d(src.AsDRegister())); + } + } +} + +void Arm64Assembler::CopyRawPtrFromThread(FrameOffset fr_offs, + ThreadOffset tr_offs, + ManagedRegister m_scratch) { + Arm64ManagedRegister scratch = m_scratch.AsArm64(); + CHECK(scratch.IsCoreRegister()) << scratch; + LoadFromOffset(scratch.AsCoreRegister(), TR, tr_offs.Int32Value()); + StoreToOffset(scratch.AsCoreRegister(), SP, fr_offs.Int32Value()); +} + +void Arm64Assembler::CopyRawPtrToThread(ThreadOffset tr_offs, + FrameOffset fr_offs, + ManagedRegister m_scratch) { + Arm64ManagedRegister scratch = m_scratch.AsArm64(); + CHECK(scratch.IsCoreRegister()) << scratch; + LoadFromOffset(scratch.AsCoreRegister(), SP, fr_offs.Int32Value()); + StoreToOffset(scratch.AsCoreRegister(), TR, tr_offs.Int32Value()); +} + +void Arm64Assembler::CopyRef(FrameOffset dest, FrameOffset src, + ManagedRegister m_scratch) { + Arm64ManagedRegister scratch = m_scratch.AsArm64(); + CHECK(scratch.IsCoreRegister()) << scratch; + LoadFromOffset(scratch.AsCoreRegister(), SP, src.Int32Value()); + StoreToOffset(scratch.AsCoreRegister(), SP, dest.Int32Value()); +} + +void Arm64Assembler::Copy(FrameOffset dest, FrameOffset src, + ManagedRegister m_scratch, size_t size) { + Arm64ManagedRegister scratch = m_scratch.AsArm64(); + CHECK(scratch.IsCoreRegister() || scratch.IsWRegister()) << scratch; + CHECK(size == 4 || size == 8) << size; + if (size == 4) { + LoadWFromOffset(kLoadWord, scratch.AsWRegister(), SP, src.Int32Value()); + StoreWToOffset(kStoreWord, scratch.AsWRegister(), SP, dest.Int32Value()); + } else if (size == 8) { + LoadFromOffset(scratch.AsCoreRegister(), SP, src.Int32Value()); + StoreToOffset(scratch.AsCoreRegister(), SP, dest.Int32Value()); + } else { + UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8"; + } +} + +void Arm64Assembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset, + ManagedRegister m_scratch, size_t size) { + Arm64ManagedRegister scratch = m_scratch.AsArm64(); + Arm64ManagedRegister base = src_base.AsArm64(); + CHECK(base.IsCoreRegister()) << base; + CHECK(scratch.IsCoreRegister() || scratch.IsWRegister()) << scratch; + CHECK(size == 4 || size == 8) << size; + if (size == 4) { + LoadWFromOffset(kLoadWord, scratch.AsWRegister(), base.AsCoreRegister(), + src_offset.Int32Value()); + StoreWToOffset(kStoreWord, scratch.AsWRegister(), SP, dest.Int32Value()); + } else if (size == 8) { + LoadFromOffset(scratch.AsCoreRegister(), base.AsCoreRegister(), src_offset.Int32Value()); + StoreToOffset(scratch.AsCoreRegister(), SP, dest.Int32Value()); + } else { + UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8"; + } +} + +void Arm64Assembler::Copy(ManagedRegister m_dest_base, Offset dest_offs, FrameOffset src, + ManagedRegister m_scratch, size_t size) { + Arm64ManagedRegister scratch = m_scratch.AsArm64(); + Arm64ManagedRegister base = m_dest_base.AsArm64(); + CHECK(base.IsCoreRegister()) << base; + CHECK(scratch.IsCoreRegister() || scratch.IsWRegister()) << scratch; + CHECK(size == 4 || size == 8) << size; + if (size == 4) { + LoadWFromOffset(kLoadWord, scratch.AsWRegister(), SP, src.Int32Value()); + StoreWToOffset(kStoreWord, scratch.AsWRegister(), base.AsCoreRegister(), + dest_offs.Int32Value()); + } else if (size == 8) { + LoadFromOffset(scratch.AsCoreRegister(), SP, src.Int32Value()); + StoreToOffset(scratch.AsCoreRegister(), base.AsCoreRegister(), dest_offs.Int32Value()); + } else { + UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8"; + } +} + +void Arm64Assembler::Copy(FrameOffset /*dst*/, FrameOffset /*src_base*/, Offset /*src_offset*/, + ManagedRegister /*mscratch*/, size_t /*size*/) { + UNIMPLEMENTED(FATAL) << "Unimplemented Copy() variant"; +} + +void Arm64Assembler::Copy(ManagedRegister m_dest, Offset dest_offset, + ManagedRegister m_src, Offset src_offset, + ManagedRegister m_scratch, size_t size) { + Arm64ManagedRegister scratch = m_scratch.AsArm64(); + Arm64ManagedRegister src = m_src.AsArm64(); + Arm64ManagedRegister dest = m_dest.AsArm64(); + CHECK(dest.IsCoreRegister()) << dest; + CHECK(src.IsCoreRegister()) << src; + CHECK(scratch.IsCoreRegister() || scratch.IsWRegister()) << scratch; + CHECK(size == 4 || size == 8) << size; + if (size == 4) { + LoadWFromOffset(kLoadWord, scratch.AsWRegister(), src.AsCoreRegister(), + src_offset.Int32Value()); + StoreWToOffset(kStoreWord, scratch.AsWRegister(), dest.AsCoreRegister(), + dest_offset.Int32Value()); + } else if (size == 8) { + LoadFromOffset(scratch.AsCoreRegister(), src.AsCoreRegister(), src_offset.Int32Value()); + StoreToOffset(scratch.AsCoreRegister(), dest.AsCoreRegister(), dest_offset.Int32Value()); + } else { + UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8"; + } +} + +void Arm64Assembler::Copy(FrameOffset /*dst*/, Offset /*dest_offset*/, + FrameOffset /*src*/, Offset /*src_offset*/, + ManagedRegister /*scratch*/, size_t /*size*/) { + UNIMPLEMENTED(FATAL) << "Unimplemented Copy() variant"; +} + +void Arm64Assembler::MemoryBarrier(ManagedRegister m_scratch) { + // TODO: Should we check that m_scratch is IP? - see arm. +#if ANDROID_SMP != 0 + ___ Dmb(vixl::InnerShareable, vixl::BarrierAll); +#endif +} + +void Arm64Assembler::SignExtend(ManagedRegister /*mreg*/, size_t /*size*/) { + UNIMPLEMENTED(FATAL) << "no sign extension necessary for Arm64"; +} + +void Arm64Assembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) { + UNIMPLEMENTED(FATAL) << "no zero extension necessary for Arm64"; +} + +void Arm64Assembler::VerifyObject(ManagedRegister /*src*/, bool /*could_be_null*/) { + // TODO: not validating references. +} + +void Arm64Assembler::VerifyObject(FrameOffset /*src*/, bool /*could_be_null*/) { + // TODO: not validating references. +} + +void Arm64Assembler::Call(ManagedRegister m_base, Offset offs, ManagedRegister m_scratch) { + Arm64ManagedRegister base = m_base.AsArm64(); + Arm64ManagedRegister scratch = m_scratch.AsArm64(); + CHECK(base.IsCoreRegister()) << base; + CHECK(scratch.IsCoreRegister()) << scratch; + LoadFromOffset(scratch.AsCoreRegister(), base.AsCoreRegister(), offs.Int32Value()); + ___ Blr(reg_x(scratch.AsCoreRegister())); +} + +void Arm64Assembler::Call(FrameOffset base, Offset offs, ManagedRegister m_scratch) { + Arm64ManagedRegister scratch = m_scratch.AsArm64(); + CHECK(scratch.IsCoreRegister()) << scratch; + // Call *(*(SP + base) + offset) + LoadFromOffset(scratch.AsCoreRegister(), SP, base.Int32Value()); + LoadFromOffset(scratch.AsCoreRegister(), scratch.AsCoreRegister(), offs.Int32Value()); + ___ Blr(reg_x(scratch.AsCoreRegister())); +} + +void Arm64Assembler::Call(ThreadOffset /*offset*/, ManagedRegister /*scratch*/) { + UNIMPLEMENTED(FATAL) << "Unimplemented Call() variant"; +} + +void Arm64Assembler::CreateSirtEntry(ManagedRegister m_out_reg, FrameOffset sirt_offs, + ManagedRegister m_in_reg, bool null_allowed) { + Arm64ManagedRegister out_reg = m_out_reg.AsArm64(); + Arm64ManagedRegister in_reg = m_in_reg.AsArm64(); + // For now we only hold stale sirt entries in x registers. + CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg; + CHECK(out_reg.IsCoreRegister()) << out_reg; + if (null_allowed) { + // Null values get a SIRT entry value of 0. Otherwise, the SIRT entry is + // the address in the SIRT holding the reference. + // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset) + if (in_reg.IsNoRegister()) { + LoadFromOffset(out_reg.AsCoreRegister(), SP, sirt_offs.Int32Value()); + in_reg = out_reg; + } + ___ Cmp(reg_x(in_reg.AsCoreRegister()), 0); + if (!out_reg.Equals(in_reg)) { + LoadImmediate(out_reg.AsCoreRegister(), 0, EQ); + } + AddConstant(out_reg.AsCoreRegister(), SP, sirt_offs.Int32Value(), NE); + } else { + AddConstant(out_reg.AsCoreRegister(), SP, sirt_offs.Int32Value(), AL); + } +} + +void Arm64Assembler::CreateSirtEntry(FrameOffset out_off, FrameOffset sirt_offset, + ManagedRegister m_scratch, bool null_allowed) { + Arm64ManagedRegister scratch = m_scratch.AsArm64(); + CHECK(scratch.IsCoreRegister()) << scratch; + if (null_allowed) { + LoadFromOffset(scratch.AsCoreRegister(), SP, sirt_offset.Int32Value()); + // Null values get a SIRT entry value of 0. Otherwise, the sirt entry is + // the address in the SIRT holding the reference. + // e.g. scratch = (scratch == 0) ? 0 : (SP+sirt_offset) + ___ Cmp(reg_x(scratch.AsCoreRegister()), 0); + // Move this logic in add constants with flags. + AddConstant(scratch.AsCoreRegister(), SP, sirt_offset.Int32Value(), NE); + } else { + AddConstant(scratch.AsCoreRegister(), SP, sirt_offset.Int32Value(), AL); + } + StoreToOffset(scratch.AsCoreRegister(), SP, out_off.Int32Value()); +} + +void Arm64Assembler::LoadReferenceFromSirt(ManagedRegister m_out_reg, + ManagedRegister m_in_reg) { + Arm64ManagedRegister out_reg = m_out_reg.AsArm64(); + Arm64ManagedRegister in_reg = m_in_reg.AsArm64(); + CHECK(out_reg.IsCoreRegister()) << out_reg; + CHECK(in_reg.IsCoreRegister()) << in_reg; + vixl::Label exit; + if (!out_reg.Equals(in_reg)) { + // FIXME: Who sets the flags here? + LoadImmediate(out_reg.AsCoreRegister(), 0, EQ); + } + ___ Cmp(reg_x(in_reg.AsCoreRegister()), 0); + ___ B(&exit, COND_OP(EQ)); + LoadFromOffset(out_reg.AsCoreRegister(), in_reg.AsCoreRegister(), 0); + ___ Bind(&exit); +} + +void Arm64Assembler::ExceptionPoll(ManagedRegister m_scratch, size_t stack_adjust) { + CHECK_ALIGNED(stack_adjust, kStackAlignment); + Arm64ManagedRegister scratch = m_scratch.AsArm64(); + Arm64Exception *current_exception = new Arm64Exception(scratch, stack_adjust); + exception_blocks_.push_back(current_exception); + LoadFromOffset(scratch.AsCoreRegister(), TR, Thread::ExceptionOffset().Int32Value()); + ___ Cmp(reg_x(scratch.AsCoreRegister()), 0); + ___ B(current_exception->Entry(), COND_OP(NE)); +} + +void Arm64Assembler::EmitExceptionPoll(Arm64Exception *exception) { + // Bind exception poll entry. + ___ Bind(exception->Entry()); + if (exception->stack_adjust_ != 0) { // Fix up the frame. + DecreaseFrameSize(exception->stack_adjust_); + } + // Pass exception object as argument. + // Don't care about preserving X0 as this won't return. + ___ Mov(reg_x(X0), reg_x(exception->scratch_.AsCoreRegister())); + LoadFromOffset(IP1, TR, QUICK_ENTRYPOINT_OFFSET(pDeliverException).Int32Value()); + ___ Blr(reg_x(IP1)); + // Call should never return. + ___ Brk(); +} + +void Arm64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg, + const std::vector<ManagedRegister>& callee_save_regs, + const std::vector<ManagedRegister>& entry_spills) { + CHECK_ALIGNED(frame_size, kStackAlignment); + CHECK(X0 == method_reg.AsArm64().AsCoreRegister()); + + // TODO: *create APCS FP - end of FP chain; + // *add support for saving a different set of callee regs. + // For now we check that the size of callee regs vector is 20 + // equivalent to the APCS callee saved regs [X19, x30] [D8, D15]. + CHECK_EQ(callee_save_regs.size(), kCalleeSavedRegsSize); + ___ PushCalleeSavedRegisters(); + + // Increate frame to required size - must be at least space to push Method*. + CHECK_GT(frame_size, kCalleeSavedRegsSize * kPointerSize); + size_t adjust = frame_size - (kCalleeSavedRegsSize * kPointerSize); + IncreaseFrameSize(adjust); + + // Write Method*. + StoreToOffset(X0, SP, 0); + + // Write out entry spills, treated as X regs. + // TODO: we can implement a %2 STRP variant of StoreToOffset. + for (size_t i = 0; i < entry_spills.size(); ++i) { + Register reg = entry_spills.at(i).AsArm64().AsCoreRegister(); + StoreToOffset(reg, SP, frame_size + kPointerSize + (i * kPointerSize)); + } +} + +void Arm64Assembler::RemoveFrame(size_t frame_size, const std::vector<ManagedRegister>& callee_save_regs) { + CHECK_ALIGNED(frame_size, kStackAlignment); + + // For now we only check that the size of the frame is greater than the + // no of APCS callee saved regs [X19, X30] [D8, D15]. + CHECK_EQ(callee_save_regs.size(), kCalleeSavedRegsSize); + CHECK_GT(frame_size, kCalleeSavedRegsSize * kPointerSize); + + // Decrease frame size to start of callee saved regs. + size_t adjust = frame_size - (kCalleeSavedRegsSize * kPointerSize); + DecreaseFrameSize(adjust); + + // Pop callee saved and return to LR. + ___ PopCalleeSavedRegisters(); + ___ Ret(); +} + +} // namespace arm64 +} // namespace art diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h new file mode 100644 index 0000000000..70df252114 --- /dev/null +++ b/compiler/utils/arm64/assembler_arm64.h @@ -0,0 +1,286 @@ +/* + * 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. + */ + +#ifndef ART_COMPILER_UTILS_ARM64_ASSEMBLER_ARM64_H_ +#define ART_COMPILER_UTILS_ARM64_ASSEMBLER_ARM64_H_ + +#include <vector> + +#include "base/logging.h" +#include "constants_arm64.h" +#include "utils/arm64/managed_register_arm64.h" +#include "utils/assembler.h" +#include "offsets.h" +#include "utils.h" +#include "UniquePtr.h" +#include "a64/macro-assembler-a64.h" +#include "a64/disasm-a64.h" + +namespace art { +namespace arm64 { + +#define MEM_OP(x...) vixl::MemOperand(x) +#define COND_OP(x) static_cast<vixl::Condition>(x) + +enum Condition { + kNoCondition = -1, + EQ = 0, + NE = 1, + HS = 2, + LO = 3, + MI = 4, + PL = 5, + VS = 6, + VC = 7, + HI = 8, + LS = 9, + GE = 10, + LT = 11, + GT = 12, + LE = 13, + AL = 14, // Always. + NV = 15, // Behaves as always/al. + kMaxCondition = 16, +}; + +enum LoadOperandType { + kLoadSignedByte, + kLoadUnsignedByte, + kLoadSignedHalfword, + kLoadUnsignedHalfword, + kLoadWord, + kLoadCoreWord, + kLoadSWord, + kLoadDWord +}; + +enum StoreOperandType { + kStoreByte, + kStoreHalfword, + kStoreWord, + kStoreCoreWord, + kStoreSWord, + kStoreDWord +}; + +class Arm64Exception; + +class Arm64Assembler : public Assembler { + public: + Arm64Assembler() : vixl_buf_(new byte[BUF_SIZE]), + vixl_masm_(new vixl::MacroAssembler(vixl_buf_, BUF_SIZE)) {} + + virtual ~Arm64Assembler() { + if (kIsDebugBuild) { + vixl::Decoder *decoder = new vixl::Decoder(); + vixl::PrintDisassembler *test = new vixl::PrintDisassembler(stdout); + decoder->AppendVisitor(test); + + for (size_t i = 0; i < CodeSize() / vixl::kInstructionSize; ++i) { + vixl::Instruction *instr = + reinterpret_cast<vixl::Instruction*>(vixl_buf_ + i * vixl::kInstructionSize); + decoder->Decode(instr); + } + } + delete[] vixl_buf_; + } + + // Emit slow paths queued during assembly. + void EmitSlowPaths(); + + // Size of generated code. + size_t CodeSize() const; + + // Copy instructions out of assembly buffer into the given region of memory. + void FinalizeInstructions(const MemoryRegion& region); + + // Emit code that will create an activation on the stack. + void BuildFrame(size_t frame_size, ManagedRegister method_reg, + const std::vector<ManagedRegister>& callee_save_regs, + const std::vector<ManagedRegister>& entry_spills); + + // Emit code that will remove an activation from the stack. + void RemoveFrame(size_t frame_size, + const std::vector<ManagedRegister>& callee_save_regs); + + void IncreaseFrameSize(size_t adjust); + void DecreaseFrameSize(size_t adjust); + + // Store routines. + void Store(FrameOffset offs, ManagedRegister src, size_t size); + void StoreRef(FrameOffset dest, ManagedRegister src); + void StoreRawPtr(FrameOffset dest, ManagedRegister src); + void StoreImmediateToFrame(FrameOffset dest, uint32_t imm, + ManagedRegister scratch); + void StoreImmediateToThread(ThreadOffset dest, uint32_t imm, + ManagedRegister scratch); + void StoreStackOffsetToThread(ThreadOffset thr_offs, + FrameOffset fr_offs, + ManagedRegister scratch); + void StoreStackPointerToThread(ThreadOffset thr_offs); + void StoreSpanning(FrameOffset dest, ManagedRegister src, + FrameOffset in_off, ManagedRegister scratch); + + // Load routines. + void Load(ManagedRegister dest, FrameOffset src, size_t size); + void Load(ManagedRegister dest, ThreadOffset src, size_t size); + void LoadRef(ManagedRegister dest, FrameOffset src); + void LoadRef(ManagedRegister dest, ManagedRegister base, + MemberOffset offs); + void LoadRawPtr(ManagedRegister dest, ManagedRegister base, + Offset offs); + void LoadRawPtrFromThread(ManagedRegister dest, + ThreadOffset offs); + // Copying routines. + void Move(ManagedRegister dest, ManagedRegister src, size_t size); + void CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset thr_offs, + ManagedRegister scratch); + void CopyRawPtrToThread(ThreadOffset thr_offs, FrameOffset fr_offs, + ManagedRegister scratch); + void CopyRef(FrameOffset dest, FrameOffset src, + ManagedRegister scratch); + void Copy(FrameOffset dest, FrameOffset src, ManagedRegister scratch, size_t size); + void Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset, + ManagedRegister scratch, size_t size); + void Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src, + ManagedRegister scratch, size_t size); + void Copy(FrameOffset dest, FrameOffset src_base, Offset src_offset, + ManagedRegister scratch, size_t size); + void Copy(ManagedRegister dest, Offset dest_offset, + ManagedRegister src, Offset src_offset, + ManagedRegister scratch, size_t size); + void Copy(FrameOffset dest, Offset dest_offset, FrameOffset src, Offset src_offset, + ManagedRegister scratch, size_t size); + void MemoryBarrier(ManagedRegister scratch); + + // Sign extension. + void SignExtend(ManagedRegister mreg, size_t size); + + // Zero extension. + void ZeroExtend(ManagedRegister mreg, size_t size); + + // Exploit fast access in managed code to Thread::Current(). + void GetCurrentThread(ManagedRegister tr); + void GetCurrentThread(FrameOffset dest_offset, + ManagedRegister scratch); + + // Set up out_reg to hold a Object** into the SIRT, or to be NULL if the + // value is null and null_allowed. in_reg holds a possibly stale reference + // that can be used to avoid loading the SIRT entry to see if the value is + // NULL. + void CreateSirtEntry(ManagedRegister out_reg, FrameOffset sirt_offset, + ManagedRegister in_reg, bool null_allowed); + + // Set up out_off to hold a Object** into the SIRT, or to be NULL if the + // value is null and null_allowed. + void CreateSirtEntry(FrameOffset out_off, FrameOffset sirt_offset, + ManagedRegister scratch, bool null_allowed); + + // src holds a SIRT entry (Object**) load this into dst. + void LoadReferenceFromSirt(ManagedRegister dst, + ManagedRegister src); + + // Heap::VerifyObject on src. In some cases (such as a reference to this) we + // know that src may not be null. + void VerifyObject(ManagedRegister src, bool could_be_null); + void VerifyObject(FrameOffset src, bool could_be_null); + + // Call to address held at [base+offset]. + void Call(ManagedRegister base, Offset offset, ManagedRegister scratch); + void Call(FrameOffset base, Offset offset, ManagedRegister scratch); + void Call(ThreadOffset offset, ManagedRegister scratch); + + // Generate code to check if Thread::Current()->exception_ is non-null + // and branch to a ExceptionSlowPath if it is. + void ExceptionPoll(ManagedRegister scratch, size_t stack_adjust); + + private: + static vixl::Register reg_x(int code) { + CHECK(code < kNumberOfCoreRegisters) << code; + if (code == SP) { + return vixl::sp; + } + return vixl::Register::XRegFromCode(code); + } + + static vixl::Register reg_w(int code) { + return vixl::Register::WRegFromCode(code); + } + + static vixl::FPRegister reg_d(int code) { + return vixl::FPRegister::DRegFromCode(code); + } + + static vixl::FPRegister reg_s(int code) { + return vixl::FPRegister::SRegFromCode(code); + } + + // Emits Exception block. + void EmitExceptionPoll(Arm64Exception *exception); + + void StoreWToOffset(StoreOperandType type, WRegister source, + Register base, int32_t offset); + void StoreToOffset(Register source, Register base, int32_t offset); + void StoreSToOffset(SRegister source, Register base, int32_t offset); + void StoreDToOffset(DRegister source, Register base, int32_t offset); + + void LoadImmediate(Register dest, int32_t value, Condition cond = AL); + void Load(Arm64ManagedRegister dst, Register src, int32_t src_offset, size_t size); + void LoadWFromOffset(LoadOperandType type, WRegister dest, + Register base, int32_t offset); + void LoadFromOffset(Register dest, Register base, int32_t offset); + void LoadSFromOffset(SRegister dest, Register base, int32_t offset); + void LoadDFromOffset(DRegister dest, Register base, int32_t offset); + void AddConstant(Register rd, int32_t value, Condition cond = AL); + void AddConstant(Register rd, Register rn, int32_t value, Condition cond = AL); + + // Vixl buffer size. + static constexpr size_t BUF_SIZE = 4096; + + // Vixl buffer. + byte* vixl_buf_; + + // Unique ptr - vixl assembler. + UniquePtr<vixl::MacroAssembler> vixl_masm_; + + // List of exception blocks to generate at the end of the code cache. + std::vector<Arm64Exception*> exception_blocks_; +}; + +class Arm64Exception { + private: + explicit Arm64Exception(Arm64ManagedRegister scratch, size_t stack_adjust) + : scratch_(scratch), stack_adjust_(stack_adjust) { + } + + vixl::Label* Entry() { return &exception_entry_; } + + // Register used for passing Thread::Current()->exception_ . + const Arm64ManagedRegister scratch_; + + // Stack adjust for ExceptionPool. + const size_t stack_adjust_; + + vixl::Label exception_entry_; + + friend class Arm64Assembler; + DISALLOW_COPY_AND_ASSIGN(Arm64Exception); +}; + +} // namespace arm64 +} // namespace art + +#endif // ART_COMPILER_UTILS_ARM64_ASSEMBLER_ARM64_H_ diff --git a/compiler/utils/arm64/constants_arm64.h b/compiler/utils/arm64/constants_arm64.h new file mode 100644 index 0000000000..c05c2f10bf --- /dev/null +++ b/compiler/utils/arm64/constants_arm64.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#ifndef ART_COMPILER_UTILS_ARM64_CONSTANTS_ARM64_H_ +#define ART_COMPILER_UTILS_ARM64_CONSTANTS_ARM64_H_ + +#include <stdint.h> +#include <iosfwd> +#include "arch/arm64/registers_arm64.h" +#include "base/casts.h" +#include "base/logging.h" +#include "globals.h" + +// TODO: Extend this file by adding missing functionality. + +namespace art { +namespace arm64 { + + constexpr unsigned int kCalleeSavedRegsSize = 20; + +} // arm64 +} // art + +#endif // ART_COMPILER_UTILS_ARM64_CONSTANTS_ARM64_H_ diff --git a/compiler/utils/arm64/managed_register_arm64.cc b/compiler/utils/arm64/managed_register_arm64.cc new file mode 100644 index 0000000000..cc0b509033 --- /dev/null +++ b/compiler/utils/arm64/managed_register_arm64.cc @@ -0,0 +1,116 @@ +/* + * 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 "managed_register_arm64.h" +#include "globals.h" + +namespace art { +namespace arm64 { + +// TODO: Define convention +// +// Do not use APCS callee saved regs for now. Use: +// * [X0, X15] +// * [W0, W15] +// * [D0, D31] +// * [S0, S31] +static const int kNumberOfAvailableCoreRegisters = (X15 - X0) + 1; +static const int kNumberOfAvailableWRegisters = (W15 - W0) + 1; +static const int kNumberOfAvailableDRegisters = kNumberOfDRegisters; +static const int kNumberOfAvailableSRegisters = kNumberOfSRegisters; + +// Returns true if this managed-register overlaps the other managed-register. +// GP Register Bank: +// 31____0 W[n] +// 63__________0 X[n] +// +// FP Register Bank: +// 31____0 S[n] +// 63__________0 D[n] +bool Arm64ManagedRegister::Overlaps(const Arm64ManagedRegister& other) const { + if (IsNoRegister() || other.IsNoRegister()) return false; + if ((IsGPRegister() && other.IsGPRegister()) || + (IsFPRegister() && other.IsFPRegister())) { + return (RegNo() == other.RegNo()); + } + return false; +} + +int Arm64ManagedRegister::RegNo() const { + CHECK(!IsNoRegister()); + int no; + if (IsCoreRegister()) { + if (IsStackPointer()) { + no = static_cast<int>(X31); + } else { + no = static_cast<int>(AsCoreRegister()); + } + } else if (IsWRegister()) { + no = static_cast<int>(AsWRegister()); + } else if (IsDRegister()) { + no = static_cast<int>(AsDRegister()); + } else if (IsSRegister()) { + no = static_cast<int>(AsSRegister()); + } else { + no = kNoRegister; + } + return no; +} + +int Arm64ManagedRegister::RegIdLow() const { + CHECK(IsCoreRegister() || IsDRegister()); + int low = RegNo(); + if (IsCoreRegister()) { + low += kNumberOfCoreRegIds; + } else if (IsDRegister()) { + low += kNumberOfCoreRegIds + kNumberOfWRegIds + kNumberOfDRegIds; + } + return low; +} + +// FIXME: Find better naming. +int Arm64ManagedRegister::RegIdHigh() const { + CHECK(IsWRegister() || IsSRegister()); + int high = RegNo(); + if (IsSRegister()) { + high += kNumberOfCoreRegIds + kNumberOfWRegIds; + } + return high; +} + +void Arm64ManagedRegister::Print(std::ostream& os) const { + if (!IsValidManagedRegister()) { + os << "No Register"; + } else if (IsCoreRegister()) { + os << "XCore: " << static_cast<int>(AsCoreRegister()); + } else if (IsWRegister()) { + os << "WCore: " << static_cast<int>(AsWRegister()); + } else if (IsDRegister()) { + os << "DRegister: " << static_cast<int>(AsDRegister()); + } else if (IsSRegister()) { + os << "SRegister: " << static_cast<int>(AsSRegister()); + } else { + os << "??: " << RegId(); + } +} + +std::ostream& operator<<(std::ostream& os, const Arm64ManagedRegister& reg) { + reg.Print(os); + return os; +} + +} // namespace arm64 +} // namespace art diff --git a/compiler/utils/arm64/managed_register_arm64.h b/compiler/utils/arm64/managed_register_arm64.h new file mode 100644 index 0000000000..5df37cc12e --- /dev/null +++ b/compiler/utils/arm64/managed_register_arm64.h @@ -0,0 +1,224 @@ +/* + * 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. + */ + +#ifndef ART_COMPILER_UTILS_ARM64_MANAGED_REGISTER_ARM64_H_ +#define ART_COMPILER_UTILS_ARM64_MANAGED_REGISTER_ARM64_H_ + +#include "base/logging.h" +#include "constants_arm64.h" +#include "utils/managed_register.h" + +namespace art { +namespace arm64 { + +const int kNumberOfCoreRegIds = kNumberOfCoreRegisters; +const int kNumberOfWRegIds = kNumberOfWRegisters; +const int kNumberOfDRegIds = kNumberOfDRegisters; +const int kNumberOfSRegIds = kNumberOfSRegisters; + +const int kNumberOfRegIds = kNumberOfCoreRegIds + kNumberOfWRegIds + + kNumberOfDRegIds + kNumberOfSRegIds; + +// Register ids map: +// [0..X[ core registers 64bit (enum Register) +// [X..W[ core registers 32bit (enum WRegister) +// [W..D[ double precision VFP registers (enum DRegister) +// [D..S[ single precision VFP registers (enum SRegister) +// +// where: +// X = kNumberOfCoreRegIds +// W = X + kNumberOfWRegIds +// D = W + kNumberOfDRegIds +// S = D + kNumberOfSRegIds +// +// An instance of class 'ManagedRegister' represents a single Arm64 +// register. A register can be one of the following: +// * core register 64bit context (enum Register) +// * core register 32bit context (enum WRegister) +// * VFP double precision register (enum DRegister) +// * VFP single precision register (enum SRegister) +// +// There is a one to one mapping between ManagedRegister and register id. + +class Arm64ManagedRegister : public ManagedRegister { + public: + Register AsCoreRegister() const { + CHECK(IsCoreRegister()); + return static_cast<Register>(id_); + } + + WRegister AsWRegister() const { + CHECK(IsWRegister()); + return static_cast<WRegister>(id_ - kNumberOfCoreRegIds); + } + + DRegister AsDRegister() const { + CHECK(IsDRegister()); + return static_cast<DRegister>(id_ - kNumberOfCoreRegIds - kNumberOfWRegIds); + } + + SRegister AsSRegister() const { + CHECK(IsSRegister()); + return static_cast<SRegister>(id_ - kNumberOfCoreRegIds - kNumberOfWRegIds - + kNumberOfDRegIds); + } + + WRegister AsOverlappingCoreRegisterLow() const { + CHECK(IsValidManagedRegister()); + if (IsStackPointer()) return W31; + return static_cast<WRegister>(AsCoreRegister()); + } + + // FIXME: Find better naming. + Register AsOverlappingWRegisterCore() const { + CHECK(IsValidManagedRegister()); + return static_cast<Register>(AsWRegister()); + } + + SRegister AsOverlappingDRegisterLow() const { + CHECK(IsValidManagedRegister()); + return static_cast<SRegister>(AsDRegister()); + } + + // FIXME: Find better naming. + DRegister AsOverlappingSRegisterD() const { + CHECK(IsValidManagedRegister()); + return static_cast<DRegister>(AsSRegister()); + } + + bool IsCoreRegister() const { + CHECK(IsValidManagedRegister()); + return (0 <= id_) && (id_ < kNumberOfCoreRegIds); + } + + bool IsWRegister() const { + CHECK(IsValidManagedRegister()); + const int test = id_ - kNumberOfCoreRegIds; + return (0 <= test) && (test < kNumberOfWRegIds); + } + + bool IsDRegister() const { + CHECK(IsValidManagedRegister()); + const int test = id_ - (kNumberOfCoreRegIds + kNumberOfWRegIds); + return (0 <= test) && (test < kNumberOfDRegIds); + } + + bool IsSRegister() const { + CHECK(IsValidManagedRegister()); + const int test = id_ - (kNumberOfCoreRegIds + kNumberOfWRegIds + + kNumberOfDRegIds); + return (0 <= test) && (test < kNumberOfSRegIds); + } + + bool IsGPRegister() const { + return IsCoreRegister() || IsWRegister(); + } + + bool IsFPRegister() const { + return IsDRegister() || IsSRegister(); + } + + bool IsSameType(Arm64ManagedRegister test) const { + CHECK(IsValidManagedRegister() && test.IsValidManagedRegister()); + return + (IsCoreRegister() && test.IsCoreRegister()) || + (IsWRegister() && test.IsWRegister()) || + (IsDRegister() && test.IsDRegister()) || + (IsSRegister() && test.IsSRegister()); + } + + // Returns true if the two managed-registers ('this' and 'other') overlap. + // Either managed-register may be the NoRegister. If both are the NoRegister + // then false is returned. + bool Overlaps(const Arm64ManagedRegister& other) const; + + void Print(std::ostream& os) const; + + static Arm64ManagedRegister FromCoreRegister(Register r) { + CHECK_NE(r, kNoRegister); + return FromRegId(r); + } + + static Arm64ManagedRegister FromWRegister(WRegister r) { + CHECK_NE(r, kNoWRegister); + return FromRegId(r + kNumberOfCoreRegIds); + } + + static Arm64ManagedRegister FromDRegister(DRegister r) { + CHECK_NE(r, kNoDRegister); + return FromRegId(r + (kNumberOfCoreRegIds + kNumberOfWRegIds)); + } + + static Arm64ManagedRegister FromSRegister(SRegister r) { + CHECK_NE(r, kNoSRegister); + return FromRegId(r + (kNumberOfCoreRegIds + kNumberOfWRegIds + + kNumberOfDRegIds)); + } + + // Returns the X register overlapping W register r. + static Arm64ManagedRegister FromWRegisterCore(WRegister r) { + CHECK_NE(r, kNoWRegister); + return FromRegId(r); + } + + // Return the D register overlapping S register r. + static Arm64ManagedRegister FromSRegisterD(SRegister r) { + CHECK_NE(r, kNoSRegister); + return FromRegId(r + (kNumberOfCoreRegIds + kNumberOfWRegIds)); + } + + private: + bool IsValidManagedRegister() const { + return (0 <= id_) && (id_ < kNumberOfRegIds); + } + + bool IsStackPointer() const { + return IsCoreRegister() && (id_ == SP); + } + + int RegId() const { + CHECK(!IsNoRegister()); + return id_; + } + + int RegNo() const; + int RegIdLow() const; + int RegIdHigh() const; + + friend class ManagedRegister; + + explicit Arm64ManagedRegister(int reg_id) : ManagedRegister(reg_id) {} + + static Arm64ManagedRegister FromRegId(int reg_id) { + Arm64ManagedRegister reg(reg_id); + CHECK(reg.IsValidManagedRegister()); + return reg; + } +}; + +std::ostream& operator<<(std::ostream& os, const Arm64ManagedRegister& reg); + +} // namespace arm64 + +inline arm64::Arm64ManagedRegister ManagedRegister::AsArm64() const { + arm64::Arm64ManagedRegister reg(id_); + CHECK(reg.IsNoRegister() || reg.IsValidManagedRegister()); + return reg; +} + +} // namespace art + +#endif // ART_COMPILER_UTILS_ARM64_MANAGED_REGISTER_ARM64_H_ diff --git a/compiler/utils/arm64/managed_register_arm64_test.cc b/compiler/utils/arm64/managed_register_arm64_test.cc new file mode 100644 index 0000000000..3d98e12dd4 --- /dev/null +++ b/compiler/utils/arm64/managed_register_arm64_test.cc @@ -0,0 +1,611 @@ +/* + * 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 "globals.h" +#include "managed_register_arm64.h" +#include "gtest/gtest.h" + +namespace art { +namespace arm64 { + +TEST(Arm64ManagedRegister, NoRegister) { + Arm64ManagedRegister reg = ManagedRegister::NoRegister().AsArm64(); + EXPECT_TRUE(reg.IsNoRegister()); + EXPECT_TRUE(!reg.Overlaps(reg)); +} + +// X Register test. +TEST(Arm64ManagedRegister, CoreRegister) { + Arm64ManagedRegister reg = Arm64ManagedRegister::FromCoreRegister(X0); + Arm64ManagedRegister wreg = Arm64ManagedRegister::FromWRegister(W0); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsWRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.Overlaps(wreg)); + EXPECT_EQ(X0, reg.AsCoreRegister()); + + reg = Arm64ManagedRegister::FromCoreRegister(X1); + wreg = Arm64ManagedRegister::FromWRegister(W1); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsWRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.Overlaps(wreg)); + EXPECT_EQ(X1, reg.AsCoreRegister()); + + reg = Arm64ManagedRegister::FromCoreRegister(X7); + wreg = Arm64ManagedRegister::FromWRegister(W7); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsWRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.Overlaps(wreg)); + EXPECT_EQ(X7, reg.AsCoreRegister()); + + reg = Arm64ManagedRegister::FromCoreRegister(X15); + wreg = Arm64ManagedRegister::FromWRegister(W15); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsWRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.Overlaps(wreg)); + EXPECT_EQ(X15, reg.AsCoreRegister()); + + reg = Arm64ManagedRegister::FromCoreRegister(X19); + wreg = Arm64ManagedRegister::FromWRegister(W19); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsWRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.Overlaps(wreg)); + EXPECT_EQ(X19, reg.AsCoreRegister()); + + reg = Arm64ManagedRegister::FromCoreRegister(X16); + wreg = Arm64ManagedRegister::FromWRegister(W16); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsWRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.Overlaps(wreg)); + EXPECT_EQ(IP0, reg.AsCoreRegister()); + + reg = Arm64ManagedRegister::FromCoreRegister(SP); + wreg = Arm64ManagedRegister::FromWRegister(WZR); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsWRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.Overlaps(wreg)); + EXPECT_EQ(SP, reg.AsCoreRegister()); +} + +// W register test. +TEST(Arm64ManagedRegister, WRegister) { + Arm64ManagedRegister reg = Arm64ManagedRegister::FromWRegister(W0); + Arm64ManagedRegister xreg = Arm64ManagedRegister::FromCoreRegister(X0); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsWRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.Overlaps(xreg)); + EXPECT_EQ(W0, reg.AsWRegister()); + + reg = Arm64ManagedRegister::FromWRegister(W5); + xreg = Arm64ManagedRegister::FromCoreRegister(X5); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsWRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.Overlaps(xreg)); + EXPECT_EQ(W5, reg.AsWRegister()); + + reg = Arm64ManagedRegister::FromWRegister(W6); + xreg = Arm64ManagedRegister::FromCoreRegister(X6); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsWRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.Overlaps(xreg)); + EXPECT_EQ(W6, reg.AsWRegister()); + + reg = Arm64ManagedRegister::FromWRegister(W18); + xreg = Arm64ManagedRegister::FromCoreRegister(X18); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsWRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.Overlaps(xreg)); + EXPECT_EQ(W18, reg.AsWRegister()); + + reg = Arm64ManagedRegister::FromWRegister(W29); + xreg = Arm64ManagedRegister::FromCoreRegister(FP); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsWRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.Overlaps(xreg)); + EXPECT_EQ(W29, reg.AsWRegister()); + + reg = Arm64ManagedRegister::FromWRegister(WZR); + xreg = Arm64ManagedRegister::FromCoreRegister(SP); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsWRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.Overlaps(xreg)); + EXPECT_EQ(W31, reg.AsWRegister()); +} + +// D Register test. +TEST(Arm64ManagedRegister, DRegister) { + Arm64ManagedRegister reg = Arm64ManagedRegister::FromDRegister(D0); + Arm64ManagedRegister sreg = Arm64ManagedRegister::FromSRegister(S0); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsWRegister()); + EXPECT_TRUE(reg.IsDRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.Overlaps(sreg)); + EXPECT_EQ(D0, reg.AsDRegister()); + EXPECT_EQ(S0, reg.AsOverlappingDRegisterLow()); + EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromDRegister(D0))); + + reg = Arm64ManagedRegister::FromDRegister(D1); + sreg = Arm64ManagedRegister::FromSRegister(S1); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsWRegister()); + EXPECT_TRUE(reg.IsDRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.Overlaps(sreg)); + EXPECT_EQ(D1, reg.AsDRegister()); + EXPECT_EQ(S1, reg.AsOverlappingDRegisterLow()); + EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromDRegister(D1))); + + reg = Arm64ManagedRegister::FromDRegister(D20); + sreg = Arm64ManagedRegister::FromSRegister(S20); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsWRegister()); + EXPECT_TRUE(reg.IsDRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.Overlaps(sreg)); + EXPECT_EQ(D20, reg.AsDRegister()); + EXPECT_EQ(S20, reg.AsOverlappingDRegisterLow()); + EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromDRegister(D20))); + + reg = Arm64ManagedRegister::FromDRegister(D31); + sreg = Arm64ManagedRegister::FromSRegister(S31); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsWRegister()); + EXPECT_TRUE(reg.IsDRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.Overlaps(sreg)); + EXPECT_EQ(D31, reg.AsDRegister()); + EXPECT_EQ(S31, reg.AsOverlappingDRegisterLow()); + EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromDRegister(D31))); +} + +// S Register test. +TEST(Arm64ManagedRegister, SRegister) { + Arm64ManagedRegister reg = Arm64ManagedRegister::FromSRegister(S0); + Arm64ManagedRegister dreg = Arm64ManagedRegister::FromDRegister(D0); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsWRegister()); + EXPECT_TRUE(reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(reg.Overlaps(dreg)); + EXPECT_EQ(S0, reg.AsSRegister()); + EXPECT_EQ(D0, reg.AsOverlappingSRegisterD()); + EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromSRegister(S0))); + + reg = Arm64ManagedRegister::FromSRegister(S5); + dreg = Arm64ManagedRegister::FromDRegister(D5); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsWRegister()); + EXPECT_TRUE(reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(reg.Overlaps(dreg)); + EXPECT_EQ(S5, reg.AsSRegister()); + EXPECT_EQ(D5, reg.AsOverlappingSRegisterD()); + EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromSRegister(S5))); + + reg = Arm64ManagedRegister::FromSRegister(S7); + dreg = Arm64ManagedRegister::FromDRegister(D7); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsWRegister()); + EXPECT_TRUE(reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(reg.Overlaps(dreg)); + EXPECT_EQ(S7, reg.AsSRegister()); + EXPECT_EQ(D7, reg.AsOverlappingSRegisterD()); + EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromSRegister(S7))); + + reg = Arm64ManagedRegister::FromSRegister(S31); + dreg = Arm64ManagedRegister::FromDRegister(D31); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsWRegister()); + EXPECT_TRUE(reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(reg.Overlaps(dreg)); + EXPECT_EQ(S31, reg.AsSRegister()); + EXPECT_EQ(D31, reg.AsOverlappingSRegisterD()); + EXPECT_TRUE(reg.Equals(Arm64ManagedRegister::FromSRegister(S31))); +} + +TEST(Arm64ManagedRegister, Equals) { + ManagedRegister no_reg = ManagedRegister::NoRegister(); + EXPECT_TRUE(no_reg.Equals(Arm64ManagedRegister::NoRegister())); + EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); + EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromWRegister(W0))); + EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromWRegister(W1))); + EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!no_reg.Equals(Arm64ManagedRegister::FromSRegister(S0))); + + Arm64ManagedRegister reg_X0 = Arm64ManagedRegister::FromCoreRegister(X0); + EXPECT_TRUE(!reg_X0.Equals(Arm64ManagedRegister::NoRegister())); + EXPECT_TRUE(reg_X0.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); + EXPECT_TRUE(!reg_X0.Equals(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg_X0.Equals(Arm64ManagedRegister::FromWRegister(W0))); + EXPECT_TRUE(!reg_X0.Equals(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_X0.Equals(Arm64ManagedRegister::FromDRegister(D0))); + + Arm64ManagedRegister reg_X1 = Arm64ManagedRegister::FromCoreRegister(X1); + EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); + EXPECT_TRUE(reg_X1.Equals(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromWRegister(W1))); + EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg_X1.Equals(Arm64ManagedRegister::FromSRegister(S1))); + + Arm64ManagedRegister reg_X31 = Arm64ManagedRegister::FromCoreRegister(X31); + EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromCoreRegister(SP))); + EXPECT_TRUE(reg_X31.Equals(Arm64ManagedRegister::FromCoreRegister(XZR))); + EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromWRegister(W31))); + EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromWRegister(WZR))); + EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_X31.Equals(Arm64ManagedRegister::FromDRegister(D0))); + + Arm64ManagedRegister reg_SP = Arm64ManagedRegister::FromCoreRegister(SP); + EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::NoRegister())); + // We expect these to pass - SP has a different semantic than X31/XZR. + EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromCoreRegister(X31))); + EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromCoreRegister(XZR))); + EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromWRegister(W31))); + EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_SP.Equals(Arm64ManagedRegister::FromDRegister(D0))); + + Arm64ManagedRegister reg_W8 = Arm64ManagedRegister::FromWRegister(W8); + EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); + EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::FromCoreRegister(X8))); + EXPECT_TRUE(reg_W8.Equals(Arm64ManagedRegister::FromWRegister(W8))); + EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg_W8.Equals(Arm64ManagedRegister::FromSRegister(S1))); + + Arm64ManagedRegister reg_W12 = Arm64ManagedRegister::FromWRegister(W12); + EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); + EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::FromCoreRegister(X8))); + EXPECT_TRUE(reg_W12.Equals(Arm64ManagedRegister::FromWRegister(W12))); + EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg_W12.Equals(Arm64ManagedRegister::FromSRegister(S1))); + + Arm64ManagedRegister reg_S0 = Arm64ManagedRegister::FromSRegister(S0); + EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); + EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::FromWRegister(W0))); + EXPECT_TRUE(reg_S0.Equals(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_S0.Equals(Arm64ManagedRegister::FromDRegister(D1))); + + Arm64ManagedRegister reg_S1 = Arm64ManagedRegister::FromSRegister(S1); + EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); + EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::FromWRegister(W0))); + EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(reg_S1.Equals(Arm64ManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_S1.Equals(Arm64ManagedRegister::FromDRegister(D1))); + + Arm64ManagedRegister reg_S31 = Arm64ManagedRegister::FromSRegister(S31); + EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); + EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::FromWRegister(W0))); + EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(reg_S31.Equals(Arm64ManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_S31.Equals(Arm64ManagedRegister::FromDRegister(D1))); + + Arm64ManagedRegister reg_D0 = Arm64ManagedRegister::FromDRegister(D0); + EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); + EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::FromWRegister(W1))); + EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(reg_D0.Equals(Arm64ManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_D0.Equals(Arm64ManagedRegister::FromDRegister(D1))); + + Arm64ManagedRegister reg_D15 = Arm64ManagedRegister::FromDRegister(D15); + EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromCoreRegister(X0))); + EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromWRegister(W0))); + EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_D15.Equals(Arm64ManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(reg_D15.Equals(Arm64ManagedRegister::FromDRegister(D15))); +} + +TEST(Arm64ManagedRegister, Overlaps) { + Arm64ManagedRegister reg = Arm64ManagedRegister::FromCoreRegister(X0); + Arm64ManagedRegister reg_o = Arm64ManagedRegister::FromWRegister(W0); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X0))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(SP))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W0))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); + EXPECT_EQ(X0, reg_o.AsOverlappingWRegisterCore()); + EXPECT_EQ(W0, reg.AsOverlappingCoreRegisterLow()); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15))); + + reg = Arm64ManagedRegister::FromCoreRegister(X10); + reg_o = Arm64ManagedRegister::FromWRegister(W10); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X10))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(SP))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W10))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); + EXPECT_EQ(X10, reg_o.AsOverlappingWRegisterCore()); + EXPECT_EQ(W10, reg.AsOverlappingCoreRegisterLow()); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15))); + + reg = Arm64ManagedRegister::FromCoreRegister(IP1); + reg_o = Arm64ManagedRegister::FromWRegister(W17); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X17))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(SP))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W17))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); + EXPECT_EQ(X17, reg_o.AsOverlappingWRegisterCore()); + EXPECT_EQ(W17, reg.AsOverlappingCoreRegisterLow()); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15))); + + reg = Arm64ManagedRegister::FromCoreRegister(XZR); + reg_o = Arm64ManagedRegister::FromWRegister(WZR); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X31))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(SP))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W19))); + EXPECT_EQ(X31, reg_o.AsOverlappingWRegisterCore()); + EXPECT_EQ(W31, reg.AsOverlappingCoreRegisterLow()); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15))); + + reg = Arm64ManagedRegister::FromCoreRegister(SP); + reg_o = Arm64ManagedRegister::FromWRegister(WZR); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X31))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X15))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31))); + EXPECT_EQ(X31, reg_o.AsOverlappingWRegisterCore()); + EXPECT_EQ(W31, reg.AsOverlappingCoreRegisterLow()); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15))); + + reg = Arm64ManagedRegister::FromWRegister(W1); + reg_o = Arm64ManagedRegister::FromCoreRegister(X1); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31))); + EXPECT_EQ(W1, reg_o.AsOverlappingCoreRegisterLow()); + EXPECT_EQ(X1, reg.AsOverlappingWRegisterCore()); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15))); + + reg = Arm64ManagedRegister::FromWRegister(W21); + reg_o = Arm64ManagedRegister::FromCoreRegister(X21); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromWRegister(W21))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X21))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31))); + EXPECT_EQ(W21, reg_o.AsOverlappingCoreRegisterLow()); + EXPECT_EQ(X21, reg.AsOverlappingWRegisterCore()); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15))); + + + reg = Arm64ManagedRegister::FromSRegister(S1); + reg_o = Arm64ManagedRegister::FromDRegister(D1); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X31))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31))); + EXPECT_EQ(S1, reg_o.AsOverlappingDRegisterLow()); + EXPECT_EQ(D1, reg.AsOverlappingSRegisterD()); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D2))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15))); + + reg = Arm64ManagedRegister::FromSRegister(S15); + reg_o = Arm64ManagedRegister::FromDRegister(D15); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X31))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31))); + EXPECT_EQ(S15, reg_o.AsOverlappingDRegisterLow()); + EXPECT_EQ(D15, reg.AsOverlappingSRegisterD()); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S17))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S16))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D2))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D17))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D20))); + + reg = Arm64ManagedRegister::FromDRegister(D15); + reg_o = Arm64ManagedRegister::FromSRegister(S15); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X31))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromCoreRegister(X15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(WZR))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W1))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W12))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromWRegister(W31))); + EXPECT_EQ(S15, reg.AsOverlappingDRegisterLow()); + EXPECT_EQ(D15, reg_o.AsOverlappingSRegisterD()); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S17))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S16))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(reg.Overlaps(Arm64ManagedRegister::FromDRegister(D15))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D2))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D17))); + EXPECT_TRUE(!reg.Overlaps(Arm64ManagedRegister::FromDRegister(D20))); +} + +} // namespace arm64 +} // namespace art diff --git a/compiler/utils/assembler.cc b/compiler/utils/assembler.cc index 92ce0b8001..67324764f0 100644 --- a/compiler/utils/assembler.cc +++ b/compiler/utils/assembler.cc @@ -20,6 +20,7 @@ #include <vector> #include "arm/assembler_arm.h" +#include "arm64/assembler_arm64.h" #include "mips/assembler_mips.h" #include "x86/assembler_x86.h" #include "globals.h" @@ -106,6 +107,8 @@ Assembler* Assembler::Create(InstructionSet instruction_set) { case kArm: case kThumb2: return new arm::ArmAssembler(); + case kArm64: + return new arm64::Arm64Assembler(); case kMips: return new mips::MipsAssembler(); case kX86: diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h index 53f3375073..f02c20f208 100644 --- a/compiler/utils/assembler.h +++ b/compiler/utils/assembler.h @@ -316,13 +316,13 @@ class Assembler { static Assembler* Create(InstructionSet instruction_set); // Emit slow paths queued during assembly - void EmitSlowPaths() { buffer_.EmitSlowPaths(this); } + virtual void EmitSlowPaths() { buffer_.EmitSlowPaths(this); } // Size of generated code - size_t CodeSize() const { return buffer_.Size(); } + virtual size_t CodeSize() const { return buffer_.Size(); } // Copy instructions out of assembly buffer into the given region of memory - void FinalizeInstructions(const MemoryRegion& region) { + virtual void FinalizeInstructions(const MemoryRegion& region) { buffer_.FinalizeInstructions(region); } diff --git a/compiler/utils/managed_register.h b/compiler/utils/managed_register.h index 4ad1763754..04c972388f 100644 --- a/compiler/utils/managed_register.h +++ b/compiler/utils/managed_register.h @@ -22,6 +22,9 @@ namespace art { namespace arm { class ArmManagedRegister; } +namespace arm64 { +class Arm64ManagedRegister; +} namespace mips { class MipsManagedRegister; } @@ -42,6 +45,7 @@ class ManagedRegister { } arm::ArmManagedRegister AsArm() const; + arm64::Arm64ManagedRegister AsArm64() const; mips::MipsManagedRegister AsMips() const; x86::X86ManagedRegister AsX86() const; diff --git a/runtime/Android.mk b/runtime/Android.mk index fd9dc4c5a7..bb1bc990ee 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -152,6 +152,7 @@ LIBART_COMMON_SRC_FILES := \ LIBART_COMMON_SRC_FILES += \ arch/context.cc \ arch/arm/registers_arm.cc \ + arch/arm64/registers_arm64.cc \ arch/x86/registers_x86.cc \ arch/mips/registers_mips.cc \ entrypoints/entrypoint_utils.cc \ diff --git a/runtime/arch/arm64/registers_arm64.cc b/runtime/arch/arm64/registers_arm64.cc new file mode 100644 index 0000000000..c5bb06be20 --- /dev/null +++ b/runtime/arch/arm64/registers_arm64.cc @@ -0,0 +1,75 @@ +/* + * 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 "registers_arm64.h" + +#include <ostream> + +namespace art { +namespace arm64 { + +static const char* kRegisterNames[] = { + "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", + "x10", "x11", "x12", "x13", "x14", "x15", "ip0", "ip1", "x18", "x19", + "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "fp", + "lr", "xzr", "sp" +}; + +static const char* kWRegisterNames[] = { + "w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7", "w8", "w9", + "w10", "w11", "w12", "w13", "w14", "w15", "w16", "w17", "w18", "w19", + "w20", "w21", "w22", "w23", "w24", "w25", "w26", "w27", "w28", "w29", + "w30", "wzr" +}; + +std::ostream& operator<<(std::ostream& os, const Register& rhs) { + if (rhs >= X0 && rhs <= SP) { + os << kRegisterNames[rhs]; + } else { + os << "XRegister[" << static_cast<int>(rhs) << "]"; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, const WRegister& rhs) { + if (rhs >= W0 && rhs <= WZR) { + os << kWRegisterNames[rhs]; + } else { + os << "WRegister[" << static_cast<int>(rhs) << "]"; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, const DRegister& rhs) { + if (rhs >= D0 && rhs < kNumberOfDRegisters) { + os << "d" << static_cast<int>(rhs); + } else { + os << "DRegister[" << static_cast<int>(rhs) << "]"; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, const SRegister& rhs) { + if (rhs >= S0 && rhs < kNumberOfSRegisters) { + os << "s" << static_cast<int>(rhs); + } else { + os << "SRegister[" << static_cast<int>(rhs) << "]"; + } + return os; +} + +} // namespace arm64 +} // namespace art diff --git a/runtime/arch/arm64/registers_arm64.h b/runtime/arch/arm64/registers_arm64.h new file mode 100644 index 0000000000..e9460e4c4f --- /dev/null +++ b/runtime/arch/arm64/registers_arm64.h @@ -0,0 +1,193 @@ +/* + * 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. + */ + +#ifndef ART_RUNTIME_ARCH_ARM64_REGISTERS_ARM64_H_ +#define ART_RUNTIME_ARCH_ARM64_REGISTERS_ARM64_H_ + +#include <iosfwd> + +namespace art { +namespace arm64 { + +// Values for GP XRegisters - 64bit registers. +enum Register { + X0 = 0, + X1 = 1, + X2 = 2, + X3 = 3, + X4 = 4, + X5 = 5, + X6 = 6, + X7 = 7, + X8 = 8, + X9 = 9, + X10 = 10, + X11 = 11, + X12 = 12, + X13 = 13, + X14 = 14, + X15 = 15, + X16 = 16, + X17 = 17, + X18 = 18, + X19 = 19, + X20 = 20, + X21 = 21, + X22 = 22, + X23 = 23, + X24 = 24, + X25 = 25, + X26 = 26, + X27 = 27, + X28 = 28, + X29 = 29, + X30 = 30, + X31 = 31, + TR = 18, // ART Thread Register. + IP0 = 16, // Used as scratch by VIXL. + IP1 = 17, // Used as scratch by ART JNI Assembler. + FP = 29, + LR = 30, + XZR = 31, + SP = 32, // SP is X31 and overlaps with XRZ but we encode it as a + // special register, due to the different instruction semantics. + kNumberOfCoreRegisters = 33, + kNoRegister = -1, +}; +std::ostream& operator<<(std::ostream& os, const Register& rhs); + +// Values for GP WRegisters - 32bit registers. +enum WRegister { + W0 = 0, + W1 = 1, + W2 = 2, + W3 = 3, + W4 = 4, + W5 = 5, + W6 = 6, + W7 = 7, + W8 = 8, + W9 = 9, + W10 = 10, + W11 = 11, + W12 = 12, + W13 = 13, + W14 = 14, + W15 = 15, + W16 = 16, + W17 = 17, + W18 = 18, + W19 = 19, + W20 = 20, + W21 = 21, + W22 = 22, + W23 = 23, + W24 = 24, + W25 = 25, + W26 = 26, + W27 = 27, + W28 = 28, + W29 = 29, + W30 = 30, + W31 = 31, + WZR = 31, + kNumberOfWRegisters = 32, + kNoWRegister = -1, +}; +std::ostream& operator<<(std::ostream& os, const WRegister& rhs); + +// Values for FP DRegisters - double precision floating point. +enum DRegister { + D0 = 0, + D1 = 1, + D2 = 2, + D3 = 3, + D4 = 4, + D5 = 5, + D6 = 6, + D7 = 7, + D8 = 8, + D9 = 9, + D10 = 10, + D11 = 11, + D12 = 12, + D13 = 13, + D14 = 14, + D15 = 15, + D16 = 16, + D17 = 17, + D18 = 18, + D19 = 19, + D20 = 20, + D21 = 21, + D22 = 22, + D23 = 23, + D24 = 24, + D25 = 25, + D26 = 26, + D27 = 27, + D28 = 28, + D29 = 29, + D30 = 30, + D31 = 31, + kNumberOfDRegisters = 32, + kNoDRegister = -1, +}; +std::ostream& operator<<(std::ostream& os, const DRegister& rhs); + +// Values for FP SRegisters - single precision floating point. +enum SRegister { + S0 = 0, + S1 = 1, + S2 = 2, + S3 = 3, + S4 = 4, + S5 = 5, + S6 = 6, + S7 = 7, + S8 = 8, + S9 = 9, + S10 = 10, + S11 = 11, + S12 = 12, + S13 = 13, + S14 = 14, + S15 = 15, + S16 = 16, + S17 = 17, + S18 = 18, + S19 = 19, + S20 = 20, + S21 = 21, + S22 = 22, + S23 = 23, + S24 = 24, + S25 = 25, + S26 = 26, + S27 = 27, + S28 = 28, + S29 = 29, + S30 = 30, + S31 = 31, + kNumberOfSRegisters = 32, + kNoSRegister = -1, +}; +std::ostream& operator<<(std::ostream& os, const SRegister& rhs); + +} // namespace arm64 +} // namespace art + +#endif // ART_RUNTIME_ARCH_ARM64_REGISTERS_ARM64_H_ diff --git a/runtime/instruction_set.h b/runtime/instruction_set.h index ac83601fc0..cbc991273d 100644 --- a/runtime/instruction_set.h +++ b/runtime/instruction_set.h @@ -27,6 +27,7 @@ namespace art { enum InstructionSet { kNone, kArm, + kArm64, kThumb2, kX86, kX86_64, |