diff options
Diffstat (limited to 'compiler')
39 files changed, 10237 insertions, 31 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk index 68223a2..c0f6181 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -76,6 +76,13 @@ LIBART_COMPILER_SRC_FILES := \ llvm/runtime_support_builder_x86.cc \ stubs/portable/stubs.cc \ stubs/quick/stubs.cc \ + utils/arm/assembler_arm.cc \ + utils/arm/managed_register_arm.cc \ + utils/assembler.cc \ + utils/mips/assembler_mips.cc \ + utils/mips/managed_register_mips.cc \ + utils/x86/assembler_x86.cc \ + utils/x86/managed_register_x86.cc \ elf_fixup.cc \ elf_stripper.cc \ elf_writer.cc \ diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index 64ebb6a..7c3ec14 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -19,7 +19,7 @@ #include "arm_lir.h" #include "codegen_arm.h" #include "dex/quick/mir_to_lir-inl.h" -#include "oat/runtime/oat_support_entrypoints.h" +#include "entrypoints/quick/quick_entrypoints.h" namespace art { diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index e1a77da..4bb507b 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -19,8 +19,8 @@ #include "arm_lir.h" #include "codegen_arm.h" #include "dex/quick/mir_to_lir-inl.h" +#include "entrypoints/quick/quick_entrypoints.h" #include "mirror/array.h" -#include "oat/runtime/oat_support_entrypoints.h" namespace art { diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index 40db2c6..8934340 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -17,8 +17,8 @@ #include "dex/compiler_ir.h" #include "dex/compiler_internals.h" #include "dex/quick/mir_to_lir-inl.h" +#include "entrypoints/quick/quick_entrypoints.h" #include "mirror/array.h" -#include "oat/runtime/oat_support_entrypoints.h" #include "verifier/method_verifier.h" namespace art { diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc index cae1319..91f2500 100644 --- a/compiler/dex/quick/gen_invoke.cc +++ b/compiler/dex/quick/gen_invoke.cc @@ -16,11 +16,11 @@ #include "dex/compiler_ir.h" #include "dex_file-inl.h" +#include "entrypoints/quick/quick_entrypoints.h" #include "invoke_type.h" #include "mirror/array.h" #include "mirror/string.h" #include "mir_to_lir-inl.h" -#include "oat/runtime/oat_support_entrypoints.h" #include "x86/codegen_x86.h" namespace art { diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc index 8b375ea..b6c200c 100644 --- a/compiler/dex/quick/mips/call_mips.cc +++ b/compiler/dex/quick/mips/call_mips.cc @@ -18,8 +18,8 @@ #include "codegen_mips.h" #include "dex/quick/mir_to_lir-inl.h" +#include "entrypoints/quick/quick_entrypoints.h" #include "mips_lir.h" -#include "oat/runtime/oat_support_entrypoints.h" namespace art { diff --git a/compiler/dex/quick/mips/fp_mips.cc b/compiler/dex/quick/mips/fp_mips.cc index 6cd9acc..620527e 100644 --- a/compiler/dex/quick/mips/fp_mips.cc +++ b/compiler/dex/quick/mips/fp_mips.cc @@ -16,8 +16,8 @@ #include "codegen_mips.h" #include "dex/quick/mir_to_lir-inl.h" +#include "entrypoints/quick/quick_entrypoints.h" #include "mips_lir.h" -#include "oat/runtime/oat_support_entrypoints.h" namespace art { diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc index ea7da60..7c8214b 100644 --- a/compiler/dex/quick/mips/int_mips.cc +++ b/compiler/dex/quick/mips/int_mips.cc @@ -18,9 +18,9 @@ #include "codegen_mips.h" #include "dex/quick/mir_to_lir-inl.h" +#include "entrypoints/quick/quick_entrypoints.h" #include "mips_lir.h" #include "mirror/array.h" -#include "oat/runtime/oat_support_entrypoints.h" namespace art { diff --git a/compiler/jni/quick/arm/calling_convention_arm.cc b/compiler/jni/quick/arm/calling_convention_arm.cc index e9b09c5..9778293 100644 --- a/compiler/jni/quick/arm/calling_convention_arm.cc +++ b/compiler/jni/quick/arm/calling_convention_arm.cc @@ -16,7 +16,7 @@ #include "base/logging.h" #include "calling_convention_arm.h" -#include "oat/utils/arm/managed_register_arm.h" +#include "utils/arm/managed_register_arm.h" namespace art { namespace arm { diff --git a/compiler/jni/quick/calling_convention.h b/compiler/jni/quick/calling_convention.h index d492b42..f2b7fd9 100644 --- a/compiler/jni/quick/calling_convention.h +++ b/compiler/jni/quick/calling_convention.h @@ -18,9 +18,9 @@ #define ART_COMPILER_JNI_QUICK_CALLING_CONVENTION_H_ #include <vector> -#include "oat/utils/managed_register.h" #include "stack_indirect_reference_table.h" #include "thread.h" +#include "utils/managed_register.h" namespace art { diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index fa227f7..aeadb54 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -25,13 +25,13 @@ #include "dex_file-inl.h" #include "driver/compiler_driver.h" #include "disassembler.h" +#include "entrypoints/quick/quick_entrypoints.h" #include "jni_internal.h" -#include "oat/runtime/oat_support_entrypoints.h" -#include "oat/utils/assembler.h" -#include "oat/utils/managed_register.h" -#include "oat/utils/arm/managed_register_arm.h" -#include "oat/utils/mips/managed_register_mips.h" -#include "oat/utils/x86/managed_register_x86.h" +#include "utils/assembler.h" +#include "utils/managed_register.h" +#include "utils/arm/managed_register_arm.h" +#include "utils/mips/managed_register_mips.h" +#include "utils/x86/managed_register_x86.h" #include "thread.h" #include "UniquePtr.h" diff --git a/compiler/jni/quick/mips/calling_convention_mips.cc b/compiler/jni/quick/mips/calling_convention_mips.cc index 053ab44..0a48500 100644 --- a/compiler/jni/quick/mips/calling_convention_mips.cc +++ b/compiler/jni/quick/mips/calling_convention_mips.cc @@ -17,7 +17,7 @@ #include "calling_convention_mips.h" #include "base/logging.h" -#include "oat/utils/mips/managed_register_mips.h" +#include "utils/mips/managed_register_mips.h" namespace art { namespace mips { diff --git a/compiler/jni/quick/x86/calling_convention_x86.cc b/compiler/jni/quick/x86/calling_convention_x86.cc index 45dd429..8b5c86d 100644 --- a/compiler/jni/quick/x86/calling_convention_x86.cc +++ b/compiler/jni/quick/x86/calling_convention_x86.cc @@ -17,7 +17,7 @@ #include "calling_convention_x86.h" #include "base/logging.h" -#include "oat/utils/x86/managed_register_x86.h" +#include "utils/x86/managed_register_x86.h" #include "utils.h" namespace art { diff --git a/compiler/llvm/runtime_support_builder.cc b/compiler/llvm/runtime_support_builder.cc index 7299803..24e283d 100644 --- a/compiler/llvm/runtime_support_builder.cc +++ b/compiler/llvm/runtime_support_builder.cc @@ -20,6 +20,7 @@ #include "ir_builder.h" #include "monitor.h" #include "mirror/object.h" +#include "runtime_support_llvm_func_list.h" #include "thread.h" #include <llvm/IR/DerivedTypes.h> @@ -47,10 +48,7 @@ RuntimeSupportBuilder::RuntimeSupportBuilder(::llvm::LLVMContext& context, runtime_support_func_decls_[runtime_support::ID] = fn; \ } while (0); -#include "runtime_support_llvm_func_list.h" RUNTIME_SUPPORT_FUNC_LIST(GET_RUNTIME_SUPPORT_FUNC_DECL) -#undef RUNTIME_SUPPORT_FUNC_LIST -#undef GET_RUNTIME_SUPPORT_FUNC_DECL } diff --git a/compiler/llvm/runtime_support_llvm_func.h b/compiler/llvm/runtime_support_llvm_func.h index 2634c68..a5ad852 100644 --- a/compiler/llvm/runtime_support_llvm_func.h +++ b/compiler/llvm/runtime_support_llvm_func.h @@ -17,16 +17,15 @@ #ifndef ART_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_FUNC_H_ #define ART_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_FUNC_H_ +#include "runtime_support_llvm_func_list.h" + namespace art { namespace llvm { namespace runtime_support { enum RuntimeId { #define DEFINE_RUNTIME_SUPPORT_FUNC_ID(ID, NAME) ID, -#include "runtime_support_llvm_func_list.h" RUNTIME_SUPPORT_FUNC_LIST(DEFINE_RUNTIME_SUPPORT_FUNC_ID) -#undef RUNTIME_SUPPORT_FUNC_LIST -#undef DEFINE_RUNTIME_SUPPORT_FUNC_ID MAX_ID }; diff --git a/compiler/llvm/runtime_support_llvm_func_list.h b/compiler/llvm/runtime_support_llvm_func_list.h new file mode 100644 index 0000000..b5ac1ff --- /dev/null +++ b/compiler/llvm/runtime_support_llvm_func_list.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 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_LLVM_RUNTIME_SUPPORT_LLVM_FUNC_LIST_H_ +#define ART_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_FUNC_LIST_H_ + +#define RUNTIME_SUPPORT_FUNC_LIST(V) \ + V(LockObject, art_portable_lock_object_from_code) \ + V(UnlockObject, art_portable_unlock_object_from_code) \ + V(GetCurrentThread, art_portable_get_current_thread_from_code) \ + V(SetCurrentThread, art_portable_set_current_thread_from_code) \ + V(PushShadowFrame, art_portable_push_shadow_frame_from_code) \ + V(PopShadowFrame, art_portable_pop_shadow_frame_from_code) \ + V(TestSuspend, art_portable_test_suspend_from_code) \ + V(ThrowException, art_portable_throw_exception_from_code) \ + V(ThrowStackOverflowException, art_portable_throw_stack_overflow_from_code) \ + V(ThrowNullPointerException, art_portable_throw_null_pointer_exception_from_code) \ + V(ThrowDivZeroException, art_portable_throw_div_zero_from_code) \ + V(ThrowIndexOutOfBounds, art_portable_throw_array_bounds_from_code) \ + V(InitializeTypeAndVerifyAccess, art_portable_initialize_type_and_verify_access_from_code) \ + V(InitializeType, art_portable_initialize_type_from_code) \ + V(IsAssignable, art_portable_is_assignable_from_code) \ + V(CheckCast, art_portable_check_cast_from_code) \ + V(CheckPutArrayElement, art_portable_check_put_array_element_from_code) \ + V(AllocObject, art_portable_alloc_object_from_code) \ + V(AllocObjectWithAccessCheck, art_portable_alloc_object_from_code_with_access_check) \ + V(AllocArray, art_portable_alloc_array_from_code) \ + V(AllocArrayWithAccessCheck, art_portable_alloc_array_from_code_with_access_check) \ + V(CheckAndAllocArray, art_portable_check_and_alloc_array_from_code) \ + V(CheckAndAllocArrayWithAccessCheck, art_portable_check_and_alloc_array_from_code_with_access_check) \ + V(FindStaticMethodWithAccessCheck, art_portable_find_static_method_from_code_with_access_check) \ + V(FindDirectMethodWithAccessCheck, art_portable_find_direct_method_from_code_with_access_check) \ + V(FindVirtualMethodWithAccessCheck, art_portable_find_virtual_method_from_code_with_access_check) \ + V(FindSuperMethodWithAccessCheck, art_portable_find_super_method_from_code_with_access_check) \ + V(FindInterfaceMethodWithAccessCheck, art_portable_find_interface_method_from_code_with_access_check) \ + V(FindInterfaceMethod, art_portable_find_interface_method_from_code) \ + V(ResolveString, art_portable_resolve_string_from_code) \ + V(Set32Static, art_portable_set32_static_from_code) \ + V(Set64Static, art_portable_set64_static_from_code) \ + V(SetObjectStatic, art_portable_set_obj_static_from_code) \ + V(Get32Static, art_portable_get32_static_from_code) \ + V(Get64Static, art_portable_get64_static_from_code) \ + V(GetObjectStatic, art_portable_get_obj_static_from_code) \ + V(Set32Instance, art_portable_set32_instance_from_code) \ + V(Set64Instance, art_portable_set64_instance_from_code) \ + V(SetObjectInstance, art_portable_set_obj_instance_from_code) \ + V(Get32Instance, art_portable_get32_instance_from_code) \ + V(Get64Instance, art_portable_get64_instance_from_code) \ + V(GetObjectInstance, art_portable_get_obj_instance_from_code) \ + V(InitializeStaticStorage, art_portable_initialize_static_storage_from_code) \ + V(FillArrayData, art_portable_fill_array_data_from_code) \ + V(GetAndClearException, art_portable_get_and_clear_exception) \ + V(IsExceptionPending, art_portable_is_exception_pending_from_code) \ + V(FindCatchBlock, art_portable_find_catch_block_from_code) \ + V(MarkGCCard, art_portable_mark_gc_card_from_code) \ + V(ProxyInvokeHandler, art_portable_proxy_invoke_handler_from_code) \ + V(art_d2l, art_d2l) \ + V(art_d2i, art_d2i) \ + V(art_f2l, art_f2l) \ + V(art_f2i, art_f2i) \ + V(JniMethodStart, art_portable_jni_method_start) \ + V(JniMethodStartSynchronized, art_portable_jni_method_start_synchronized) \ + V(JniMethodEnd, art_portable_jni_method_end) \ + V(JniMethodEndSynchronized, art_portable_jni_method_end_synchronized) \ + V(JniMethodEndWithReference, art_portable_jni_method_end_with_reference) \ + V(JniMethodEndWithReferenceSynchronized, art_portable_jni_method_end_with_reference_synchronized) + +#endif // ART_COMPILER_LLVM_RUNTIME_SUPPORT_LLVM_FUNC_LIST_H_ diff --git a/compiler/stubs/portable/stubs.cc b/compiler/stubs/portable/stubs.cc index 69568d7..cee6847 100644 --- a/compiler/stubs/portable/stubs.cc +++ b/compiler/stubs/portable/stubs.cc @@ -16,11 +16,11 @@ #include "stubs/stubs.h" +#include "entrypoints/quick/quick_entrypoints.h" #include "jni_internal.h" -#include "oat/utils/arm/assembler_arm.h" -#include "oat/utils/mips/assembler_mips.h" -#include "oat/utils/x86/assembler_x86.h" -#include "oat/runtime/oat_support_entrypoints.h" +#include "utils/arm/assembler_arm.h" +#include "utils/mips/assembler_mips.h" +#include "utils/x86/assembler_x86.h" #include "stack_indirect_reference_table.h" #include "sirt_ref.h" diff --git a/compiler/stubs/quick/stubs.cc b/compiler/stubs/quick/stubs.cc index 8fc2a81..598481f 100644 --- a/compiler/stubs/quick/stubs.cc +++ b/compiler/stubs/quick/stubs.cc @@ -16,11 +16,11 @@ #include "stubs/stubs.h" +#include "entrypoints/quick/quick_entrypoints.h" #include "jni_internal.h" -#include "oat/runtime/oat_support_entrypoints.h" -#include "oat/utils/arm/assembler_arm.h" -#include "oat/utils/mips/assembler_mips.h" -#include "oat/utils/x86/assembler_x86.h" +#include "utils/arm/assembler_arm.h" +#include "utils/mips/assembler_mips.h" +#include "utils/x86/assembler_x86.h" #include "sirt_ref.h" #include "stack_indirect_reference_table.h" diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc new file mode 100644 index 0000000..0778cd3 --- /dev/null +++ b/compiler/utils/arm/assembler_arm.cc @@ -0,0 +1,1895 @@ +/* + * Copyright (C) 2011 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_arm.h" + +#include "base/logging.h" +#include "entrypoints/quick/quick_entrypoints.h" +#include "offsets.h" +#include "thread.h" +#include "utils.h" + +namespace art { +namespace arm { + +// Instruction encoding bits. +enum { + H = 1 << 5, // halfword (or byte) + L = 1 << 20, // load (or store) + S = 1 << 20, // set condition code (or leave unchanged) + W = 1 << 21, // writeback base register (or leave unchanged) + A = 1 << 21, // accumulate in multiply instruction (or not) + B = 1 << 22, // unsigned byte (or word) + N = 1 << 22, // long (or short) + U = 1 << 23, // positive (or negative) offset/index + P = 1 << 24, // offset/pre-indexed addressing (or post-indexed addressing) + I = 1 << 25, // immediate shifter operand (or not) + + B0 = 1, + B1 = 1 << 1, + B2 = 1 << 2, + B3 = 1 << 3, + B4 = 1 << 4, + B5 = 1 << 5, + B6 = 1 << 6, + B7 = 1 << 7, + B8 = 1 << 8, + B9 = 1 << 9, + B10 = 1 << 10, + B11 = 1 << 11, + B12 = 1 << 12, + B16 = 1 << 16, + B17 = 1 << 17, + B18 = 1 << 18, + B19 = 1 << 19, + B20 = 1 << 20, + B21 = 1 << 21, + B22 = 1 << 22, + B23 = 1 << 23, + B24 = 1 << 24, + B25 = 1 << 25, + B26 = 1 << 26, + B27 = 1 << 27, + + // Instruction bit masks. + RdMask = 15 << 12, // in str instruction + CondMask = 15 << 28, + CoprocessorMask = 15 << 8, + OpCodeMask = 15 << 21, // in data-processing instructions + Imm24Mask = (1 << 24) - 1, + Off12Mask = (1 << 12) - 1, + + // ldrex/strex register field encodings. + kLdExRnShift = 16, + kLdExRtShift = 12, + kStrExRnShift = 16, + kStrExRdShift = 12, + kStrExRtShift = 0, +}; + + +static const char* kRegisterNames[] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", + "fp", "ip", "sp", "lr", "pc" +}; +std::ostream& operator<<(std::ostream& os, const Register& rhs) { + if (rhs >= R0 && rhs <= PC) { + os << kRegisterNames[rhs]; + } else { + os << "Register[" << 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; +} + + +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; +} + + +static const char* kConditionNames[] = { + "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC", "HI", "LS", "GE", "LT", "GT", + "LE", "AL", +}; +std::ostream& operator<<(std::ostream& os, const Condition& rhs) { + if (rhs >= EQ && rhs <= AL) { + os << kConditionNames[rhs]; + } else { + os << "Condition[" << static_cast<int>(rhs) << "]"; + } + return os; +} + +void ArmAssembler::Emit(int32_t value) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + buffer_.Emit<int32_t>(value); +} + + +void ArmAssembler::EmitType01(Condition cond, + int type, + Opcode opcode, + int set_cc, + Register rn, + Register rd, + ShifterOperand so) { + CHECK_NE(rd, kNoRegister); + CHECK_NE(cond, kNoCondition); + int32_t encoding = static_cast<int32_t>(cond) << kConditionShift | + type << kTypeShift | + static_cast<int32_t>(opcode) << kOpcodeShift | + set_cc << kSShift | + static_cast<int32_t>(rn) << kRnShift | + static_cast<int32_t>(rd) << kRdShift | + so.encoding(); + Emit(encoding); +} + + +void ArmAssembler::EmitType5(Condition cond, int offset, bool link) { + CHECK_NE(cond, kNoCondition); + int32_t encoding = static_cast<int32_t>(cond) << kConditionShift | + 5 << kTypeShift | + (link ? 1 : 0) << kLinkShift; + Emit(ArmAssembler::EncodeBranchOffset(offset, encoding)); +} + + +void ArmAssembler::EmitMemOp(Condition cond, + bool load, + bool byte, + Register rd, + Address ad) { + CHECK_NE(rd, kNoRegister); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B26 | + (load ? L : 0) | + (byte ? B : 0) | + (static_cast<int32_t>(rd) << kRdShift) | + ad.encoding(); + Emit(encoding); +} + + +void ArmAssembler::EmitMemOpAddressMode3(Condition cond, + int32_t mode, + Register rd, + Address ad) { + CHECK_NE(rd, kNoRegister); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B22 | + mode | + (static_cast<int32_t>(rd) << kRdShift) | + ad.encoding3(); + Emit(encoding); +} + + +void ArmAssembler::EmitMultiMemOp(Condition cond, + BlockAddressMode am, + bool load, + Register base, + RegList regs) { + CHECK_NE(base, kNoRegister); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B27 | + am | + (load ? L : 0) | + (static_cast<int32_t>(base) << kRnShift) | + regs; + Emit(encoding); +} + + +void ArmAssembler::EmitShiftImmediate(Condition cond, + Shift opcode, + Register rd, + Register rm, + ShifterOperand so) { + CHECK_NE(cond, kNoCondition); + CHECK_EQ(so.type(), 1U); + int32_t encoding = static_cast<int32_t>(cond) << kConditionShift | + static_cast<int32_t>(MOV) << kOpcodeShift | + static_cast<int32_t>(rd) << kRdShift | + so.encoding() << kShiftImmShift | + static_cast<int32_t>(opcode) << kShiftShift | + static_cast<int32_t>(rm); + Emit(encoding); +} + + +void ArmAssembler::EmitShiftRegister(Condition cond, + Shift opcode, + Register rd, + Register rm, + ShifterOperand so) { + CHECK_NE(cond, kNoCondition); + CHECK_EQ(so.type(), 0U); + int32_t encoding = static_cast<int32_t>(cond) << kConditionShift | + static_cast<int32_t>(MOV) << kOpcodeShift | + static_cast<int32_t>(rd) << kRdShift | + so.encoding() << kShiftRegisterShift | + static_cast<int32_t>(opcode) << kShiftShift | + B4 | + static_cast<int32_t>(rm); + Emit(encoding); +} + + +void ArmAssembler::EmitBranch(Condition cond, Label* label, bool link) { + if (label->IsBound()) { + EmitType5(cond, label->Position() - buffer_.Size(), link); + } else { + int position = buffer_.Size(); + // Use the offset field of the branch instruction for linking the sites. + EmitType5(cond, label->position_, link); + label->LinkTo(position); + } +} + +void ArmAssembler::and_(Register rd, Register rn, ShifterOperand so, + Condition cond) { + EmitType01(cond, so.type(), AND, 0, rn, rd, so); +} + + +void ArmAssembler::eor(Register rd, Register rn, ShifterOperand so, + Condition cond) { + EmitType01(cond, so.type(), EOR, 0, rn, rd, so); +} + + +void ArmAssembler::sub(Register rd, Register rn, ShifterOperand so, + Condition cond) { + EmitType01(cond, so.type(), SUB, 0, rn, rd, so); +} + +void ArmAssembler::rsb(Register rd, Register rn, ShifterOperand so, + Condition cond) { + EmitType01(cond, so.type(), RSB, 0, rn, rd, so); +} + +void ArmAssembler::rsbs(Register rd, Register rn, ShifterOperand so, + Condition cond) { + EmitType01(cond, so.type(), RSB, 1, rn, rd, so); +} + + +void ArmAssembler::add(Register rd, Register rn, ShifterOperand so, + Condition cond) { + EmitType01(cond, so.type(), ADD, 0, rn, rd, so); +} + + +void ArmAssembler::adds(Register rd, Register rn, ShifterOperand so, + Condition cond) { + EmitType01(cond, so.type(), ADD, 1, rn, rd, so); +} + + +void ArmAssembler::subs(Register rd, Register rn, ShifterOperand so, + Condition cond) { + EmitType01(cond, so.type(), SUB, 1, rn, rd, so); +} + + +void ArmAssembler::adc(Register rd, Register rn, ShifterOperand so, + Condition cond) { + EmitType01(cond, so.type(), ADC, 0, rn, rd, so); +} + + +void ArmAssembler::sbc(Register rd, Register rn, ShifterOperand so, + Condition cond) { + EmitType01(cond, so.type(), SBC, 0, rn, rd, so); +} + + +void ArmAssembler::rsc(Register rd, Register rn, ShifterOperand so, + Condition cond) { + EmitType01(cond, so.type(), RSC, 0, rn, rd, so); +} + + +void ArmAssembler::tst(Register rn, ShifterOperand so, Condition cond) { + CHECK_NE(rn, PC); // Reserve tst pc instruction for exception handler marker. + EmitType01(cond, so.type(), TST, 1, rn, R0, so); +} + + +void ArmAssembler::teq(Register rn, ShifterOperand so, Condition cond) { + CHECK_NE(rn, PC); // Reserve teq pc instruction for exception handler marker. + EmitType01(cond, so.type(), TEQ, 1, rn, R0, so); +} + + +void ArmAssembler::cmp(Register rn, ShifterOperand so, Condition cond) { + EmitType01(cond, so.type(), CMP, 1, rn, R0, so); +} + + +void ArmAssembler::cmn(Register rn, ShifterOperand so, Condition cond) { + EmitType01(cond, so.type(), CMN, 1, rn, R0, so); +} + + +void ArmAssembler::orr(Register rd, Register rn, + ShifterOperand so, Condition cond) { + EmitType01(cond, so.type(), ORR, 0, rn, rd, so); +} + + +void ArmAssembler::orrs(Register rd, Register rn, + ShifterOperand so, Condition cond) { + EmitType01(cond, so.type(), ORR, 1, rn, rd, so); +} + + +void ArmAssembler::mov(Register rd, ShifterOperand so, Condition cond) { + EmitType01(cond, so.type(), MOV, 0, R0, rd, so); +} + + +void ArmAssembler::movs(Register rd, ShifterOperand so, Condition cond) { + EmitType01(cond, so.type(), MOV, 1, R0, rd, so); +} + + +void ArmAssembler::bic(Register rd, Register rn, ShifterOperand so, + Condition cond) { + EmitType01(cond, so.type(), BIC, 0, rn, rd, so); +} + + +void ArmAssembler::mvn(Register rd, ShifterOperand so, Condition cond) { + EmitType01(cond, so.type(), MVN, 0, R0, rd, so); +} + + +void ArmAssembler::mvns(Register rd, ShifterOperand so, Condition cond) { + EmitType01(cond, so.type(), MVN, 1, R0, rd, so); +} + + +void ArmAssembler::clz(Register rd, Register rm, Condition cond) { + CHECK_NE(rd, kNoRegister); + CHECK_NE(rm, kNoRegister); + CHECK_NE(cond, kNoCondition); + CHECK_NE(rd, PC); + CHECK_NE(rm, PC); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B24 | B22 | B21 | (0xf << 16) | + (static_cast<int32_t>(rd) << kRdShift) | + (0xf << 8) | B4 | static_cast<int32_t>(rm); + Emit(encoding); +} + + +void ArmAssembler::movw(Register rd, uint16_t imm16, Condition cond) { + CHECK_NE(cond, kNoCondition); + int32_t encoding = static_cast<int32_t>(cond) << kConditionShift | + B25 | B24 | ((imm16 >> 12) << 16) | + static_cast<int32_t>(rd) << kRdShift | (imm16 & 0xfff); + Emit(encoding); +} + + +void ArmAssembler::movt(Register rd, uint16_t imm16, Condition cond) { + CHECK_NE(cond, kNoCondition); + int32_t encoding = static_cast<int32_t>(cond) << kConditionShift | + B25 | B24 | B22 | ((imm16 >> 12) << 16) | + static_cast<int32_t>(rd) << kRdShift | (imm16 & 0xfff); + Emit(encoding); +} + + +void ArmAssembler::EmitMulOp(Condition cond, int32_t opcode, + Register rd, Register rn, + Register rm, Register rs) { + CHECK_NE(rd, kNoRegister); + CHECK_NE(rn, kNoRegister); + CHECK_NE(rm, kNoRegister); + CHECK_NE(rs, kNoRegister); + CHECK_NE(cond, kNoCondition); + int32_t encoding = opcode | + (static_cast<int32_t>(cond) << kConditionShift) | + (static_cast<int32_t>(rn) << kRnShift) | + (static_cast<int32_t>(rd) << kRdShift) | + (static_cast<int32_t>(rs) << kRsShift) | + B7 | B4 | + (static_cast<int32_t>(rm) << kRmShift); + Emit(encoding); +} + + +void ArmAssembler::mul(Register rd, Register rn, Register rm, Condition cond) { + // Assembler registers rd, rn, rm are encoded as rn, rm, rs. + EmitMulOp(cond, 0, R0, rd, rn, rm); +} + + +void ArmAssembler::mla(Register rd, Register rn, Register rm, Register ra, + Condition cond) { + // Assembler registers rd, rn, rm, ra are encoded as rn, rm, rs, rd. + EmitMulOp(cond, B21, ra, rd, rn, rm); +} + + +void ArmAssembler::mls(Register rd, Register rn, Register rm, Register ra, + Condition cond) { + // Assembler registers rd, rn, rm, ra are encoded as rn, rm, rs, rd. + EmitMulOp(cond, B22 | B21, ra, rd, rn, rm); +} + + +void ArmAssembler::umull(Register rd_lo, Register rd_hi, Register rn, + Register rm, Condition cond) { + // Assembler registers rd_lo, rd_hi, rn, rm are encoded as rd, rn, rm, rs. + EmitMulOp(cond, B23, rd_lo, rd_hi, rn, rm); +} + + +void ArmAssembler::ldr(Register rd, Address ad, Condition cond) { + EmitMemOp(cond, true, false, rd, ad); +} + + +void ArmAssembler::str(Register rd, Address ad, Condition cond) { + EmitMemOp(cond, false, false, rd, ad); +} + + +void ArmAssembler::ldrb(Register rd, Address ad, Condition cond) { + EmitMemOp(cond, true, true, rd, ad); +} + + +void ArmAssembler::strb(Register rd, Address ad, Condition cond) { + EmitMemOp(cond, false, true, rd, ad); +} + + +void ArmAssembler::ldrh(Register rd, Address ad, Condition cond) { + EmitMemOpAddressMode3(cond, L | B7 | H | B4, rd, ad); +} + + +void ArmAssembler::strh(Register rd, Address ad, Condition cond) { + EmitMemOpAddressMode3(cond, B7 | H | B4, rd, ad); +} + + +void ArmAssembler::ldrsb(Register rd, Address ad, Condition cond) { + EmitMemOpAddressMode3(cond, L | B7 | B6 | B4, rd, ad); +} + + +void ArmAssembler::ldrsh(Register rd, Address ad, Condition cond) { + EmitMemOpAddressMode3(cond, L | B7 | B6 | H | B4, rd, ad); +} + + +void ArmAssembler::ldrd(Register rd, Address ad, Condition cond) { + CHECK_EQ(rd % 2, 0); + EmitMemOpAddressMode3(cond, B7 | B6 | B4, rd, ad); +} + + +void ArmAssembler::strd(Register rd, Address ad, Condition cond) { + CHECK_EQ(rd % 2, 0); + EmitMemOpAddressMode3(cond, B7 | B6 | B5 | B4, rd, ad); +} + + +void ArmAssembler::ldm(BlockAddressMode am, + Register base, + RegList regs, + Condition cond) { + EmitMultiMemOp(cond, am, true, base, regs); +} + + +void ArmAssembler::stm(BlockAddressMode am, + Register base, + RegList regs, + Condition cond) { + EmitMultiMemOp(cond, am, false, base, regs); +} + + +void ArmAssembler::ldrex(Register rt, Register rn, Condition cond) { + CHECK_NE(rn, kNoRegister); + CHECK_NE(rt, kNoRegister); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B24 | + B23 | + L | + (static_cast<int32_t>(rn) << kLdExRnShift) | + (static_cast<int32_t>(rt) << kLdExRtShift) | + B11 | B10 | B9 | B8 | B7 | B4 | B3 | B2 | B1 | B0; + Emit(encoding); +} + + +void ArmAssembler::strex(Register rd, + Register rt, + Register rn, + Condition cond) { + CHECK_NE(rn, kNoRegister); + CHECK_NE(rd, kNoRegister); + CHECK_NE(rt, kNoRegister); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B24 | + B23 | + (static_cast<int32_t>(rn) << kStrExRnShift) | + (static_cast<int32_t>(rd) << kStrExRdShift) | + B11 | B10 | B9 | B8 | B7 | B4 | + (static_cast<int32_t>(rt) << kStrExRtShift); + Emit(encoding); +} + + +void ArmAssembler::clrex() { + int32_t encoding = (kSpecialCondition << kConditionShift) | + B26 | B24 | B22 | B21 | B20 | (0xff << 12) | B4 | 0xf; + Emit(encoding); +} + + +void ArmAssembler::nop(Condition cond) { + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B25 | B24 | B21 | (0xf << 12); + Emit(encoding); +} + + +void ArmAssembler::vmovsr(SRegister sn, Register rt, Condition cond) { + CHECK_NE(sn, kNoSRegister); + CHECK_NE(rt, kNoRegister); + CHECK_NE(rt, SP); + CHECK_NE(rt, PC); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B27 | B26 | B25 | + ((static_cast<int32_t>(sn) >> 1)*B16) | + (static_cast<int32_t>(rt)*B12) | B11 | B9 | + ((static_cast<int32_t>(sn) & 1)*B7) | B4; + Emit(encoding); +} + + +void ArmAssembler::vmovrs(Register rt, SRegister sn, Condition cond) { + CHECK_NE(sn, kNoSRegister); + CHECK_NE(rt, kNoRegister); + CHECK_NE(rt, SP); + CHECK_NE(rt, PC); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B27 | B26 | B25 | B20 | + ((static_cast<int32_t>(sn) >> 1)*B16) | + (static_cast<int32_t>(rt)*B12) | B11 | B9 | + ((static_cast<int32_t>(sn) & 1)*B7) | B4; + Emit(encoding); +} + + +void ArmAssembler::vmovsrr(SRegister sm, Register rt, Register rt2, + Condition cond) { + CHECK_NE(sm, kNoSRegister); + CHECK_NE(sm, S31); + CHECK_NE(rt, kNoRegister); + CHECK_NE(rt, SP); + CHECK_NE(rt, PC); + CHECK_NE(rt2, kNoRegister); + CHECK_NE(rt2, SP); + CHECK_NE(rt2, PC); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B27 | B26 | B22 | + (static_cast<int32_t>(rt2)*B16) | + (static_cast<int32_t>(rt)*B12) | B11 | B9 | + ((static_cast<int32_t>(sm) & 1)*B5) | B4 | + (static_cast<int32_t>(sm) >> 1); + Emit(encoding); +} + + +void ArmAssembler::vmovrrs(Register rt, Register rt2, SRegister sm, + Condition cond) { + CHECK_NE(sm, kNoSRegister); + CHECK_NE(sm, S31); + CHECK_NE(rt, kNoRegister); + CHECK_NE(rt, SP); + CHECK_NE(rt, PC); + CHECK_NE(rt2, kNoRegister); + CHECK_NE(rt2, SP); + CHECK_NE(rt2, PC); + CHECK_NE(rt, rt2); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B27 | B26 | B22 | B20 | + (static_cast<int32_t>(rt2)*B16) | + (static_cast<int32_t>(rt)*B12) | B11 | B9 | + ((static_cast<int32_t>(sm) & 1)*B5) | B4 | + (static_cast<int32_t>(sm) >> 1); + Emit(encoding); +} + + +void ArmAssembler::vmovdrr(DRegister dm, Register rt, Register rt2, + Condition cond) { + CHECK_NE(dm, kNoDRegister); + CHECK_NE(rt, kNoRegister); + CHECK_NE(rt, SP); + CHECK_NE(rt, PC); + CHECK_NE(rt2, kNoRegister); + CHECK_NE(rt2, SP); + CHECK_NE(rt2, PC); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B27 | B26 | B22 | + (static_cast<int32_t>(rt2)*B16) | + (static_cast<int32_t>(rt)*B12) | B11 | B9 | B8 | + ((static_cast<int32_t>(dm) >> 4)*B5) | B4 | + (static_cast<int32_t>(dm) & 0xf); + Emit(encoding); +} + + +void ArmAssembler::vmovrrd(Register rt, Register rt2, DRegister dm, + Condition cond) { + CHECK_NE(dm, kNoDRegister); + CHECK_NE(rt, kNoRegister); + CHECK_NE(rt, SP); + CHECK_NE(rt, PC); + CHECK_NE(rt2, kNoRegister); + CHECK_NE(rt2, SP); + CHECK_NE(rt2, PC); + CHECK_NE(rt, rt2); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B27 | B26 | B22 | B20 | + (static_cast<int32_t>(rt2)*B16) | + (static_cast<int32_t>(rt)*B12) | B11 | B9 | B8 | + ((static_cast<int32_t>(dm) >> 4)*B5) | B4 | + (static_cast<int32_t>(dm) & 0xf); + Emit(encoding); +} + + +void ArmAssembler::vldrs(SRegister sd, Address ad, Condition cond) { + CHECK_NE(sd, kNoSRegister); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B27 | B26 | B24 | B20 | + ((static_cast<int32_t>(sd) & 1)*B22) | + ((static_cast<int32_t>(sd) >> 1)*B12) | + B11 | B9 | ad.vencoding(); + Emit(encoding); +} + + +void ArmAssembler::vstrs(SRegister sd, Address ad, Condition cond) { + CHECK_NE(static_cast<Register>(ad.encoding_ & (0xf << kRnShift)), PC); + CHECK_NE(sd, kNoSRegister); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B27 | B26 | B24 | + ((static_cast<int32_t>(sd) & 1)*B22) | + ((static_cast<int32_t>(sd) >> 1)*B12) | + B11 | B9 | ad.vencoding(); + Emit(encoding); +} + + +void ArmAssembler::vldrd(DRegister dd, Address ad, Condition cond) { + CHECK_NE(dd, kNoDRegister); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B27 | B26 | B24 | B20 | + ((static_cast<int32_t>(dd) >> 4)*B22) | + ((static_cast<int32_t>(dd) & 0xf)*B12) | + B11 | B9 | B8 | ad.vencoding(); + Emit(encoding); +} + + +void ArmAssembler::vstrd(DRegister dd, Address ad, Condition cond) { + CHECK_NE(static_cast<Register>(ad.encoding_ & (0xf << kRnShift)), PC); + CHECK_NE(dd, kNoDRegister); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B27 | B26 | B24 | + ((static_cast<int32_t>(dd) >> 4)*B22) | + ((static_cast<int32_t>(dd) & 0xf)*B12) | + B11 | B9 | B8 | ad.vencoding(); + Emit(encoding); +} + + +void ArmAssembler::EmitVFPsss(Condition cond, int32_t opcode, + SRegister sd, SRegister sn, SRegister sm) { + CHECK_NE(sd, kNoSRegister); + CHECK_NE(sn, kNoSRegister); + CHECK_NE(sm, kNoSRegister); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B27 | B26 | B25 | B11 | B9 | opcode | + ((static_cast<int32_t>(sd) & 1)*B22) | + ((static_cast<int32_t>(sn) >> 1)*B16) | + ((static_cast<int32_t>(sd) >> 1)*B12) | + ((static_cast<int32_t>(sn) & 1)*B7) | + ((static_cast<int32_t>(sm) & 1)*B5) | + (static_cast<int32_t>(sm) >> 1); + Emit(encoding); +} + + +void ArmAssembler::EmitVFPddd(Condition cond, int32_t opcode, + DRegister dd, DRegister dn, DRegister dm) { + CHECK_NE(dd, kNoDRegister); + CHECK_NE(dn, kNoDRegister); + CHECK_NE(dm, kNoDRegister); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B27 | B26 | B25 | B11 | B9 | B8 | opcode | + ((static_cast<int32_t>(dd) >> 4)*B22) | + ((static_cast<int32_t>(dn) & 0xf)*B16) | + ((static_cast<int32_t>(dd) & 0xf)*B12) | + ((static_cast<int32_t>(dn) >> 4)*B7) | + ((static_cast<int32_t>(dm) >> 4)*B5) | + (static_cast<int32_t>(dm) & 0xf); + Emit(encoding); +} + + +void ArmAssembler::vmovs(SRegister sd, SRegister sm, Condition cond) { + EmitVFPsss(cond, B23 | B21 | B20 | B6, sd, S0, sm); +} + + +void ArmAssembler::vmovd(DRegister dd, DRegister dm, Condition cond) { + EmitVFPddd(cond, B23 | B21 | B20 | B6, dd, D0, dm); +} + + +bool ArmAssembler::vmovs(SRegister sd, float s_imm, Condition cond) { + uint32_t imm32 = bit_cast<uint32_t, float>(s_imm); + if (((imm32 & ((1 << 19) - 1)) == 0) && + ((((imm32 >> 25) & ((1 << 6) - 1)) == (1 << 5)) || + (((imm32 >> 25) & ((1 << 6) - 1)) == ((1 << 5) -1)))) { + uint8_t imm8 = ((imm32 >> 31) << 7) | (((imm32 >> 29) & 1) << 6) | + ((imm32 >> 19) & ((1 << 6) -1)); + EmitVFPsss(cond, B23 | B21 | B20 | ((imm8 >> 4)*B16) | (imm8 & 0xf), + sd, S0, S0); + return true; + } + return false; +} + + +bool ArmAssembler::vmovd(DRegister dd, double d_imm, Condition cond) { + uint64_t imm64 = bit_cast<uint64_t, double>(d_imm); + if (((imm64 & ((1LL << 48) - 1)) == 0) && + ((((imm64 >> 54) & ((1 << 9) - 1)) == (1 << 8)) || + (((imm64 >> 54) & ((1 << 9) - 1)) == ((1 << 8) -1)))) { + uint8_t imm8 = ((imm64 >> 63) << 7) | (((imm64 >> 61) & 1) << 6) | + ((imm64 >> 48) & ((1 << 6) -1)); + EmitVFPddd(cond, B23 | B21 | B20 | ((imm8 >> 4)*B16) | B8 | (imm8 & 0xf), + dd, D0, D0); + return true; + } + return false; +} + + +void ArmAssembler::vadds(SRegister sd, SRegister sn, SRegister sm, + Condition cond) { + EmitVFPsss(cond, B21 | B20, sd, sn, sm); +} + + +void ArmAssembler::vaddd(DRegister dd, DRegister dn, DRegister dm, + Condition cond) { + EmitVFPddd(cond, B21 | B20, dd, dn, dm); +} + + +void ArmAssembler::vsubs(SRegister sd, SRegister sn, SRegister sm, + Condition cond) { + EmitVFPsss(cond, B21 | B20 | B6, sd, sn, sm); +} + + +void ArmAssembler::vsubd(DRegister dd, DRegister dn, DRegister dm, + Condition cond) { + EmitVFPddd(cond, B21 | B20 | B6, dd, dn, dm); +} + + +void ArmAssembler::vmuls(SRegister sd, SRegister sn, SRegister sm, + Condition cond) { + EmitVFPsss(cond, B21, sd, sn, sm); +} + + +void ArmAssembler::vmuld(DRegister dd, DRegister dn, DRegister dm, + Condition cond) { + EmitVFPddd(cond, B21, dd, dn, dm); +} + + +void ArmAssembler::vmlas(SRegister sd, SRegister sn, SRegister sm, + Condition cond) { + EmitVFPsss(cond, 0, sd, sn, sm); +} + + +void ArmAssembler::vmlad(DRegister dd, DRegister dn, DRegister dm, + Condition cond) { + EmitVFPddd(cond, 0, dd, dn, dm); +} + + +void ArmAssembler::vmlss(SRegister sd, SRegister sn, SRegister sm, + Condition cond) { + EmitVFPsss(cond, B6, sd, sn, sm); +} + + +void ArmAssembler::vmlsd(DRegister dd, DRegister dn, DRegister dm, + Condition cond) { + EmitVFPddd(cond, B6, dd, dn, dm); +} + + +void ArmAssembler::vdivs(SRegister sd, SRegister sn, SRegister sm, + Condition cond) { + EmitVFPsss(cond, B23, sd, sn, sm); +} + + +void ArmAssembler::vdivd(DRegister dd, DRegister dn, DRegister dm, + Condition cond) { + EmitVFPddd(cond, B23, dd, dn, dm); +} + + +void ArmAssembler::vabss(SRegister sd, SRegister sm, Condition cond) { + EmitVFPsss(cond, B23 | B21 | B20 | B7 | B6, sd, S0, sm); +} + + +void ArmAssembler::vabsd(DRegister dd, DRegister dm, Condition cond) { + EmitVFPddd(cond, B23 | B21 | B20 | B7 | B6, dd, D0, dm); +} + + +void ArmAssembler::vnegs(SRegister sd, SRegister sm, Condition cond) { + EmitVFPsss(cond, B23 | B21 | B20 | B16 | B6, sd, S0, sm); +} + + +void ArmAssembler::vnegd(DRegister dd, DRegister dm, Condition cond) { + EmitVFPddd(cond, B23 | B21 | B20 | B16 | B6, dd, D0, dm); +} + + +void ArmAssembler::vsqrts(SRegister sd, SRegister sm, Condition cond) { + EmitVFPsss(cond, B23 | B21 | B20 | B16 | B7 | B6, sd, S0, sm); +} + +void ArmAssembler::vsqrtd(DRegister dd, DRegister dm, Condition cond) { + EmitVFPddd(cond, B23 | B21 | B20 | B16 | B7 | B6, dd, D0, dm); +} + + +void ArmAssembler::EmitVFPsd(Condition cond, int32_t opcode, + SRegister sd, DRegister dm) { + CHECK_NE(sd, kNoSRegister); + CHECK_NE(dm, kNoDRegister); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B27 | B26 | B25 | B11 | B9 | opcode | + ((static_cast<int32_t>(sd) & 1)*B22) | + ((static_cast<int32_t>(sd) >> 1)*B12) | + ((static_cast<int32_t>(dm) >> 4)*B5) | + (static_cast<int32_t>(dm) & 0xf); + Emit(encoding); +} + + +void ArmAssembler::EmitVFPds(Condition cond, int32_t opcode, + DRegister dd, SRegister sm) { + CHECK_NE(dd, kNoDRegister); + CHECK_NE(sm, kNoSRegister); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B27 | B26 | B25 | B11 | B9 | opcode | + ((static_cast<int32_t>(dd) >> 4)*B22) | + ((static_cast<int32_t>(dd) & 0xf)*B12) | + ((static_cast<int32_t>(sm) & 1)*B5) | + (static_cast<int32_t>(sm) >> 1); + Emit(encoding); +} + + +void ArmAssembler::vcvtsd(SRegister sd, DRegister dm, Condition cond) { + EmitVFPsd(cond, B23 | B21 | B20 | B18 | B17 | B16 | B8 | B7 | B6, sd, dm); +} + + +void ArmAssembler::vcvtds(DRegister dd, SRegister sm, Condition cond) { + EmitVFPds(cond, B23 | B21 | B20 | B18 | B17 | B16 | B7 | B6, dd, sm); +} + + +void ArmAssembler::vcvtis(SRegister sd, SRegister sm, Condition cond) { + EmitVFPsss(cond, B23 | B21 | B20 | B19 | B18 | B16 | B7 | B6, sd, S0, sm); +} + + +void ArmAssembler::vcvtid(SRegister sd, DRegister dm, Condition cond) { + EmitVFPsd(cond, B23 | B21 | B20 | B19 | B18 | B16 | B8 | B7 | B6, sd, dm); +} + + +void ArmAssembler::vcvtsi(SRegister sd, SRegister sm, Condition cond) { + EmitVFPsss(cond, B23 | B21 | B20 | B19 | B7 | B6, sd, S0, sm); +} + + +void ArmAssembler::vcvtdi(DRegister dd, SRegister sm, Condition cond) { + EmitVFPds(cond, B23 | B21 | B20 | B19 | B8 | B7 | B6, dd, sm); +} + + +void ArmAssembler::vcvtus(SRegister sd, SRegister sm, Condition cond) { + EmitVFPsss(cond, B23 | B21 | B20 | B19 | B18 | B7 | B6, sd, S0, sm); +} + + +void ArmAssembler::vcvtud(SRegister sd, DRegister dm, Condition cond) { + EmitVFPsd(cond, B23 | B21 | B20 | B19 | B18 | B8 | B7 | B6, sd, dm); +} + + +void ArmAssembler::vcvtsu(SRegister sd, SRegister sm, Condition cond) { + EmitVFPsss(cond, B23 | B21 | B20 | B19 | B6, sd, S0, sm); +} + + +void ArmAssembler::vcvtdu(DRegister dd, SRegister sm, Condition cond) { + EmitVFPds(cond, B23 | B21 | B20 | B19 | B8 | B6, dd, sm); +} + + +void ArmAssembler::vcmps(SRegister sd, SRegister sm, Condition cond) { + EmitVFPsss(cond, B23 | B21 | B20 | B18 | B6, sd, S0, sm); +} + + +void ArmAssembler::vcmpd(DRegister dd, DRegister dm, Condition cond) { + EmitVFPddd(cond, B23 | B21 | B20 | B18 | B6, dd, D0, dm); +} + + +void ArmAssembler::vcmpsz(SRegister sd, Condition cond) { + EmitVFPsss(cond, B23 | B21 | B20 | B18 | B16 | B6, sd, S0, S0); +} + + +void ArmAssembler::vcmpdz(DRegister dd, Condition cond) { + EmitVFPddd(cond, B23 | B21 | B20 | B18 | B16 | B6, dd, D0, D0); +} + + +void ArmAssembler::vmstat(Condition cond) { // VMRS APSR_nzcv, FPSCR + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B27 | B26 | B25 | B23 | B22 | B21 | B20 | B16 | + (static_cast<int32_t>(PC)*B12) | + B11 | B9 | B4; + Emit(encoding); +} + + +void ArmAssembler::svc(uint32_t imm24) { + CHECK(IsUint(24, imm24)) << imm24; + int32_t encoding = (AL << kConditionShift) | B27 | B26 | B25 | B24 | imm24; + Emit(encoding); +} + + +void ArmAssembler::bkpt(uint16_t imm16) { + int32_t encoding = (AL << kConditionShift) | B24 | B21 | + ((imm16 >> 4) << 8) | B6 | B5 | B4 | (imm16 & 0xf); + Emit(encoding); +} + + +void ArmAssembler::b(Label* label, Condition cond) { + EmitBranch(cond, label, false); +} + + +void ArmAssembler::bl(Label* label, Condition cond) { + EmitBranch(cond, label, true); +} + + +void ArmAssembler::blx(Register rm, Condition cond) { + CHECK_NE(rm, kNoRegister); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B24 | B21 | (0xfff << 8) | B5 | B4 | + (static_cast<int32_t>(rm) << kRmShift); + Emit(encoding); +} + +void ArmAssembler::bx(Register rm, Condition cond) { + CHECK_NE(rm, kNoRegister); + CHECK_NE(cond, kNoCondition); + int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | + B24 | B21 | (0xfff << 8) | B4 | + (static_cast<int32_t>(rm) << kRmShift); + Emit(encoding); +} + +void ArmAssembler::MarkExceptionHandler(Label* label) { + EmitType01(AL, 1, TST, 1, PC, R0, ShifterOperand(0)); + Label l; + b(&l); + EmitBranch(AL, label, false); + Bind(&l); +} + + +void ArmAssembler::Bind(Label* label) { + CHECK(!label->IsBound()); + int bound_pc = buffer_.Size(); + while (label->IsLinked()) { + int32_t position = label->Position(); + int32_t next = buffer_.Load<int32_t>(position); + int32_t encoded = ArmAssembler::EncodeBranchOffset(bound_pc - position, next); + buffer_.Store<int32_t>(position, encoded); + label->position_ = ArmAssembler::DecodeBranchOffset(next); + } + label->BindTo(bound_pc); +} + + +void ArmAssembler::EncodeUint32InTstInstructions(uint32_t data) { + // TODO: Consider using movw ip, <16 bits>. + while (!IsUint(8, data)) { + tst(R0, ShifterOperand(data & 0xFF), VS); + data >>= 8; + } + tst(R0, ShifterOperand(data), MI); +} + + +int32_t ArmAssembler::EncodeBranchOffset(int offset, int32_t inst) { + // The offset is off by 8 due to the way the ARM CPUs read PC. + offset -= 8; + CHECK_ALIGNED(offset, 4); + CHECK(IsInt(CountOneBits(kBranchOffsetMask), offset)) << offset; + + // Properly preserve only the bits supported in the instruction. + offset >>= 2; + offset &= kBranchOffsetMask; + return (inst & ~kBranchOffsetMask) | offset; +} + + +int ArmAssembler::DecodeBranchOffset(int32_t inst) { + // Sign-extend, left-shift by 2, then add 8. + return ((((inst & kBranchOffsetMask) << 8) >> 6) + 8); +} + +void ArmAssembler::AddConstant(Register rd, int32_t value, Condition cond) { + AddConstant(rd, rd, value, cond); +} + + +void ArmAssembler::AddConstant(Register rd, Register rn, int32_t value, + Condition cond) { + if (value == 0) { + if (rd != rn) { + mov(rd, ShifterOperand(rn), cond); + } + return; + } + // We prefer to select the shorter code sequence rather than selecting add for + // positive values and sub for negatives ones, which would slightly improve + // the readability of generated code for some constants. + ShifterOperand shifter_op; + if (ShifterOperand::CanHold(value, &shifter_op)) { + add(rd, rn, shifter_op, cond); + } else if (ShifterOperand::CanHold(-value, &shifter_op)) { + sub(rd, rn, shifter_op, cond); + } else { + CHECK(rn != IP); + if (ShifterOperand::CanHold(~value, &shifter_op)) { + mvn(IP, shifter_op, cond); + add(rd, rn, ShifterOperand(IP), cond); + } else if (ShifterOperand::CanHold(~(-value), &shifter_op)) { + mvn(IP, shifter_op, cond); + sub(rd, rn, ShifterOperand(IP), cond); + } else { + movw(IP, Low16Bits(value), cond); + uint16_t value_high = High16Bits(value); + if (value_high != 0) { + movt(IP, value_high, cond); + } + add(rd, rn, ShifterOperand(IP), cond); + } + } +} + + +void ArmAssembler::AddConstantSetFlags(Register rd, Register rn, int32_t value, + Condition cond) { + ShifterOperand shifter_op; + if (ShifterOperand::CanHold(value, &shifter_op)) { + adds(rd, rn, shifter_op, cond); + } else if (ShifterOperand::CanHold(-value, &shifter_op)) { + subs(rd, rn, shifter_op, cond); + } else { + CHECK(rn != IP); + if (ShifterOperand::CanHold(~value, &shifter_op)) { + mvn(IP, shifter_op, cond); + adds(rd, rn, ShifterOperand(IP), cond); + } else if (ShifterOperand::CanHold(~(-value), &shifter_op)) { + mvn(IP, shifter_op, cond); + subs(rd, rn, ShifterOperand(IP), cond); + } else { + movw(IP, Low16Bits(value), cond); + uint16_t value_high = High16Bits(value); + if (value_high != 0) { + movt(IP, value_high, cond); + } + adds(rd, rn, ShifterOperand(IP), cond); + } + } +} + + +void ArmAssembler::LoadImmediate(Register rd, int32_t value, Condition cond) { + ShifterOperand shifter_op; + if (ShifterOperand::CanHold(value, &shifter_op)) { + mov(rd, shifter_op, cond); + } else if (ShifterOperand::CanHold(~value, &shifter_op)) { + mvn(rd, shifter_op, cond); + } else { + movw(rd, Low16Bits(value), cond); + uint16_t value_high = High16Bits(value); + if (value_high != 0) { + movt(rd, value_high, cond); + } + } +} + + +bool Address::CanHoldLoadOffset(LoadOperandType type, int offset) { + switch (type) { + case kLoadSignedByte: + case kLoadSignedHalfword: + case kLoadUnsignedHalfword: + case kLoadWordPair: + return IsAbsoluteUint(8, offset); // Addressing mode 3. + case kLoadUnsignedByte: + case kLoadWord: + return IsAbsoluteUint(12, offset); // Addressing mode 2. + case kLoadSWord: + case kLoadDWord: + return IsAbsoluteUint(10, offset); // VFP addressing mode. + default: + LOG(FATAL) << "UNREACHABLE"; + return false; + } +} + + +bool Address::CanHoldStoreOffset(StoreOperandType type, int offset) { + switch (type) { + case kStoreHalfword: + case kStoreWordPair: + return IsAbsoluteUint(8, offset); // Addressing mode 3. + case kStoreByte: + case kStoreWord: + return IsAbsoluteUint(12, offset); // Addressing mode 2. + case kStoreSWord: + case kStoreDWord: + return IsAbsoluteUint(10, offset); // VFP addressing mode. + default: + LOG(FATAL) << "UNREACHABLE"; + return false; + } +} + + +// Implementation note: this method must emit at most one instruction when +// Address::CanHoldLoadOffset. +void ArmAssembler::LoadFromOffset(LoadOperandType type, + Register reg, + Register base, + int32_t offset, + Condition cond) { + if (!Address::CanHoldLoadOffset(type, offset)) { + CHECK(base != IP); + LoadImmediate(IP, offset, cond); + add(IP, IP, ShifterOperand(base), cond); + base = IP; + offset = 0; + } + CHECK(Address::CanHoldLoadOffset(type, offset)); + switch (type) { + case kLoadSignedByte: + ldrsb(reg, Address(base, offset), cond); + break; + case kLoadUnsignedByte: + ldrb(reg, Address(base, offset), cond); + break; + case kLoadSignedHalfword: + ldrsh(reg, Address(base, offset), cond); + break; + case kLoadUnsignedHalfword: + ldrh(reg, Address(base, offset), cond); + break; + case kLoadWord: + ldr(reg, Address(base, offset), cond); + break; + case kLoadWordPair: + ldrd(reg, Address(base, offset), cond); + break; + default: + LOG(FATAL) << "UNREACHABLE"; + } +} + +// Implementation note: this method must emit at most one instruction when +// Address::CanHoldLoadOffset, as expected by JIT::GuardedLoadFromOffset. +void ArmAssembler::LoadSFromOffset(SRegister reg, + Register base, + int32_t offset, + Condition cond) { + if (!Address::CanHoldLoadOffset(kLoadSWord, offset)) { + CHECK_NE(base, IP); + LoadImmediate(IP, offset, cond); + add(IP, IP, ShifterOperand(base), cond); + base = IP; + offset = 0; + } + CHECK(Address::CanHoldLoadOffset(kLoadSWord, offset)); + vldrs(reg, Address(base, offset), cond); +} + +// Implementation note: this method must emit at most one instruction when +// Address::CanHoldLoadOffset, as expected by JIT::GuardedLoadFromOffset. +void ArmAssembler::LoadDFromOffset(DRegister reg, + Register base, + int32_t offset, + Condition cond) { + if (!Address::CanHoldLoadOffset(kLoadDWord, offset)) { + CHECK_NE(base, IP); + LoadImmediate(IP, offset, cond); + add(IP, IP, ShifterOperand(base), cond); + base = IP; + offset = 0; + } + CHECK(Address::CanHoldLoadOffset(kLoadDWord, offset)); + vldrd(reg, Address(base, offset), cond); +} + +// Implementation note: this method must emit at most one instruction when +// Address::CanHoldStoreOffset. +void ArmAssembler::StoreToOffset(StoreOperandType type, + Register reg, + Register base, + int32_t offset, + Condition cond) { + if (!Address::CanHoldStoreOffset(type, offset)) { + CHECK(reg != IP); + CHECK(base != IP); + LoadImmediate(IP, offset, cond); + add(IP, IP, ShifterOperand(base), cond); + base = IP; + offset = 0; + } + CHECK(Address::CanHoldStoreOffset(type, offset)); + switch (type) { + case kStoreByte: + strb(reg, Address(base, offset), cond); + break; + case kStoreHalfword: + strh(reg, Address(base, offset), cond); + break; + case kStoreWord: + str(reg, Address(base, offset), cond); + break; + case kStoreWordPair: + strd(reg, Address(base, offset), cond); + break; + default: + LOG(FATAL) << "UNREACHABLE"; + } +} + +// Implementation note: this method must emit at most one instruction when +// Address::CanHoldStoreOffset, as expected by JIT::GuardedStoreToOffset. +void ArmAssembler::StoreSToOffset(SRegister reg, + Register base, + int32_t offset, + Condition cond) { + if (!Address::CanHoldStoreOffset(kStoreSWord, offset)) { + CHECK_NE(base, IP); + LoadImmediate(IP, offset, cond); + add(IP, IP, ShifterOperand(base), cond); + base = IP; + offset = 0; + } + CHECK(Address::CanHoldStoreOffset(kStoreSWord, offset)); + vstrs(reg, Address(base, offset), cond); +} + +// Implementation note: this method must emit at most one instruction when +// Address::CanHoldStoreOffset, as expected by JIT::GuardedStoreSToOffset. +void ArmAssembler::StoreDToOffset(DRegister reg, + Register base, + int32_t offset, + Condition cond) { + if (!Address::CanHoldStoreOffset(kStoreDWord, offset)) { + CHECK_NE(base, IP); + LoadImmediate(IP, offset, cond); + add(IP, IP, ShifterOperand(base), cond); + base = IP; + offset = 0; + } + CHECK(Address::CanHoldStoreOffset(kStoreDWord, offset)); + vstrd(reg, Address(base, offset), cond); +} + +void ArmAssembler::Push(Register rd, Condition cond) { + str(rd, Address(SP, -kRegisterSize, Address::PreIndex), cond); +} + +void ArmAssembler::Pop(Register rd, Condition cond) { + ldr(rd, Address(SP, kRegisterSize, Address::PostIndex), cond); +} + +void ArmAssembler::PushList(RegList regs, Condition cond) { + stm(DB_W, SP, regs, cond); +} + +void ArmAssembler::PopList(RegList regs, Condition cond) { + ldm(IA_W, SP, regs, cond); +} + +void ArmAssembler::Mov(Register rd, Register rm, Condition cond) { + if (rd != rm) { + mov(rd, ShifterOperand(rm), cond); + } +} + +void ArmAssembler::Lsl(Register rd, Register rm, uint32_t shift_imm, + Condition cond) { + CHECK_NE(shift_imm, 0u); // Do not use Lsl if no shift is wanted. + mov(rd, ShifterOperand(rm, LSL, shift_imm), cond); +} + +void ArmAssembler::Lsr(Register rd, Register rm, uint32_t shift_imm, + Condition cond) { + CHECK_NE(shift_imm, 0u); // Do not use Lsr if no shift is wanted. + if (shift_imm == 32) shift_imm = 0; // Comply to UAL syntax. + mov(rd, ShifterOperand(rm, LSR, shift_imm), cond); +} + +void ArmAssembler::Asr(Register rd, Register rm, uint32_t shift_imm, + Condition cond) { + CHECK_NE(shift_imm, 0u); // Do not use Asr if no shift is wanted. + if (shift_imm == 32) shift_imm = 0; // Comply to UAL syntax. + mov(rd, ShifterOperand(rm, ASR, shift_imm), cond); +} + +void ArmAssembler::Ror(Register rd, Register rm, uint32_t shift_imm, + Condition cond) { + CHECK_NE(shift_imm, 0u); // Use Rrx instruction. + mov(rd, ShifterOperand(rm, ROR, shift_imm), cond); +} + +void ArmAssembler::Rrx(Register rd, Register rm, Condition cond) { + mov(rd, ShifterOperand(rm, ROR, 0), cond); +} + +void ArmAssembler::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_EQ(R0, method_reg.AsArm().AsCoreRegister()); + + // Push callee saves and link register. + RegList push_list = 1 << LR; + size_t pushed_values = 1; + for (size_t i = 0; i < callee_save_regs.size(); i++) { + Register reg = callee_save_regs.at(i).AsArm().AsCoreRegister(); + push_list |= 1 << reg; + pushed_values++; + } + PushList(push_list); + + // Increase frame to required size. + CHECK_GT(frame_size, pushed_values * kPointerSize); // Must be at least space to push Method* + size_t adjust = frame_size - (pushed_values * kPointerSize); + IncreaseFrameSize(adjust); + + // Write out Method*. + StoreToOffset(kStoreWord, R0, SP, 0); + + // Write out entry spills. + for (size_t i = 0; i < entry_spills.size(); ++i) { + Register reg = entry_spills.at(i).AsArm().AsCoreRegister(); + StoreToOffset(kStoreWord, reg, SP, frame_size + kPointerSize + (i * kPointerSize)); + } +} + +void ArmAssembler::RemoveFrame(size_t frame_size, + const std::vector<ManagedRegister>& callee_save_regs) { + CHECK_ALIGNED(frame_size, kStackAlignment); + // Compute callee saves to pop and PC + RegList pop_list = 1 << PC; + size_t pop_values = 1; + for (size_t i = 0; i < callee_save_regs.size(); i++) { + Register reg = callee_save_regs.at(i).AsArm().AsCoreRegister(); + pop_list |= 1 << reg; + pop_values++; + } + + // Decrease frame to start of callee saves + CHECK_GT(frame_size, pop_values * kPointerSize); + size_t adjust = frame_size - (pop_values * kPointerSize); + DecreaseFrameSize(adjust); + + // Pop callee saves and PC + PopList(pop_list); +} + +void ArmAssembler::IncreaseFrameSize(size_t adjust) { + AddConstant(SP, -adjust); +} + +void ArmAssembler::DecreaseFrameSize(size_t adjust) { + AddConstant(SP, adjust); +} + +void ArmAssembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) { + ArmManagedRegister src = msrc.AsArm(); + if (src.IsNoRegister()) { + CHECK_EQ(0u, size); + } else if (src.IsCoreRegister()) { + CHECK_EQ(4u, size); + StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value()); + } else if (src.IsRegisterPair()) { + CHECK_EQ(8u, size); + StoreToOffset(kStoreWord, src.AsRegisterPairLow(), SP, dest.Int32Value()); + StoreToOffset(kStoreWord, src.AsRegisterPairHigh(), + SP, dest.Int32Value() + 4); + } else if (src.IsSRegister()) { + StoreSToOffset(src.AsSRegister(), SP, dest.Int32Value()); + } else { + CHECK(src.IsDRegister()) << src; + StoreDToOffset(src.AsDRegister(), SP, dest.Int32Value()); + } +} + +void ArmAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) { + ArmManagedRegister src = msrc.AsArm(); + CHECK(src.IsCoreRegister()) << src; + StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value()); +} + +void ArmAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) { + ArmManagedRegister src = msrc.AsArm(); + CHECK(src.IsCoreRegister()) << src; + StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value()); +} + +void ArmAssembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc, + FrameOffset in_off, ManagedRegister mscratch) { + ArmManagedRegister src = msrc.AsArm(); + ArmManagedRegister scratch = mscratch.AsArm(); + StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value()); + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, in_off.Int32Value()); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + 4); +} + +void ArmAssembler::CopyRef(FrameOffset dest, FrameOffset src, + ManagedRegister mscratch) { + ArmManagedRegister scratch = mscratch.AsArm(); + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value()); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value()); +} + +void ArmAssembler::LoadRef(ManagedRegister mdest, ManagedRegister base, + MemberOffset offs) { + ArmManagedRegister dst = mdest.AsArm(); + CHECK(dst.IsCoreRegister() && dst.IsCoreRegister()) << dst; + LoadFromOffset(kLoadWord, dst.AsCoreRegister(), + base.AsArm().AsCoreRegister(), offs.Int32Value()); +} + +void ArmAssembler::LoadRef(ManagedRegister mdest, FrameOffset src) { + ArmManagedRegister dst = mdest.AsArm(); + CHECK(dst.IsCoreRegister()) << dst; + LoadFromOffset(kLoadWord, dst.AsCoreRegister(), SP, src.Int32Value()); +} + +void ArmAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base, + Offset offs) { + ArmManagedRegister dst = mdest.AsArm(); + CHECK(dst.IsCoreRegister() && dst.IsCoreRegister()) << dst; + LoadFromOffset(kLoadWord, dst.AsCoreRegister(), + base.AsArm().AsCoreRegister(), offs.Int32Value()); +} + +void ArmAssembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm, + ManagedRegister mscratch) { + ArmManagedRegister scratch = mscratch.AsArm(); + CHECK(scratch.IsCoreRegister()) << scratch; + LoadImmediate(scratch.AsCoreRegister(), imm); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value()); +} + +void ArmAssembler::StoreImmediateToThread(ThreadOffset dest, uint32_t imm, + ManagedRegister mscratch) { + ArmManagedRegister scratch = mscratch.AsArm(); + CHECK(scratch.IsCoreRegister()) << scratch; + LoadImmediate(scratch.AsCoreRegister(), imm); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), TR, dest.Int32Value()); +} + +static void EmitLoad(ArmAssembler* assembler, ManagedRegister m_dst, + Register src_register, int32_t src_offset, size_t size) { + ArmManagedRegister dst = m_dst.AsArm(); + if (dst.IsNoRegister()) { + CHECK_EQ(0u, size) << dst; + } else if (dst.IsCoreRegister()) { + CHECK_EQ(4u, size) << dst; + assembler->LoadFromOffset(kLoadWord, dst.AsCoreRegister(), src_register, src_offset); + } else if (dst.IsRegisterPair()) { + CHECK_EQ(8u, size) << dst; + assembler->LoadFromOffset(kLoadWord, dst.AsRegisterPairLow(), src_register, src_offset); + assembler->LoadFromOffset(kLoadWord, dst.AsRegisterPairHigh(), src_register, src_offset + 4); + } else if (dst.IsSRegister()) { + assembler->LoadSFromOffset(dst.AsSRegister(), src_register, src_offset); + } else { + CHECK(dst.IsDRegister()) << dst; + assembler->LoadDFromOffset(dst.AsDRegister(), src_register, src_offset); + } +} + +void ArmAssembler::Load(ManagedRegister m_dst, FrameOffset src, size_t size) { + return EmitLoad(this, m_dst, SP, src.Int32Value(), size); +} + +void ArmAssembler::Load(ManagedRegister m_dst, ThreadOffset src, size_t size) { + return EmitLoad(this, m_dst, TR, src.Int32Value(), size); +} + +void ArmAssembler::LoadRawPtrFromThread(ManagedRegister m_dst, ThreadOffset offs) { + ArmManagedRegister dst = m_dst.AsArm(); + CHECK(dst.IsCoreRegister()) << dst; + LoadFromOffset(kLoadWord, dst.AsCoreRegister(), TR, offs.Int32Value()); +} + +void ArmAssembler::CopyRawPtrFromThread(FrameOffset fr_offs, + ThreadOffset thr_offs, + ManagedRegister mscratch) { + ArmManagedRegister scratch = mscratch.AsArm(); + CHECK(scratch.IsCoreRegister()) << scratch; + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), + TR, thr_offs.Int32Value()); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), + SP, fr_offs.Int32Value()); +} + +void ArmAssembler::CopyRawPtrToThread(ThreadOffset thr_offs, + FrameOffset fr_offs, + ManagedRegister mscratch) { + ArmManagedRegister scratch = mscratch.AsArm(); + CHECK(scratch.IsCoreRegister()) << scratch; + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), + SP, fr_offs.Int32Value()); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), + TR, thr_offs.Int32Value()); +} + +void ArmAssembler::StoreStackOffsetToThread(ThreadOffset thr_offs, + FrameOffset fr_offs, + ManagedRegister mscratch) { + ArmManagedRegister scratch = mscratch.AsArm(); + CHECK(scratch.IsCoreRegister()) << scratch; + AddConstant(scratch.AsCoreRegister(), SP, fr_offs.Int32Value(), AL); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), + TR, thr_offs.Int32Value()); +} + +void ArmAssembler::StoreStackPointerToThread(ThreadOffset thr_offs) { + StoreToOffset(kStoreWord, SP, TR, thr_offs.Int32Value()); +} + +void ArmAssembler::SignExtend(ManagedRegister /*mreg*/, size_t /*size*/) { + UNIMPLEMENTED(FATAL) << "no sign extension necessary for arm"; +} + +void ArmAssembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) { + UNIMPLEMENTED(FATAL) << "no zero extension necessary for arm"; +} + +void ArmAssembler::Move(ManagedRegister m_dst, ManagedRegister m_src, size_t /*size*/) { + ArmManagedRegister dst = m_dst.AsArm(); + ArmManagedRegister src = m_src.AsArm(); + if (!dst.Equals(src)) { + if (dst.IsCoreRegister()) { + CHECK(src.IsCoreRegister()) << src; + mov(dst.AsCoreRegister(), ShifterOperand(src.AsCoreRegister())); + } else if (dst.IsDRegister()) { + CHECK(src.IsDRegister()) << src; + vmovd(dst.AsDRegister(), src.AsDRegister()); + } else if (dst.IsSRegister()) { + CHECK(src.IsSRegister()) << src; + vmovs(dst.AsSRegister(), src.AsSRegister()); + } else { + CHECK(dst.IsRegisterPair()) << dst; + CHECK(src.IsRegisterPair()) << src; + // Ensure that the first move doesn't clobber the input of the second + if (src.AsRegisterPairHigh() != dst.AsRegisterPairLow()) { + mov(dst.AsRegisterPairLow(), ShifterOperand(src.AsRegisterPairLow())); + mov(dst.AsRegisterPairHigh(), ShifterOperand(src.AsRegisterPairHigh())); + } else { + mov(dst.AsRegisterPairHigh(), ShifterOperand(src.AsRegisterPairHigh())); + mov(dst.AsRegisterPairLow(), ShifterOperand(src.AsRegisterPairLow())); + } + } + } +} + +void ArmAssembler::Copy(FrameOffset dest, FrameOffset src, ManagedRegister mscratch, size_t size) { + ArmManagedRegister scratch = mscratch.AsArm(); + CHECK(scratch.IsCoreRegister()) << scratch; + CHECK(size == 4 || size == 8) << size; + if (size == 4) { + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value()); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value()); + } else if (size == 8) { + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value()); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value()); + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value() + 4); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + 4); + } +} + +void ArmAssembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset, + ManagedRegister mscratch, size_t size) { + Register scratch = mscratch.AsArm().AsCoreRegister(); + CHECK_EQ(size, 4u); + LoadFromOffset(kLoadWord, scratch, src_base.AsArm().AsCoreRegister(), src_offset.Int32Value()); + StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value()); +} + +void ArmAssembler::Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src, + ManagedRegister mscratch, size_t size) { + Register scratch = mscratch.AsArm().AsCoreRegister(); + CHECK_EQ(size, 4u); + LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value()); + StoreToOffset(kStoreWord, scratch, dest_base.AsArm().AsCoreRegister(), dest_offset.Int32Value()); +} + +void ArmAssembler::Copy(FrameOffset /*dst*/, FrameOffset /*src_base*/, Offset /*src_offset*/, + ManagedRegister /*mscratch*/, size_t /*size*/) { + UNIMPLEMENTED(FATAL); +} + +void ArmAssembler::Copy(ManagedRegister dest, Offset dest_offset, + ManagedRegister src, Offset src_offset, + ManagedRegister mscratch, size_t size) { + CHECK_EQ(size, 4u); + Register scratch = mscratch.AsArm().AsCoreRegister(); + LoadFromOffset(kLoadWord, scratch, src.AsArm().AsCoreRegister(), src_offset.Int32Value()); + StoreToOffset(kStoreWord, scratch, dest.AsArm().AsCoreRegister(), dest_offset.Int32Value()); +} + +void ArmAssembler::Copy(FrameOffset /*dst*/, Offset /*dest_offset*/, FrameOffset /*src*/, Offset /*src_offset*/, + ManagedRegister /*scratch*/, size_t /*size*/) { + UNIMPLEMENTED(FATAL); +} + + +void ArmAssembler::MemoryBarrier(ManagedRegister mscratch) { + CHECK_EQ(mscratch.AsArm().AsCoreRegister(), R12); +#if ANDROID_SMP != 0 +#if defined(__ARM_HAVE_DMB) + int32_t encoding = 0xf57ff05f; // dmb + Emit(encoding); +#elif defined(__ARM_HAVE_LDREX_STREX) + LoadImmediate(R12, 0); + int32_t encoding = 0xee07cfba; // mcr p15, 0, r12, c7, c10, 5 + Emit(encoding); +#else + LoadImmediate(R12, 0xffff0fa0); // kuser_memory_barrier + blx(R12); +#endif +#endif +} + +void ArmAssembler::CreateSirtEntry(ManagedRegister mout_reg, + FrameOffset sirt_offset, + ManagedRegister min_reg, bool null_allowed) { + ArmManagedRegister out_reg = mout_reg.AsArm(); + ArmManagedRegister in_reg = min_reg.AsArm(); + 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(kLoadWord, out_reg.AsCoreRegister(), + SP, sirt_offset.Int32Value()); + in_reg = out_reg; + } + cmp(in_reg.AsCoreRegister(), ShifterOperand(0)); + if (!out_reg.Equals(in_reg)) { + LoadImmediate(out_reg.AsCoreRegister(), 0, EQ); + } + AddConstant(out_reg.AsCoreRegister(), SP, sirt_offset.Int32Value(), NE); + } else { + AddConstant(out_reg.AsCoreRegister(), SP, sirt_offset.Int32Value(), AL); + } +} + +void ArmAssembler::CreateSirtEntry(FrameOffset out_off, + FrameOffset sirt_offset, + ManagedRegister mscratch, + bool null_allowed) { + ArmManagedRegister scratch = mscratch.AsArm(); + CHECK(scratch.IsCoreRegister()) << scratch; + if (null_allowed) { + LoadFromOffset(kLoadWord, 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(scratch.AsCoreRegister(), ShifterOperand(0)); + AddConstant(scratch.AsCoreRegister(), SP, sirt_offset.Int32Value(), NE); + } else { + AddConstant(scratch.AsCoreRegister(), SP, sirt_offset.Int32Value(), AL); + } + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, out_off.Int32Value()); +} + +void ArmAssembler::LoadReferenceFromSirt(ManagedRegister mout_reg, + ManagedRegister min_reg) { + ArmManagedRegister out_reg = mout_reg.AsArm(); + ArmManagedRegister in_reg = min_reg.AsArm(); + CHECK(out_reg.IsCoreRegister()) << out_reg; + CHECK(in_reg.IsCoreRegister()) << in_reg; + Label null_arg; + if (!out_reg.Equals(in_reg)) { + LoadImmediate(out_reg.AsCoreRegister(), 0, EQ); + } + cmp(in_reg.AsCoreRegister(), ShifterOperand(0)); + LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(), + in_reg.AsCoreRegister(), 0, NE); +} + +void ArmAssembler::VerifyObject(ManagedRegister /*src*/, bool /*could_be_null*/) { + // TODO: not validating references +} + +void ArmAssembler::VerifyObject(FrameOffset /*src*/, bool /*could_be_null*/) { + // TODO: not validating references +} + +void ArmAssembler::Call(ManagedRegister mbase, Offset offset, + ManagedRegister mscratch) { + ArmManagedRegister base = mbase.AsArm(); + ArmManagedRegister scratch = mscratch.AsArm(); + CHECK(base.IsCoreRegister()) << base; + CHECK(scratch.IsCoreRegister()) << scratch; + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), + base.AsCoreRegister(), offset.Int32Value()); + blx(scratch.AsCoreRegister()); + // TODO: place reference map on call +} + +void ArmAssembler::Call(FrameOffset base, Offset offset, + ManagedRegister mscratch) { + ArmManagedRegister scratch = mscratch.AsArm(); + CHECK(scratch.IsCoreRegister()) << scratch; + // Call *(*(SP + base) + offset) + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), + SP, base.Int32Value()); + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), + scratch.AsCoreRegister(), offset.Int32Value()); + blx(scratch.AsCoreRegister()); + // TODO: place reference map on call +} + +void ArmAssembler::Call(ThreadOffset /*offset*/, ManagedRegister /*scratch*/) { + UNIMPLEMENTED(FATAL); +} + +void ArmAssembler::GetCurrentThread(ManagedRegister tr) { + mov(tr.AsArm().AsCoreRegister(), ShifterOperand(TR)); +} + +void ArmAssembler::GetCurrentThread(FrameOffset offset, + ManagedRegister /*scratch*/) { + StoreToOffset(kStoreWord, TR, SP, offset.Int32Value(), AL); +} + +void ArmAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) { + ArmManagedRegister scratch = mscratch.AsArm(); + ArmExceptionSlowPath* slow = new ArmExceptionSlowPath(scratch, stack_adjust); + buffer_.EnqueueSlowPath(slow); + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), + TR, Thread::ExceptionOffset().Int32Value()); + cmp(scratch.AsCoreRegister(), ShifterOperand(0)); + b(slow->Entry(), NE); +} + +void ArmExceptionSlowPath::Emit(Assembler* sasm) { + ArmAssembler* sp_asm = down_cast<ArmAssembler*>(sasm); +#define __ sp_asm-> + __ Bind(&entry_); + if (stack_adjust_ != 0) { // Fix up the frame. + __ DecreaseFrameSize(stack_adjust_); + } + // Pass exception object as argument + // Don't care about preserving R0 as this call won't return + __ mov(R0, ShifterOperand(scratch_.AsCoreRegister())); + // Set up call to Thread::Current()->pDeliverException + __ LoadFromOffset(kLoadWord, R12, TR, ENTRYPOINT_OFFSET(pDeliverException)); + __ blx(R12); + // Call never returns + __ bkpt(0); +#undef __ +} + +} // namespace arm +} // namespace art diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h new file mode 100644 index 0000000..757a8a2 --- /dev/null +++ b/compiler/utils/arm/assembler_arm.h @@ -0,0 +1,659 @@ +/* + * Copyright (C) 2011 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_ARM_ASSEMBLER_ARM_H_ +#define ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_H_ + +#include <vector> + +#include "base/logging.h" +#include "constants_arm.h" +#include "utils/arm/managed_register_arm.h" +#include "utils/assembler.h" +#include "offsets.h" +#include "utils.h" + +namespace art { +namespace arm { + +// Encodes Addressing Mode 1 - Data-processing operands defined in Section 5.1. +class ShifterOperand { + public: + // Data-processing operands - Uninitialized + ShifterOperand() { + type_ = -1; + } + + // Data-processing operands - Immediate + explicit ShifterOperand(uint32_t immediate) { + CHECK(immediate < (1 << kImmed8Bits)); + type_ = 1; + encoding_ = immediate; + } + + // Data-processing operands - Rotated immediate + ShifterOperand(uint32_t rotate, uint32_t immed8) { + CHECK((rotate < (1 << kRotateBits)) && (immed8 < (1 << kImmed8Bits))); + type_ = 1; + encoding_ = (rotate << kRotateShift) | (immed8 << kImmed8Shift); + } + + // Data-processing operands - Register + explicit ShifterOperand(Register rm) { + type_ = 0; + encoding_ = static_cast<uint32_t>(rm); + } + + // Data-processing operands - Logical shift/rotate by immediate + ShifterOperand(Register rm, Shift shift, uint32_t shift_imm) { + CHECK(shift_imm < (1 << kShiftImmBits)); + type_ = 0; + encoding_ = shift_imm << kShiftImmShift | + static_cast<uint32_t>(shift) << kShiftShift | + static_cast<uint32_t>(rm); + } + + // Data-processing operands - Logical shift/rotate by register + ShifterOperand(Register rm, Shift shift, Register rs) { + type_ = 0; + encoding_ = static_cast<uint32_t>(rs) << kShiftRegisterShift | + static_cast<uint32_t>(shift) << kShiftShift | (1 << 4) | + static_cast<uint32_t>(rm); + } + + static bool CanHold(uint32_t immediate, ShifterOperand* shifter_op) { + // Avoid the more expensive test for frequent small immediate values. + if (immediate < (1 << kImmed8Bits)) { + shifter_op->type_ = 1; + shifter_op->encoding_ = (0 << kRotateShift) | (immediate << kImmed8Shift); + return true; + } + // Note that immediate must be unsigned for the test to work correctly. + for (int rot = 0; rot < 16; rot++) { + uint32_t imm8 = (immediate << 2*rot) | (immediate >> (32 - 2*rot)); + if (imm8 < (1 << kImmed8Bits)) { + shifter_op->type_ = 1; + shifter_op->encoding_ = (rot << kRotateShift) | (imm8 << kImmed8Shift); + return true; + } + } + return false; + } + + private: + bool is_valid() const { return (type_ == 0) || (type_ == 1); } + + uint32_t type() const { + CHECK(is_valid()); + return type_; + } + + uint32_t encoding() const { + CHECK(is_valid()); + return encoding_; + } + + uint32_t type_; // Encodes the type field (bits 27-25) in the instruction. + uint32_t encoding_; + + friend class ArmAssembler; +#ifdef SOURCE_ASSEMBLER_SUPPORT + friend class BinaryAssembler; +#endif +}; + + +enum LoadOperandType { + kLoadSignedByte, + kLoadUnsignedByte, + kLoadSignedHalfword, + kLoadUnsignedHalfword, + kLoadWord, + kLoadWordPair, + kLoadSWord, + kLoadDWord +}; + + +enum StoreOperandType { + kStoreByte, + kStoreHalfword, + kStoreWord, + kStoreWordPair, + kStoreSWord, + kStoreDWord +}; + + +// Load/store multiple addressing mode. +enum BlockAddressMode { + // bit encoding P U W + DA = (0|0|0) << 21, // decrement after + IA = (0|4|0) << 21, // increment after + DB = (8|0|0) << 21, // decrement before + IB = (8|4|0) << 21, // increment before + DA_W = (0|0|1) << 21, // decrement after with writeback to base + IA_W = (0|4|1) << 21, // increment after with writeback to base + DB_W = (8|0|1) << 21, // decrement before with writeback to base + IB_W = (8|4|1) << 21 // increment before with writeback to base +}; + + +class Address { + public: + // Memory operand addressing mode + enum Mode { + // bit encoding P U W + Offset = (8|4|0) << 21, // offset (w/o writeback to base) + PreIndex = (8|4|1) << 21, // pre-indexed addressing with writeback + PostIndex = (0|4|0) << 21, // post-indexed addressing with writeback + NegOffset = (8|0|0) << 21, // negative offset (w/o writeback to base) + NegPreIndex = (8|0|1) << 21, // negative pre-indexed with writeback + NegPostIndex = (0|0|0) << 21 // negative post-indexed with writeback + }; + + explicit Address(Register rn, int32_t offset = 0, Mode am = Offset) { + CHECK(IsAbsoluteUint(12, offset)); + if (offset < 0) { + encoding_ = (am ^ (1 << kUShift)) | -offset; // Flip U to adjust sign. + } else { + encoding_ = am | offset; + } + encoding_ |= static_cast<uint32_t>(rn) << kRnShift; + } + + static bool CanHoldLoadOffset(LoadOperandType type, int offset); + static bool CanHoldStoreOffset(StoreOperandType type, int offset); + + private: + uint32_t encoding() const { return encoding_; } + + // Encoding for addressing mode 3. + uint32_t encoding3() const { + const uint32_t offset_mask = (1 << 12) - 1; + uint32_t offset = encoding_ & offset_mask; + CHECK_LT(offset, 256u); + return (encoding_ & ~offset_mask) | ((offset & 0xf0) << 4) | (offset & 0xf); + } + + // Encoding for vfp load/store addressing. + uint32_t vencoding() const { + const uint32_t offset_mask = (1 << 12) - 1; + uint32_t offset = encoding_ & offset_mask; + CHECK(IsAbsoluteUint(10, offset)); // In the range -1020 to +1020. + CHECK_ALIGNED(offset, 2); // Multiple of 4. + int mode = encoding_ & ((8|4|1) << 21); + CHECK((mode == Offset) || (mode == NegOffset)); + uint32_t vencoding = (encoding_ & (0xf << kRnShift)) | (offset >> 2); + if (mode == Offset) { + vencoding |= 1 << 23; + } + return vencoding; + } + + uint32_t encoding_; + + friend class ArmAssembler; +}; + + +class ArmAssembler : public Assembler { + public: + ArmAssembler() {} + virtual ~ArmAssembler() {} + + // Data-processing instructions. + void and_(Register rd, Register rn, ShifterOperand so, Condition cond = AL); + + void eor(Register rd, Register rn, ShifterOperand so, Condition cond = AL); + + void sub(Register rd, Register rn, ShifterOperand so, Condition cond = AL); + void subs(Register rd, Register rn, ShifterOperand so, Condition cond = AL); + + void rsb(Register rd, Register rn, ShifterOperand so, Condition cond = AL); + void rsbs(Register rd, Register rn, ShifterOperand so, Condition cond = AL); + + void add(Register rd, Register rn, ShifterOperand so, Condition cond = AL); + + void adds(Register rd, Register rn, ShifterOperand so, Condition cond = AL); + + void adc(Register rd, Register rn, ShifterOperand so, Condition cond = AL); + + void sbc(Register rd, Register rn, ShifterOperand so, Condition cond = AL); + + void rsc(Register rd, Register rn, ShifterOperand so, Condition cond = AL); + + void tst(Register rn, ShifterOperand so, Condition cond = AL); + + void teq(Register rn, ShifterOperand so, Condition cond = AL); + + void cmp(Register rn, ShifterOperand so, Condition cond = AL); + + void cmn(Register rn, ShifterOperand so, Condition cond = AL); + + void orr(Register rd, Register rn, ShifterOperand so, Condition cond = AL); + void orrs(Register rd, Register rn, ShifterOperand so, Condition cond = AL); + + void mov(Register rd, ShifterOperand so, Condition cond = AL); + void movs(Register rd, ShifterOperand so, Condition cond = AL); + + void bic(Register rd, Register rn, ShifterOperand so, Condition cond = AL); + + void mvn(Register rd, ShifterOperand so, Condition cond = AL); + void mvns(Register rd, ShifterOperand so, Condition cond = AL); + + // Miscellaneous data-processing instructions. + void clz(Register rd, Register rm, Condition cond = AL); + void movw(Register rd, uint16_t imm16, Condition cond = AL); + void movt(Register rd, uint16_t imm16, Condition cond = AL); + + // Multiply instructions. + void mul(Register rd, Register rn, Register rm, Condition cond = AL); + void mla(Register rd, Register rn, Register rm, Register ra, + Condition cond = AL); + void mls(Register rd, Register rn, Register rm, Register ra, + Condition cond = AL); + void umull(Register rd_lo, Register rd_hi, Register rn, Register rm, + Condition cond = AL); + + // Load/store instructions. + void ldr(Register rd, Address ad, Condition cond = AL); + void str(Register rd, Address ad, Condition cond = AL); + + void ldrb(Register rd, Address ad, Condition cond = AL); + void strb(Register rd, Address ad, Condition cond = AL); + + void ldrh(Register rd, Address ad, Condition cond = AL); + void strh(Register rd, Address ad, Condition cond = AL); + + void ldrsb(Register rd, Address ad, Condition cond = AL); + void ldrsh(Register rd, Address ad, Condition cond = AL); + + void ldrd(Register rd, Address ad, Condition cond = AL); + void strd(Register rd, Address ad, Condition cond = AL); + + void ldm(BlockAddressMode am, Register base, + RegList regs, Condition cond = AL); + void stm(BlockAddressMode am, Register base, + RegList regs, Condition cond = AL); + + void ldrex(Register rd, Register rn, Condition cond = AL); + void strex(Register rd, Register rt, Register rn, Condition cond = AL); + + // Miscellaneous instructions. + void clrex(); + void nop(Condition cond = AL); + + // Note that gdb sets breakpoints using the undefined instruction 0xe7f001f0. + void bkpt(uint16_t imm16); + void svc(uint32_t imm24); + + // Floating point instructions (VFPv3-D16 and VFPv3-D32 profiles). + void vmovsr(SRegister sn, Register rt, Condition cond = AL); + void vmovrs(Register rt, SRegister sn, Condition cond = AL); + void vmovsrr(SRegister sm, Register rt, Register rt2, Condition cond = AL); + void vmovrrs(Register rt, Register rt2, SRegister sm, Condition cond = AL); + void vmovdrr(DRegister dm, Register rt, Register rt2, Condition cond = AL); + void vmovrrd(Register rt, Register rt2, DRegister dm, Condition cond = AL); + void vmovs(SRegister sd, SRegister sm, Condition cond = AL); + void vmovd(DRegister dd, DRegister dm, Condition cond = AL); + + // Returns false if the immediate cannot be encoded. + bool vmovs(SRegister sd, float s_imm, Condition cond = AL); + bool vmovd(DRegister dd, double d_imm, Condition cond = AL); + + void vldrs(SRegister sd, Address ad, Condition cond = AL); + void vstrs(SRegister sd, Address ad, Condition cond = AL); + void vldrd(DRegister dd, Address ad, Condition cond = AL); + void vstrd(DRegister dd, Address ad, Condition cond = AL); + + void vadds(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL); + void vaddd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL); + void vsubs(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL); + void vsubd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL); + void vmuls(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL); + void vmuld(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL); + void vmlas(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL); + void vmlad(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL); + void vmlss(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL); + void vmlsd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL); + void vdivs(SRegister sd, SRegister sn, SRegister sm, Condition cond = AL); + void vdivd(DRegister dd, DRegister dn, DRegister dm, Condition cond = AL); + + void vabss(SRegister sd, SRegister sm, Condition cond = AL); + void vabsd(DRegister dd, DRegister dm, Condition cond = AL); + void vnegs(SRegister sd, SRegister sm, Condition cond = AL); + void vnegd(DRegister dd, DRegister dm, Condition cond = AL); + void vsqrts(SRegister sd, SRegister sm, Condition cond = AL); + void vsqrtd(DRegister dd, DRegister dm, Condition cond = AL); + + void vcvtsd(SRegister sd, DRegister dm, Condition cond = AL); + void vcvtds(DRegister dd, SRegister sm, Condition cond = AL); + void vcvtis(SRegister sd, SRegister sm, Condition cond = AL); + void vcvtid(SRegister sd, DRegister dm, Condition cond = AL); + void vcvtsi(SRegister sd, SRegister sm, Condition cond = AL); + void vcvtdi(DRegister dd, SRegister sm, Condition cond = AL); + void vcvtus(SRegister sd, SRegister sm, Condition cond = AL); + void vcvtud(SRegister sd, DRegister dm, Condition cond = AL); + void vcvtsu(SRegister sd, SRegister sm, Condition cond = AL); + void vcvtdu(DRegister dd, SRegister sm, Condition cond = AL); + + void vcmps(SRegister sd, SRegister sm, Condition cond = AL); + void vcmpd(DRegister dd, DRegister dm, Condition cond = AL); + void vcmpsz(SRegister sd, Condition cond = AL); + void vcmpdz(DRegister dd, Condition cond = AL); + void vmstat(Condition cond = AL); // VMRS APSR_nzcv, FPSCR + + // Branch instructions. + void b(Label* label, Condition cond = AL); + void bl(Label* label, Condition cond = AL); + void blx(Register rm, Condition cond = AL); + void bx(Register rm, Condition cond = AL); + + // Macros. + // Add signed constant value to rd. May clobber IP. + void AddConstant(Register rd, int32_t value, Condition cond = AL); + void AddConstant(Register rd, Register rn, int32_t value, + Condition cond = AL); + void AddConstantSetFlags(Register rd, Register rn, int32_t value, + Condition cond = AL); + void AddConstantWithCarry(Register rd, Register rn, int32_t value, + Condition cond = AL); + + // Load and Store. May clobber IP. + void LoadImmediate(Register rd, int32_t value, Condition cond = AL); + void LoadSImmediate(SRegister sd, float value, Condition cond = AL); + void LoadDImmediate(DRegister dd, double value, + Register scratch, Condition cond = AL); + void MarkExceptionHandler(Label* label); + void LoadFromOffset(LoadOperandType type, + Register reg, + Register base, + int32_t offset, + Condition cond = AL); + void StoreToOffset(StoreOperandType type, + Register reg, + Register base, + int32_t offset, + Condition cond = AL); + void LoadSFromOffset(SRegister reg, + Register base, + int32_t offset, + Condition cond = AL); + void StoreSToOffset(SRegister reg, + Register base, + int32_t offset, + Condition cond = AL); + void LoadDFromOffset(DRegister reg, + Register base, + int32_t offset, + Condition cond = AL); + void StoreDToOffset(DRegister reg, + Register base, + int32_t offset, + Condition cond = AL); + + void Push(Register rd, Condition cond = AL); + void Pop(Register rd, Condition cond = AL); + + void PushList(RegList regs, Condition cond = AL); + void PopList(RegList regs, Condition cond = AL); + + void Mov(Register rd, Register rm, Condition cond = AL); + + // Convenience shift instructions. Use mov instruction with shifter operand + // for variants setting the status flags or using a register shift count. + void Lsl(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL); + void Lsr(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL); + void Asr(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL); + void Ror(Register rd, Register rm, uint32_t shift_imm, Condition cond = AL); + void Rrx(Register rd, Register rm, Condition cond = AL); + + // Encode a signed constant in tst instructions, only affecting the flags. + void EncodeUint32InTstInstructions(uint32_t data); + // ... and decode from a pc pointing to the start of encoding instructions. + static uint32_t DecodeUint32FromTstInstructions(uword pc); + static bool IsInstructionForExceptionHandling(uword pc); + + // Emit data (e.g. encoded instruction or immediate) to the + // instruction stream. + void Emit(int32_t value); + void Bind(Label* label); + + // + // Overridden common assembler high-level functionality + // + + // Emit code that will create an activation on the stack + virtual 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 + virtual void RemoveFrame(size_t frame_size, + const std::vector<ManagedRegister>& callee_save_regs); + + virtual void IncreaseFrameSize(size_t adjust); + virtual void DecreaseFrameSize(size_t adjust); + + // Store routines + virtual void Store(FrameOffset offs, ManagedRegister src, size_t size); + virtual void StoreRef(FrameOffset dest, ManagedRegister src); + virtual void StoreRawPtr(FrameOffset dest, ManagedRegister src); + + virtual void StoreImmediateToFrame(FrameOffset dest, uint32_t imm, + ManagedRegister scratch); + + virtual void StoreImmediateToThread(ThreadOffset dest, uint32_t imm, + ManagedRegister scratch); + + virtual void StoreStackOffsetToThread(ThreadOffset thr_offs, + FrameOffset fr_offs, + ManagedRegister scratch); + + virtual void StoreStackPointerToThread(ThreadOffset thr_offs); + + virtual void StoreSpanning(FrameOffset dest, ManagedRegister src, + FrameOffset in_off, ManagedRegister scratch); + + // Load routines + virtual void Load(ManagedRegister dest, FrameOffset src, size_t size); + + virtual void Load(ManagedRegister dest, ThreadOffset src, size_t size); + + virtual void LoadRef(ManagedRegister dest, FrameOffset src); + + virtual void LoadRef(ManagedRegister dest, ManagedRegister base, + MemberOffset offs); + + virtual void LoadRawPtr(ManagedRegister dest, ManagedRegister base, + Offset offs); + + virtual void LoadRawPtrFromThread(ManagedRegister dest, + ThreadOffset offs); + + // Copying routines + virtual void Move(ManagedRegister dest, ManagedRegister src, size_t size); + + virtual void CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset thr_offs, + ManagedRegister scratch); + + virtual void CopyRawPtrToThread(ThreadOffset thr_offs, FrameOffset fr_offs, + ManagedRegister scratch); + + virtual void CopyRef(FrameOffset dest, FrameOffset src, + ManagedRegister scratch); + + virtual void Copy(FrameOffset dest, FrameOffset src, ManagedRegister scratch, size_t size); + + virtual void Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset, + ManagedRegister scratch, size_t size); + + virtual void Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src, + ManagedRegister scratch, size_t size); + + virtual void Copy(FrameOffset dest, FrameOffset src_base, Offset src_offset, + ManagedRegister scratch, size_t size); + + virtual void Copy(ManagedRegister dest, Offset dest_offset, + ManagedRegister src, Offset src_offset, + ManagedRegister scratch, size_t size); + + virtual void Copy(FrameOffset dest, Offset dest_offset, FrameOffset src, Offset src_offset, + ManagedRegister scratch, size_t size); + + virtual void MemoryBarrier(ManagedRegister scratch); + + // Sign extension + virtual void SignExtend(ManagedRegister mreg, size_t size); + + // Zero extension + virtual void ZeroExtend(ManagedRegister mreg, size_t size); + + // Exploit fast access in managed code to Thread::Current() + virtual void GetCurrentThread(ManagedRegister tr); + virtual 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. + virtual 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. + virtual void CreateSirtEntry(FrameOffset out_off, FrameOffset sirt_offset, + ManagedRegister scratch, bool null_allowed); + + // src holds a SIRT entry (Object**) load this into dst + virtual 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. + virtual void VerifyObject(ManagedRegister src, bool could_be_null); + virtual void VerifyObject(FrameOffset src, bool could_be_null); + + // Call to address held at [base+offset] + virtual void Call(ManagedRegister base, Offset offset, + ManagedRegister scratch); + virtual void Call(FrameOffset base, Offset offset, + ManagedRegister scratch); + virtual 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. + virtual void ExceptionPoll(ManagedRegister scratch, size_t stack_adjust); + + private: + void EmitType01(Condition cond, + int type, + Opcode opcode, + int set_cc, + Register rn, + Register rd, + ShifterOperand so); + + void EmitType5(Condition cond, int offset, bool link); + + void EmitMemOp(Condition cond, + bool load, + bool byte, + Register rd, + Address ad); + + void EmitMemOpAddressMode3(Condition cond, + int32_t mode, + Register rd, + Address ad); + + void EmitMultiMemOp(Condition cond, + BlockAddressMode am, + bool load, + Register base, + RegList regs); + + void EmitShiftImmediate(Condition cond, + Shift opcode, + Register rd, + Register rm, + ShifterOperand so); + + void EmitShiftRegister(Condition cond, + Shift opcode, + Register rd, + Register rm, + ShifterOperand so); + + void EmitMulOp(Condition cond, + int32_t opcode, + Register rd, + Register rn, + Register rm, + Register rs); + + void EmitVFPsss(Condition cond, + int32_t opcode, + SRegister sd, + SRegister sn, + SRegister sm); + + void EmitVFPddd(Condition cond, + int32_t opcode, + DRegister dd, + DRegister dn, + DRegister dm); + + void EmitVFPsd(Condition cond, + int32_t opcode, + SRegister sd, + DRegister dm); + + void EmitVFPds(Condition cond, + int32_t opcode, + DRegister dd, + SRegister sm); + + void EmitBranch(Condition cond, Label* label, bool link); + static int32_t EncodeBranchOffset(int offset, int32_t inst); + static int DecodeBranchOffset(int32_t inst); + int32_t EncodeTstOffset(int offset, int32_t inst); + int DecodeTstOffset(int32_t inst); + + // Returns whether or not the given register is used for passing parameters. + static int RegisterCompare(const Register* reg1, const Register* reg2) { + return *reg1 - *reg2; + } +}; + +// Slowpath entered when Thread::Current()->_exception is non-null +class ArmExceptionSlowPath : public SlowPath { + public: + explicit ArmExceptionSlowPath(ArmManagedRegister scratch, size_t stack_adjust) + : scratch_(scratch), stack_adjust_(stack_adjust) { + } + virtual void Emit(Assembler *sp_asm); + private: + const ArmManagedRegister scratch_; + const size_t stack_adjust_; +}; + +} // namespace arm +} // namespace art + +#endif // ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_H_ diff --git a/compiler/utils/arm/constants_arm.h b/compiler/utils/arm/constants_arm.h new file mode 100644 index 0000000..cc795b1 --- /dev/null +++ b/compiler/utils/arm/constants_arm.h @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2009 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_ARM_CONSTANTS_ARM_H_ +#define ART_COMPILER_UTILS_ARM_CONSTANTS_ARM_H_ + +#include <stdint.h> + +#include <iosfwd> + +#include "arch/arm/registers_arm.h" +#include "base/casts.h" +#include "base/logging.h" +#include "globals.h" + +namespace art { +namespace arm { + +// Defines constants and accessor classes to assemble, disassemble and +// simulate ARM instructions. +// +// Section references in the code refer to the "ARM Architecture Reference +// Manual" from July 2005 (available at http://www.arm.com/miscPDFs/14128.pdf) +// +// Constants for specific fields are defined in their respective named enums. +// General constants are in an anonymous enum in class Instr. + + +// We support both VFPv3-D16 and VFPv3-D32 profiles, but currently only one at +// a time, so that compile time optimizations can be applied. +// Warning: VFPv3-D32 is untested. +#define VFPv3_D16 +#if defined(VFPv3_D16) == defined(VFPv3_D32) +#error "Exactly one of VFPv3_D16 or VFPv3_D32 can be defined at a time." +#endif + + +enum ScaleFactor { + TIMES_1 = 0, + TIMES_2 = 1, + TIMES_4 = 2, + TIMES_8 = 3 +}; + +// Values for double-precision floating point registers. +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, +#ifdef VFPv3_D16 + kNumberOfDRegisters = 16, +#else + 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, +#endif + kNumberOfOverlappingDRegisters = 16, + kNoDRegister = -1, +}; +std::ostream& operator<<(std::ostream& os, const DRegister& rhs); + + +// Values for the condition field as defined in section A3.2. +enum Condition { + kNoCondition = -1, + EQ = 0, // equal + NE = 1, // not equal + CS = 2, // carry set/unsigned higher or same + CC = 3, // carry clear/unsigned lower + MI = 4, // minus/negative + PL = 5, // plus/positive or zero + VS = 6, // overflow + VC = 7, // no overflow + HI = 8, // unsigned higher + LS = 9, // unsigned lower or same + GE = 10, // signed greater than or equal + LT = 11, // signed less than + GT = 12, // signed greater than + LE = 13, // signed less than or equal + AL = 14, // always (unconditional) + kSpecialCondition = 15, // special condition (refer to section A3.2.1) + kMaxCondition = 16, +}; +std::ostream& operator<<(std::ostream& os, const Condition& rhs); + + +// Opcodes for Data-processing instructions (instructions with a type 0 and 1) +// as defined in section A3.4 +enum Opcode { + kNoOperand = -1, + AND = 0, // Logical AND + EOR = 1, // Logical Exclusive OR + SUB = 2, // Subtract + RSB = 3, // Reverse Subtract + ADD = 4, // Add + ADC = 5, // Add with Carry + SBC = 6, // Subtract with Carry + RSC = 7, // Reverse Subtract with Carry + TST = 8, // Test + TEQ = 9, // Test Equivalence + CMP = 10, // Compare + CMN = 11, // Compare Negated + ORR = 12, // Logical (inclusive) OR + MOV = 13, // Move + BIC = 14, // Bit Clear + MVN = 15, // Move Not + kMaxOperand = 16 +}; + + +// Shifter types for Data-processing operands as defined in section A5.1.2. +enum Shift { + kNoShift = -1, + LSL = 0, // Logical shift left + LSR = 1, // Logical shift right + ASR = 2, // Arithmetic shift right + ROR = 3, // Rotate right + kMaxShift = 4 +}; + + +// Constants used for the decoding or encoding of the individual fields of +// instructions. Based on the "Figure 3-1 ARM instruction set summary". +enum InstructionFields { + kConditionShift = 28, + kConditionBits = 4, + kTypeShift = 25, + kTypeBits = 3, + kLinkShift = 24, + kLinkBits = 1, + kUShift = 23, + kUBits = 1, + kOpcodeShift = 21, + kOpcodeBits = 4, + kSShift = 20, + kSBits = 1, + kRnShift = 16, + kRnBits = 4, + kRdShift = 12, + kRdBits = 4, + kRsShift = 8, + kRsBits = 4, + kRmShift = 0, + kRmBits = 4, + + // Immediate instruction fields encoding. + kRotateShift = 8, + kRotateBits = 4, + kImmed8Shift = 0, + kImmed8Bits = 8, + + // Shift instruction register fields encodings. + kShiftImmShift = 7, + kShiftRegisterShift = 8, + kShiftImmBits = 5, + kShiftShift = 5, + kShiftBits = 2, + + // Load/store instruction offset field encoding. + kOffset12Shift = 0, + kOffset12Bits = 12, + kOffset12Mask = 0x00000fff, + + // Mul instruction register fields encodings. + kMulRdShift = 16, + kMulRdBits = 4, + kMulRnShift = 12, + kMulRnBits = 4, + + kBranchOffsetMask = 0x00ffffff +}; + + +// Size (in bytes) of registers. +const int kRegisterSize = 4; + +// List of registers used in load/store multiple. +typedef uint16_t RegList; + +// The class Instr enables access to individual fields defined in the ARM +// architecture instruction set encoding as described in figure A3-1. +// +// Example: Test whether the instruction at ptr does set the condition code +// bits. +// +// bool InstructionSetsConditionCodes(byte* ptr) { +// Instr* instr = Instr::At(ptr); +// int type = instr->TypeField(); +// return ((type == 0) || (type == 1)) && instr->HasS(); +// } +// +class Instr { + public: + enum { + kInstrSize = 4, + kInstrSizeLog2 = 2, + kPCReadOffset = 8 + }; + + bool IsBreakPoint() { + return IsBkpt(); + } + + // Get the raw instruction bits. + inline int32_t InstructionBits() const { + return *reinterpret_cast<const int32_t*>(this); + } + + // Set the raw instruction bits to value. + inline void SetInstructionBits(int32_t value) { + *reinterpret_cast<int32_t*>(this) = value; + } + + // Read one particular bit out of the instruction bits. + inline int Bit(int nr) const { + return (InstructionBits() >> nr) & 1; + } + + // Read a bit field out of the instruction bits. + inline int Bits(int shift, int count) const { + return (InstructionBits() >> shift) & ((1 << count) - 1); + } + + + // Accessors for the different named fields used in the ARM encoding. + // The naming of these accessor corresponds to figure A3-1. + // Generally applicable fields + inline Condition ConditionField() const { + return static_cast<Condition>(Bits(kConditionShift, kConditionBits)); + } + inline int TypeField() const { return Bits(kTypeShift, kTypeBits); } + + inline Register RnField() const { return static_cast<Register>( + Bits(kRnShift, kRnBits)); } + inline Register RdField() const { return static_cast<Register>( + Bits(kRdShift, kRdBits)); } + + // Fields used in Data processing instructions + inline Opcode OpcodeField() const { + return static_cast<Opcode>(Bits(kOpcodeShift, kOpcodeBits)); + } + inline int SField() const { return Bits(kSShift, kSBits); } + // with register + inline Register RmField() const { + return static_cast<Register>(Bits(kRmShift, kRmBits)); + } + inline Shift ShiftField() const { return static_cast<Shift>( + Bits(kShiftShift, kShiftBits)); } + inline int RegShiftField() const { return Bit(4); } + inline Register RsField() const { + return static_cast<Register>(Bits(kRsShift, kRsBits)); + } + inline int ShiftAmountField() const { return Bits(kShiftImmShift, + kShiftImmBits); } + // with immediate + inline int RotateField() const { return Bits(kRotateShift, kRotateBits); } + inline int Immed8Field() const { return Bits(kImmed8Shift, kImmed8Bits); } + + // Fields used in Load/Store instructions + inline int PUField() const { return Bits(23, 2); } + inline int BField() const { return Bit(22); } + inline int WField() const { return Bit(21); } + inline int LField() const { return Bit(20); } + // with register uses same fields as Data processing instructions above + // with immediate + inline int Offset12Field() const { return Bits(kOffset12Shift, + kOffset12Bits); } + // multiple + inline int RlistField() const { return Bits(0, 16); } + // extra loads and stores + inline int SignField() const { return Bit(6); } + inline int HField() const { return Bit(5); } + inline int ImmedHField() const { return Bits(8, 4); } + inline int ImmedLField() const { return Bits(0, 4); } + + // Fields used in Branch instructions + inline int LinkField() const { return Bits(kLinkShift, kLinkBits); } + inline int SImmed24Field() const { return ((InstructionBits() << 8) >> 8); } + + // Fields used in Supervisor Call instructions + inline uint32_t SvcField() const { return Bits(0, 24); } + + // Field used in Breakpoint instruction + inline uint16_t BkptField() const { + return ((Bits(8, 12) << 4) | Bits(0, 4)); + } + + // Field used in 16-bit immediate move instructions + inline uint16_t MovwField() const { + return ((Bits(16, 4) << 12) | Bits(0, 12)); + } + + // Field used in VFP float immediate move instruction + inline float ImmFloatField() const { + uint32_t imm32 = (Bit(19) << 31) | (((1 << 5) - Bit(18)) << 25) | + (Bits(16, 2) << 23) | (Bits(0, 4) << 19); + return bit_cast<float, uint32_t>(imm32); + } + + // Field used in VFP double immediate move instruction + inline double ImmDoubleField() const { + uint64_t imm64 = (Bit(19)*(1LL << 63)) | (((1LL << 8) - Bit(18)) << 54) | + (Bits(16, 2)*(1LL << 52)) | (Bits(0, 4)*(1LL << 48)); + return bit_cast<double, uint64_t>(imm64); + } + + // Test for data processing instructions of type 0 or 1. + // See "ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition", + // section A5.1 "ARM instruction set encoding". + inline bool IsDataProcessing() const { + CHECK_NE(ConditionField(), kSpecialCondition); + CHECK_EQ(Bits(26, 2), 0); // Type 0 or 1. + return ((Bits(20, 5) & 0x19) != 0x10) && + ((Bit(25) == 1) || // Data processing immediate. + (Bit(4) == 0) || // Data processing register. + (Bit(7) == 0)); // Data processing register-shifted register. + } + + // Tests for special encodings of type 0 instructions (extra loads and stores, + // as well as multiplications, synchronization primitives, and miscellaneous). + // Can only be called for a type 0 or 1 instruction. + inline bool IsMiscellaneous() const { + CHECK_EQ(Bits(26, 2), 0); // Type 0 or 1. + return ((Bit(25) == 0) && ((Bits(20, 5) & 0x19) == 0x10) && (Bit(7) == 0)); + } + inline bool IsMultiplyOrSyncPrimitive() const { + CHECK_EQ(Bits(26, 2), 0); // Type 0 or 1. + return ((Bit(25) == 0) && (Bits(4, 4) == 9)); + } + + // Test for Supervisor Call instruction. + inline bool IsSvc() const { + return ((InstructionBits() & 0xff000000) == 0xef000000); + } + + // Test for Breakpoint instruction. + inline bool IsBkpt() const { + return ((InstructionBits() & 0xfff000f0) == 0xe1200070); + } + + // VFP register fields. + inline SRegister SnField() const { + return static_cast<SRegister>((Bits(kRnShift, kRnBits) << 1) + Bit(7)); + } + inline SRegister SdField() const { + return static_cast<SRegister>((Bits(kRdShift, kRdBits) << 1) + Bit(22)); + } + inline SRegister SmField() const { + return static_cast<SRegister>((Bits(kRmShift, kRmBits) << 1) + Bit(5)); + } + inline DRegister DnField() const { + return static_cast<DRegister>(Bits(kRnShift, kRnBits) + (Bit(7) << 4)); + } + inline DRegister DdField() const { + return static_cast<DRegister>(Bits(kRdShift, kRdBits) + (Bit(22) << 4)); + } + inline DRegister DmField() const { + return static_cast<DRegister>(Bits(kRmShift, kRmBits) + (Bit(5) << 4)); + } + + // Test for VFP data processing or single transfer instructions of type 7. + inline bool IsVFPDataProcessingOrSingleTransfer() const { + CHECK_NE(ConditionField(), kSpecialCondition); + CHECK_EQ(TypeField(), 7); + return ((Bit(24) == 0) && (Bits(9, 3) == 5)); + // Bit(4) == 0: Data Processing + // Bit(4) == 1: 8, 16, or 32-bit Transfer between ARM Core and VFP + } + + // Test for VFP 64-bit transfer instructions of type 6. + inline bool IsVFPDoubleTransfer() const { + CHECK_NE(ConditionField(), kSpecialCondition); + CHECK_EQ(TypeField(), 6); + return ((Bits(21, 4) == 2) && (Bits(9, 3) == 5) && + ((Bits(4, 4) & 0xd) == 1)); + } + + // Test for VFP load and store instructions of type 6. + inline bool IsVFPLoadStore() const { + CHECK_NE(ConditionField(), kSpecialCondition); + CHECK_EQ(TypeField(), 6); + return ((Bits(20, 5) & 0x12) == 0x10) && (Bits(9, 3) == 5); + } + + // Special accessors that test for existence of a value. + inline bool HasS() const { return SField() == 1; } + inline bool HasB() const { return BField() == 1; } + inline bool HasW() const { return WField() == 1; } + inline bool HasL() const { return LField() == 1; } + inline bool HasSign() const { return SignField() == 1; } + inline bool HasH() const { return HField() == 1; } + inline bool HasLink() const { return LinkField() == 1; } + + // Instructions are read out of a code stream. The only way to get a + // reference to an instruction is to convert a pointer. There is no way + // to allocate or create instances of class Instr. + // Use the At(pc) function to create references to Instr. + static Instr* At(uword pc) { return reinterpret_cast<Instr*>(pc); } + Instr* Next() { return this + kInstrSize; } + + private: + // We need to prevent the creation of instances of class Instr. + DISALLOW_IMPLICIT_CONSTRUCTORS(Instr); +}; + +} // namespace arm +} // namespace art + +#endif // ART_COMPILER_UTILS_ARM_CONSTANTS_ARM_H_ diff --git a/compiler/utils/arm/managed_register_arm.cc b/compiler/utils/arm/managed_register_arm.cc new file mode 100644 index 0000000..57c2305 --- /dev/null +++ b/compiler/utils/arm/managed_register_arm.cc @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2011 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_arm.h" + +#include "globals.h" + +namespace art { +namespace arm { + +// We need all registers for caching of locals. +// Register R9 .. R15 are reserved. +static const int kNumberOfAvailableCoreRegisters = (R8 - R0) + 1; +static const int kNumberOfAvailableSRegisters = kNumberOfSRegisters; +static const int kNumberOfAvailableDRegisters = kNumberOfDRegisters; +static const int kNumberOfAvailableOverlappingDRegisters = + kNumberOfOverlappingDRegisters; +static const int kNumberOfAvailableRegisterPairs = kNumberOfRegisterPairs; + + +// Returns true if this managed-register overlaps the other managed-register. +bool ArmManagedRegister::Overlaps(const ArmManagedRegister& other) const { + if (IsNoRegister() || other.IsNoRegister()) return false; + if (Equals(other)) return true; + if (IsRegisterPair()) { + Register low = AsRegisterPairLow(); + Register high = AsRegisterPairHigh(); + return ArmManagedRegister::FromCoreRegister(low).Overlaps(other) || + ArmManagedRegister::FromCoreRegister(high).Overlaps(other); + } + if (IsOverlappingDRegister()) { + if (other.IsDRegister()) return Equals(other); + if (other.IsSRegister()) { + SRegister low = AsOverlappingDRegisterLow(); + SRegister high = AsOverlappingDRegisterHigh(); + SRegister other_sreg = other.AsSRegister(); + return (low == other_sreg) || (high == other_sreg); + } + return false; + } + if (other.IsRegisterPair() || other.IsOverlappingDRegister()) { + return other.Overlaps(*this); + } + return false; +} + + +int ArmManagedRegister::AllocIdLow() const { + CHECK(IsOverlappingDRegister() || IsRegisterPair()); + const int r = RegId() - (kNumberOfCoreRegIds + kNumberOfSRegIds); + int low; + if (r < kNumberOfOverlappingDRegIds) { + CHECK(IsOverlappingDRegister()); + low = (r * 2) + kNumberOfCoreRegIds; // Return a SRegister. + } else { + CHECK(IsRegisterPair()); + low = (r - kNumberOfDRegIds) * 2; // Return a Register. + if (low > 6) { + // we didn't got a pair higher than R6_R7, must be the dalvik special case + low = 1; + } + } + return low; +} + + +int ArmManagedRegister::AllocIdHigh() const { + return AllocIdLow() + 1; +} + + +void ArmManagedRegister::Print(std::ostream& os) const { + if (!IsValidManagedRegister()) { + os << "No Register"; + } else if (IsCoreRegister()) { + os << "Core: " << static_cast<int>(AsCoreRegister()); + } else if (IsRegisterPair()) { + os << "Pair: " << static_cast<int>(AsRegisterPairLow()) << ", " + << static_cast<int>(AsRegisterPairHigh()); + } else if (IsSRegister()) { + os << "SRegister: " << static_cast<int>(AsSRegister()); + } else if (IsDRegister()) { + os << "DRegister: " << static_cast<int>(AsDRegister()); + } else { + os << "??: " << RegId(); + } +} + +std::ostream& operator<<(std::ostream& os, const ArmManagedRegister& reg) { + reg.Print(os); + return os; +} + +std::ostream& operator<<(std::ostream& os, const RegisterPair& r) { + os << ArmManagedRegister::FromRegisterPair(r); + return os; +} + +} // namespace arm +} // namespace art diff --git a/compiler/utils/arm/managed_register_arm.h b/compiler/utils/arm/managed_register_arm.h new file mode 100644 index 0000000..a496c87 --- /dev/null +++ b/compiler/utils/arm/managed_register_arm.h @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2011 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_ARM_MANAGED_REGISTER_ARM_H_ +#define ART_COMPILER_UTILS_ARM_MANAGED_REGISTER_ARM_H_ + +#include "base/logging.h" +#include "constants_arm.h" +#include "utils/managed_register.h" + +namespace art { +namespace arm { + +// Values for register pairs. +enum RegisterPair { + R0_R1 = 0, + R2_R3 = 1, + R4_R5 = 2, + R6_R7 = 3, + R1_R2 = 4, // Dalvik style passing + kNumberOfRegisterPairs = 5, + kNoRegisterPair = -1, +}; + +std::ostream& operator<<(std::ostream& os, const RegisterPair& reg); + +const int kNumberOfCoreRegIds = kNumberOfCoreRegisters; +const int kNumberOfCoreAllocIds = kNumberOfCoreRegisters; + +const int kNumberOfSRegIds = kNumberOfSRegisters; +const int kNumberOfSAllocIds = kNumberOfSRegisters; + +const int kNumberOfDRegIds = kNumberOfDRegisters; +const int kNumberOfOverlappingDRegIds = kNumberOfOverlappingDRegisters; +const int kNumberOfDAllocIds = kNumberOfDRegIds - kNumberOfOverlappingDRegIds; + +const int kNumberOfPairRegIds = kNumberOfRegisterPairs; + +const int kNumberOfRegIds = kNumberOfCoreRegIds + kNumberOfSRegIds + + kNumberOfDRegIds + kNumberOfPairRegIds; +const int kNumberOfAllocIds = + kNumberOfCoreAllocIds + kNumberOfSAllocIds + kNumberOfDAllocIds; + +// Register ids map: +// [0..R[ core registers (enum Register) +// [R..S[ single precision VFP registers (enum SRegister) +// [S..D[ double precision VFP registers (enum DRegister) +// [D..P[ core register pairs (enum RegisterPair) +// where +// R = kNumberOfCoreRegIds +// S = R + kNumberOfSRegIds +// D = S + kNumberOfDRegIds +// P = D + kNumberOfRegisterPairs + +// Allocation ids map: +// [0..R[ core registers (enum Register) +// [R..S[ single precision VFP registers (enum SRegister) +// [S..N[ non-overlapping double precision VFP registers (16-31 in enum +// DRegister, VFPv3-D32 only) +// where +// R = kNumberOfCoreAllocIds +// S = R + kNumberOfSAllocIds +// N = S + kNumberOfDAllocIds + + +// An instance of class 'ManagedRegister' represents a single ARM register or a +// pair of core ARM registers (enum RegisterPair). A single register is either a +// core register (enum Register), a VFP single precision register +// (enum SRegister), or a VFP double precision register (enum DRegister). +// 'ManagedRegister::NoRegister()' returns an invalid ManagedRegister. +// There is a one-to-one mapping between ManagedRegister and register id. +class ArmManagedRegister : public ManagedRegister { + public: + Register AsCoreRegister() const { + CHECK(IsCoreRegister()); + return static_cast<Register>(id_); + } + + SRegister AsSRegister() const { + CHECK(IsSRegister()); + return static_cast<SRegister>(id_ - kNumberOfCoreRegIds); + } + + DRegister AsDRegister() const { + CHECK(IsDRegister()); + return static_cast<DRegister>(id_ - kNumberOfCoreRegIds - kNumberOfSRegIds); + } + + SRegister AsOverlappingDRegisterLow() const { + CHECK(IsOverlappingDRegister()); + DRegister d_reg = AsDRegister(); + return static_cast<SRegister>(d_reg * 2); + } + + SRegister AsOverlappingDRegisterHigh() const { + CHECK(IsOverlappingDRegister()); + DRegister d_reg = AsDRegister(); + return static_cast<SRegister>(d_reg * 2 + 1); + } + + RegisterPair AsRegisterPair() const { + CHECK(IsRegisterPair()); + Register reg_low = AsRegisterPairLow(); + if (reg_low == R1) { + return R1_R2; + } else { + return static_cast<RegisterPair>(reg_low / 2); + } + } + + Register AsRegisterPairLow() const { + CHECK(IsRegisterPair()); + // Appropriate mapping of register ids allows to use AllocIdLow(). + return FromRegId(AllocIdLow()).AsCoreRegister(); + } + + Register AsRegisterPairHigh() const { + CHECK(IsRegisterPair()); + // Appropriate mapping of register ids allows to use AllocIdHigh(). + return FromRegId(AllocIdHigh()).AsCoreRegister(); + } + + bool IsCoreRegister() const { + CHECK(IsValidManagedRegister()); + return (0 <= id_) && (id_ < kNumberOfCoreRegIds); + } + + bool IsSRegister() const { + CHECK(IsValidManagedRegister()); + const int test = id_ - kNumberOfCoreRegIds; + return (0 <= test) && (test < kNumberOfSRegIds); + } + + bool IsDRegister() const { + CHECK(IsValidManagedRegister()); + const int test = id_ - (kNumberOfCoreRegIds + kNumberOfSRegIds); + return (0 <= test) && (test < kNumberOfDRegIds); + } + + // Returns true if this DRegister overlaps SRegisters. + bool IsOverlappingDRegister() const { + CHECK(IsValidManagedRegister()); + const int test = id_ - (kNumberOfCoreRegIds + kNumberOfSRegIds); + return (0 <= test) && (test < kNumberOfOverlappingDRegIds); + } + + bool IsRegisterPair() const { + CHECK(IsValidManagedRegister()); + const int test = + id_ - (kNumberOfCoreRegIds + kNumberOfSRegIds + kNumberOfDRegIds); + return (0 <= test) && (test < kNumberOfPairRegIds); + } + + bool IsSameType(ArmManagedRegister test) const { + CHECK(IsValidManagedRegister() && test.IsValidManagedRegister()); + return + (IsCoreRegister() && test.IsCoreRegister()) || + (IsSRegister() && test.IsSRegister()) || + (IsDRegister() && test.IsDRegister()) || + (IsRegisterPair() && test.IsRegisterPair()); + } + + + // 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 ArmManagedRegister& other) const; + + void Print(std::ostream& os) const; + + static ArmManagedRegister FromCoreRegister(Register r) { + CHECK_NE(r, kNoRegister); + return FromRegId(r); + } + + static ArmManagedRegister FromSRegister(SRegister r) { + CHECK_NE(r, kNoSRegister); + return FromRegId(r + kNumberOfCoreRegIds); + } + + static ArmManagedRegister FromDRegister(DRegister r) { + CHECK_NE(r, kNoDRegister); + return FromRegId(r + (kNumberOfCoreRegIds + kNumberOfSRegIds)); + } + + static ArmManagedRegister FromRegisterPair(RegisterPair r) { + CHECK_NE(r, kNoRegisterPair); + return FromRegId(r + (kNumberOfCoreRegIds + + kNumberOfSRegIds + kNumberOfDRegIds)); + } + + // Return a RegisterPair consisting of Register r_low and r_low + 1. + static ArmManagedRegister FromCoreRegisterPair(Register r_low) { + if (r_low != R1) { // not the dalvik special case + CHECK_NE(r_low, kNoRegister); + CHECK_EQ(0, (r_low % 2)); + const int r = r_low / 2; + CHECK_LT(r, kNumberOfPairRegIds); + return FromRegisterPair(static_cast<RegisterPair>(r)); + } else { + return FromRegisterPair(R1_R2); + } + } + + // Return a DRegister overlapping SRegister r_low and r_low + 1. + static ArmManagedRegister FromSRegisterPair(SRegister r_low) { + CHECK_NE(r_low, kNoSRegister); + CHECK_EQ(0, (r_low % 2)); + const int r = r_low / 2; + CHECK_LT(r, kNumberOfOverlappingDRegIds); + return FromDRegister(static_cast<DRegister>(r)); + } + + private: + bool IsValidManagedRegister() const { + return (0 <= id_) && (id_ < kNumberOfRegIds); + } + + int RegId() const { + CHECK(!IsNoRegister()); + return id_; + } + + int AllocId() const { + CHECK(IsValidManagedRegister() && + !IsOverlappingDRegister() && !IsRegisterPair()); + int r = id_; + if ((kNumberOfDAllocIds > 0) && IsDRegister()) { // VFPv3-D32 only. + r -= kNumberOfOverlappingDRegIds; + } + CHECK_LT(r, kNumberOfAllocIds); + return r; + } + + int AllocIdLow() const; + int AllocIdHigh() const; + + friend class ManagedRegister; + + explicit ArmManagedRegister(int reg_id) : ManagedRegister(reg_id) {} + + static ArmManagedRegister FromRegId(int reg_id) { + ArmManagedRegister reg(reg_id); + CHECK(reg.IsValidManagedRegister()); + return reg; + } +}; + +std::ostream& operator<<(std::ostream& os, const ArmManagedRegister& reg); + +} // namespace arm + +inline arm::ArmManagedRegister ManagedRegister::AsArm() const { + arm::ArmManagedRegister reg(id_); + CHECK(reg.IsNoRegister() || reg.IsValidManagedRegister()); + return reg; +} + +} // namespace art + +#endif // ART_COMPILER_UTILS_ARM_MANAGED_REGISTER_ARM_H_ diff --git a/compiler/utils/arm/managed_register_arm_test.cc b/compiler/utils/arm/managed_register_arm_test.cc new file mode 100644 index 0000000..f5d4cc0 --- /dev/null +++ b/compiler/utils/arm/managed_register_arm_test.cc @@ -0,0 +1,767 @@ +/* + * Copyright (C) 2011 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_arm.h" +#include "gtest/gtest.h" + +namespace art { +namespace arm { + +TEST(ArmManagedRegister, NoRegister) { + ArmManagedRegister reg = ManagedRegister::NoRegister().AsArm(); + EXPECT_TRUE(reg.IsNoRegister()); + EXPECT_TRUE(!reg.Overlaps(reg)); +} + +TEST(ArmManagedRegister, CoreRegister) { + ArmManagedRegister reg = ArmManagedRegister::FromCoreRegister(R0); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(R0, reg.AsCoreRegister()); + + reg = ArmManagedRegister::FromCoreRegister(R1); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsOverlappingDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(R1, reg.AsCoreRegister()); + + reg = ArmManagedRegister::FromCoreRegister(R8); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsOverlappingDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(R8, reg.AsCoreRegister()); + + reg = ArmManagedRegister::FromCoreRegister(R15); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsOverlappingDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(R15, reg.AsCoreRegister()); +} + + +TEST(ArmManagedRegister, SRegister) { + ArmManagedRegister reg = ArmManagedRegister::FromSRegister(S0); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsOverlappingDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(S0, reg.AsSRegister()); + + reg = ArmManagedRegister::FromSRegister(S1); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsOverlappingDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(S1, reg.AsSRegister()); + + reg = ArmManagedRegister::FromSRegister(S3); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsOverlappingDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(S3, reg.AsSRegister()); + + reg = ArmManagedRegister::FromSRegister(S15); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsOverlappingDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(S15, reg.AsSRegister()); + + reg = ArmManagedRegister::FromSRegister(S30); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsOverlappingDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(S30, reg.AsSRegister()); + + reg = ArmManagedRegister::FromSRegister(S31); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsOverlappingDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(S31, reg.AsSRegister()); +} + + +TEST(ArmManagedRegister, DRegister) { + ArmManagedRegister reg = ArmManagedRegister::FromDRegister(D0); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.IsDRegister()); + EXPECT_TRUE(reg.IsOverlappingDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(D0, reg.AsDRegister()); + EXPECT_EQ(S0, reg.AsOverlappingDRegisterLow()); + EXPECT_EQ(S1, reg.AsOverlappingDRegisterHigh()); + EXPECT_TRUE(reg.Equals(ArmManagedRegister::FromSRegisterPair(S0))); + + reg = ArmManagedRegister::FromDRegister(D1); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.IsDRegister()); + EXPECT_TRUE(reg.IsOverlappingDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(D1, reg.AsDRegister()); + EXPECT_EQ(S2, reg.AsOverlappingDRegisterLow()); + EXPECT_EQ(S3, reg.AsOverlappingDRegisterHigh()); + EXPECT_TRUE(reg.Equals(ArmManagedRegister::FromSRegisterPair(S2))); + + reg = ArmManagedRegister::FromDRegister(D6); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.IsDRegister()); + EXPECT_TRUE(reg.IsOverlappingDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(D6, reg.AsDRegister()); + EXPECT_EQ(S12, reg.AsOverlappingDRegisterLow()); + EXPECT_EQ(S13, reg.AsOverlappingDRegisterHigh()); + EXPECT_TRUE(reg.Equals(ArmManagedRegister::FromSRegisterPair(S12))); + + reg = ArmManagedRegister::FromDRegister(D14); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.IsDRegister()); + EXPECT_TRUE(reg.IsOverlappingDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(D14, reg.AsDRegister()); + EXPECT_EQ(S28, reg.AsOverlappingDRegisterLow()); + EXPECT_EQ(S29, reg.AsOverlappingDRegisterHigh()); + EXPECT_TRUE(reg.Equals(ArmManagedRegister::FromSRegisterPair(S28))); + + reg = ArmManagedRegister::FromDRegister(D15); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.IsDRegister()); + EXPECT_TRUE(reg.IsOverlappingDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(D15, reg.AsDRegister()); + EXPECT_EQ(S30, reg.AsOverlappingDRegisterLow()); + EXPECT_EQ(S31, reg.AsOverlappingDRegisterHigh()); + EXPECT_TRUE(reg.Equals(ArmManagedRegister::FromSRegisterPair(S30))); + +#ifdef VFPv3_D32 + reg = ArmManagedRegister::FromDRegister(D16); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.IsDRegister()); + EXPECT_TRUE(!reg.IsOverlappingDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(D16, reg.AsDRegister()); + + reg = ArmManagedRegister::FromDRegister(D18); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.IsDRegister()); + EXPECT_TRUE(!reg.IsOverlappingDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(D18, reg.AsDRegister()); + + reg = ArmManagedRegister::FromDRegister(D30); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.IsDRegister()); + EXPECT_TRUE(!reg.IsOverlappingDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(D30, reg.AsDRegister()); + + reg = ArmManagedRegister::FromDRegister(D31); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(reg.IsDRegister()); + EXPECT_TRUE(!reg.IsOverlappingDRegister()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(D31, reg.AsDRegister()); +#endif // VFPv3_D32 +} + + +TEST(ArmManagedRegister, Pair) { + ArmManagedRegister reg = ArmManagedRegister::FromRegisterPair(R0_R1); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsOverlappingDRegister()); + EXPECT_TRUE(reg.IsRegisterPair()); + EXPECT_EQ(R0_R1, reg.AsRegisterPair()); + EXPECT_EQ(R0, reg.AsRegisterPairLow()); + EXPECT_EQ(R1, reg.AsRegisterPairHigh()); + EXPECT_TRUE(reg.Equals(ArmManagedRegister::FromCoreRegisterPair(R0))); + + reg = ArmManagedRegister::FromRegisterPair(R1_R2); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsOverlappingDRegister()); + EXPECT_TRUE(reg.IsRegisterPair()); + EXPECT_EQ(R1_R2, reg.AsRegisterPair()); + EXPECT_EQ(R1, reg.AsRegisterPairLow()); + EXPECT_EQ(R2, reg.AsRegisterPairHigh()); + EXPECT_TRUE(reg.Equals(ArmManagedRegister::FromCoreRegisterPair(R1))); + + reg = ArmManagedRegister::FromRegisterPair(R2_R3); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsOverlappingDRegister()); + EXPECT_TRUE(reg.IsRegisterPair()); + EXPECT_EQ(R2_R3, reg.AsRegisterPair()); + EXPECT_EQ(R2, reg.AsRegisterPairLow()); + EXPECT_EQ(R3, reg.AsRegisterPairHigh()); + EXPECT_TRUE(reg.Equals(ArmManagedRegister::FromCoreRegisterPair(R2))); + + reg = ArmManagedRegister::FromRegisterPair(R4_R5); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsOverlappingDRegister()); + EXPECT_TRUE(reg.IsRegisterPair()); + EXPECT_EQ(R4_R5, reg.AsRegisterPair()); + EXPECT_EQ(R4, reg.AsRegisterPairLow()); + EXPECT_EQ(R5, reg.AsRegisterPairHigh()); + EXPECT_TRUE(reg.Equals(ArmManagedRegister::FromCoreRegisterPair(R4))); + + reg = ArmManagedRegister::FromRegisterPair(R6_R7); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCoreRegister()); + EXPECT_TRUE(!reg.IsSRegister()); + EXPECT_TRUE(!reg.IsDRegister()); + EXPECT_TRUE(!reg.IsOverlappingDRegister()); + EXPECT_TRUE(reg.IsRegisterPair()); + EXPECT_EQ(R6_R7, reg.AsRegisterPair()); + EXPECT_EQ(R6, reg.AsRegisterPairLow()); + EXPECT_EQ(R7, reg.AsRegisterPairHigh()); + EXPECT_TRUE(reg.Equals(ArmManagedRegister::FromCoreRegisterPair(R6))); +} + + +TEST(ArmManagedRegister, Equals) { + ManagedRegister no_reg = ManagedRegister::NoRegister(); + EXPECT_TRUE(no_reg.Equals(ArmManagedRegister::NoRegister())); + EXPECT_TRUE(!no_reg.Equals(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!no_reg.Equals(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!no_reg.Equals(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!no_reg.Equals(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!no_reg.Equals(ArmManagedRegister::FromRegisterPair(R0_R1))); + + ArmManagedRegister reg_R0 = ArmManagedRegister::FromCoreRegister(R0); + EXPECT_TRUE(!reg_R0.Equals(ArmManagedRegister::NoRegister())); + EXPECT_TRUE(reg_R0.Equals(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg_R0.Equals(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg_R0.Equals(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_R0.Equals(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_R0.Equals(ArmManagedRegister::FromRegisterPair(R0_R1))); + + ArmManagedRegister reg_R1 = ArmManagedRegister::FromCoreRegister(R1); + EXPECT_TRUE(!reg_R1.Equals(ArmManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_R1.Equals(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(reg_R1.Equals(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg_R1.Equals(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_R1.Equals(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_R1.Equals(ArmManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg_R1.Equals(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg_R1.Equals(ArmManagedRegister::FromRegisterPair(R0_R1))); + + ArmManagedRegister reg_R8 = ArmManagedRegister::FromCoreRegister(R8); + EXPECT_TRUE(!reg_R8.Equals(ArmManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_R8.Equals(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(reg_R8.Equals(ArmManagedRegister::FromCoreRegister(R8))); + EXPECT_TRUE(!reg_R8.Equals(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_R8.Equals(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_R8.Equals(ArmManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg_R8.Equals(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg_R8.Equals(ArmManagedRegister::FromRegisterPair(R0_R1))); + + ArmManagedRegister reg_S0 = ArmManagedRegister::FromSRegister(S0); + EXPECT_TRUE(!reg_S0.Equals(ArmManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_S0.Equals(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg_S0.Equals(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(reg_S0.Equals(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_S0.Equals(ArmManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg_S0.Equals(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_S0.Equals(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg_S0.Equals(ArmManagedRegister::FromRegisterPair(R0_R1))); + + ArmManagedRegister reg_S1 = ArmManagedRegister::FromSRegister(S1); + EXPECT_TRUE(!reg_S1.Equals(ArmManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_S1.Equals(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg_S1.Equals(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg_S1.Equals(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(reg_S1.Equals(ArmManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg_S1.Equals(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_S1.Equals(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg_S1.Equals(ArmManagedRegister::FromRegisterPair(R0_R1))); + + ArmManagedRegister reg_S31 = ArmManagedRegister::FromSRegister(S31); + EXPECT_TRUE(!reg_S31.Equals(ArmManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_S31.Equals(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg_S31.Equals(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg_S31.Equals(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(reg_S31.Equals(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg_S31.Equals(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_S31.Equals(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg_S31.Equals(ArmManagedRegister::FromRegisterPair(R0_R1))); + + ArmManagedRegister reg_D0 = ArmManagedRegister::FromDRegister(D0); + EXPECT_TRUE(!reg_D0.Equals(ArmManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_D0.Equals(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg_D0.Equals(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg_D0.Equals(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_D0.Equals(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(reg_D0.Equals(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_D0.Equals(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg_D0.Equals(ArmManagedRegister::FromRegisterPair(R0_R1))); + + ArmManagedRegister reg_D15 = ArmManagedRegister::FromDRegister(D15); + EXPECT_TRUE(!reg_D15.Equals(ArmManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_D15.Equals(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg_D15.Equals(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg_D15.Equals(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_D15.Equals(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg_D15.Equals(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_D15.Equals(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(reg_D15.Equals(ArmManagedRegister::FromDRegister(D15))); + EXPECT_TRUE(!reg_D15.Equals(ArmManagedRegister::FromRegisterPair(R0_R1))); + +#ifdef VFPv3_D32 + ArmManagedRegister reg_D16 = ArmManagedRegister::FromDRegister(D16); + EXPECT_TRUE(!reg_D16.Equals(ArmManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_D16.Equals(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg_D16.Equals(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg_D16.Equals(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_D16.Equals(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg_D16.Equals(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_D16.Equals(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg_D16.Equals(ArmManagedRegister::FromDRegister(D15))); + EXPECT_TRUE(reg_D16.Equals(ArmManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(!reg_D16.Equals(ArmManagedRegister::FromRegisterPair(R0_R1))); + + ArmManagedRegister reg_D30 = ArmManagedRegister::FromDRegister(D30); + EXPECT_TRUE(!reg_D30.Equals(ArmManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_D30.Equals(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg_D30.Equals(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg_D30.Equals(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_D30.Equals(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg_D30.Equals(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_D30.Equals(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg_D30.Equals(ArmManagedRegister::FromDRegister(D15))); + EXPECT_TRUE(!reg_D30.Equals(ArmManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(reg_D30.Equals(ArmManagedRegister::FromDRegister(D30))); + EXPECT_TRUE(!reg_D30.Equals(ArmManagedRegister::FromRegisterPair(R0_R1))); + + ArmManagedRegister reg_D31 = ArmManagedRegister::FromDRegister(D30); + EXPECT_TRUE(!reg_D31.Equals(ArmManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_D31.Equals(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg_D31.Equals(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg_D31.Equals(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_D31.Equals(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg_D31.Equals(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_D31.Equals(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg_D31.Equals(ArmManagedRegister::FromDRegister(D15))); + EXPECT_TRUE(!reg_D31.Equals(ArmManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(!reg_D31.Equals(ArmManagedRegister::FromDRegister(D30))); + EXPECT_TRUE(reg_D31.Equals(ArmManagedRegister::FromDRegister(D31))); + EXPECT_TRUE(!reg_D31.Equals(ArmManagedRegister::FromRegisterPair(R0_R1))); +#endif // VFPv3_D32 + + ArmManagedRegister reg_R0R1 = ArmManagedRegister::FromRegisterPair(R0_R1); + EXPECT_TRUE(!reg_R0R1.Equals(ArmManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_R0R1.Equals(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg_R0R1.Equals(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg_R0R1.Equals(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_R0R1.Equals(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg_R0R1.Equals(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_R0R1.Equals(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg_R0R1.Equals(ArmManagedRegister::FromDRegister(D15))); + EXPECT_TRUE(reg_R0R1.Equals(ArmManagedRegister::FromRegisterPair(R0_R1))); + EXPECT_TRUE(!reg_R0R1.Equals(ArmManagedRegister::FromRegisterPair(R2_R3))); + + ArmManagedRegister reg_R4R5 = ArmManagedRegister::FromRegisterPair(R4_R5); + EXPECT_TRUE(!reg_R4R5.Equals(ArmManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_R4R5.Equals(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg_R4R5.Equals(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg_R4R5.Equals(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_R4R5.Equals(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg_R4R5.Equals(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_R4R5.Equals(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg_R4R5.Equals(ArmManagedRegister::FromDRegister(D15))); + EXPECT_TRUE(!reg_R4R5.Equals(ArmManagedRegister::FromRegisterPair(R0_R1))); + EXPECT_TRUE(reg_R4R5.Equals(ArmManagedRegister::FromRegisterPair(R4_R5))); + EXPECT_TRUE(!reg_R4R5.Equals(ArmManagedRegister::FromRegisterPair(R6_R7))); + + ArmManagedRegister reg_R6R7 = ArmManagedRegister::FromRegisterPair(R6_R7); + EXPECT_TRUE(!reg_R6R7.Equals(ArmManagedRegister::NoRegister())); + EXPECT_TRUE(!reg_R6R7.Equals(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg_R6R7.Equals(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg_R6R7.Equals(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg_R6R7.Equals(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg_R6R7.Equals(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg_R6R7.Equals(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg_R6R7.Equals(ArmManagedRegister::FromDRegister(D15))); + EXPECT_TRUE(!reg_R6R7.Equals(ArmManagedRegister::FromRegisterPair(R0_R1))); + EXPECT_TRUE(!reg_R6R7.Equals(ArmManagedRegister::FromRegisterPair(R4_R5))); + EXPECT_TRUE(reg_R6R7.Equals(ArmManagedRegister::FromRegisterPair(R6_R7))); +} + + +TEST(ArmManagedRegister, Overlaps) { + ArmManagedRegister reg = ArmManagedRegister::FromCoreRegister(R0); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R8))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D15))); +#ifdef VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D31))); +#endif // VFPv3_D32 + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromRegisterPair(R0_R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R4_R5))); + + reg = ArmManagedRegister::FromCoreRegister(R1); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R8))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D15))); +#ifdef VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D31))); +#endif // VFPv3_D32 + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromRegisterPair(R0_R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R4_R5))); + + reg = ArmManagedRegister::FromCoreRegister(R7); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromCoreRegister(R7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R8))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D15))); +#ifdef VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D31))); +#endif // VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R0_R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R4_R5))); + + reg = ArmManagedRegister::FromSRegister(S0); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R8))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D15))); +#ifdef VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D31))); +#endif // VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R0_R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R4_R5))); + + reg = ArmManagedRegister::FromSRegister(S1); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R8))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D15))); +#ifdef VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D31))); +#endif // VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R0_R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R4_R5))); + + reg = ArmManagedRegister::FromSRegister(S15); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R8))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D15))); +#ifdef VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D31))); +#endif // VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R0_R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R4_R5))); + + reg = ArmManagedRegister::FromSRegister(S31); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R8))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromDRegister(D15))); +#ifdef VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D31))); +#endif // VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R0_R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R4_R5))); + + reg = ArmManagedRegister::FromDRegister(D0); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R8))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D15))); +#ifdef VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D31))); +#endif // VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R0_R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R4_R5))); + + reg = ArmManagedRegister::FromDRegister(D7); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R8))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D15))); +#ifdef VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D31))); +#endif // VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R0_R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R4_R5))); + + reg = ArmManagedRegister::FromDRegister(D15); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R8))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromDRegister(D15))); +#ifdef VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D31))); +#endif // VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R0_R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R4_R5))); + +#ifdef VFPv3_D32 + reg = ArmManagedRegister::FromDRegister(D16); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R8))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D15))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D31))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R0_R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R4_R5))); + + reg = ArmManagedRegister::FromDRegister(D31); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R8))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D15))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromDRegister(D31))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R0_R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R4_R5))); +#endif // VFPv3_D32 + + reg = ArmManagedRegister::FromRegisterPair(R0_R1); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R8))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D15))); +#ifdef VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D31))); +#endif // VFPv3_D32 + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromRegisterPair(R0_R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R4_R5))); + + reg = ArmManagedRegister::FromRegisterPair(R4_R5); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromCoreRegister(R8))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S2))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S15))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S30))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromSRegister(S31))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D0))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D1))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D7))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D15))); +#ifdef VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D16))); + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromDRegister(D31))); +#endif // VFPv3_D32 + EXPECT_TRUE(!reg.Overlaps(ArmManagedRegister::FromRegisterPair(R0_R1))); + EXPECT_TRUE(reg.Overlaps(ArmManagedRegister::FromRegisterPair(R4_R5))); +} + +} // namespace arm +} // namespace art diff --git a/compiler/utils/assembler.cc b/compiler/utils/assembler.cc new file mode 100644 index 0000000..92ce0b8 --- /dev/null +++ b/compiler/utils/assembler.cc @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2011 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.h" + +#include <algorithm> +#include <vector> + +#include "arm/assembler_arm.h" +#include "mips/assembler_mips.h" +#include "x86/assembler_x86.h" +#include "globals.h" +#include "memory_region.h" + +namespace art { + +static byte* NewContents(size_t capacity) { + return new byte[capacity]; +} + + +AssemblerBuffer::AssemblerBuffer() { + static const size_t kInitialBufferCapacity = 4 * KB; + contents_ = NewContents(kInitialBufferCapacity); + cursor_ = contents_; + limit_ = ComputeLimit(contents_, kInitialBufferCapacity); + fixup_ = NULL; + slow_path_ = NULL; +#ifndef NDEBUG + has_ensured_capacity_ = false; + fixups_processed_ = false; +#endif + + // Verify internal state. + CHECK_EQ(Capacity(), kInitialBufferCapacity); + CHECK_EQ(Size(), 0U); +} + + +AssemblerBuffer::~AssemblerBuffer() { + delete[] contents_; +} + + +void AssemblerBuffer::ProcessFixups(const MemoryRegion& region) { + AssemblerFixup* fixup = fixup_; + while (fixup != NULL) { + fixup->Process(region, fixup->position()); + fixup = fixup->previous(); + } +} + + +void AssemblerBuffer::FinalizeInstructions(const MemoryRegion& instructions) { + // Copy the instructions from the buffer. + MemoryRegion from(reinterpret_cast<void*>(contents()), Size()); + instructions.CopyFrom(0, from); + // Process fixups in the instructions. + ProcessFixups(instructions); +#ifndef NDEBUG + fixups_processed_ = true; +#endif +} + + +void AssemblerBuffer::ExtendCapacity() { + size_t old_size = Size(); + size_t old_capacity = Capacity(); + size_t new_capacity = std::min(old_capacity * 2, old_capacity + 1 * MB); + + // Allocate the new data area and copy contents of the old one to it. + byte* new_contents = NewContents(new_capacity); + memmove(reinterpret_cast<void*>(new_contents), + reinterpret_cast<void*>(contents_), + old_size); + + // Compute the relocation delta and switch to the new contents area. + ptrdiff_t delta = new_contents - contents_; + contents_ = new_contents; + + // Update the cursor and recompute the limit. + cursor_ += delta; + limit_ = ComputeLimit(new_contents, new_capacity); + + // Verify internal state. + CHECK_EQ(Capacity(), new_capacity); + CHECK_EQ(Size(), old_size); +} + + +Assembler* Assembler::Create(InstructionSet instruction_set) { + switch (instruction_set) { + case kArm: + case kThumb2: + return new arm::ArmAssembler(); + case kMips: + return new mips::MipsAssembler(); + case kX86: + return new x86::X86Assembler(); + default: + LOG(FATAL) << "Unknown InstructionSet: " << instruction_set; + return NULL; + } +} + +} // namespace art diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h new file mode 100644 index 0000000..9d79002 --- /dev/null +++ b/compiler/utils/assembler.h @@ -0,0 +1,459 @@ +/* + * Copyright (C) 2011 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_ASSEMBLER_H_ +#define ART_COMPILER_UTILS_ASSEMBLER_H_ + +#include <vector> + +#include "base/logging.h" +#include "base/macros.h" +#include "arm/constants_arm.h" +#include "mips/constants_mips.h" +#include "x86/constants_x86.h" +#include "instruction_set.h" +#include "managed_register.h" +#include "memory_region.h" +#include "offsets.h" + +namespace art { + +class Assembler; +class AssemblerBuffer; +class AssemblerFixup; + +namespace arm { + class ArmAssembler; +} +namespace mips { + class MipsAssembler; +} +namespace x86 { + class X86Assembler; +} + +class Label { + public: + Label() : position_(0) {} + + ~Label() { + // Assert if label is being destroyed with unresolved branches pending. + CHECK(!IsLinked()); + } + + // Returns the position for bound and linked labels. Cannot be used + // for unused labels. + int Position() const { + CHECK(!IsUnused()); + return IsBound() ? -position_ - kPointerSize : position_ - kPointerSize; + } + + int LinkPosition() const { + CHECK(IsLinked()); + return position_ - kWordSize; + } + + bool IsBound() const { return position_ < 0; } + bool IsUnused() const { return position_ == 0; } + bool IsLinked() const { return position_ > 0; } + + private: + int position_; + + void Reinitialize() { + position_ = 0; + } + + void BindTo(int position) { + CHECK(!IsBound()); + position_ = -position - kPointerSize; + CHECK(IsBound()); + } + + void LinkTo(int position) { + CHECK(!IsBound()); + position_ = position + kPointerSize; + CHECK(IsLinked()); + } + + friend class arm::ArmAssembler; + friend class mips::MipsAssembler; + friend class x86::X86Assembler; + + DISALLOW_COPY_AND_ASSIGN(Label); +}; + + +// Assembler fixups are positions in generated code that require processing +// after the code has been copied to executable memory. This includes building +// relocation information. +class AssemblerFixup { + public: + virtual void Process(const MemoryRegion& region, int position) = 0; + virtual ~AssemblerFixup() {} + + private: + AssemblerFixup* previous_; + int position_; + + AssemblerFixup* previous() const { return previous_; } + void set_previous(AssemblerFixup* previous) { previous_ = previous; } + + int position() const { return position_; } + void set_position(int position) { position_ = position; } + + friend class AssemblerBuffer; +}; + +// Parent of all queued slow paths, emitted during finalization +class SlowPath { + public: + SlowPath() : next_(NULL) {} + virtual ~SlowPath() {} + + Label* Continuation() { return &continuation_; } + Label* Entry() { return &entry_; } + // Generate code for slow path + virtual void Emit(Assembler *sp_asm) = 0; + + protected: + // Entry branched to by fast path + Label entry_; + // Optional continuation that is branched to at the end of the slow path + Label continuation_; + // Next in linked list of slow paths + SlowPath *next_; + + friend class AssemblerBuffer; + DISALLOW_COPY_AND_ASSIGN(SlowPath); +}; + +class AssemblerBuffer { + public: + AssemblerBuffer(); + ~AssemblerBuffer(); + + // Basic support for emitting, loading, and storing. + template<typename T> void Emit(T value) { + CHECK(HasEnsuredCapacity()); + *reinterpret_cast<T*>(cursor_) = value; + cursor_ += sizeof(T); + } + + template<typename T> T Load(size_t position) { + CHECK_LE(position, Size() - static_cast<int>(sizeof(T))); + return *reinterpret_cast<T*>(contents_ + position); + } + + template<typename T> void Store(size_t position, T value) { + CHECK_LE(position, Size() - static_cast<int>(sizeof(T))); + *reinterpret_cast<T*>(contents_ + position) = value; + } + + // Emit a fixup at the current location. + void EmitFixup(AssemblerFixup* fixup) { + fixup->set_previous(fixup_); + fixup->set_position(Size()); + fixup_ = fixup; + } + + void EnqueueSlowPath(SlowPath* slowpath) { + if (slow_path_ == NULL) { + slow_path_ = slowpath; + } else { + SlowPath* cur = slow_path_; + for ( ; cur->next_ != NULL ; cur = cur->next_) {} + cur->next_ = slowpath; + } + } + + void EmitSlowPaths(Assembler* sp_asm) { + SlowPath* cur = slow_path_; + SlowPath* next = NULL; + slow_path_ = NULL; + for ( ; cur != NULL ; cur = next) { + cur->Emit(sp_asm); + next = cur->next_; + delete cur; + } + } + + // Get the size of the emitted code. + size_t Size() const { + CHECK_GE(cursor_, contents_); + return cursor_ - contents_; + } + + byte* contents() const { return contents_; } + + // Copy the assembled instructions into the specified memory block + // and apply all fixups. + void FinalizeInstructions(const MemoryRegion& region); + + // To emit an instruction to the assembler buffer, the EnsureCapacity helper + // must be used to guarantee that the underlying data area is big enough to + // hold the emitted instruction. Usage: + // + // AssemblerBuffer buffer; + // AssemblerBuffer::EnsureCapacity ensured(&buffer); + // ... emit bytes for single instruction ... + +#ifndef NDEBUG + + class EnsureCapacity { + public: + explicit EnsureCapacity(AssemblerBuffer* buffer) { + if (buffer->cursor() >= buffer->limit()) { + buffer->ExtendCapacity(); + } + // In debug mode, we save the assembler buffer along with the gap + // size before we start emitting to the buffer. This allows us to + // check that any single generated instruction doesn't overflow the + // limit implied by the minimum gap size. + buffer_ = buffer; + gap_ = ComputeGap(); + // Make sure that extending the capacity leaves a big enough gap + // for any kind of instruction. + CHECK_GE(gap_, kMinimumGap); + // Mark the buffer as having ensured the capacity. + CHECK(!buffer->HasEnsuredCapacity()); // Cannot nest. + buffer->has_ensured_capacity_ = true; + } + + ~EnsureCapacity() { + // Unmark the buffer, so we cannot emit after this. + buffer_->has_ensured_capacity_ = false; + // Make sure the generated instruction doesn't take up more + // space than the minimum gap. + int delta = gap_ - ComputeGap(); + CHECK_LE(delta, kMinimumGap); + } + + private: + AssemblerBuffer* buffer_; + int gap_; + + int ComputeGap() { return buffer_->Capacity() - buffer_->Size(); } + }; + + bool has_ensured_capacity_; + bool HasEnsuredCapacity() const { return has_ensured_capacity_; } + +#else + + class EnsureCapacity { + public: + explicit EnsureCapacity(AssemblerBuffer* buffer) { + if (buffer->cursor() >= buffer->limit()) buffer->ExtendCapacity(); + } + }; + + // When building the C++ tests, assertion code is enabled. To allow + // asserting that the user of the assembler buffer has ensured the + // capacity needed for emitting, we add a dummy method in non-debug mode. + bool HasEnsuredCapacity() const { return true; } + +#endif + + // Returns the position in the instruction stream. + int GetPosition() { return cursor_ - contents_; } + + private: + // The limit is set to kMinimumGap bytes before the end of the data area. + // This leaves enough space for the longest possible instruction and allows + // for a single, fast space check per instruction. + static const int kMinimumGap = 32; + + byte* contents_; + byte* cursor_; + byte* limit_; + AssemblerFixup* fixup_; + bool fixups_processed_; + + // Head of linked list of slow paths + SlowPath* slow_path_; + + byte* cursor() const { return cursor_; } + byte* limit() const { return limit_; } + size_t Capacity() const { + CHECK_GE(limit_, contents_); + return (limit_ - contents_) + kMinimumGap; + } + + // Process the fixup chain starting at the given fixup. The offset is + // non-zero for fixups in the body if the preamble is non-empty. + void ProcessFixups(const MemoryRegion& region); + + // Compute the limit based on the data area and the capacity. See + // description of kMinimumGap for the reasoning behind the value. + static byte* ComputeLimit(byte* data, size_t capacity) { + return data + capacity - kMinimumGap; + } + + void ExtendCapacity(); + + friend class AssemblerFixup; +}; + +class Assembler { + public: + static Assembler* Create(InstructionSet instruction_set); + + // Emit slow paths queued during assembly + void EmitSlowPaths() { buffer_.EmitSlowPaths(this); } + + // Size of generated code + size_t CodeSize() const { return buffer_.Size(); } + + // Copy instructions out of assembly buffer into the given region of memory + void FinalizeInstructions(const MemoryRegion& region) { + buffer_.FinalizeInstructions(region); + } + + // Emit code that will create an activation on the stack + virtual void BuildFrame(size_t frame_size, ManagedRegister method_reg, + const std::vector<ManagedRegister>& callee_save_regs, + const std::vector<ManagedRegister>& entry_spills) = 0; + + // Emit code that will remove an activation from the stack + virtual void RemoveFrame(size_t frame_size, + const std::vector<ManagedRegister>& callee_save_regs) = 0; + + virtual void IncreaseFrameSize(size_t adjust) = 0; + virtual void DecreaseFrameSize(size_t adjust) = 0; + + // Store routines + virtual void Store(FrameOffset offs, ManagedRegister src, size_t size) = 0; + virtual void StoreRef(FrameOffset dest, ManagedRegister src) = 0; + virtual void StoreRawPtr(FrameOffset dest, ManagedRegister src) = 0; + + virtual void StoreImmediateToFrame(FrameOffset dest, uint32_t imm, + ManagedRegister scratch) = 0; + + virtual void StoreImmediateToThread(ThreadOffset dest, uint32_t imm, + ManagedRegister scratch) = 0; + + virtual void StoreStackOffsetToThread(ThreadOffset thr_offs, + FrameOffset fr_offs, + ManagedRegister scratch) = 0; + + virtual void StoreStackPointerToThread(ThreadOffset thr_offs) = 0; + + virtual void StoreSpanning(FrameOffset dest, ManagedRegister src, + FrameOffset in_off, ManagedRegister scratch) = 0; + + // Load routines + virtual void Load(ManagedRegister dest, FrameOffset src, size_t size) = 0; + + virtual void Load(ManagedRegister dest, ThreadOffset src, size_t size) = 0; + + virtual void LoadRef(ManagedRegister dest, FrameOffset src) = 0; + + virtual void LoadRef(ManagedRegister dest, ManagedRegister base, + MemberOffset offs) = 0; + + virtual void LoadRawPtr(ManagedRegister dest, ManagedRegister base, + Offset offs) = 0; + + virtual void LoadRawPtrFromThread(ManagedRegister dest, + ThreadOffset offs) = 0; + + // Copying routines + virtual void Move(ManagedRegister dest, ManagedRegister src, size_t size) = 0; + + virtual void CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset thr_offs, + ManagedRegister scratch) = 0; + + virtual void CopyRawPtrToThread(ThreadOffset thr_offs, FrameOffset fr_offs, + ManagedRegister scratch) = 0; + + virtual void CopyRef(FrameOffset dest, FrameOffset src, + ManagedRegister scratch) = 0; + + virtual void Copy(FrameOffset dest, FrameOffset src, ManagedRegister scratch, size_t size) = 0; + + virtual void Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset, + ManagedRegister scratch, size_t size) = 0; + + virtual void Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src, + ManagedRegister scratch, size_t size) = 0; + + virtual void Copy(FrameOffset dest, FrameOffset src_base, Offset src_offset, + ManagedRegister scratch, size_t size) = 0; + + virtual void Copy(ManagedRegister dest, Offset dest_offset, + ManagedRegister src, Offset src_offset, + ManagedRegister scratch, size_t size) = 0; + + virtual void Copy(FrameOffset dest, Offset dest_offset, FrameOffset src, Offset src_offset, + ManagedRegister scratch, size_t size) = 0; + + virtual void MemoryBarrier(ManagedRegister scratch) = 0; + + // Sign extension + virtual void SignExtend(ManagedRegister mreg, size_t size) = 0; + + // Zero extension + virtual void ZeroExtend(ManagedRegister mreg, size_t size) = 0; + + // Exploit fast access in managed code to Thread::Current() + virtual void GetCurrentThread(ManagedRegister tr) = 0; + virtual void GetCurrentThread(FrameOffset dest_offset, + ManagedRegister scratch) = 0; + + // 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. + virtual void CreateSirtEntry(ManagedRegister out_reg, FrameOffset sirt_offset, + ManagedRegister in_reg, bool null_allowed) = 0; + + // Set up out_off to hold a Object** into the SIRT, or to be NULL if the + // value is null and null_allowed. + virtual void CreateSirtEntry(FrameOffset out_off, FrameOffset sirt_offset, + ManagedRegister scratch, bool null_allowed) = 0; + + // src holds a SIRT entry (Object**) load this into dst + virtual void LoadReferenceFromSirt(ManagedRegister dst, + ManagedRegister src) = 0; + + // Heap::VerifyObject on src. In some cases (such as a reference to this) we + // know that src may not be null. + virtual void VerifyObject(ManagedRegister src, bool could_be_null) = 0; + virtual void VerifyObject(FrameOffset src, bool could_be_null) = 0; + + // Call to address held at [base+offset] + virtual void Call(ManagedRegister base, Offset offset, + ManagedRegister scratch) = 0; + virtual void Call(FrameOffset base, Offset offset, + ManagedRegister scratch) = 0; + virtual void Call(ThreadOffset offset, ManagedRegister scratch) = 0; + + // Generate code to check if Thread::Current()->exception_ is non-null + // and branch to a ExceptionSlowPath if it is. + virtual void ExceptionPoll(ManagedRegister scratch, size_t stack_adjust) = 0; + + virtual ~Assembler() {} + + protected: + Assembler() : buffer_() {} + + AssemblerBuffer buffer_; +}; + +} // namespace art + +#endif // ART_COMPILER_UTILS_ASSEMBLER_H_ diff --git a/compiler/utils/managed_register.h b/compiler/utils/managed_register.h new file mode 100644 index 0000000..4ad1763 --- /dev/null +++ b/compiler/utils/managed_register.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2011 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_MANAGED_REGISTER_H_ +#define ART_COMPILER_UTILS_MANAGED_REGISTER_H_ + +namespace art { + +namespace arm { +class ArmManagedRegister; +} +namespace mips { +class MipsManagedRegister; +} +namespace x86 { +class X86ManagedRegister; +} + +class ManagedRegister { + public: + // ManagedRegister is a value class. There exists no method to change the + // internal state. We therefore allow a copy constructor and an + // assignment-operator. + ManagedRegister(const ManagedRegister& other) : id_(other.id_) { } + + ManagedRegister& operator=(const ManagedRegister& other) { + id_ = other.id_; + return *this; + } + + arm::ArmManagedRegister AsArm() const; + mips::MipsManagedRegister AsMips() const; + x86::X86ManagedRegister AsX86() const; + + // It is valid to invoke Equals on and with a NoRegister. + bool Equals(const ManagedRegister& other) const { + return id_ == other.id_; + } + + bool IsNoRegister() const { + return id_ == kNoRegister; + } + + static ManagedRegister NoRegister() { + return ManagedRegister(); + } + + protected: + static const int kNoRegister = -1; + + ManagedRegister() : id_(kNoRegister) { } + explicit ManagedRegister(int reg_id) : id_(reg_id) { } + + int id_; +}; + +} // namespace art + +#endif // ART_COMPILER_UTILS_MANAGED_REGISTER_H_ diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc new file mode 100644 index 0000000..58815da --- /dev/null +++ b/compiler/utils/mips/assembler_mips.cc @@ -0,0 +1,999 @@ +/* + * Copyright (C) 2011 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_mips.h" + +#include "base/casts.h" +#include "entrypoints/quick/quick_entrypoints.h" +#include "memory_region.h" +#include "thread.h" + +namespace art { +namespace mips { +#if 0 +class DirectCallRelocation : public AssemblerFixup { + public: + void Process(const MemoryRegion& region, int position) { + // Direct calls are relative to the following instruction on mips. + int32_t pointer = region.Load<int32_t>(position); + int32_t start = reinterpret_cast<int32_t>(region.start()); + int32_t delta = start + position + sizeof(int32_t); + region.Store<int32_t>(position, pointer - delta); + } +}; +#endif + +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; +} + +void MipsAssembler::Emit(int32_t value) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + buffer_.Emit<int32_t>(value); +} + +void MipsAssembler::EmitR(int opcode, Register rs, Register rt, Register rd, int shamt, int funct) { + CHECK_NE(rs, kNoRegister); + CHECK_NE(rt, kNoRegister); + CHECK_NE(rd, kNoRegister); + int32_t encoding = opcode << kOpcodeShift | + static_cast<int32_t>(rs) << kRsShift | + static_cast<int32_t>(rt) << kRtShift | + static_cast<int32_t>(rd) << kRdShift | + shamt << kShamtShift | + funct; + Emit(encoding); +} + +void MipsAssembler::EmitI(int opcode, Register rs, Register rt, uint16_t imm) { + CHECK_NE(rs, kNoRegister); + CHECK_NE(rt, kNoRegister); + int32_t encoding = opcode << kOpcodeShift | + static_cast<int32_t>(rs) << kRsShift | + static_cast<int32_t>(rt) << kRtShift | + imm; + Emit(encoding); +} + +void MipsAssembler::EmitJ(int opcode, int address) { + int32_t encoding = opcode << kOpcodeShift | + address; + Emit(encoding); +} + +void MipsAssembler::EmitFR(int opcode, int fmt, FRegister ft, FRegister fs, FRegister fd, int funct) { + CHECK_NE(ft, kNoFRegister); + CHECK_NE(fs, kNoFRegister); + CHECK_NE(fd, kNoFRegister); + int32_t encoding = opcode << kOpcodeShift | + fmt << kFmtShift | + static_cast<int32_t>(ft) << kFtShift | + static_cast<int32_t>(fs) << kFsShift | + static_cast<int32_t>(fd) << kFdShift | + funct; + Emit(encoding); +} + +void MipsAssembler::EmitFI(int opcode, int fmt, FRegister rt, uint16_t imm) { + CHECK_NE(rt, kNoFRegister); + int32_t encoding = opcode << kOpcodeShift | + fmt << kFmtShift | + static_cast<int32_t>(rt) << kRtShift | + imm; + Emit(encoding); +} + +void MipsAssembler::EmitBranch(Register rt, Register rs, Label* label, bool equal) { + int offset; + if (label->IsBound()) { + offset = label->Position() - buffer_.Size(); + } else { + // Use the offset field of the branch instruction for linking the sites. + offset = label->position_; + label->LinkTo(buffer_.Size()); + } + if (equal) { + Beq(rt, rs, (offset >> 2) & kBranchOffsetMask); + } else { + Bne(rt, rs, (offset >> 2) & kBranchOffsetMask); + } +} + +void MipsAssembler::EmitJump(Label* label, bool link) { + int offset; + if (label->IsBound()) { + offset = label->Position() - buffer_.Size(); + } else { + // Use the offset field of the jump instruction for linking the sites. + offset = label->position_; + label->LinkTo(buffer_.Size()); + } + if (link) { + Jal((offset >> 2) & kJumpOffsetMask); + } else { + J((offset >> 2) & kJumpOffsetMask); + } +} + +int32_t MipsAssembler::EncodeBranchOffset(int offset, int32_t inst, bool is_jump) { + CHECK_ALIGNED(offset, 4); + CHECK(IsInt(CountOneBits(kBranchOffsetMask), offset)) << offset; + + // Properly preserve only the bits supported in the instruction. + offset >>= 2; + if (is_jump) { + offset &= kJumpOffsetMask; + return (inst & ~kJumpOffsetMask) | offset; + } else { + offset &= kBranchOffsetMask; + return (inst & ~kBranchOffsetMask) | offset; + } +} + +int MipsAssembler::DecodeBranchOffset(int32_t inst, bool is_jump) { + // Sign-extend, then left-shift by 2. + if (is_jump) { + return (((inst & kJumpOffsetMask) << 6) >> 4); + } else { + return (((inst & kBranchOffsetMask) << 16) >> 14); + } +} + +void MipsAssembler::Bind(Label* label, bool is_jump) { + CHECK(!label->IsBound()); + int bound_pc = buffer_.Size(); + while (label->IsLinked()) { + int32_t position = label->Position(); + int32_t next = buffer_.Load<int32_t>(position); + int32_t offset = is_jump ? bound_pc - position : bound_pc - position - 4; + int32_t encoded = MipsAssembler::EncodeBranchOffset(offset, next, is_jump); + buffer_.Store<int32_t>(position, encoded); + label->position_ = MipsAssembler::DecodeBranchOffset(next, is_jump); + } + label->BindTo(bound_pc); +} + +void MipsAssembler::Add(Register rd, Register rs, Register rt) { + EmitR(0, rs, rt, rd, 0, 0x20); +} + +void MipsAssembler::Addu(Register rd, Register rs, Register rt) { + EmitR(0, rs, rt, rd, 0, 0x21); +} + +void MipsAssembler::Addi(Register rt, Register rs, uint16_t imm16) { + EmitI(0x8, rs, rt, imm16); +} + +void MipsAssembler::Addiu(Register rt, Register rs, uint16_t imm16) { + EmitI(0x9, rs, rt, imm16); +} + +void MipsAssembler::Sub(Register rd, Register rs, Register rt) { + EmitR(0, rs, rt, rd, 0, 0x22); +} + +void MipsAssembler::Subu(Register rd, Register rs, Register rt) { + EmitR(0, rs, rt, rd, 0, 0x23); +} + +void MipsAssembler::Mult(Register rs, Register rt) { + EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x18); +} + +void MipsAssembler::Multu(Register rs, Register rt) { + EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x19); +} + +void MipsAssembler::Div(Register rs, Register rt) { + EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1a); +} + +void MipsAssembler::Divu(Register rs, Register rt) { + EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1b); +} + +void MipsAssembler::And(Register rd, Register rs, Register rt) { + EmitR(0, rs, rt, rd, 0, 0x24); +} + +void MipsAssembler::Andi(Register rt, Register rs, uint16_t imm16) { + EmitI(0xc, rs, rt, imm16); +} + +void MipsAssembler::Or(Register rd, Register rs, Register rt) { + EmitR(0, rs, rt, rd, 0, 0x25); +} + +void MipsAssembler::Ori(Register rt, Register rs, uint16_t imm16) { + EmitI(0xd, rs, rt, imm16); +} + +void MipsAssembler::Xor(Register rd, Register rs, Register rt) { + EmitR(0, rs, rt, rd, 0, 0x26); +} + +void MipsAssembler::Xori(Register rt, Register rs, uint16_t imm16) { + EmitI(0xe, rs, rt, imm16); +} + +void MipsAssembler::Nor(Register rd, Register rs, Register rt) { + EmitR(0, rs, rt, rd, 0, 0x27); +} + +void MipsAssembler::Sll(Register rd, Register rs, int shamt) { + EmitR(0, rs, static_cast<Register>(0), rd, shamt, 0x00); +} + +void MipsAssembler::Srl(Register rd, Register rs, int shamt) { + EmitR(0, rs, static_cast<Register>(0), rd, shamt, 0x02); +} + +void MipsAssembler::Sra(Register rd, Register rs, int shamt) { + EmitR(0, rs, static_cast<Register>(0), rd, shamt, 0x03); +} + +void MipsAssembler::Sllv(Register rd, Register rs, Register rt) { + EmitR(0, rs, rt, rd, 0, 0x04); +} + +void MipsAssembler::Srlv(Register rd, Register rs, Register rt) { + EmitR(0, rs, rt, rd, 0, 0x06); +} + +void MipsAssembler::Srav(Register rd, Register rs, Register rt) { + EmitR(0, rs, rt, rd, 0, 0x07); +} + +void MipsAssembler::Lb(Register rt, Register rs, uint16_t imm16) { + EmitI(0x20, rs, rt, imm16); +} + +void MipsAssembler::Lh(Register rt, Register rs, uint16_t imm16) { + EmitI(0x21, rs, rt, imm16); +} + +void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16) { + EmitI(0x23, rs, rt, imm16); +} + +void MipsAssembler::Lbu(Register rt, Register rs, uint16_t imm16) { + EmitI(0x24, rs, rt, imm16); +} + +void MipsAssembler::Lhu(Register rt, Register rs, uint16_t imm16) { + EmitI(0x25, rs, rt, imm16); +} + +void MipsAssembler::Lui(Register rt, uint16_t imm16) { + EmitI(0xf, static_cast<Register>(0), rt, imm16); +} + +void MipsAssembler::Mfhi(Register rd) { + EmitR(0, static_cast<Register>(0), static_cast<Register>(0), rd, 0, 0x10); +} + +void MipsAssembler::Mflo(Register rd) { + EmitR(0, static_cast<Register>(0), static_cast<Register>(0), rd, 0, 0x12); +} + +void MipsAssembler::Sb(Register rt, Register rs, uint16_t imm16) { + EmitI(0x28, rs, rt, imm16); +} + +void MipsAssembler::Sh(Register rt, Register rs, uint16_t imm16) { + EmitI(0x29, rs, rt, imm16); +} + +void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16) { + EmitI(0x2b, rs, rt, imm16); +} + +void MipsAssembler::Slt(Register rd, Register rs, Register rt) { + EmitR(0, rs, rt, rd, 0, 0x2a); +} + +void MipsAssembler::Sltu(Register rd, Register rs, Register rt) { + EmitR(0, rs, rt, rd, 0, 0x2b); +} + +void MipsAssembler::Slti(Register rt, Register rs, uint16_t imm16) { + EmitI(0xa, rs, rt, imm16); +} + +void MipsAssembler::Sltiu(Register rt, Register rs, uint16_t imm16) { + EmitI(0xb, rs, rt, imm16); +} + +void MipsAssembler::Beq(Register rt, Register rs, uint16_t imm16) { + EmitI(0x4, rs, rt, imm16); + Nop(); +} + +void MipsAssembler::Bne(Register rt, Register rs, uint16_t imm16) { + EmitI(0x5, rs, rt, imm16); + Nop(); +} + +void MipsAssembler::J(uint32_t address) { + EmitJ(0x2, address); + Nop(); +} + +void MipsAssembler::Jal(uint32_t address) { + EmitJ(0x2, address); + Nop(); +} + +void MipsAssembler::Jr(Register rs) { + EmitR(0, rs, static_cast<Register>(0), static_cast<Register>(0), 0, 0x08); + Nop(); +} + +void MipsAssembler::Jalr(Register rs) { + EmitR(0, rs, static_cast<Register>(0), RA, 0, 0x09); + Nop(); +} + +void MipsAssembler::AddS(FRegister fd, FRegister fs, FRegister ft) { + EmitFR(0x11, 0x10, ft, fs, fd, 0x0); +} + +void MipsAssembler::SubS(FRegister fd, FRegister fs, FRegister ft) { + EmitFR(0x11, 0x10, ft, fs, fd, 0x1); +} + +void MipsAssembler::MulS(FRegister fd, FRegister fs, FRegister ft) { + EmitFR(0x11, 0x10, ft, fs, fd, 0x2); +} + +void MipsAssembler::DivS(FRegister fd, FRegister fs, FRegister ft) { + EmitFR(0x11, 0x10, ft, fs, fd, 0x3); +} + +void MipsAssembler::AddD(DRegister fd, DRegister fs, DRegister ft) { + EmitFR(0x11, 0x11, static_cast<FRegister>(ft), static_cast<FRegister>(fs), + static_cast<FRegister>(fd), 0x0); +} + +void MipsAssembler::SubD(DRegister fd, DRegister fs, DRegister ft) { + EmitFR(0x11, 0x11, static_cast<FRegister>(ft), static_cast<FRegister>(fs), + static_cast<FRegister>(fd), 0x1); +} + +void MipsAssembler::MulD(DRegister fd, DRegister fs, DRegister ft) { + EmitFR(0x11, 0x11, static_cast<FRegister>(ft), static_cast<FRegister>(fs), + static_cast<FRegister>(fd), 0x2); +} + +void MipsAssembler::DivD(DRegister fd, DRegister fs, DRegister ft) { + EmitFR(0x11, 0x11, static_cast<FRegister>(ft), static_cast<FRegister>(fs), + static_cast<FRegister>(fd), 0x3); +} + +void MipsAssembler::MovS(FRegister fd, FRegister fs) { + EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x6); +} + +void MipsAssembler::MovD(DRegister fd, DRegister fs) { + EmitFR(0x11, 0x11, static_cast<FRegister>(0), static_cast<FRegister>(fs), + static_cast<FRegister>(fd), 0x6); +} + +void MipsAssembler::Mfc1(Register rt, FRegister fs) { + EmitFR(0x11, 0x00, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0); +} + +void MipsAssembler::Mtc1(FRegister ft, Register rs) { + EmitFR(0x11, 0x04, ft, static_cast<FRegister>(rs), static_cast<FRegister>(0), 0x0); +} + +void MipsAssembler::Lwc1(FRegister ft, Register rs, uint16_t imm16) { + EmitI(0x31, rs, static_cast<Register>(ft), imm16); +} + +void MipsAssembler::Ldc1(DRegister ft, Register rs, uint16_t imm16) { + EmitI(0x35, rs, static_cast<Register>(ft), imm16); +} + +void MipsAssembler::Swc1(FRegister ft, Register rs, uint16_t imm16) { + EmitI(0x39, rs, static_cast<Register>(ft), imm16); +} + +void MipsAssembler::Sdc1(DRegister ft, Register rs, uint16_t imm16) { + EmitI(0x3d, rs, static_cast<Register>(ft), imm16); +} + +void MipsAssembler::Break() { + EmitR(0, static_cast<Register>(0), static_cast<Register>(0), + static_cast<Register>(0), 0, 0xD); +} + +void MipsAssembler::Nop() { + EmitR(0x0, static_cast<Register>(0), static_cast<Register>(0), static_cast<Register>(0), 0, 0x0); +} + +void MipsAssembler::Move(Register rt, Register rs) { + EmitI(0x8, rs, rt, 0); +} + +void MipsAssembler::Clear(Register rt) { + EmitR(0, static_cast<Register>(0), static_cast<Register>(0), rt, 0, 0x20); +} + +void MipsAssembler::Not(Register rt, Register rs) { + EmitR(0, static_cast<Register>(0), rs, rt, 0, 0x27); +} + +void MipsAssembler::Mul(Register rd, Register rs, Register rt) { + Mult(rs, rt); + Mflo(rd); +} + +void MipsAssembler::Div(Register rd, Register rs, Register rt) { + Div(rs, rt); + Mflo(rd); +} + +void MipsAssembler::Rem(Register rd, Register rs, Register rt) { + Div(rs, rt); + Mfhi(rd); +} + +void MipsAssembler::AddConstant(Register rt, Register rs, int32_t value) { + Addi(rt, rs, value); +} + +void MipsAssembler::LoadImmediate(Register rt, int32_t value) { + Addi(rt, ZERO, value); +} + +void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset, + size_t size) { + MipsManagedRegister dst = m_dst.AsMips(); + if (dst.IsNoRegister()) { + CHECK_EQ(0u, size) << dst; + } else if (dst.IsCoreRegister()) { + CHECK_EQ(4u, size) << dst; + LoadFromOffset(kLoadWord, dst.AsCoreRegister(), src_register, src_offset); + } else if (dst.IsRegisterPair()) { + CHECK_EQ(8u, size) << dst; + LoadFromOffset(kLoadWord, dst.AsRegisterPairLow(), src_register, src_offset); + LoadFromOffset(kLoadWord, dst.AsRegisterPairHigh(), src_register, src_offset + 4); + } else if (dst.IsFRegister()) { + LoadSFromOffset(dst.AsFRegister(), src_register, src_offset); + } else { + CHECK(dst.IsDRegister()) << dst; + LoadDFromOffset(dst.AsDRegister(), src_register, src_offset); + } +} + +void MipsAssembler::LoadFromOffset(LoadOperandType type, Register reg, Register base, + int32_t offset) { + switch (type) { + case kLoadSignedByte: + Lb(reg, base, offset); + break; + case kLoadUnsignedByte: + Lbu(reg, base, offset); + break; + case kLoadSignedHalfword: + Lh(reg, base, offset); + break; + case kLoadUnsignedHalfword: + Lhu(reg, base, offset); + break; + case kLoadWord: + Lw(reg, base, offset); + break; + case kLoadWordPair: + LOG(FATAL) << "UNREACHABLE"; + break; + default: + LOG(FATAL) << "UNREACHABLE"; + } +} + +void MipsAssembler::LoadSFromOffset(FRegister reg, Register base, int32_t offset) { + Lwc1(reg, base, offset); +} + +void MipsAssembler::LoadDFromOffset(DRegister reg, Register base, int32_t offset) { + Ldc1(reg, base, offset); +} + +void MipsAssembler::StoreToOffset(StoreOperandType type, Register reg, Register base, + int32_t offset) { + switch (type) { + case kStoreByte: + Sb(reg, base, offset); + break; + case kStoreHalfword: + Sh(reg, base, offset); + break; + case kStoreWord: + Sw(reg, base, offset); + break; + case kStoreWordPair: + LOG(FATAL) << "UNREACHABLE"; + break; + default: + LOG(FATAL) << "UNREACHABLE"; + } +} + +void MipsAssembler::StoreFToOffset(FRegister reg, Register base, int32_t offset) { + Swc1(reg, base, offset); +} + +void MipsAssembler::StoreDToOffset(DRegister reg, Register base, int32_t offset) { + Sdc1(reg, base, offset); +} + +void MipsAssembler::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); + + // Increase frame to required size. + IncreaseFrameSize(frame_size); + + // Push callee saves and return address + int stack_offset = frame_size - kPointerSize; + StoreToOffset(kStoreWord, RA, SP, stack_offset); + for (int i = callee_save_regs.size() - 1; i >= 0; --i) { + stack_offset -= kPointerSize; + Register reg = callee_save_regs.at(i).AsMips().AsCoreRegister(); + StoreToOffset(kStoreWord, reg, SP, stack_offset); + } + + // Write out Method*. + StoreToOffset(kStoreWord, method_reg.AsMips().AsCoreRegister(), SP, 0); + + // Write out entry spills. + for (size_t i = 0; i < entry_spills.size(); ++i) { + Register reg = entry_spills.at(i).AsMips().AsCoreRegister(); + StoreToOffset(kStoreWord, reg, SP, frame_size + kPointerSize + (i * kPointerSize)); + } +} + +void MipsAssembler::RemoveFrame(size_t frame_size, + const std::vector<ManagedRegister>& callee_save_regs) { + CHECK_ALIGNED(frame_size, kStackAlignment); + + // Pop callee saves and return address + int stack_offset = frame_size - (callee_save_regs.size() * kPointerSize) - kPointerSize; + for (size_t i = 0; i < callee_save_regs.size(); ++i) { + Register reg = callee_save_regs.at(i).AsMips().AsCoreRegister(); + LoadFromOffset(kLoadWord, reg, SP, stack_offset); + stack_offset += kPointerSize; + } + LoadFromOffset(kLoadWord, RA, SP, stack_offset); + + // Decrease frame to required size. + DecreaseFrameSize(frame_size); + + // Then jump to the return address. + Jr(RA); +} + +void MipsAssembler::IncreaseFrameSize(size_t adjust) { + CHECK_ALIGNED(adjust, kStackAlignment); + AddConstant(SP, SP, -adjust); +} + +void MipsAssembler::DecreaseFrameSize(size_t adjust) { + CHECK_ALIGNED(adjust, kStackAlignment); + AddConstant(SP, SP, adjust); +} + +void MipsAssembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) { + MipsManagedRegister src = msrc.AsMips(); + if (src.IsNoRegister()) { + CHECK_EQ(0u, size); + } else if (src.IsCoreRegister()) { + CHECK_EQ(4u, size); + StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value()); + } else if (src.IsRegisterPair()) { + CHECK_EQ(8u, size); + StoreToOffset(kStoreWord, src.AsRegisterPairLow(), SP, dest.Int32Value()); + StoreToOffset(kStoreWord, src.AsRegisterPairHigh(), + SP, dest.Int32Value() + 4); + } else if (src.IsFRegister()) { + StoreFToOffset(src.AsFRegister(), SP, dest.Int32Value()); + } else { + CHECK(src.IsDRegister()); + StoreDToOffset(src.AsDRegister(), SP, dest.Int32Value()); + } +} + +void MipsAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) { + MipsManagedRegister src = msrc.AsMips(); + CHECK(src.IsCoreRegister()); + StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value()); +} + +void MipsAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) { + MipsManagedRegister src = msrc.AsMips(); + CHECK(src.IsCoreRegister()); + StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value()); +} + +void MipsAssembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm, + ManagedRegister mscratch) { + MipsManagedRegister scratch = mscratch.AsMips(); + CHECK(scratch.IsCoreRegister()) << scratch; + LoadImmediate(scratch.AsCoreRegister(), imm); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value()); +} + +void MipsAssembler::StoreImmediateToThread(ThreadOffset dest, uint32_t imm, + ManagedRegister mscratch) { + MipsManagedRegister scratch = mscratch.AsMips(); + CHECK(scratch.IsCoreRegister()) << scratch; + LoadImmediate(scratch.AsCoreRegister(), imm); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), S1, dest.Int32Value()); +} + +void MipsAssembler::StoreStackOffsetToThread(ThreadOffset thr_offs, + FrameOffset fr_offs, + ManagedRegister mscratch) { + MipsManagedRegister scratch = mscratch.AsMips(); + CHECK(scratch.IsCoreRegister()) << scratch; + AddConstant(scratch.AsCoreRegister(), SP, fr_offs.Int32Value()); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), + S1, thr_offs.Int32Value()); +} + +void MipsAssembler::StoreStackPointerToThread(ThreadOffset thr_offs) { + StoreToOffset(kStoreWord, SP, S1, thr_offs.Int32Value()); +} + +void MipsAssembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc, + FrameOffset in_off, ManagedRegister mscratch) { + MipsManagedRegister src = msrc.AsMips(); + MipsManagedRegister scratch = mscratch.AsMips(); + StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value()); + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, in_off.Int32Value()); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + 4); +} + +void MipsAssembler::Load(ManagedRegister mdest, FrameOffset src, size_t size) { + return EmitLoad(mdest, SP, src.Int32Value(), size); +} + +void MipsAssembler::Load(ManagedRegister mdest, ThreadOffset src, size_t size) { + return EmitLoad(mdest, S1, src.Int32Value(), size); +} + +void MipsAssembler::LoadRef(ManagedRegister mdest, FrameOffset src) { + MipsManagedRegister dest = mdest.AsMips(); + CHECK(dest.IsCoreRegister()); + LoadFromOffset(kLoadWord, dest.AsCoreRegister(), SP, src.Int32Value()); +} + +void MipsAssembler::LoadRef(ManagedRegister mdest, ManagedRegister base, + MemberOffset offs) { + MipsManagedRegister dest = mdest.AsMips(); + CHECK(dest.IsCoreRegister() && dest.IsCoreRegister()); + LoadFromOffset(kLoadWord, dest.AsCoreRegister(), + base.AsMips().AsCoreRegister(), offs.Int32Value()); +} + +void MipsAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base, + Offset offs) { + MipsManagedRegister dest = mdest.AsMips(); + CHECK(dest.IsCoreRegister() && dest.IsCoreRegister()) << dest; + LoadFromOffset(kLoadWord, dest.AsCoreRegister(), + base.AsMips().AsCoreRegister(), offs.Int32Value()); +} + +void MipsAssembler::LoadRawPtrFromThread(ManagedRegister mdest, + ThreadOffset offs) { + MipsManagedRegister dest = mdest.AsMips(); + CHECK(dest.IsCoreRegister()); + LoadFromOffset(kLoadWord, dest.AsCoreRegister(), S1, offs.Int32Value()); +} + +void MipsAssembler::SignExtend(ManagedRegister /*mreg*/, size_t /*size*/) { + UNIMPLEMENTED(FATAL) << "no sign extension necessary for mips"; +} + +void MipsAssembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) { + UNIMPLEMENTED(FATAL) << "no zero extension necessary for mips"; +} + +void MipsAssembler::Move(ManagedRegister mdest, ManagedRegister msrc, size_t /*size*/) { + MipsManagedRegister dest = mdest.AsMips(); + MipsManagedRegister src = msrc.AsMips(); + if (!dest.Equals(src)) { + if (dest.IsCoreRegister()) { + CHECK(src.IsCoreRegister()) << src; + Move(dest.AsCoreRegister(), src.AsCoreRegister()); + } else if (dest.IsFRegister()) { + CHECK(src.IsFRegister()) << src; + MovS(dest.AsFRegister(), src.AsFRegister()); + } else if (dest.IsDRegister()) { + CHECK(src.IsDRegister()) << src; + MovD(dest.AsDRegister(), src.AsDRegister()); + } else { + CHECK(dest.IsRegisterPair()) << dest; + CHECK(src.IsRegisterPair()) << src; + // Ensure that the first move doesn't clobber the input of the second + if (src.AsRegisterPairHigh() != dest.AsRegisterPairLow()) { + Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow()); + Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh()); + } else { + Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh()); + Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow()); + } + } + } +} + +void MipsAssembler::CopyRef(FrameOffset dest, FrameOffset src, + ManagedRegister mscratch) { + MipsManagedRegister scratch = mscratch.AsMips(); + CHECK(scratch.IsCoreRegister()) << scratch; + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value()); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value()); +} + +void MipsAssembler::CopyRawPtrFromThread(FrameOffset fr_offs, + ThreadOffset thr_offs, + ManagedRegister mscratch) { + MipsManagedRegister scratch = mscratch.AsMips(); + CHECK(scratch.IsCoreRegister()) << scratch; + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), + S1, thr_offs.Int32Value()); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), + SP, fr_offs.Int32Value()); +} + +void MipsAssembler::CopyRawPtrToThread(ThreadOffset thr_offs, + FrameOffset fr_offs, + ManagedRegister mscratch) { + MipsManagedRegister scratch = mscratch.AsMips(); + CHECK(scratch.IsCoreRegister()) << scratch; + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), + SP, fr_offs.Int32Value()); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), + S1, thr_offs.Int32Value()); +} + +void MipsAssembler::Copy(FrameOffset dest, FrameOffset src, + ManagedRegister mscratch, size_t size) { + MipsManagedRegister scratch = mscratch.AsMips(); + CHECK(scratch.IsCoreRegister()) << scratch; + CHECK(size == 4 || size == 8) << size; + if (size == 4) { + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value()); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value()); + } else if (size == 8) { + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value()); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value()); + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value() + 4); + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + 4); + } +} + +void MipsAssembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset, + ManagedRegister mscratch, size_t size) { + Register scratch = mscratch.AsMips().AsCoreRegister(); + CHECK_EQ(size, 4u); + LoadFromOffset(kLoadWord, scratch, src_base.AsMips().AsCoreRegister(), src_offset.Int32Value()); + StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value()); +} + +void MipsAssembler::Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src, + ManagedRegister mscratch, size_t size) { + Register scratch = mscratch.AsMips().AsCoreRegister(); + CHECK_EQ(size, 4u); + LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value()); + StoreToOffset(kStoreWord, scratch, dest_base.AsMips().AsCoreRegister(), dest_offset.Int32Value()); +} + +void MipsAssembler::Copy(FrameOffset /*dest*/, FrameOffset /*src_base*/, Offset /*src_offset*/, + ManagedRegister /*mscratch*/, size_t /*size*/) { + UNIMPLEMENTED(FATAL) << "no arm implementation"; +#if 0 + Register scratch = mscratch.AsMips().AsCoreRegister(); + CHECK_EQ(size, 4u); + movl(scratch, Address(ESP, src_base)); + movl(scratch, Address(scratch, src_offset)); + movl(Address(ESP, dest), scratch); +#endif +} + +void MipsAssembler::Copy(ManagedRegister dest, Offset dest_offset, + ManagedRegister src, Offset src_offset, + ManagedRegister mscratch, size_t size) { + CHECK_EQ(size, 4u); + Register scratch = mscratch.AsMips().AsCoreRegister(); + LoadFromOffset(kLoadWord, scratch, src.AsMips().AsCoreRegister(), src_offset.Int32Value()); + StoreToOffset(kStoreWord, scratch, dest.AsMips().AsCoreRegister(), dest_offset.Int32Value()); +} + +void MipsAssembler::Copy(FrameOffset /*dest*/, Offset /*dest_offset*/, FrameOffset /*src*/, Offset /*src_offset*/, + ManagedRegister /*mscratch*/, size_t /*size*/) { + UNIMPLEMENTED(FATAL) << "no arm implementation"; +#if 0 + Register scratch = mscratch.AsMips().AsCoreRegister(); + CHECK_EQ(size, 4u); + CHECK_EQ(dest.Int32Value(), src.Int32Value()); + movl(scratch, Address(ESP, src)); + pushl(Address(scratch, src_offset)); + popl(Address(scratch, dest_offset)); +#endif +} + +void MipsAssembler::MemoryBarrier(ManagedRegister) { + UNIMPLEMENTED(FATAL) << "NEEDS TO BE IMPLEMENTED"; +#if 0 +#if ANDROID_SMP != 0 + mfence(); +#endif +#endif +} + +void MipsAssembler::CreateSirtEntry(ManagedRegister mout_reg, + FrameOffset sirt_offset, + ManagedRegister min_reg, bool null_allowed) { + MipsManagedRegister out_reg = mout_reg.AsMips(); + MipsManagedRegister in_reg = min_reg.AsMips(); + CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg; + CHECK(out_reg.IsCoreRegister()) << out_reg; + if (null_allowed) { + Label null_arg; + // 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(kLoadWord, out_reg.AsCoreRegister(), + SP, sirt_offset.Int32Value()); + in_reg = out_reg; + } + if (!out_reg.Equals(in_reg)) { + LoadImmediate(out_reg.AsCoreRegister(), 0); + } + EmitBranch(in_reg.AsCoreRegister(), ZERO, &null_arg, true); + AddConstant(out_reg.AsCoreRegister(), SP, sirt_offset.Int32Value()); + Bind(&null_arg, false); + } else { + AddConstant(out_reg.AsCoreRegister(), SP, sirt_offset.Int32Value()); + } +} + +void MipsAssembler::CreateSirtEntry(FrameOffset out_off, + FrameOffset sirt_offset, + ManagedRegister mscratch, + bool null_allowed) { + MipsManagedRegister scratch = mscratch.AsMips(); + CHECK(scratch.IsCoreRegister()) << scratch; + if (null_allowed) { + Label null_arg; + LoadFromOffset(kLoadWord, 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) + EmitBranch(scratch.AsCoreRegister(), ZERO, &null_arg, true); + AddConstant(scratch.AsCoreRegister(), SP, sirt_offset.Int32Value()); + Bind(&null_arg, false); + } else { + AddConstant(scratch.AsCoreRegister(), SP, sirt_offset.Int32Value()); + } + StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, out_off.Int32Value()); +} + +// Given a SIRT entry, load the associated reference. +void MipsAssembler::LoadReferenceFromSirt(ManagedRegister mout_reg, + ManagedRegister min_reg) { + MipsManagedRegister out_reg = mout_reg.AsMips(); + MipsManagedRegister in_reg = min_reg.AsMips(); + CHECK(out_reg.IsCoreRegister()) << out_reg; + CHECK(in_reg.IsCoreRegister()) << in_reg; + Label null_arg; + if (!out_reg.Equals(in_reg)) { + LoadImmediate(out_reg.AsCoreRegister(), 0); + } + EmitBranch(in_reg.AsCoreRegister(), ZERO, &null_arg, true); + LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(), + in_reg.AsCoreRegister(), 0); + Bind(&null_arg, false); +} + +void MipsAssembler::VerifyObject(ManagedRegister /*src*/, bool /*could_be_null*/) { + // TODO: not validating references +} + +void MipsAssembler::VerifyObject(FrameOffset /*src*/, bool /*could_be_null*/) { + // TODO: not validating references +} + +void MipsAssembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister mscratch) { + MipsManagedRegister base = mbase.AsMips(); + MipsManagedRegister scratch = mscratch.AsMips(); + CHECK(base.IsCoreRegister()) << base; + CHECK(scratch.IsCoreRegister()) << scratch; + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), + base.AsCoreRegister(), offset.Int32Value()); + Jalr(scratch.AsCoreRegister()); + // TODO: place reference map on call +} + +void MipsAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) { + MipsManagedRegister scratch = mscratch.AsMips(); + CHECK(scratch.IsCoreRegister()) << scratch; + // Call *(*(SP + base) + offset) + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), + SP, base.Int32Value()); + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), + scratch.AsCoreRegister(), offset.Int32Value()); + Jalr(scratch.AsCoreRegister()); + // TODO: place reference map on call +} + +void MipsAssembler::Call(ThreadOffset /*offset*/, ManagedRegister /*mscratch*/) { + UNIMPLEMENTED(FATAL) << "no arm implementation"; +#if 0 + fs()->call(Address::Absolute(offset)); +#endif +} + +void MipsAssembler::GetCurrentThread(ManagedRegister tr) { + Move(tr.AsMips().AsCoreRegister(), S1); +} + +void MipsAssembler::GetCurrentThread(FrameOffset offset, + ManagedRegister /*mscratch*/) { + StoreToOffset(kStoreWord, S1, SP, offset.Int32Value()); +} + +void MipsAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) { + MipsManagedRegister scratch = mscratch.AsMips(); + MipsExceptionSlowPath* slow = new MipsExceptionSlowPath(scratch, stack_adjust); + buffer_.EnqueueSlowPath(slow); + LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), + S1, Thread::ExceptionOffset().Int32Value()); + EmitBranch(scratch.AsCoreRegister(), ZERO, slow->Entry(), false); +} + +void MipsExceptionSlowPath::Emit(Assembler* sasm) { + MipsAssembler* sp_asm = down_cast<MipsAssembler*>(sasm); +#define __ sp_asm-> + __ Bind(&entry_, false); + if (stack_adjust_ != 0) { // Fix up the frame. + __ DecreaseFrameSize(stack_adjust_); + } + // Pass exception object as argument + // Don't care about preserving A0 as this call won't return + __ Move(A0, scratch_.AsCoreRegister()); + // Set up call to Thread::Current()->pDeliverException + __ LoadFromOffset(kLoadWord, T9, S1, ENTRYPOINT_OFFSET(pDeliverException)); + __ Jr(T9); + // Call never returns + __ Break(); +#undef __ +} + +} // namespace mips +} // namespace art diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h new file mode 100644 index 0000000..0f5f2fe --- /dev/null +++ b/compiler/utils/mips/assembler_mips.h @@ -0,0 +1,507 @@ +/* + * Copyright (C) 2011 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_MIPS_ASSEMBLER_MIPS_H_ +#define ART_COMPILER_UTILS_MIPS_ASSEMBLER_MIPS_H_ + +#include <vector> + +#include "base/macros.h" +#include "constants_mips.h" +#include "globals.h" +#include "managed_register_mips.h" +#include "utils/assembler.h" +#include "offsets.h" +#include "utils.h" + +namespace art { +namespace mips { +#if 0 +class Operand { + public: + uint8_t mod() const { + return (encoding_at(0) >> 6) & 3; + } + + Register rm() const { + return static_cast<Register>(encoding_at(0) & 7); + } + + ScaleFactor scale() const { + return static_cast<ScaleFactor>((encoding_at(1) >> 6) & 3); + } + + Register index() const { + return static_cast<Register>((encoding_at(1) >> 3) & 7); + } + + Register base() const { + return static_cast<Register>(encoding_at(1) & 7); + } + + int8_t disp8() const { + CHECK_GE(length_, 2); + return static_cast<int8_t>(encoding_[length_ - 1]); + } + + int32_t disp32() const { + CHECK_GE(length_, 5); + int32_t value; + memcpy(&value, &encoding_[length_ - 4], sizeof(value)); + return value; + } + + bool IsRegister(Register reg) const { + return ((encoding_[0] & 0xF8) == 0xC0) // Addressing mode is register only. + && ((encoding_[0] & 0x07) == reg); // Register codes match. + } + + protected: + // Operand can be sub classed (e.g: Address). + Operand() : length_(0) { } + + void SetModRM(int mod, Register rm) { + CHECK_EQ(mod & ~3, 0); + encoding_[0] = (mod << 6) | rm; + length_ = 1; + } + + void SetSIB(ScaleFactor scale, Register index, Register base) { + CHECK_EQ(length_, 1); + CHECK_EQ(scale & ~3, 0); + encoding_[1] = (scale << 6) | (index << 3) | base; + length_ = 2; + } + + void SetDisp8(int8_t disp) { + CHECK(length_ == 1 || length_ == 2); + encoding_[length_++] = static_cast<uint8_t>(disp); + } + + void SetDisp32(int32_t disp) { + CHECK(length_ == 1 || length_ == 2); + int disp_size = sizeof(disp); + memmove(&encoding_[length_], &disp, disp_size); + length_ += disp_size; + } + + private: + byte length_; + byte encoding_[6]; + byte padding_; + + explicit Operand(Register reg) { SetModRM(3, reg); } + + // Get the operand encoding byte at the given index. + uint8_t encoding_at(int index) const { + CHECK_GE(index, 0); + CHECK_LT(index, length_); + return encoding_[index]; + } + + friend class MipsAssembler; + + DISALLOW_COPY_AND_ASSIGN(Operand); +}; + + +class Address : public Operand { + public: + Address(Register base, int32_t disp) { + Init(base, disp); + } + + Address(Register base, Offset disp) { + Init(base, disp.Int32Value()); + } + + Address(Register base, FrameOffset disp) { + CHECK_EQ(base, ESP); + Init(ESP, disp.Int32Value()); + } + + Address(Register base, MemberOffset disp) { + Init(base, disp.Int32Value()); + } + + void Init(Register base, int32_t disp) { + if (disp == 0 && base != EBP) { + SetModRM(0, base); + if (base == ESP) SetSIB(TIMES_1, ESP, base); + } else if (disp >= -128 && disp <= 127) { + SetModRM(1, base); + if (base == ESP) SetSIB(TIMES_1, ESP, base); + SetDisp8(disp); + } else { + SetModRM(2, base); + if (base == ESP) SetSIB(TIMES_1, ESP, base); + SetDisp32(disp); + } + } + + + Address(Register index, ScaleFactor scale, int32_t disp) { + CHECK_NE(index, ESP); // Illegal addressing mode. + SetModRM(0, ESP); + SetSIB(scale, index, EBP); + SetDisp32(disp); + } + + Address(Register base, Register index, ScaleFactor scale, int32_t disp) { + CHECK_NE(index, ESP); // Illegal addressing mode. + if (disp == 0 && base != EBP) { + SetModRM(0, ESP); + SetSIB(scale, index, base); + } else if (disp >= -128 && disp <= 127) { + SetModRM(1, ESP); + SetSIB(scale, index, base); + SetDisp8(disp); + } else { + SetModRM(2, ESP); + SetSIB(scale, index, base); + SetDisp32(disp); + } + } + + static Address Absolute(uword addr) { + Address result; + result.SetModRM(0, EBP); + result.SetDisp32(addr); + return result; + } + + static Address Absolute(ThreadOffset addr) { + return Absolute(addr.Int32Value()); + } + + private: + Address() {} + + DISALLOW_COPY_AND_ASSIGN(Address); +}; + +#endif + +enum LoadOperandType { + kLoadSignedByte, + kLoadUnsignedByte, + kLoadSignedHalfword, + kLoadUnsignedHalfword, + kLoadWord, + kLoadWordPair, + kLoadSWord, + kLoadDWord +}; + +enum StoreOperandType { + kStoreByte, + kStoreHalfword, + kStoreWord, + kStoreWordPair, + kStoreSWord, + kStoreDWord +}; + +class MipsAssembler : public Assembler { + public: + MipsAssembler() {} + virtual ~MipsAssembler() {} + + // Emit Machine Instructions. + void Add(Register rd, Register rs, Register rt); + void Addu(Register rd, Register rs, Register rt); + void Addi(Register rt, Register rs, uint16_t imm16); + void Addiu(Register rt, Register rs, uint16_t imm16); + void Sub(Register rd, Register rs, Register rt); + void Subu(Register rd, Register rs, Register rt); + void Mult(Register rs, Register rt); + void Multu(Register rs, Register rt); + void Div(Register rs, Register rt); + void Divu(Register rs, Register rt); + + void And(Register rd, Register rs, Register rt); + void Andi(Register rt, Register rs, uint16_t imm16); + void Or(Register rd, Register rs, Register rt); + void Ori(Register rt, Register rs, uint16_t imm16); + void Xor(Register rd, Register rs, Register rt); + void Xori(Register rt, Register rs, uint16_t imm16); + void Nor(Register rd, Register rs, Register rt); + + void Sll(Register rd, Register rs, int shamt); + void Srl(Register rd, Register rs, int shamt); + void Sra(Register rd, Register rs, int shamt); + void Sllv(Register rd, Register rs, Register rt); + void Srlv(Register rd, Register rs, Register rt); + void Srav(Register rd, Register rs, Register rt); + + void Lb(Register rt, Register rs, uint16_t imm16); + void Lh(Register rt, Register rs, uint16_t imm16); + void Lw(Register rt, Register rs, uint16_t imm16); + void Lbu(Register rt, Register rs, uint16_t imm16); + void Lhu(Register rt, Register rs, uint16_t imm16); + void Lui(Register rt, uint16_t imm16); + void Mfhi(Register rd); + void Mflo(Register rd); + + void Sb(Register rt, Register rs, uint16_t imm16); + void Sh(Register rt, Register rs, uint16_t imm16); + void Sw(Register rt, Register rs, uint16_t imm16); + + void Slt(Register rd, Register rs, Register rt); + void Sltu(Register rd, Register rs, Register rt); + void Slti(Register rt, Register rs, uint16_t imm16); + void Sltiu(Register rt, Register rs, uint16_t imm16); + + void Beq(Register rt, Register rs, uint16_t imm16); + void Bne(Register rt, Register rs, uint16_t imm16); + void J(uint32_t address); + void Jal(uint32_t address); + void Jr(Register rs); + void Jalr(Register rs); + + void AddS(FRegister fd, FRegister fs, FRegister ft); + void SubS(FRegister fd, FRegister fs, FRegister ft); + void MulS(FRegister fd, FRegister fs, FRegister ft); + void DivS(FRegister fd, FRegister fs, FRegister ft); + void AddD(DRegister fd, DRegister fs, DRegister ft); + void SubD(DRegister fd, DRegister fs, DRegister ft); + void MulD(DRegister fd, DRegister fs, DRegister ft); + void DivD(DRegister fd, DRegister fs, DRegister ft); + void MovS(FRegister fd, FRegister fs); + void MovD(DRegister fd, DRegister fs); + + void Mfc1(Register rt, FRegister fs); + void Mtc1(FRegister ft, Register rs); + void Lwc1(FRegister ft, Register rs, uint16_t imm16); + void Ldc1(DRegister ft, Register rs, uint16_t imm16); + void Swc1(FRegister ft, Register rs, uint16_t imm16); + void Sdc1(DRegister ft, Register rs, uint16_t imm16); + + void Break(); + void Nop(); + void Move(Register rt, Register rs); + void Clear(Register rt); + void Not(Register rt, Register rs); + void Mul(Register rd, Register rs, Register rt); + void Div(Register rd, Register rs, Register rt); + void Rem(Register rd, Register rs, Register rt); + + void AddConstant(Register rt, Register rs, int32_t value); + void LoadImmediate(Register rt, int32_t value); + + void EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset, size_t size); + void LoadFromOffset(LoadOperandType type, Register reg, Register base, int32_t offset); + void LoadSFromOffset(FRegister reg, Register base, int32_t offset); + void LoadDFromOffset(DRegister reg, Register base, int32_t offset); + void StoreToOffset(StoreOperandType type, Register reg, Register base, int32_t offset); + void StoreFToOffset(FRegister reg, Register base, int32_t offset); + void StoreDToOffset(DRegister reg, Register base, int32_t offset); + +#if 0 + MipsAssembler* lock(); + + void mfence(); + + MipsAssembler* fs(); + + // + // Macros for High-level operations. + // + + void AddImmediate(Register reg, const Immediate& imm); + + void LoadDoubleConstant(XmmRegister dst, double value); + + void DoubleNegate(XmmRegister d); + void FloatNegate(XmmRegister f); + + void DoubleAbs(XmmRegister reg); + + void LockCmpxchgl(const Address& address, Register reg) { + lock()->cmpxchgl(address, reg); + } + + // + // Misc. functionality + // + int PreferredLoopAlignment() { return 16; } + void Align(int alignment, int offset); + + // Debugging and bringup support. + void Stop(const char* message); +#endif + + // Emit data (e.g. encoded instruction or immediate) to the instruction stream. + void Emit(int32_t value); + void EmitBranch(Register rt, Register rs, Label* label, bool equal); + void EmitJump(Label* label, bool link); + void Bind(Label* label, bool is_jump); + + // + // Overridden common assembler high-level functionality + // + + // Emit code that will create an activation on the stack + virtual 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 + virtual void RemoveFrame(size_t frame_size, + const std::vector<ManagedRegister>& callee_save_regs); + + virtual void IncreaseFrameSize(size_t adjust); + virtual void DecreaseFrameSize(size_t adjust); + + // Store routines + virtual void Store(FrameOffset offs, ManagedRegister msrc, size_t size); + virtual void StoreRef(FrameOffset dest, ManagedRegister msrc); + virtual void StoreRawPtr(FrameOffset dest, ManagedRegister msrc); + + virtual void StoreImmediateToFrame(FrameOffset dest, uint32_t imm, + ManagedRegister mscratch); + + virtual void StoreImmediateToThread(ThreadOffset dest, uint32_t imm, + ManagedRegister mscratch); + + virtual void StoreStackOffsetToThread(ThreadOffset thr_offs, + FrameOffset fr_offs, + ManagedRegister mscratch); + + virtual void StoreStackPointerToThread(ThreadOffset thr_offs); + + virtual void StoreSpanning(FrameOffset dest, ManagedRegister msrc, + FrameOffset in_off, ManagedRegister mscratch); + + // Load routines + virtual void Load(ManagedRegister mdest, FrameOffset src, size_t size); + + virtual void Load(ManagedRegister mdest, ThreadOffset src, size_t size); + + virtual void LoadRef(ManagedRegister dest, FrameOffset src); + + virtual void LoadRef(ManagedRegister mdest, ManagedRegister base, + MemberOffset offs); + + virtual void LoadRawPtr(ManagedRegister mdest, ManagedRegister base, + Offset offs); + + virtual void LoadRawPtrFromThread(ManagedRegister mdest, + ThreadOffset offs); + + // Copying routines + virtual void Move(ManagedRegister mdest, ManagedRegister msrc, size_t size); + + virtual void CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset thr_offs, + ManagedRegister mscratch); + + virtual void CopyRawPtrToThread(ThreadOffset thr_offs, FrameOffset fr_offs, + ManagedRegister mscratch); + + virtual void CopyRef(FrameOffset dest, FrameOffset src, + ManagedRegister mscratch); + + virtual void Copy(FrameOffset dest, FrameOffset src, ManagedRegister mscratch, size_t size); + + virtual void Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset, + ManagedRegister mscratch, size_t size); + + virtual void Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src, + ManagedRegister mscratch, size_t size); + + virtual void Copy(FrameOffset dest, FrameOffset src_base, Offset src_offset, + ManagedRegister mscratch, size_t size); + + virtual void Copy(ManagedRegister dest, Offset dest_offset, + ManagedRegister src, Offset src_offset, + ManagedRegister mscratch, size_t size); + + virtual void Copy(FrameOffset dest, Offset dest_offset, FrameOffset src, Offset src_offset, + ManagedRegister mscratch, size_t size); + + virtual void MemoryBarrier(ManagedRegister); + + // Sign extension + virtual void SignExtend(ManagedRegister mreg, size_t size); + + // Zero extension + virtual void ZeroExtend(ManagedRegister mreg, size_t size); + + // Exploit fast access in managed code to Thread::Current() + virtual void GetCurrentThread(ManagedRegister tr); + virtual void GetCurrentThread(FrameOffset dest_offset, + ManagedRegister mscratch); + + // 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. + virtual 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. + virtual void CreateSirtEntry(FrameOffset out_off, FrameOffset sirt_offset, + ManagedRegister mscratch, bool null_allowed); + + // src holds a SIRT entry (Object**) load this into dst + virtual 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. + virtual void VerifyObject(ManagedRegister src, bool could_be_null); + virtual void VerifyObject(FrameOffset src, bool could_be_null); + + // Call to address held at [base+offset] + virtual void Call(ManagedRegister base, Offset offset, + ManagedRegister mscratch); + virtual void Call(FrameOffset base, Offset offset, + ManagedRegister mscratch); + virtual void Call(ThreadOffset offset, ManagedRegister mscratch); + + // Generate code to check if Thread::Current()->exception_ is non-null + // and branch to a ExceptionSlowPath if it is. + virtual void ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust); + + private: + void EmitR(int opcode, Register rs, Register rt, Register rd, int shamt, int funct); + void EmitI(int opcode, Register rs, Register rt, uint16_t imm); + void EmitJ(int opcode, int address); + void EmitFR(int opcode, int fmt, FRegister ft, FRegister fs, FRegister fd, int funct); + void EmitFI(int opcode, int fmt, FRegister rt, uint16_t imm); + + int32_t EncodeBranchOffset(int offset, int32_t inst, bool is_jump); + int DecodeBranchOffset(int32_t inst, bool is_jump); + + DISALLOW_COPY_AND_ASSIGN(MipsAssembler); +}; + +// Slowpath entered when Thread::Current()->_exception is non-null +class MipsExceptionSlowPath : public SlowPath { + public: + explicit MipsExceptionSlowPath(MipsManagedRegister scratch, size_t stack_adjust) + : scratch_(scratch), stack_adjust_(stack_adjust) {} + virtual void Emit(Assembler *sp_asm); + private: + const MipsManagedRegister scratch_; + const size_t stack_adjust_; +}; + +} // namespace mips +} // namespace art + +#endif // ART_COMPILER_UTILS_MIPS_ASSEMBLER_MIPS_H_ diff --git a/compiler/utils/mips/constants_mips.h b/compiler/utils/mips/constants_mips.h new file mode 100644 index 0000000..44ed5cc --- /dev/null +++ b/compiler/utils/mips/constants_mips.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2012 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_MIPS_CONSTANTS_MIPS_H_ +#define ART_COMPILER_UTILS_MIPS_CONSTANTS_MIPS_H_ + +#include <iosfwd> + +#include "arch/mips/registers_mips.h" +#include "base/logging.h" +#include "base/macros.h" +#include "globals.h" + +namespace art { +namespace mips { + +// Values for double-precision floating point registers. +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, + kNumberOfDRegisters = 16, + kNumberOfOverlappingDRegisters = 16, + kNoDRegister = -1, +}; +std::ostream& operator<<(std::ostream& os, const DRegister& rhs); + +// Constants used for the decoding or encoding of the individual fields of instructions. +enum InstructionFields { + kOpcodeShift = 26, + kOpcodeBits = 6, + kRsShift = 21, + kRsBits = 5, + kRtShift = 16, + kRtBits = 5, + kRdShift = 11, + kRdBits = 5, + kShamtShift = 6, + kShamtBits = 5, + kFunctShift = 0, + kFunctBits = 6, + + kFmtShift = 21, + kFmtBits = 5, + kFtShift = 16, + kFtBits = 5, + kFsShift = 11, + kFsBits = 5, + kFdShift = 6, + kFdBits = 5, + + kBranchOffsetMask = 0x0000ffff, + kJumpOffsetMask = 0x03ffffff, +}; + +enum ScaleFactor { + TIMES_1 = 0, + TIMES_2 = 1, + TIMES_4 = 2, + TIMES_8 = 3 +}; + +class Instr { + public: + static const uint32_t kBreakPointInstruction = 0x0000000D; + + bool IsBreakPoint() { + return ((*reinterpret_cast<const uint32_t*>(this)) & 0xFC0000CF) == kBreakPointInstruction; + } + + // Instructions are read out of a code stream. The only way to get a + // reference to an instruction is to convert a pointer. There is no way + // to allocate or create instances of class Instr. + // Use the At(pc) function to create references to Instr. + static Instr* At(uintptr_t pc) { return reinterpret_cast<Instr*>(pc); } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(Instr); +}; + +} // namespace mips +} // namespace art + +#endif // ART_COMPILER_UTILS_MIPS_CONSTANTS_MIPS_H_ diff --git a/compiler/utils/mips/managed_register_mips.cc b/compiler/utils/mips/managed_register_mips.cc new file mode 100644 index 0000000..195dafb --- /dev/null +++ b/compiler/utils/mips/managed_register_mips.cc @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2011 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_mips.h" + +#include "globals.h" + +namespace art { +namespace mips { + +// These core registers are never available for allocation. +static const Register kReservedCoreRegistersArray[] = { S0, S1 }; + +// We need all registers for caching. +static const int kNumberOfAvailableCoreRegisters = (S7 - T0) + 1; +static const int kNumberOfAvailableFRegisters = kNumberOfFRegisters; +static const int kNumberOfAvailableDRegisters = kNumberOfDRegisters; +static const int kNumberOfAvailableOverlappingDRegisters = + kNumberOfOverlappingDRegisters; +static const int kNumberOfAvailableRegisterPairs = kNumberOfRegisterPairs; + +bool MipsManagedRegister::Overlaps(const MipsManagedRegister& other) const { + if (IsNoRegister() || other.IsNoRegister()) return false; + CHECK(IsValidManagedRegister()); + CHECK(other.IsValidManagedRegister()); + if (Equals(other)) return true; + if (IsRegisterPair()) { + Register low = AsRegisterPairLow(); + Register high = AsRegisterPairHigh(); + return MipsManagedRegister::FromCoreRegister(low).Overlaps(other) || + MipsManagedRegister::FromCoreRegister(high).Overlaps(other); + } + if (IsOverlappingDRegister()) { + if (other.IsDRegister()) return Equals(other); + if (other.IsFRegister()) { + FRegister low = AsOverlappingDRegisterLow(); + FRegister high = AsOverlappingDRegisterHigh(); + FRegister other_freg = other.AsFRegister(); + return (low == other_freg) || (high == other_freg); + } + return false; + } + if (other.IsRegisterPair() || other.IsOverlappingDRegister()) { + return other.Overlaps(*this); + } + return false; +} + + +int MipsManagedRegister::AllocIdLow() const { + CHECK(IsOverlappingDRegister() || IsRegisterPair()); + const int r = RegId() - (kNumberOfCoreRegIds + kNumberOfFRegIds); + int low; + if (r < kNumberOfOverlappingDRegIds) { + CHECK(IsOverlappingDRegister()); + low = (r * 2) + kNumberOfCoreRegIds; // Return an FRegister. + } else { + CHECK(IsRegisterPair()); + low = (r - kNumberOfDRegIds) * 2 + 2; // Return a Register. + if (low >= 24) { + // we got a pair higher than S6_S7, must be the dalvik special case + low = 5; + } + } + return low; +} + + +int MipsManagedRegister::AllocIdHigh() const { + return AllocIdLow() + 1; +} + + +void MipsManagedRegister::Print(std::ostream& os) const { + if (!IsValidManagedRegister()) { + os << "No Register"; + } else if (IsCoreRegister()) { + os << "Core: " << static_cast<int>(AsCoreRegister()); + } else if (IsRegisterPair()) { + os << "Pair: " << AsRegisterPairLow() << ", " << AsRegisterPairHigh(); + } else if (IsFRegister()) { + os << "FRegister: " << static_cast<int>(AsFRegister()); + } else if (IsDRegister()) { + os << "DRegister: " << static_cast<int>(AsDRegister()); + } else { + os << "??: " << RegId(); + } +} + +std::ostream& operator<<(std::ostream& os, const MipsManagedRegister& reg) { + reg.Print(os); + return os; +} + +std::ostream& operator<<(std::ostream& os, const RegisterPair& reg) { + os << MipsManagedRegister::FromRegisterPair(reg); + return os; +} + +} // namespace mips +} // namespace art diff --git a/compiler/utils/mips/managed_register_mips.h b/compiler/utils/mips/managed_register_mips.h new file mode 100644 index 0000000..dd55cc4 --- /dev/null +++ b/compiler/utils/mips/managed_register_mips.h @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2011 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_MIPS_MANAGED_REGISTER_MIPS_H_ +#define ART_COMPILER_UTILS_MIPS_MANAGED_REGISTER_MIPS_H_ + +#include "constants_mips.h" +#include "utils/managed_register.h" + +namespace art { +namespace mips { + +// Values for register pairs. +enum RegisterPair { + V0_V1 = 0, + A0_A1 = 1, + A2_A3 = 2, + T0_T1 = 3, + T2_T3 = 4, + T4_T5 = 5, + T6_T7 = 6, + S0_S1 = 7, + S2_S3 = 8, + S4_S5 = 9, + S6_S7 = 10, + A1_A2 = 11, // Dalvik style passing + kNumberOfRegisterPairs = 12, + kNoRegisterPair = -1, +}; + +std::ostream& operator<<(std::ostream& os, const RegisterPair& reg); + +const int kNumberOfCoreRegIds = kNumberOfCoreRegisters; +const int kNumberOfCoreAllocIds = kNumberOfCoreRegisters; + +const int kNumberOfFRegIds = kNumberOfFRegisters; +const int kNumberOfFAllocIds = kNumberOfFRegisters; + +const int kNumberOfDRegIds = kNumberOfDRegisters; +const int kNumberOfOverlappingDRegIds = kNumberOfOverlappingDRegisters; +const int kNumberOfDAllocIds = kNumberOfDRegisters; + +const int kNumberOfPairRegIds = kNumberOfRegisterPairs; + +const int kNumberOfRegIds = kNumberOfCoreRegIds + kNumberOfFRegIds + + kNumberOfDRegIds + kNumberOfPairRegIds; +const int kNumberOfAllocIds = + kNumberOfCoreAllocIds + kNumberOfFAllocIds + kNumberOfDAllocIds; + +// Register ids map: +// [0..R[ core registers (enum Register) +// [R..F[ single precision FP registers (enum FRegister) +// [F..D[ double precision FP registers (enum DRegister) +// [D..P[ core register pairs (enum RegisterPair) +// where +// R = kNumberOfCoreRegIds +// F = R + kNumberOfFRegIds +// D = F + kNumberOfDRegIds +// P = D + kNumberOfRegisterPairs + +// Allocation ids map: +// [0..R[ core registers (enum Register) +// [R..F[ single precision FP registers (enum FRegister) +// where +// R = kNumberOfCoreRegIds +// F = R + kNumberOfFRegIds + + +// An instance of class 'ManagedRegister' represents a single core register (enum +// Register), a single precision FP register (enum FRegister), a double precision +// FP register (enum DRegister), or a pair of core registers (enum RegisterPair). +// 'ManagedRegister::NoRegister()' provides an invalid register. +// There is a one-to-one mapping between ManagedRegister and register id. +class MipsManagedRegister : public ManagedRegister { + public: + Register AsCoreRegister() const { + CHECK(IsCoreRegister()); + return static_cast<Register>(id_); + } + + FRegister AsFRegister() const { + CHECK(IsFRegister()); + return static_cast<FRegister>(id_ - kNumberOfCoreRegIds); + } + + DRegister AsDRegister() const { + CHECK(IsDRegister()); + return static_cast<DRegister>(id_ - kNumberOfCoreRegIds - kNumberOfFRegIds); + } + + FRegister AsOverlappingDRegisterLow() const { + CHECK(IsOverlappingDRegister()); + DRegister d_reg = AsDRegister(); + return static_cast<FRegister>(d_reg * 2); + } + + FRegister AsOverlappingDRegisterHigh() const { + CHECK(IsOverlappingDRegister()); + DRegister d_reg = AsDRegister(); + return static_cast<FRegister>(d_reg * 2 + 1); + } + + Register AsRegisterPairLow() const { + CHECK(IsRegisterPair()); + // Appropriate mapping of register ids allows to use AllocIdLow(). + return FromRegId(AllocIdLow()).AsCoreRegister(); + } + + Register AsRegisterPairHigh() const { + CHECK(IsRegisterPair()); + // Appropriate mapping of register ids allows to use AllocIdHigh(). + return FromRegId(AllocIdHigh()).AsCoreRegister(); + } + + bool IsCoreRegister() const { + CHECK(IsValidManagedRegister()); + return (0 <= id_) && (id_ < kNumberOfCoreRegIds); + } + + bool IsFRegister() const { + CHECK(IsValidManagedRegister()); + const int test = id_ - kNumberOfCoreRegIds; + return (0 <= test) && (test < kNumberOfFRegIds); + } + + bool IsDRegister() const { + CHECK(IsValidManagedRegister()); + const int test = id_ - (kNumberOfCoreRegIds + kNumberOfFRegIds); + return (0 <= test) && (test < kNumberOfDRegIds); + } + + // Returns true if this DRegister overlaps FRegisters. + bool IsOverlappingDRegister() const { + CHECK(IsValidManagedRegister()); + const int test = id_ - (kNumberOfCoreRegIds + kNumberOfFRegIds); + return (0 <= test) && (test < kNumberOfOverlappingDRegIds); + } + + bool IsRegisterPair() const { + CHECK(IsValidManagedRegister()); + const int test = + id_ - (kNumberOfCoreRegIds + kNumberOfFRegIds + kNumberOfDRegIds); + return (0 <= test) && (test < kNumberOfPairRegIds); + } + + void Print(std::ostream& os) const; + + // 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 MipsManagedRegister& other) const; + + static MipsManagedRegister FromCoreRegister(Register r) { + CHECK_NE(r, kNoRegister); + return FromRegId(r); + } + + static MipsManagedRegister FromFRegister(FRegister r) { + CHECK_NE(r, kNoFRegister); + return FromRegId(r + kNumberOfCoreRegIds); + } + + static MipsManagedRegister FromDRegister(DRegister r) { + CHECK_NE(r, kNoDRegister); + return FromRegId(r + kNumberOfCoreRegIds + kNumberOfFRegIds); + } + + static MipsManagedRegister FromRegisterPair(RegisterPair r) { + CHECK_NE(r, kNoRegisterPair); + return FromRegId(r + (kNumberOfCoreRegIds + kNumberOfFRegIds + kNumberOfDRegIds)); + } + + private: + bool IsValidManagedRegister() const { + return (0 <= id_) && (id_ < kNumberOfRegIds); + } + + int RegId() const { + CHECK(!IsNoRegister()); + return id_; + } + + int AllocId() const { + CHECK(IsValidManagedRegister() && !IsOverlappingDRegister() && !IsRegisterPair()); + CHECK_LT(id_, kNumberOfAllocIds); + return id_; + } + + int AllocIdLow() const; + int AllocIdHigh() const; + + friend class ManagedRegister; + + explicit MipsManagedRegister(int reg_id) : ManagedRegister(reg_id) {} + + static MipsManagedRegister FromRegId(int reg_id) { + MipsManagedRegister reg(reg_id); + CHECK(reg.IsValidManagedRegister()); + return reg; + } +}; + +std::ostream& operator<<(std::ostream& os, const MipsManagedRegister& reg); + +} // namespace mips + +inline mips::MipsManagedRegister ManagedRegister::AsMips() const { + mips::MipsManagedRegister reg(id_); + CHECK(reg.IsNoRegister() || reg.IsValidManagedRegister()); + return reg; +} + +} // namespace art + +#endif // ART_COMPILER_UTILS_MIPS_MANAGED_REGISTER_MIPS_H_ diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc new file mode 100644 index 0000000..89bfeb5 --- /dev/null +++ b/compiler/utils/x86/assembler_x86.cc @@ -0,0 +1,1847 @@ +/* + * Copyright (C) 2011 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_x86.h" + +#include "base/casts.h" +#include "entrypoints/quick/quick_entrypoints.h" +#include "memory_region.h" +#include "thread.h" + +namespace art { +namespace x86 { + +class DirectCallRelocation : public AssemblerFixup { + public: + void Process(const MemoryRegion& region, int position) { + // Direct calls are relative to the following instruction on x86. + int32_t pointer = region.Load<int32_t>(position); + int32_t start = reinterpret_cast<int32_t>(region.start()); + int32_t delta = start + position + sizeof(int32_t); + region.Store<int32_t>(position, pointer - delta); + } +}; + +std::ostream& operator<<(std::ostream& os, const XmmRegister& reg) { + return os << "XMM" << static_cast<int>(reg); +} + +std::ostream& operator<<(std::ostream& os, const X87Register& reg) { + return os << "ST" << static_cast<int>(reg); +} + +void X86Assembler::call(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xFF); + EmitRegisterOperand(2, reg); +} + + +void X86Assembler::call(const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xFF); + EmitOperand(2, address); +} + + +void X86Assembler::call(Label* label) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xE8); + static const int kSize = 5; + EmitLabel(label, kSize); +} + + +void X86Assembler::pushl(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x50 + reg); +} + + +void X86Assembler::pushl(const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xFF); + EmitOperand(6, address); +} + + +void X86Assembler::pushl(const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + if (imm.is_int8()) { + EmitUint8(0x6A); + EmitUint8(imm.value() & 0xFF); + } else { + EmitUint8(0x68); + EmitImmediate(imm); + } +} + + +void X86Assembler::popl(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x58 + reg); +} + + +void X86Assembler::popl(const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x8F); + EmitOperand(0, address); +} + + +void X86Assembler::movl(Register dst, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xB8 + dst); + EmitImmediate(imm); +} + + +void X86Assembler::movl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x89); + EmitRegisterOperand(src, dst); +} + + +void X86Assembler::movl(Register dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x8B); + EmitOperand(dst, src); +} + + +void X86Assembler::movl(const Address& dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x89); + EmitOperand(src, dst); +} + + +void X86Assembler::movl(const Address& dst, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xC7); + EmitOperand(0, dst); + EmitImmediate(imm); +} + +void X86Assembler::movl(const Address& dst, Label* lbl) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xC7); + EmitOperand(0, dst); + EmitLabel(lbl, dst.length_ + 5); +} + +void X86Assembler::movzxb(Register dst, ByteRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xB6); + EmitRegisterOperand(dst, src); +} + + +void X86Assembler::movzxb(Register dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xB6); + EmitOperand(dst, src); +} + + +void X86Assembler::movsxb(Register dst, ByteRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xBE); + EmitRegisterOperand(dst, src); +} + + +void X86Assembler::movsxb(Register dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xBE); + EmitOperand(dst, src); +} + + +void X86Assembler::movb(Register /*dst*/, const Address& /*src*/) { + LOG(FATAL) << "Use movzxb or movsxb instead."; +} + + +void X86Assembler::movb(const Address& dst, ByteRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x88); + EmitOperand(src, dst); +} + + +void X86Assembler::movb(const Address& dst, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xC6); + EmitOperand(EAX, dst); + CHECK(imm.is_int8()); + EmitUint8(imm.value() & 0xFF); +} + + +void X86Assembler::movzxw(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xB7); + EmitRegisterOperand(dst, src); +} + + +void X86Assembler::movzxw(Register dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xB7); + EmitOperand(dst, src); +} + + +void X86Assembler::movsxw(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xBF); + EmitRegisterOperand(dst, src); +} + + +void X86Assembler::movsxw(Register dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xBF); + EmitOperand(dst, src); +} + + +void X86Assembler::movw(Register /*dst*/, const Address& /*src*/) { + LOG(FATAL) << "Use movzxw or movsxw instead."; +} + + +void X86Assembler::movw(const Address& dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitOperandSizeOverride(); + EmitUint8(0x89); + EmitOperand(src, dst); +} + + +void X86Assembler::leal(Register dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x8D); + EmitOperand(dst, src); +} + + +void X86Assembler::cmovl(Condition condition, Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x40 + condition); + EmitRegisterOperand(dst, src); +} + + +void X86Assembler::setb(Condition condition, Register dst) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x90 + condition); + EmitOperand(0, Operand(dst)); +} + + +void X86Assembler::movss(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x10); + EmitOperand(dst, src); +} + + +void X86Assembler::movss(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x11); + EmitOperand(src, dst); +} + + +void X86Assembler::movss(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x11); + EmitXmmRegisterOperand(src, dst); +} + + +void X86Assembler::movd(XmmRegister dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x6E); + EmitOperand(dst, Operand(src)); +} + + +void X86Assembler::movd(Register dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x7E); + EmitOperand(src, Operand(dst)); +} + + +void X86Assembler::addss(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x58); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::addss(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x58); + EmitOperand(dst, src); +} + + +void X86Assembler::subss(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x5C); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::subss(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x5C); + EmitOperand(dst, src); +} + + +void X86Assembler::mulss(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x59); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::mulss(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x59); + EmitOperand(dst, src); +} + + +void X86Assembler::divss(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x5E); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::divss(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x5E); + EmitOperand(dst, src); +} + + +void X86Assembler::flds(const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xD9); + EmitOperand(0, src); +} + + +void X86Assembler::fstps(const Address& dst) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xD9); + EmitOperand(3, dst); +} + + +void X86Assembler::movsd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x10); + EmitOperand(dst, src); +} + + +void X86Assembler::movsd(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x11); + EmitOperand(src, dst); +} + + +void X86Assembler::movsd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x11); + EmitXmmRegisterOperand(src, dst); +} + + +void X86Assembler::addsd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x58); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::addsd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x58); + EmitOperand(dst, src); +} + + +void X86Assembler::subsd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x5C); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::subsd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x5C); + EmitOperand(dst, src); +} + + +void X86Assembler::mulsd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x59); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::mulsd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x59); + EmitOperand(dst, src); +} + + +void X86Assembler::divsd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x5E); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::divsd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x5E); + EmitOperand(dst, src); +} + + +void X86Assembler::cvtsi2ss(XmmRegister dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x2A); + EmitOperand(dst, Operand(src)); +} + + +void X86Assembler::cvtsi2sd(XmmRegister dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x2A); + EmitOperand(dst, Operand(src)); +} + + +void X86Assembler::cvtss2si(Register dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x2D); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::cvtss2sd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x5A); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::cvtsd2si(Register dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x2D); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::cvttss2si(Register dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x2C); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::cvttsd2si(Register dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x2C); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::cvtsd2ss(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x5A); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::cvtdq2pd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0xE6); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::comiss(XmmRegister a, XmmRegister b) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x2F); + EmitXmmRegisterOperand(a, b); +} + + +void X86Assembler::comisd(XmmRegister a, XmmRegister b) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x2F); + EmitXmmRegisterOperand(a, b); +} + + +void X86Assembler::sqrtsd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF2); + EmitUint8(0x0F); + EmitUint8(0x51); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::sqrtss(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x51); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::xorpd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x57); + EmitOperand(dst, src); +} + + +void X86Assembler::xorpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x57); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::xorps(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x57); + EmitOperand(dst, src); +} + + +void X86Assembler::xorps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x57); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::andpd(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x54); + EmitOperand(dst, src); +} + + +void X86Assembler::fldl(const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xDD); + EmitOperand(0, src); +} + + +void X86Assembler::fstpl(const Address& dst) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xDD); + EmitOperand(3, dst); +} + + +void X86Assembler::fnstcw(const Address& dst) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xD9); + EmitOperand(7, dst); +} + + +void X86Assembler::fldcw(const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xD9); + EmitOperand(5, src); +} + + +void X86Assembler::fistpl(const Address& dst) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xDF); + EmitOperand(7, dst); +} + + +void X86Assembler::fistps(const Address& dst) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xDB); + EmitOperand(3, dst); +} + + +void X86Assembler::fildl(const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xDF); + EmitOperand(5, src); +} + + +void X86Assembler::fincstp() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xD9); + EmitUint8(0xF7); +} + + +void X86Assembler::ffree(const Immediate& index) { + CHECK_LT(index.value(), 7); + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xDD); + EmitUint8(0xC0 + index.value()); +} + + +void X86Assembler::fsin() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xD9); + EmitUint8(0xFE); +} + + +void X86Assembler::fcos() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xD9); + EmitUint8(0xFF); +} + + +void X86Assembler::fptan() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xD9); + EmitUint8(0xF2); +} + + +void X86Assembler::xchgl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x87); + EmitRegisterOperand(dst, src); +} + +void X86Assembler::xchgl(Register reg, const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x87); + EmitOperand(reg, address); +} + + +void X86Assembler::cmpl(Register reg, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(7, Operand(reg), imm); +} + + +void X86Assembler::cmpl(Register reg0, Register reg1) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x3B); + EmitOperand(reg0, Operand(reg1)); +} + + +void X86Assembler::cmpl(Register reg, const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x3B); + EmitOperand(reg, address); +} + + +void X86Assembler::addl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x03); + EmitRegisterOperand(dst, src); +} + + +void X86Assembler::addl(Register reg, const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x03); + EmitOperand(reg, address); +} + + +void X86Assembler::cmpl(const Address& address, Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x39); + EmitOperand(reg, address); +} + + +void X86Assembler::cmpl(const Address& address, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(7, address, imm); +} + + +void X86Assembler::testl(Register reg1, Register reg2) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x85); + EmitRegisterOperand(reg1, reg2); +} + + +void X86Assembler::testl(Register reg, const Immediate& immediate) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + // For registers that have a byte variant (EAX, EBX, ECX, and EDX) + // we only test the byte register to keep the encoding short. + if (immediate.is_uint8() && reg < 4) { + // Use zero-extended 8-bit immediate. + if (reg == EAX) { + EmitUint8(0xA8); + } else { + EmitUint8(0xF6); + EmitUint8(0xC0 + reg); + } + EmitUint8(immediate.value() & 0xFF); + } else if (reg == EAX) { + // Use short form if the destination is EAX. + EmitUint8(0xA9); + EmitImmediate(immediate); + } else { + EmitUint8(0xF7); + EmitOperand(0, Operand(reg)); + EmitImmediate(immediate); + } +} + + +void X86Assembler::andl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x23); + EmitOperand(dst, Operand(src)); +} + + +void X86Assembler::andl(Register dst, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(4, Operand(dst), imm); +} + + +void X86Assembler::orl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0B); + EmitOperand(dst, Operand(src)); +} + + +void X86Assembler::orl(Register dst, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(1, Operand(dst), imm); +} + + +void X86Assembler::xorl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x33); + EmitOperand(dst, Operand(src)); +} + + +void X86Assembler::addl(Register reg, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(0, Operand(reg), imm); +} + + +void X86Assembler::addl(const Address& address, Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x01); + EmitOperand(reg, address); +} + + +void X86Assembler::addl(const Address& address, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(0, address, imm); +} + + +void X86Assembler::adcl(Register reg, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(2, Operand(reg), imm); +} + + +void X86Assembler::adcl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x13); + EmitOperand(dst, Operand(src)); +} + + +void X86Assembler::adcl(Register dst, const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x13); + EmitOperand(dst, address); +} + + +void X86Assembler::subl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x2B); + EmitOperand(dst, Operand(src)); +} + + +void X86Assembler::subl(Register reg, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(5, Operand(reg), imm); +} + + +void X86Assembler::subl(Register reg, const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x2B); + EmitOperand(reg, address); +} + + +void X86Assembler::cdq() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x99); +} + + +void X86Assembler::idivl(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF7); + EmitUint8(0xF8 | reg); +} + + +void X86Assembler::imull(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xAF); + EmitOperand(dst, Operand(src)); +} + + +void X86Assembler::imull(Register reg, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x69); + EmitOperand(reg, Operand(reg)); + EmitImmediate(imm); +} + + +void X86Assembler::imull(Register reg, const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xAF); + EmitOperand(reg, address); +} + + +void X86Assembler::imull(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF7); + EmitOperand(5, Operand(reg)); +} + + +void X86Assembler::imull(const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF7); + EmitOperand(5, address); +} + + +void X86Assembler::mull(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF7); + EmitOperand(4, Operand(reg)); +} + + +void X86Assembler::mull(const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF7); + EmitOperand(4, address); +} + + +void X86Assembler::sbbl(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x1B); + EmitOperand(dst, Operand(src)); +} + + +void X86Assembler::sbbl(Register reg, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitComplex(3, Operand(reg), imm); +} + + +void X86Assembler::sbbl(Register dst, const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x1B); + EmitOperand(dst, address); +} + + +void X86Assembler::incl(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x40 + reg); +} + + +void X86Assembler::incl(const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xFF); + EmitOperand(0, address); +} + + +void X86Assembler::decl(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x48 + reg); +} + + +void X86Assembler::decl(const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xFF); + EmitOperand(1, address); +} + + +void X86Assembler::shll(Register reg, const Immediate& imm) { + EmitGenericShift(4, reg, imm); +} + + +void X86Assembler::shll(Register operand, Register shifter) { + EmitGenericShift(4, operand, shifter); +} + + +void X86Assembler::shrl(Register reg, const Immediate& imm) { + EmitGenericShift(5, reg, imm); +} + + +void X86Assembler::shrl(Register operand, Register shifter) { + EmitGenericShift(5, operand, shifter); +} + + +void X86Assembler::sarl(Register reg, const Immediate& imm) { + EmitGenericShift(7, reg, imm); +} + + +void X86Assembler::sarl(Register operand, Register shifter) { + EmitGenericShift(7, operand, shifter); +} + + +void X86Assembler::shld(Register dst, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xA5); + EmitRegisterOperand(src, dst); +} + + +void X86Assembler::negl(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF7); + EmitOperand(3, Operand(reg)); +} + + +void X86Assembler::notl(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF7); + EmitUint8(0xD0 | reg); +} + + +void X86Assembler::enter(const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xC8); + CHECK(imm.is_uint16()); + EmitUint8(imm.value() & 0xFF); + EmitUint8((imm.value() >> 8) & 0xFF); + EmitUint8(0x00); +} + + +void X86Assembler::leave() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xC9); +} + + +void X86Assembler::ret() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xC3); +} + + +void X86Assembler::ret(const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xC2); + CHECK(imm.is_uint16()); + EmitUint8(imm.value() & 0xFF); + EmitUint8((imm.value() >> 8) & 0xFF); +} + + + +void X86Assembler::nop() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x90); +} + + +void X86Assembler::int3() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xCC); +} + + +void X86Assembler::hlt() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF4); +} + + +void X86Assembler::j(Condition condition, Label* label) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + if (label->IsBound()) { + static const int kShortSize = 2; + static const int kLongSize = 6; + int offset = label->Position() - buffer_.Size(); + CHECK_LE(offset, 0); + if (IsInt(8, offset - kShortSize)) { + EmitUint8(0x70 + condition); + EmitUint8((offset - kShortSize) & 0xFF); + } else { + EmitUint8(0x0F); + EmitUint8(0x80 + condition); + EmitInt32(offset - kLongSize); + } + } else { + EmitUint8(0x0F); + EmitUint8(0x80 + condition); + EmitLabelLink(label); + } +} + + +void X86Assembler::jmp(Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xFF); + EmitRegisterOperand(4, reg); +} + +void X86Assembler::jmp(const Address& address) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xFF); + EmitOperand(4, address); +} + +void X86Assembler::jmp(Label* label) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + if (label->IsBound()) { + static const int kShortSize = 2; + static const int kLongSize = 5; + int offset = label->Position() - buffer_.Size(); + CHECK_LE(offset, 0); + if (IsInt(8, offset - kShortSize)) { + EmitUint8(0xEB); + EmitUint8((offset - kShortSize) & 0xFF); + } else { + EmitUint8(0xE9); + EmitInt32(offset - kLongSize); + } + } else { + EmitUint8(0xE9); + EmitLabelLink(label); + } +} + + +X86Assembler* X86Assembler::lock() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF0); + return this; +} + + +void X86Assembler::cmpxchgl(const Address& address, Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xB1); + EmitOperand(reg, address); +} + +void X86Assembler::mfence() { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xAE); + EmitUint8(0xF0); +} + +X86Assembler* X86Assembler::fs() { + // TODO: fs is a prefix and not an instruction + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x64); + return this; +} + +void X86Assembler::AddImmediate(Register reg, const Immediate& imm) { + int value = imm.value(); + if (value > 0) { + if (value == 1) { + incl(reg); + } else if (value != 0) { + addl(reg, imm); + } + } else if (value < 0) { + value = -value; + if (value == 1) { + decl(reg); + } else if (value != 0) { + subl(reg, Immediate(value)); + } + } +} + + +void X86Assembler::LoadDoubleConstant(XmmRegister dst, double value) { + // TODO: Need to have a code constants table. + int64_t constant = bit_cast<int64_t, double>(value); + pushl(Immediate(High32Bits(constant))); + pushl(Immediate(Low32Bits(constant))); + movsd(dst, Address(ESP, 0)); + addl(ESP, Immediate(2 * kWordSize)); +} + + +void X86Assembler::FloatNegate(XmmRegister f) { + static const struct { + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + } float_negate_constant __attribute__((aligned(16))) = + { 0x80000000, 0x00000000, 0x80000000, 0x00000000 }; + xorps(f, Address::Absolute(reinterpret_cast<uword>(&float_negate_constant))); +} + + +void X86Assembler::DoubleNegate(XmmRegister d) { + static const struct { + uint64_t a; + uint64_t b; + } double_negate_constant __attribute__((aligned(16))) = + {0x8000000000000000LL, 0x8000000000000000LL}; + xorpd(d, Address::Absolute(reinterpret_cast<uword>(&double_negate_constant))); +} + + +void X86Assembler::DoubleAbs(XmmRegister reg) { + static const struct { + uint64_t a; + uint64_t b; + } double_abs_constant __attribute__((aligned(16))) = + {0x7FFFFFFFFFFFFFFFLL, 0x7FFFFFFFFFFFFFFFLL}; + andpd(reg, Address::Absolute(reinterpret_cast<uword>(&double_abs_constant))); +} + + +void X86Assembler::Align(int alignment, int offset) { + CHECK(IsPowerOfTwo(alignment)); + // Emit nop instruction until the real position is aligned. + while (((offset + buffer_.GetPosition()) & (alignment-1)) != 0) { + nop(); + } +} + + +void X86Assembler::Bind(Label* label) { + int bound = buffer_.Size(); + CHECK(!label->IsBound()); // Labels can only be bound once. + while (label->IsLinked()) { + int position = label->LinkPosition(); + int next = buffer_.Load<int32_t>(position); + buffer_.Store<int32_t>(position, bound - (position + 4)); + label->position_ = next; + } + label->BindTo(bound); +} + + +void X86Assembler::Stop(const char* message) { + // Emit the message address as immediate operand in the test rax instruction, + // followed by the int3 instruction. + // Execution can be resumed with the 'cont' command in gdb. + testl(EAX, Immediate(reinterpret_cast<int32_t>(message))); + int3(); +} + + +void X86Assembler::EmitOperand(int reg_or_opcode, const Operand& operand) { + CHECK_GE(reg_or_opcode, 0); + CHECK_LT(reg_or_opcode, 8); + const int length = operand.length_; + CHECK_GT(length, 0); + // Emit the ModRM byte updated with the given reg value. + CHECK_EQ(operand.encoding_[0] & 0x38, 0); + EmitUint8(operand.encoding_[0] + (reg_or_opcode << 3)); + // Emit the rest of the encoded operand. + for (int i = 1; i < length; i++) { + EmitUint8(operand.encoding_[i]); + } +} + + +void X86Assembler::EmitImmediate(const Immediate& imm) { + EmitInt32(imm.value()); +} + + +void X86Assembler::EmitComplex(int reg_or_opcode, + const Operand& operand, + const Immediate& immediate) { + CHECK_GE(reg_or_opcode, 0); + CHECK_LT(reg_or_opcode, 8); + if (immediate.is_int8()) { + // Use sign-extended 8-bit immediate. + EmitUint8(0x83); + EmitOperand(reg_or_opcode, operand); + EmitUint8(immediate.value() & 0xFF); + } else if (operand.IsRegister(EAX)) { + // Use short form if the destination is eax. + EmitUint8(0x05 + (reg_or_opcode << 3)); + EmitImmediate(immediate); + } else { + EmitUint8(0x81); + EmitOperand(reg_or_opcode, operand); + EmitImmediate(immediate); + } +} + + +void X86Assembler::EmitLabel(Label* label, int instruction_size) { + if (label->IsBound()) { + int offset = label->Position() - buffer_.Size(); + CHECK_LE(offset, 0); + EmitInt32(offset - instruction_size); + } else { + EmitLabelLink(label); + } +} + + +void X86Assembler::EmitLabelLink(Label* label) { + CHECK(!label->IsBound()); + int position = buffer_.Size(); + EmitInt32(label->position_); + label->LinkTo(position); +} + + +void X86Assembler::EmitGenericShift(int reg_or_opcode, + Register reg, + const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + CHECK(imm.is_int8()); + if (imm.value() == 1) { + EmitUint8(0xD1); + EmitOperand(reg_or_opcode, Operand(reg)); + } else { + EmitUint8(0xC1); + EmitOperand(reg_or_opcode, Operand(reg)); + EmitUint8(imm.value() & 0xFF); + } +} + + +void X86Assembler::EmitGenericShift(int reg_or_opcode, + Register operand, + Register shifter) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + CHECK_EQ(shifter, ECX); + EmitUint8(0xD3); + EmitOperand(reg_or_opcode, Operand(operand)); +} + +void X86Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg, + const std::vector<ManagedRegister>& spill_regs, + const std::vector<ManagedRegister>& entry_spills) { + CHECK_ALIGNED(frame_size, kStackAlignment); + for (int i = spill_regs.size() - 1; i >= 0; --i) { + pushl(spill_regs.at(i).AsX86().AsCpuRegister()); + } + // return address then method on stack + addl(ESP, Immediate(-frame_size + (spill_regs.size() * kPointerSize) + + kPointerSize /*method*/ + kPointerSize /*return address*/)); + pushl(method_reg.AsX86().AsCpuRegister()); + for (size_t i = 0; i < entry_spills.size(); ++i) { + movl(Address(ESP, frame_size + kPointerSize + (i * kPointerSize)), + entry_spills.at(i).AsX86().AsCpuRegister()); + } +} + +void X86Assembler::RemoveFrame(size_t frame_size, + const std::vector<ManagedRegister>& spill_regs) { + CHECK_ALIGNED(frame_size, kStackAlignment); + addl(ESP, Immediate(frame_size - (spill_regs.size() * kPointerSize) - kPointerSize)); + for (size_t i = 0; i < spill_regs.size(); ++i) { + popl(spill_regs.at(i).AsX86().AsCpuRegister()); + } + ret(); +} + +void X86Assembler::IncreaseFrameSize(size_t adjust) { + CHECK_ALIGNED(adjust, kStackAlignment); + addl(ESP, Immediate(-adjust)); +} + +void X86Assembler::DecreaseFrameSize(size_t adjust) { + CHECK_ALIGNED(adjust, kStackAlignment); + addl(ESP, Immediate(adjust)); +} + +void X86Assembler::Store(FrameOffset offs, ManagedRegister msrc, size_t size) { + X86ManagedRegister src = msrc.AsX86(); + if (src.IsNoRegister()) { + CHECK_EQ(0u, size); + } else if (src.IsCpuRegister()) { + CHECK_EQ(4u, size); + movl(Address(ESP, offs), src.AsCpuRegister()); + } else if (src.IsRegisterPair()) { + CHECK_EQ(8u, size); + movl(Address(ESP, offs), src.AsRegisterPairLow()); + movl(Address(ESP, FrameOffset(offs.Int32Value()+4)), + src.AsRegisterPairHigh()); + } else if (src.IsX87Register()) { + if (size == 4) { + fstps(Address(ESP, offs)); + } else { + fstpl(Address(ESP, offs)); + } + } else { + CHECK(src.IsXmmRegister()); + if (size == 4) { + movss(Address(ESP, offs), src.AsXmmRegister()); + } else { + movsd(Address(ESP, offs), src.AsXmmRegister()); + } + } +} + +void X86Assembler::StoreRef(FrameOffset dest, ManagedRegister msrc) { + X86ManagedRegister src = msrc.AsX86(); + CHECK(src.IsCpuRegister()); + movl(Address(ESP, dest), src.AsCpuRegister()); +} + +void X86Assembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) { + X86ManagedRegister src = msrc.AsX86(); + CHECK(src.IsCpuRegister()); + movl(Address(ESP, dest), src.AsCpuRegister()); +} + +void X86Assembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm, + ManagedRegister) { + movl(Address(ESP, dest), Immediate(imm)); +} + +void X86Assembler::StoreImmediateToThread(ThreadOffset dest, uint32_t imm, + ManagedRegister) { + fs()->movl(Address::Absolute(dest), Immediate(imm)); +} + +void X86Assembler::StoreStackOffsetToThread(ThreadOffset thr_offs, + FrameOffset fr_offs, + ManagedRegister mscratch) { + X86ManagedRegister scratch = mscratch.AsX86(); + CHECK(scratch.IsCpuRegister()); + leal(scratch.AsCpuRegister(), Address(ESP, fr_offs)); + fs()->movl(Address::Absolute(thr_offs), scratch.AsCpuRegister()); +} + +void X86Assembler::StoreStackPointerToThread(ThreadOffset thr_offs) { + fs()->movl(Address::Absolute(thr_offs), ESP); +} + +void X86Assembler::StoreLabelToThread(ThreadOffset thr_offs, Label* lbl) { + fs()->movl(Address::Absolute(thr_offs), lbl); +} + +void X86Assembler::StoreSpanning(FrameOffset /*dst*/, ManagedRegister /*src*/, + FrameOffset /*in_off*/, ManagedRegister /*scratch*/) { + UNIMPLEMENTED(FATAL); // this case only currently exists for ARM +} + +void X86Assembler::Load(ManagedRegister mdest, FrameOffset src, size_t size) { + X86ManagedRegister dest = mdest.AsX86(); + if (dest.IsNoRegister()) { + CHECK_EQ(0u, size); + } else if (dest.IsCpuRegister()) { + CHECK_EQ(4u, size); + movl(dest.AsCpuRegister(), Address(ESP, src)); + } else if (dest.IsRegisterPair()) { + CHECK_EQ(8u, size); + movl(dest.AsRegisterPairLow(), Address(ESP, src)); + movl(dest.AsRegisterPairHigh(), Address(ESP, FrameOffset(src.Int32Value()+4))); + } else if (dest.IsX87Register()) { + if (size == 4) { + flds(Address(ESP, src)); + } else { + fldl(Address(ESP, src)); + } + } else { + CHECK(dest.IsXmmRegister()); + if (size == 4) { + movss(dest.AsXmmRegister(), Address(ESP, src)); + } else { + movsd(dest.AsXmmRegister(), Address(ESP, src)); + } + } +} + +void X86Assembler::Load(ManagedRegister mdest, ThreadOffset src, size_t size) { + X86ManagedRegister dest = mdest.AsX86(); + if (dest.IsNoRegister()) { + CHECK_EQ(0u, size); + } else if (dest.IsCpuRegister()) { + CHECK_EQ(4u, size); + fs()->movl(dest.AsCpuRegister(), Address::Absolute(src)); + } else if (dest.IsRegisterPair()) { + CHECK_EQ(8u, size); + fs()->movl(dest.AsRegisterPairLow(), Address::Absolute(src)); + fs()->movl(dest.AsRegisterPairHigh(), Address::Absolute(ThreadOffset(src.Int32Value()+4))); + } else if (dest.IsX87Register()) { + if (size == 4) { + fs()->flds(Address::Absolute(src)); + } else { + fs()->fldl(Address::Absolute(src)); + } + } else { + CHECK(dest.IsXmmRegister()); + if (size == 4) { + fs()->movss(dest.AsXmmRegister(), Address::Absolute(src)); + } else { + fs()->movsd(dest.AsXmmRegister(), Address::Absolute(src)); + } + } +} + +void X86Assembler::LoadRef(ManagedRegister mdest, FrameOffset src) { + X86ManagedRegister dest = mdest.AsX86(); + CHECK(dest.IsCpuRegister()); + movl(dest.AsCpuRegister(), Address(ESP, src)); +} + +void X86Assembler::LoadRef(ManagedRegister mdest, ManagedRegister base, + MemberOffset offs) { + X86ManagedRegister dest = mdest.AsX86(); + CHECK(dest.IsCpuRegister() && dest.IsCpuRegister()); + movl(dest.AsCpuRegister(), Address(base.AsX86().AsCpuRegister(), offs)); +} + +void X86Assembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base, + Offset offs) { + X86ManagedRegister dest = mdest.AsX86(); + CHECK(dest.IsCpuRegister() && dest.IsCpuRegister()); + movl(dest.AsCpuRegister(), Address(base.AsX86().AsCpuRegister(), offs)); +} + +void X86Assembler::LoadRawPtrFromThread(ManagedRegister mdest, + ThreadOffset offs) { + X86ManagedRegister dest = mdest.AsX86(); + CHECK(dest.IsCpuRegister()); + fs()->movl(dest.AsCpuRegister(), Address::Absolute(offs)); +} + +void X86Assembler::SignExtend(ManagedRegister mreg, size_t size) { + X86ManagedRegister reg = mreg.AsX86(); + CHECK(size == 1 || size == 2) << size; + CHECK(reg.IsCpuRegister()) << reg; + if (size == 1) { + movsxb(reg.AsCpuRegister(), reg.AsByteRegister()); + } else { + movsxw(reg.AsCpuRegister(), reg.AsCpuRegister()); + } +} + +void X86Assembler::ZeroExtend(ManagedRegister mreg, size_t size) { + X86ManagedRegister reg = mreg.AsX86(); + CHECK(size == 1 || size == 2) << size; + CHECK(reg.IsCpuRegister()) << reg; + if (size == 1) { + movzxb(reg.AsCpuRegister(), reg.AsByteRegister()); + } else { + movzxw(reg.AsCpuRegister(), reg.AsCpuRegister()); + } +} + +void X86Assembler::Move(ManagedRegister mdest, ManagedRegister msrc, size_t size) { + X86ManagedRegister dest = mdest.AsX86(); + X86ManagedRegister src = msrc.AsX86(); + if (!dest.Equals(src)) { + if (dest.IsCpuRegister() && src.IsCpuRegister()) { + movl(dest.AsCpuRegister(), src.AsCpuRegister()); + } else if (src.IsX87Register() && dest.IsXmmRegister()) { + // Pass via stack and pop X87 register + subl(ESP, Immediate(16)); + if (size == 4) { + CHECK_EQ(src.AsX87Register(), ST0); + fstps(Address(ESP, 0)); + movss(dest.AsXmmRegister(), Address(ESP, 0)); + } else { + CHECK_EQ(src.AsX87Register(), ST0); + fstpl(Address(ESP, 0)); + movsd(dest.AsXmmRegister(), Address(ESP, 0)); + } + addl(ESP, Immediate(16)); + } else { + // TODO: x87, SSE + UNIMPLEMENTED(FATAL) << ": Move " << dest << ", " << src; + } + } +} + +void X86Assembler::CopyRef(FrameOffset dest, FrameOffset src, + ManagedRegister mscratch) { + X86ManagedRegister scratch = mscratch.AsX86(); + CHECK(scratch.IsCpuRegister()); + movl(scratch.AsCpuRegister(), Address(ESP, src)); + movl(Address(ESP, dest), scratch.AsCpuRegister()); +} + +void X86Assembler::CopyRawPtrFromThread(FrameOffset fr_offs, + ThreadOffset thr_offs, + ManagedRegister mscratch) { + X86ManagedRegister scratch = mscratch.AsX86(); + CHECK(scratch.IsCpuRegister()); + fs()->movl(scratch.AsCpuRegister(), Address::Absolute(thr_offs)); + Store(fr_offs, scratch, 4); +} + +void X86Assembler::CopyRawPtrToThread(ThreadOffset thr_offs, + FrameOffset fr_offs, + ManagedRegister mscratch) { + X86ManagedRegister scratch = mscratch.AsX86(); + CHECK(scratch.IsCpuRegister()); + Load(scratch, fr_offs, 4); + fs()->movl(Address::Absolute(thr_offs), scratch.AsCpuRegister()); +} + +void X86Assembler::Copy(FrameOffset dest, FrameOffset src, + ManagedRegister mscratch, + size_t size) { + X86ManagedRegister scratch = mscratch.AsX86(); + if (scratch.IsCpuRegister() && size == 8) { + Load(scratch, src, 4); + Store(dest, scratch, 4); + Load(scratch, FrameOffset(src.Int32Value() + 4), 4); + Store(FrameOffset(dest.Int32Value() + 4), scratch, 4); + } else { + Load(scratch, src, size); + Store(dest, scratch, size); + } +} + +void X86Assembler::Copy(FrameOffset /*dst*/, ManagedRegister /*src_base*/, Offset /*src_offset*/, + ManagedRegister /*scratch*/, size_t /*size*/) { + UNIMPLEMENTED(FATAL); +} + +void X86Assembler::Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src, + ManagedRegister scratch, size_t size) { + CHECK(scratch.IsNoRegister()); + CHECK_EQ(size, 4u); + pushl(Address(ESP, src)); + popl(Address(dest_base.AsX86().AsCpuRegister(), dest_offset)); +} + +void X86Assembler::Copy(FrameOffset dest, FrameOffset src_base, Offset src_offset, + ManagedRegister mscratch, size_t size) { + Register scratch = mscratch.AsX86().AsCpuRegister(); + CHECK_EQ(size, 4u); + movl(scratch, Address(ESP, src_base)); + movl(scratch, Address(scratch, src_offset)); + movl(Address(ESP, dest), scratch); +} + +void X86Assembler::Copy(ManagedRegister dest, Offset dest_offset, + ManagedRegister src, Offset src_offset, + ManagedRegister scratch, size_t size) { + CHECK_EQ(size, 4u); + CHECK(scratch.IsNoRegister()); + pushl(Address(src.AsX86().AsCpuRegister(), src_offset)); + popl(Address(dest.AsX86().AsCpuRegister(), dest_offset)); +} + +void X86Assembler::Copy(FrameOffset dest, Offset dest_offset, FrameOffset src, Offset src_offset, + ManagedRegister mscratch, size_t size) { + Register scratch = mscratch.AsX86().AsCpuRegister(); + CHECK_EQ(size, 4u); + CHECK_EQ(dest.Int32Value(), src.Int32Value()); + movl(scratch, Address(ESP, src)); + pushl(Address(scratch, src_offset)); + popl(Address(scratch, dest_offset)); +} + +void X86Assembler::MemoryBarrier(ManagedRegister) { +#if ANDROID_SMP != 0 + mfence(); +#endif +} + +void X86Assembler::CreateSirtEntry(ManagedRegister mout_reg, + FrameOffset sirt_offset, + ManagedRegister min_reg, bool null_allowed) { + X86ManagedRegister out_reg = mout_reg.AsX86(); + X86ManagedRegister in_reg = min_reg.AsX86(); + CHECK(in_reg.IsCpuRegister()); + CHECK(out_reg.IsCpuRegister()); + VerifyObject(in_reg, null_allowed); + if (null_allowed) { + Label null_arg; + if (!out_reg.Equals(in_reg)) { + xorl(out_reg.AsCpuRegister(), out_reg.AsCpuRegister()); + } + testl(in_reg.AsCpuRegister(), in_reg.AsCpuRegister()); + j(kZero, &null_arg); + leal(out_reg.AsCpuRegister(), Address(ESP, sirt_offset)); + Bind(&null_arg); + } else { + leal(out_reg.AsCpuRegister(), Address(ESP, sirt_offset)); + } +} + +void X86Assembler::CreateSirtEntry(FrameOffset out_off, + FrameOffset sirt_offset, + ManagedRegister mscratch, + bool null_allowed) { + X86ManagedRegister scratch = mscratch.AsX86(); + CHECK(scratch.IsCpuRegister()); + if (null_allowed) { + Label null_arg; + movl(scratch.AsCpuRegister(), Address(ESP, sirt_offset)); + testl(scratch.AsCpuRegister(), scratch.AsCpuRegister()); + j(kZero, &null_arg); + leal(scratch.AsCpuRegister(), Address(ESP, sirt_offset)); + Bind(&null_arg); + } else { + leal(scratch.AsCpuRegister(), Address(ESP, sirt_offset)); + } + Store(out_off, scratch, 4); +} + +// Given a SIRT entry, load the associated reference. +void X86Assembler::LoadReferenceFromSirt(ManagedRegister mout_reg, + ManagedRegister min_reg) { + X86ManagedRegister out_reg = mout_reg.AsX86(); + X86ManagedRegister in_reg = min_reg.AsX86(); + CHECK(out_reg.IsCpuRegister()); + CHECK(in_reg.IsCpuRegister()); + Label null_arg; + if (!out_reg.Equals(in_reg)) { + xorl(out_reg.AsCpuRegister(), out_reg.AsCpuRegister()); + } + testl(in_reg.AsCpuRegister(), in_reg.AsCpuRegister()); + j(kZero, &null_arg); + movl(out_reg.AsCpuRegister(), Address(in_reg.AsCpuRegister(), 0)); + Bind(&null_arg); +} + +void X86Assembler::VerifyObject(ManagedRegister /*src*/, bool /*could_be_null*/) { + // TODO: not validating references +} + +void X86Assembler::VerifyObject(FrameOffset /*src*/, bool /*could_be_null*/) { + // TODO: not validating references +} + +void X86Assembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister) { + X86ManagedRegister base = mbase.AsX86(); + CHECK(base.IsCpuRegister()); + call(Address(base.AsCpuRegister(), offset.Int32Value())); + // TODO: place reference map on call +} + +void X86Assembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) { + Register scratch = mscratch.AsX86().AsCpuRegister(); + movl(scratch, Address(ESP, base)); + call(Address(scratch, offset)); +} + +void X86Assembler::Call(ThreadOffset offset, ManagedRegister /*mscratch*/) { + fs()->call(Address::Absolute(offset)); +} + +void X86Assembler::GetCurrentThread(ManagedRegister tr) { + fs()->movl(tr.AsX86().AsCpuRegister(), + Address::Absolute(Thread::SelfOffset())); +} + +void X86Assembler::GetCurrentThread(FrameOffset offset, + ManagedRegister mscratch) { + X86ManagedRegister scratch = mscratch.AsX86(); + fs()->movl(scratch.AsCpuRegister(), Address::Absolute(Thread::SelfOffset())); + movl(Address(ESP, offset), scratch.AsCpuRegister()); +} + +void X86Assembler::ExceptionPoll(ManagedRegister /*scratch*/, size_t stack_adjust) { + X86ExceptionSlowPath* slow = new X86ExceptionSlowPath(stack_adjust); + buffer_.EnqueueSlowPath(slow); + fs()->cmpl(Address::Absolute(Thread::ExceptionOffset()), Immediate(0)); + j(kNotEqual, slow->Entry()); +} + +void X86ExceptionSlowPath::Emit(Assembler *sasm) { + X86Assembler* sp_asm = down_cast<X86Assembler*>(sasm); +#define __ sp_asm-> + __ Bind(&entry_); + // Note: the return value is dead + if (stack_adjust_ != 0) { // Fix up the frame. + __ DecreaseFrameSize(stack_adjust_); + } + // Pass exception as argument in EAX + __ fs()->movl(EAX, Address::Absolute(Thread::ExceptionOffset())); + __ fs()->call(Address::Absolute(ENTRYPOINT_OFFSET(pDeliverException))); + // this call should never return + __ int3(); +#undef __ +} + +} // namespace x86 +} // namespace art diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h new file mode 100644 index 0000000..4ba03d1 --- /dev/null +++ b/compiler/utils/x86/assembler_x86.h @@ -0,0 +1,646 @@ +/* + * Copyright (C) 2011 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_X86_ASSEMBLER_X86_H_ +#define ART_COMPILER_UTILS_X86_ASSEMBLER_X86_H_ + +#include <vector> +#include "base/macros.h" +#include "constants_x86.h" +#include "globals.h" +#include "managed_register_x86.h" +#include "offsets.h" +#include "utils/assembler.h" +#include "utils.h" + +namespace art { +namespace x86 { + +class Immediate { + public: + explicit Immediate(int32_t value) : value_(value) {} + + int32_t value() const { return value_; } + + bool is_int8() const { return IsInt(8, value_); } + bool is_uint8() const { return IsUint(8, value_); } + bool is_uint16() const { return IsUint(16, value_); } + + private: + const int32_t value_; + + DISALLOW_COPY_AND_ASSIGN(Immediate); +}; + + +class Operand { + public: + uint8_t mod() const { + return (encoding_at(0) >> 6) & 3; + } + + Register rm() const { + return static_cast<Register>(encoding_at(0) & 7); + } + + ScaleFactor scale() const { + return static_cast<ScaleFactor>((encoding_at(1) >> 6) & 3); + } + + Register index() const { + return static_cast<Register>((encoding_at(1) >> 3) & 7); + } + + Register base() const { + return static_cast<Register>(encoding_at(1) & 7); + } + + int8_t disp8() const { + CHECK_GE(length_, 2); + return static_cast<int8_t>(encoding_[length_ - 1]); + } + + int32_t disp32() const { + CHECK_GE(length_, 5); + int32_t value; + memcpy(&value, &encoding_[length_ - 4], sizeof(value)); + return value; + } + + bool IsRegister(Register reg) const { + return ((encoding_[0] & 0xF8) == 0xC0) // Addressing mode is register only. + && ((encoding_[0] & 0x07) == reg); // Register codes match. + } + + protected: + // Operand can be sub classed (e.g: Address). + Operand() : length_(0) { } + + void SetModRM(int mod, Register rm) { + CHECK_EQ(mod & ~3, 0); + encoding_[0] = (mod << 6) | rm; + length_ = 1; + } + + void SetSIB(ScaleFactor scale, Register index, Register base) { + CHECK_EQ(length_, 1); + CHECK_EQ(scale & ~3, 0); + encoding_[1] = (scale << 6) | (index << 3) | base; + length_ = 2; + } + + void SetDisp8(int8_t disp) { + CHECK(length_ == 1 || length_ == 2); + encoding_[length_++] = static_cast<uint8_t>(disp); + } + + void SetDisp32(int32_t disp) { + CHECK(length_ == 1 || length_ == 2); + int disp_size = sizeof(disp); + memmove(&encoding_[length_], &disp, disp_size); + length_ += disp_size; + } + + private: + byte length_; + byte encoding_[6]; + byte padding_; + + explicit Operand(Register reg) { SetModRM(3, reg); } + + // Get the operand encoding byte at the given index. + uint8_t encoding_at(int index) const { + CHECK_GE(index, 0); + CHECK_LT(index, length_); + return encoding_[index]; + } + + friend class X86Assembler; + + DISALLOW_COPY_AND_ASSIGN(Operand); +}; + + +class Address : public Operand { + public: + Address(Register base, int32_t disp) { + Init(base, disp); + } + + Address(Register base, Offset disp) { + Init(base, disp.Int32Value()); + } + + Address(Register base, FrameOffset disp) { + CHECK_EQ(base, ESP); + Init(ESP, disp.Int32Value()); + } + + Address(Register base, MemberOffset disp) { + Init(base, disp.Int32Value()); + } + + void Init(Register base, int32_t disp) { + if (disp == 0 && base != EBP) { + SetModRM(0, base); + if (base == ESP) SetSIB(TIMES_1, ESP, base); + } else if (disp >= -128 && disp <= 127) { + SetModRM(1, base); + if (base == ESP) SetSIB(TIMES_1, ESP, base); + SetDisp8(disp); + } else { + SetModRM(2, base); + if (base == ESP) SetSIB(TIMES_1, ESP, base); + SetDisp32(disp); + } + } + + + Address(Register index, ScaleFactor scale, int32_t disp) { + CHECK_NE(index, ESP); // Illegal addressing mode. + SetModRM(0, ESP); + SetSIB(scale, index, EBP); + SetDisp32(disp); + } + + Address(Register base, Register index, ScaleFactor scale, int32_t disp) { + CHECK_NE(index, ESP); // Illegal addressing mode. + if (disp == 0 && base != EBP) { + SetModRM(0, ESP); + SetSIB(scale, index, base); + } else if (disp >= -128 && disp <= 127) { + SetModRM(1, ESP); + SetSIB(scale, index, base); + SetDisp8(disp); + } else { + SetModRM(2, ESP); + SetSIB(scale, index, base); + SetDisp32(disp); + } + } + + static Address Absolute(uword addr) { + Address result; + result.SetModRM(0, EBP); + result.SetDisp32(addr); + return result; + } + + static Address Absolute(ThreadOffset addr) { + return Absolute(addr.Int32Value()); + } + + private: + Address() {} + + DISALLOW_COPY_AND_ASSIGN(Address); +}; + + +class X86Assembler : public Assembler { + public: + X86Assembler() {} + virtual ~X86Assembler() {} + + /* + * Emit Machine Instructions. + */ + void call(Register reg); + void call(const Address& address); + void call(Label* label); + + void pushl(Register reg); + void pushl(const Address& address); + void pushl(const Immediate& imm); + + void popl(Register reg); + void popl(const Address& address); + + void movl(Register dst, const Immediate& src); + void movl(Register dst, Register src); + + void movl(Register dst, const Address& src); + void movl(const Address& dst, Register src); + void movl(const Address& dst, const Immediate& imm); + void movl(const Address& dst, Label* lbl); + + void movzxb(Register dst, ByteRegister src); + void movzxb(Register dst, const Address& src); + void movsxb(Register dst, ByteRegister src); + void movsxb(Register dst, const Address& src); + void movb(Register dst, const Address& src); + void movb(const Address& dst, ByteRegister src); + void movb(const Address& dst, const Immediate& imm); + + void movzxw(Register dst, Register src); + void movzxw(Register dst, const Address& src); + void movsxw(Register dst, Register src); + void movsxw(Register dst, const Address& src); + void movw(Register dst, const Address& src); + void movw(const Address& dst, Register src); + + void leal(Register dst, const Address& src); + + void cmovl(Condition condition, Register dst, Register src); + + void setb(Condition condition, Register dst); + + void movss(XmmRegister dst, const Address& src); + void movss(const Address& dst, XmmRegister src); + void movss(XmmRegister dst, XmmRegister src); + + void movd(XmmRegister dst, Register src); + void movd(Register dst, XmmRegister src); + + void addss(XmmRegister dst, XmmRegister src); + void addss(XmmRegister dst, const Address& src); + void subss(XmmRegister dst, XmmRegister src); + void subss(XmmRegister dst, const Address& src); + void mulss(XmmRegister dst, XmmRegister src); + void mulss(XmmRegister dst, const Address& src); + void divss(XmmRegister dst, XmmRegister src); + void divss(XmmRegister dst, const Address& src); + + void movsd(XmmRegister dst, const Address& src); + void movsd(const Address& dst, XmmRegister src); + void movsd(XmmRegister dst, XmmRegister src); + + void addsd(XmmRegister dst, XmmRegister src); + void addsd(XmmRegister dst, const Address& src); + void subsd(XmmRegister dst, XmmRegister src); + void subsd(XmmRegister dst, const Address& src); + void mulsd(XmmRegister dst, XmmRegister src); + void mulsd(XmmRegister dst, const Address& src); + void divsd(XmmRegister dst, XmmRegister src); + void divsd(XmmRegister dst, const Address& src); + + void cvtsi2ss(XmmRegister dst, Register src); + void cvtsi2sd(XmmRegister dst, Register src); + + void cvtss2si(Register dst, XmmRegister src); + void cvtss2sd(XmmRegister dst, XmmRegister src); + + void cvtsd2si(Register dst, XmmRegister src); + void cvtsd2ss(XmmRegister dst, XmmRegister src); + + void cvttss2si(Register dst, XmmRegister src); + void cvttsd2si(Register dst, XmmRegister src); + + void cvtdq2pd(XmmRegister dst, XmmRegister src); + + void comiss(XmmRegister a, XmmRegister b); + void comisd(XmmRegister a, XmmRegister b); + + void sqrtsd(XmmRegister dst, XmmRegister src); + void sqrtss(XmmRegister dst, XmmRegister src); + + void xorpd(XmmRegister dst, const Address& src); + void xorpd(XmmRegister dst, XmmRegister src); + void xorps(XmmRegister dst, const Address& src); + void xorps(XmmRegister dst, XmmRegister src); + + void andpd(XmmRegister dst, const Address& src); + + void flds(const Address& src); + void fstps(const Address& dst); + + void fldl(const Address& src); + void fstpl(const Address& dst); + + void fnstcw(const Address& dst); + void fldcw(const Address& src); + + void fistpl(const Address& dst); + void fistps(const Address& dst); + void fildl(const Address& src); + + void fincstp(); + void ffree(const Immediate& index); + + void fsin(); + void fcos(); + void fptan(); + + void xchgl(Register dst, Register src); + void xchgl(Register reg, const Address& address); + + void cmpl(Register reg, const Immediate& imm); + void cmpl(Register reg0, Register reg1); + void cmpl(Register reg, const Address& address); + + void cmpl(const Address& address, Register reg); + void cmpl(const Address& address, const Immediate& imm); + + void testl(Register reg1, Register reg2); + void testl(Register reg, const Immediate& imm); + + void andl(Register dst, const Immediate& imm); + void andl(Register dst, Register src); + + void orl(Register dst, const Immediate& imm); + void orl(Register dst, Register src); + + void xorl(Register dst, Register src); + + void addl(Register dst, Register src); + void addl(Register reg, const Immediate& imm); + void addl(Register reg, const Address& address); + + void addl(const Address& address, Register reg); + void addl(const Address& address, const Immediate& imm); + + void adcl(Register dst, Register src); + void adcl(Register reg, const Immediate& imm); + void adcl(Register dst, const Address& address); + + void subl(Register dst, Register src); + void subl(Register reg, const Immediate& imm); + void subl(Register reg, const Address& address); + + void cdq(); + + void idivl(Register reg); + + void imull(Register dst, Register src); + void imull(Register reg, const Immediate& imm); + void imull(Register reg, const Address& address); + + void imull(Register reg); + void imull(const Address& address); + + void mull(Register reg); + void mull(const Address& address); + + void sbbl(Register dst, Register src); + void sbbl(Register reg, const Immediate& imm); + void sbbl(Register reg, const Address& address); + + void incl(Register reg); + void incl(const Address& address); + + void decl(Register reg); + void decl(const Address& address); + + void shll(Register reg, const Immediate& imm); + void shll(Register operand, Register shifter); + void shrl(Register reg, const Immediate& imm); + void shrl(Register operand, Register shifter); + void sarl(Register reg, const Immediate& imm); + void sarl(Register operand, Register shifter); + void shld(Register dst, Register src); + + void negl(Register reg); + void notl(Register reg); + + void enter(const Immediate& imm); + void leave(); + + void ret(); + void ret(const Immediate& imm); + + void nop(); + void int3(); + void hlt(); + + void j(Condition condition, Label* label); + + void jmp(Register reg); + void jmp(const Address& address); + void jmp(Label* label); + + X86Assembler* lock(); + void cmpxchgl(const Address& address, Register reg); + + void mfence(); + + X86Assembler* fs(); + + // + // Macros for High-level operations. + // + + void AddImmediate(Register reg, const Immediate& imm); + + void LoadDoubleConstant(XmmRegister dst, double value); + + void DoubleNegate(XmmRegister d); + void FloatNegate(XmmRegister f); + + void DoubleAbs(XmmRegister reg); + + void LockCmpxchgl(const Address& address, Register reg) { + lock()->cmpxchgl(address, reg); + } + + // + // Misc. functionality + // + int PreferredLoopAlignment() { return 16; } + void Align(int alignment, int offset); + void Bind(Label* label); + + // Debugging and bringup support. + void Stop(const char* message); + + // + // Overridden common assembler high-level functionality + // + + // Emit code that will create an activation on the stack + virtual 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 + virtual void RemoveFrame(size_t frame_size, + const std::vector<ManagedRegister>& callee_save_regs); + + virtual void IncreaseFrameSize(size_t adjust); + virtual void DecreaseFrameSize(size_t adjust); + + // Store routines + virtual void Store(FrameOffset offs, ManagedRegister src, size_t size); + virtual void StoreRef(FrameOffset dest, ManagedRegister src); + virtual void StoreRawPtr(FrameOffset dest, ManagedRegister src); + + virtual void StoreImmediateToFrame(FrameOffset dest, uint32_t imm, + ManagedRegister scratch); + + virtual void StoreImmediateToThread(ThreadOffset dest, uint32_t imm, + ManagedRegister scratch); + + virtual void StoreStackOffsetToThread(ThreadOffset thr_offs, + FrameOffset fr_offs, + ManagedRegister scratch); + + virtual void StoreStackPointerToThread(ThreadOffset thr_offs); + + void StoreLabelToThread(ThreadOffset thr_offs, Label* lbl); + + virtual void StoreSpanning(FrameOffset dest, ManagedRegister src, + FrameOffset in_off, ManagedRegister scratch); + + // Load routines + virtual void Load(ManagedRegister dest, FrameOffset src, size_t size); + + virtual void Load(ManagedRegister dest, ThreadOffset src, size_t size); + + virtual void LoadRef(ManagedRegister dest, FrameOffset src); + + virtual void LoadRef(ManagedRegister dest, ManagedRegister base, + MemberOffset offs); + + virtual void LoadRawPtr(ManagedRegister dest, ManagedRegister base, + Offset offs); + + virtual void LoadRawPtrFromThread(ManagedRegister dest, + ThreadOffset offs); + + // Copying routines + virtual void Move(ManagedRegister dest, ManagedRegister src, size_t size); + + virtual void CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset thr_offs, + ManagedRegister scratch); + + virtual void CopyRawPtrToThread(ThreadOffset thr_offs, FrameOffset fr_offs, + ManagedRegister scratch); + + virtual void CopyRef(FrameOffset dest, FrameOffset src, + ManagedRegister scratch); + + virtual void Copy(FrameOffset dest, FrameOffset src, ManagedRegister scratch, size_t size); + + virtual void Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset, + ManagedRegister scratch, size_t size); + + virtual void Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src, + ManagedRegister scratch, size_t size); + + virtual void Copy(FrameOffset dest, FrameOffset src_base, Offset src_offset, + ManagedRegister scratch, size_t size); + + virtual void Copy(ManagedRegister dest, Offset dest_offset, + ManagedRegister src, Offset src_offset, + ManagedRegister scratch, size_t size); + + virtual void Copy(FrameOffset dest, Offset dest_offset, FrameOffset src, Offset src_offset, + ManagedRegister scratch, size_t size); + + virtual void MemoryBarrier(ManagedRegister); + + // Sign extension + virtual void SignExtend(ManagedRegister mreg, size_t size); + + // Zero extension + virtual void ZeroExtend(ManagedRegister mreg, size_t size); + + // Exploit fast access in managed code to Thread::Current() + virtual void GetCurrentThread(ManagedRegister tr); + virtual 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. + virtual 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. + virtual void CreateSirtEntry(FrameOffset out_off, FrameOffset sirt_offset, + ManagedRegister scratch, bool null_allowed); + + // src holds a SIRT entry (Object**) load this into dst + virtual 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. + virtual void VerifyObject(ManagedRegister src, bool could_be_null); + virtual void VerifyObject(FrameOffset src, bool could_be_null); + + // Call to address held at [base+offset] + virtual void Call(ManagedRegister base, Offset offset, + ManagedRegister scratch); + virtual void Call(FrameOffset base, Offset offset, + ManagedRegister scratch); + virtual 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. + virtual void ExceptionPoll(ManagedRegister scratch, size_t stack_adjust); + + private: + inline void EmitUint8(uint8_t value); + inline void EmitInt32(int32_t value); + inline void EmitRegisterOperand(int rm, int reg); + inline void EmitXmmRegisterOperand(int rm, XmmRegister reg); + inline void EmitFixup(AssemblerFixup* fixup); + inline void EmitOperandSizeOverride(); + + void EmitOperand(int rm, const Operand& operand); + void EmitImmediate(const Immediate& imm); + void EmitComplex(int rm, const Operand& operand, const Immediate& immediate); + void EmitLabel(Label* label, int instruction_size); + void EmitLabelLink(Label* label); + void EmitNearLabelLink(Label* label); + + void EmitGenericShift(int rm, Register reg, const Immediate& imm); + void EmitGenericShift(int rm, Register operand, Register shifter); + + DISALLOW_COPY_AND_ASSIGN(X86Assembler); +}; + +inline void X86Assembler::EmitUint8(uint8_t value) { + buffer_.Emit<uint8_t>(value); +} + +inline void X86Assembler::EmitInt32(int32_t value) { + buffer_.Emit<int32_t>(value); +} + +inline void X86Assembler::EmitRegisterOperand(int rm, int reg) { + CHECK_GE(rm, 0); + CHECK_LT(rm, 8); + buffer_.Emit<uint8_t>(0xC0 + (rm << 3) + reg); +} + +inline void X86Assembler::EmitXmmRegisterOperand(int rm, XmmRegister reg) { + EmitRegisterOperand(rm, static_cast<Register>(reg)); +} + +inline void X86Assembler::EmitFixup(AssemblerFixup* fixup) { + buffer_.EmitFixup(fixup); +} + +inline void X86Assembler::EmitOperandSizeOverride() { + EmitUint8(0x66); +} + +// Slowpath entered when Thread::Current()->_exception is non-null +class X86ExceptionSlowPath : public SlowPath { + public: + explicit X86ExceptionSlowPath(size_t stack_adjust) : stack_adjust_(stack_adjust) {} + virtual void Emit(Assembler *sp_asm); + private: + const size_t stack_adjust_; +}; + +} // namespace x86 +} // namespace art + +#endif // ART_COMPILER_UTILS_X86_ASSEMBLER_X86_H_ diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc new file mode 100644 index 0000000..5d8a3b1 --- /dev/null +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2011 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_x86.h" + +#include "gtest/gtest.h" + +namespace art { + +TEST(AssemblerX86, CreateBuffer) { + AssemblerBuffer buffer; + AssemblerBuffer::EnsureCapacity ensured(&buffer); + buffer.Emit<uint8_t>(0x42); + ASSERT_EQ(static_cast<size_t>(1), buffer.Size()); + buffer.Emit<int32_t>(42); + ASSERT_EQ(static_cast<size_t>(5), buffer.Size()); +} + +} // namespace art diff --git a/compiler/utils/x86/constants_x86.h b/compiler/utils/x86/constants_x86.h new file mode 100644 index 0000000..45c3834 --- /dev/null +++ b/compiler/utils/x86/constants_x86.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2011 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_X86_CONSTANTS_X86_H_ +#define ART_COMPILER_UTILS_X86_CONSTANTS_X86_H_ + +#include <iosfwd> + +#include "arch/x86/registers_x86.h" +#include "base/logging.h" +#include "base/macros.h" +#include "globals.h" + +namespace art { +namespace x86 { + +enum ByteRegister { + AL = 0, + CL = 1, + DL = 2, + BL = 3, + AH = 4, + CH = 5, + DH = 6, + BH = 7, + kNoByteRegister = -1 // Signals an illegal register. +}; + + +enum XmmRegister { + XMM0 = 0, + XMM1 = 1, + XMM2 = 2, + XMM3 = 3, + XMM4 = 4, + XMM5 = 5, + XMM6 = 6, + XMM7 = 7, + kNumberOfXmmRegisters = 8, + kNoXmmRegister = -1 // Signals an illegal register. +}; +std::ostream& operator<<(std::ostream& os, const XmmRegister& reg); + +enum X87Register { + ST0 = 0, + ST1 = 1, + ST2 = 2, + ST3 = 3, + ST4 = 4, + ST5 = 5, + ST6 = 6, + ST7 = 7, + kNumberOfX87Registers = 8, + kNoX87Register = -1 // Signals an illegal register. +}; +std::ostream& operator<<(std::ostream& os, const X87Register& reg); + +enum ScaleFactor { + TIMES_1 = 0, + TIMES_2 = 1, + TIMES_4 = 2, + TIMES_8 = 3 +}; + +enum Condition { + kOverflow = 0, + kNoOverflow = 1, + kBelow = 2, + kAboveEqual = 3, + kEqual = 4, + kNotEqual = 5, + kBelowEqual = 6, + kAbove = 7, + kSign = 8, + kNotSign = 9, + kParityEven = 10, + kParityOdd = 11, + kLess = 12, + kGreaterEqual = 13, + kLessEqual = 14, + kGreater = 15, + + kZero = kEqual, + kNotZero = kNotEqual, + kNegative = kSign, + kPositive = kNotSign +}; + + +class Instr { + public: + static const uint8_t kHltInstruction = 0xF4; + // We prefer not to use the int3 instruction since it conflicts with gdb. + static const uint8_t kBreakPointInstruction = kHltInstruction; + + bool IsBreakPoint() { + return (*reinterpret_cast<const uint8_t*>(this)) == kBreakPointInstruction; + } + + // Instructions are read out of a code stream. The only way to get a + // reference to an instruction is to convert a pointer. There is no way + // to allocate or create instances of class Instr. + // Use the At(pc) function to create references to Instr. + static Instr* At(uintptr_t pc) { return reinterpret_cast<Instr*>(pc); } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(Instr); +}; + +} // namespace x86 +} // namespace art + +#endif // ART_COMPILER_UTILS_X86_CONSTANTS_X86_H_ diff --git a/compiler/utils/x86/managed_register_x86.cc b/compiler/utils/x86/managed_register_x86.cc new file mode 100644 index 0000000..4697d06 --- /dev/null +++ b/compiler/utils/x86/managed_register_x86.cc @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2011 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_x86.h" + +#include "globals.h" + +namespace art { +namespace x86 { + +// These cpu registers are never available for allocation. +static const Register kReservedCpuRegistersArray[] = { ESP }; + + +// We reduce the number of available registers for allocation in debug-code +// mode in order to increase register pressure. + +// We need all registers for caching. +static const int kNumberOfAvailableCpuRegisters = kNumberOfCpuRegisters; +static const int kNumberOfAvailableXmmRegisters = kNumberOfXmmRegisters; +static const int kNumberOfAvailableRegisterPairs = kNumberOfRegisterPairs; + + +// Define register pairs. +// This list must be kept in sync with the RegisterPair enum. +#define REGISTER_PAIR_LIST(P) \ + P(EAX, EDX) \ + P(EAX, ECX) \ + P(EAX, EBX) \ + P(EAX, EDI) \ + P(EDX, ECX) \ + P(EDX, EBX) \ + P(EDX, EDI) \ + P(ECX, EBX) \ + P(ECX, EDI) \ + P(EBX, EDI) + + +struct RegisterPairDescriptor { + RegisterPair reg; // Used to verify that the enum is in sync. + Register low; + Register high; +}; + + +static const RegisterPairDescriptor kRegisterPairs[] = { +#define REGISTER_PAIR_ENUMERATION(low, high) { low##_##high, low, high }, + REGISTER_PAIR_LIST(REGISTER_PAIR_ENUMERATION) +#undef REGISTER_PAIR_ENUMERATION +}; + +std::ostream& operator<<(std::ostream& os, const RegisterPair& reg) { + os << X86ManagedRegister::FromRegisterPair(reg); + return os; +} + +bool X86ManagedRegister::Overlaps(const X86ManagedRegister& other) const { + if (IsNoRegister() || other.IsNoRegister()) return false; + CHECK(IsValidManagedRegister()); + CHECK(other.IsValidManagedRegister()); + if (Equals(other)) return true; + if (IsRegisterPair()) { + Register low = AsRegisterPairLow(); + Register high = AsRegisterPairHigh(); + return X86ManagedRegister::FromCpuRegister(low).Overlaps(other) || + X86ManagedRegister::FromCpuRegister(high).Overlaps(other); + } + if (other.IsRegisterPair()) { + return other.Overlaps(*this); + } + return false; +} + + +int X86ManagedRegister::AllocIdLow() const { + CHECK(IsRegisterPair()); + const int r = RegId() - (kNumberOfCpuRegIds + kNumberOfXmmRegIds + + kNumberOfX87RegIds); + CHECK_EQ(r, kRegisterPairs[r].reg); + return kRegisterPairs[r].low; +} + + +int X86ManagedRegister::AllocIdHigh() const { + CHECK(IsRegisterPair()); + const int r = RegId() - (kNumberOfCpuRegIds + kNumberOfXmmRegIds + + kNumberOfX87RegIds); + CHECK_EQ(r, kRegisterPairs[r].reg); + return kRegisterPairs[r].high; +} + + +void X86ManagedRegister::Print(std::ostream& os) const { + if (!IsValidManagedRegister()) { + os << "No Register"; + } else if (IsXmmRegister()) { + os << "XMM: " << static_cast<int>(AsXmmRegister()); + } else if (IsX87Register()) { + os << "X87: " << static_cast<int>(AsX87Register()); + } else if (IsCpuRegister()) { + os << "CPU: " << static_cast<int>(AsCpuRegister()); + } else if (IsRegisterPair()) { + os << "Pair: " << AsRegisterPairLow() << ", " << AsRegisterPairHigh(); + } else { + os << "??: " << RegId(); + } +} + +std::ostream& operator<<(std::ostream& os, const X86ManagedRegister& reg) { + reg.Print(os); + return os; +} + +} // namespace x86 +} // namespace art diff --git a/compiler/utils/x86/managed_register_x86.h b/compiler/utils/x86/managed_register_x86.h new file mode 100644 index 0000000..0201a96 --- /dev/null +++ b/compiler/utils/x86/managed_register_x86.h @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2011 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_X86_MANAGED_REGISTER_X86_H_ +#define ART_COMPILER_UTILS_X86_MANAGED_REGISTER_X86_H_ + +#include "constants_x86.h" +#include "utils/managed_register.h" + +namespace art { +namespace x86 { + +// Values for register pairs. +// The registers in kReservedCpuRegistersArray in x86.cc are not used in pairs. +// The table kRegisterPairs in x86.cc must be kept in sync with this enum. +enum RegisterPair { + EAX_EDX = 0, + EAX_ECX = 1, + EAX_EBX = 2, + EAX_EDI = 3, + EDX_ECX = 4, + EDX_EBX = 5, + EDX_EDI = 6, + ECX_EBX = 7, + ECX_EDI = 8, + EBX_EDI = 9, + kNumberOfRegisterPairs = 10, + kNoRegisterPair = -1, +}; + +std::ostream& operator<<(std::ostream& os, const RegisterPair& reg); + +const int kNumberOfCpuRegIds = kNumberOfCpuRegisters; +const int kNumberOfCpuAllocIds = kNumberOfCpuRegisters; + +const int kNumberOfXmmRegIds = kNumberOfXmmRegisters; +const int kNumberOfXmmAllocIds = kNumberOfXmmRegisters; + +const int kNumberOfX87RegIds = kNumberOfX87Registers; +const int kNumberOfX87AllocIds = kNumberOfX87Registers; + +const int kNumberOfPairRegIds = kNumberOfRegisterPairs; + +const int kNumberOfRegIds = kNumberOfCpuRegIds + kNumberOfXmmRegIds + + kNumberOfX87RegIds + kNumberOfPairRegIds; +const int kNumberOfAllocIds = kNumberOfCpuAllocIds + kNumberOfXmmAllocIds + + kNumberOfX87RegIds; + +// Register ids map: +// [0..R[ cpu registers (enum Register) +// [R..X[ xmm registers (enum XmmRegister) +// [X..S[ x87 registers (enum X87Register) +// [S..P[ register pairs (enum RegisterPair) +// where +// R = kNumberOfCpuRegIds +// X = R + kNumberOfXmmRegIds +// S = X + kNumberOfX87RegIds +// P = X + kNumberOfRegisterPairs + +// Allocation ids map: +// [0..R[ cpu registers (enum Register) +// [R..X[ xmm registers (enum XmmRegister) +// [X..S[ x87 registers (enum X87Register) +// where +// R = kNumberOfCpuRegIds +// X = R + kNumberOfXmmRegIds +// S = X + kNumberOfX87RegIds + + +// An instance of class 'ManagedRegister' represents a single cpu register (enum +// Register), an xmm register (enum XmmRegister), or a pair of cpu registers +// (enum RegisterPair). +// 'ManagedRegister::NoRegister()' provides an invalid register. +// There is a one-to-one mapping between ManagedRegister and register id. +class X86ManagedRegister : public ManagedRegister { + public: + ByteRegister AsByteRegister() const { + CHECK(IsCpuRegister()); + CHECK_LT(AsCpuRegister(), ESP); // ESP, EBP, ESI and EDI cannot be encoded as byte registers. + return static_cast<ByteRegister>(id_); + } + + Register AsCpuRegister() const { + CHECK(IsCpuRegister()); + return static_cast<Register>(id_); + } + + XmmRegister AsXmmRegister() const { + CHECK(IsXmmRegister()); + return static_cast<XmmRegister>(id_ - kNumberOfCpuRegIds); + } + + X87Register AsX87Register() const { + CHECK(IsX87Register()); + return static_cast<X87Register>(id_ - + (kNumberOfCpuRegIds + kNumberOfXmmRegIds)); + } + + Register AsRegisterPairLow() const { + CHECK(IsRegisterPair()); + // Appropriate mapping of register ids allows to use AllocIdLow(). + return FromRegId(AllocIdLow()).AsCpuRegister(); + } + + Register AsRegisterPairHigh() const { + CHECK(IsRegisterPair()); + // Appropriate mapping of register ids allows to use AllocIdHigh(). + return FromRegId(AllocIdHigh()).AsCpuRegister(); + } + + bool IsCpuRegister() const { + CHECK(IsValidManagedRegister()); + return (0 <= id_) && (id_ < kNumberOfCpuRegIds); + } + + bool IsXmmRegister() const { + CHECK(IsValidManagedRegister()); + const int test = id_ - kNumberOfCpuRegIds; + return (0 <= test) && (test < kNumberOfXmmRegIds); + } + + bool IsX87Register() const { + CHECK(IsValidManagedRegister()); + const int test = id_ - (kNumberOfCpuRegIds + kNumberOfXmmRegIds); + return (0 <= test) && (test < kNumberOfX87RegIds); + } + + bool IsRegisterPair() const { + CHECK(IsValidManagedRegister()); + const int test = id_ - + (kNumberOfCpuRegIds + kNumberOfXmmRegIds + kNumberOfX87RegIds); + return (0 <= test) && (test < kNumberOfPairRegIds); + } + + void Print(std::ostream& os) const; + + // 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 X86ManagedRegister& other) const; + + static X86ManagedRegister FromCpuRegister(Register r) { + CHECK_NE(r, kNoRegister); + return FromRegId(r); + } + + static X86ManagedRegister FromXmmRegister(XmmRegister r) { + CHECK_NE(r, kNoXmmRegister); + return FromRegId(r + kNumberOfCpuRegIds); + } + + static X86ManagedRegister FromX87Register(X87Register r) { + CHECK_NE(r, kNoX87Register); + return FromRegId(r + kNumberOfCpuRegIds + kNumberOfXmmRegIds); + } + + static X86ManagedRegister FromRegisterPair(RegisterPair r) { + CHECK_NE(r, kNoRegisterPair); + return FromRegId(r + (kNumberOfCpuRegIds + kNumberOfXmmRegIds + + kNumberOfX87RegIds)); + } + + private: + bool IsValidManagedRegister() const { + return (0 <= id_) && (id_ < kNumberOfRegIds); + } + + int RegId() const { + CHECK(!IsNoRegister()); + return id_; + } + + int AllocId() const { + CHECK(IsValidManagedRegister() && !IsRegisterPair()); + CHECK_LT(id_, kNumberOfAllocIds); + return id_; + } + + int AllocIdLow() const; + int AllocIdHigh() const; + + friend class ManagedRegister; + + explicit X86ManagedRegister(int reg_id) : ManagedRegister(reg_id) {} + + static X86ManagedRegister FromRegId(int reg_id) { + X86ManagedRegister reg(reg_id); + CHECK(reg.IsValidManagedRegister()); + return reg; + } +}; + +std::ostream& operator<<(std::ostream& os, const X86ManagedRegister& reg); + +} // namespace x86 + +inline x86::X86ManagedRegister ManagedRegister::AsX86() const { + x86::X86ManagedRegister reg(id_); + CHECK(reg.IsNoRegister() || reg.IsValidManagedRegister()); + return reg; +} + +} // namespace art + +#endif // ART_COMPILER_UTILS_X86_MANAGED_REGISTER_X86_H_ diff --git a/compiler/utils/x86/managed_register_x86_test.cc b/compiler/utils/x86/managed_register_x86_test.cc new file mode 100644 index 0000000..4fbafda --- /dev/null +++ b/compiler/utils/x86/managed_register_x86_test.cc @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2011 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_x86.h" +#include "gtest/gtest.h" + +namespace art { +namespace x86 { + +TEST(X86ManagedRegister, NoRegister) { + X86ManagedRegister reg = ManagedRegister::NoRegister().AsX86(); + EXPECT_TRUE(reg.IsNoRegister()); + EXPECT_TRUE(!reg.Overlaps(reg)); +} + +TEST(X86ManagedRegister, CpuRegister) { + X86ManagedRegister reg = X86ManagedRegister::FromCpuRegister(EAX); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(reg.IsCpuRegister()); + EXPECT_TRUE(!reg.IsXmmRegister()); + EXPECT_TRUE(!reg.IsX87Register()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(EAX, reg.AsCpuRegister()); + + reg = X86ManagedRegister::FromCpuRegister(EBX); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(reg.IsCpuRegister()); + EXPECT_TRUE(!reg.IsXmmRegister()); + EXPECT_TRUE(!reg.IsX87Register()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(EBX, reg.AsCpuRegister()); + + reg = X86ManagedRegister::FromCpuRegister(ECX); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(reg.IsCpuRegister()); + EXPECT_TRUE(!reg.IsXmmRegister()); + EXPECT_TRUE(!reg.IsX87Register()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(ECX, reg.AsCpuRegister()); + + reg = X86ManagedRegister::FromCpuRegister(EDI); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(reg.IsCpuRegister()); + EXPECT_TRUE(!reg.IsXmmRegister()); + EXPECT_TRUE(!reg.IsX87Register()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(EDI, reg.AsCpuRegister()); +} + +TEST(X86ManagedRegister, XmmRegister) { + X86ManagedRegister reg = X86ManagedRegister::FromXmmRegister(XMM0); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCpuRegister()); + EXPECT_TRUE(reg.IsXmmRegister()); + EXPECT_TRUE(!reg.IsX87Register()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(XMM0, reg.AsXmmRegister()); + + reg = X86ManagedRegister::FromXmmRegister(XMM1); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCpuRegister()); + EXPECT_TRUE(reg.IsXmmRegister()); + EXPECT_TRUE(!reg.IsX87Register()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(XMM1, reg.AsXmmRegister()); + + reg = X86ManagedRegister::FromXmmRegister(XMM7); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCpuRegister()); + EXPECT_TRUE(reg.IsXmmRegister()); + EXPECT_TRUE(!reg.IsX87Register()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(XMM7, reg.AsXmmRegister()); +} + +TEST(X86ManagedRegister, X87Register) { + X86ManagedRegister reg = X86ManagedRegister::FromX87Register(ST0); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCpuRegister()); + EXPECT_TRUE(!reg.IsXmmRegister()); + EXPECT_TRUE(reg.IsX87Register()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(ST0, reg.AsX87Register()); + + reg = X86ManagedRegister::FromX87Register(ST1); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCpuRegister()); + EXPECT_TRUE(!reg.IsXmmRegister()); + EXPECT_TRUE(reg.IsX87Register()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(ST1, reg.AsX87Register()); + + reg = X86ManagedRegister::FromX87Register(ST7); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCpuRegister()); + EXPECT_TRUE(!reg.IsXmmRegister()); + EXPECT_TRUE(reg.IsX87Register()); + EXPECT_TRUE(!reg.IsRegisterPair()); + EXPECT_EQ(ST7, reg.AsX87Register()); +} + +TEST(X86ManagedRegister, RegisterPair) { + X86ManagedRegister reg = X86ManagedRegister::FromRegisterPair(EAX_EDX); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCpuRegister()); + EXPECT_TRUE(!reg.IsXmmRegister()); + EXPECT_TRUE(!reg.IsX87Register()); + EXPECT_TRUE(reg.IsRegisterPair()); + EXPECT_EQ(EAX, reg.AsRegisterPairLow()); + EXPECT_EQ(EDX, reg.AsRegisterPairHigh()); + + reg = X86ManagedRegister::FromRegisterPair(EAX_ECX); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCpuRegister()); + EXPECT_TRUE(!reg.IsXmmRegister()); + EXPECT_TRUE(!reg.IsX87Register()); + EXPECT_TRUE(reg.IsRegisterPair()); + EXPECT_EQ(EAX, reg.AsRegisterPairLow()); + EXPECT_EQ(ECX, reg.AsRegisterPairHigh()); + + reg = X86ManagedRegister::FromRegisterPair(EAX_EBX); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCpuRegister()); + EXPECT_TRUE(!reg.IsXmmRegister()); + EXPECT_TRUE(!reg.IsX87Register()); + EXPECT_TRUE(reg.IsRegisterPair()); + EXPECT_EQ(EAX, reg.AsRegisterPairLow()); + EXPECT_EQ(EBX, reg.AsRegisterPairHigh()); + + reg = X86ManagedRegister::FromRegisterPair(EAX_EDI); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCpuRegister()); + EXPECT_TRUE(!reg.IsXmmRegister()); + EXPECT_TRUE(!reg.IsX87Register()); + EXPECT_TRUE(reg.IsRegisterPair()); + EXPECT_EQ(EAX, reg.AsRegisterPairLow()); + EXPECT_EQ(EDI, reg.AsRegisterPairHigh()); + + reg = X86ManagedRegister::FromRegisterPair(EDX_ECX); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCpuRegister()); + EXPECT_TRUE(!reg.IsXmmRegister()); + EXPECT_TRUE(!reg.IsX87Register()); + EXPECT_TRUE(reg.IsRegisterPair()); + EXPECT_EQ(EDX, reg.AsRegisterPairLow()); + EXPECT_EQ(ECX, reg.AsRegisterPairHigh()); + + reg = X86ManagedRegister::FromRegisterPair(EDX_EBX); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCpuRegister()); + EXPECT_TRUE(!reg.IsXmmRegister()); + EXPECT_TRUE(!reg.IsX87Register()); + EXPECT_TRUE(reg.IsRegisterPair()); + EXPECT_EQ(EDX, reg.AsRegisterPairLow()); + EXPECT_EQ(EBX, reg.AsRegisterPairHigh()); + + reg = X86ManagedRegister::FromRegisterPair(EDX_EDI); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCpuRegister()); + EXPECT_TRUE(!reg.IsXmmRegister()); + EXPECT_TRUE(!reg.IsX87Register()); + EXPECT_TRUE(reg.IsRegisterPair()); + EXPECT_EQ(EDX, reg.AsRegisterPairLow()); + EXPECT_EQ(EDI, reg.AsRegisterPairHigh()); + + reg = X86ManagedRegister::FromRegisterPair(ECX_EBX); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCpuRegister()); + EXPECT_TRUE(!reg.IsXmmRegister()); + EXPECT_TRUE(!reg.IsX87Register()); + EXPECT_TRUE(reg.IsRegisterPair()); + EXPECT_EQ(ECX, reg.AsRegisterPairLow()); + EXPECT_EQ(EBX, reg.AsRegisterPairHigh()); + + reg = X86ManagedRegister::FromRegisterPair(ECX_EDI); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCpuRegister()); + EXPECT_TRUE(!reg.IsXmmRegister()); + EXPECT_TRUE(!reg.IsX87Register()); + EXPECT_TRUE(reg.IsRegisterPair()); + EXPECT_EQ(ECX, reg.AsRegisterPairLow()); + EXPECT_EQ(EDI, reg.AsRegisterPairHigh()); + + reg = X86ManagedRegister::FromRegisterPair(EBX_EDI); + EXPECT_TRUE(!reg.IsNoRegister()); + EXPECT_TRUE(!reg.IsCpuRegister()); + EXPECT_TRUE(!reg.IsXmmRegister()); + EXPECT_TRUE(!reg.IsX87Register()); + EXPECT_TRUE(reg.IsRegisterPair()); + EXPECT_EQ(EBX, reg.AsRegisterPairLow()); + EXPECT_EQ(EDI, reg.AsRegisterPairHigh()); +} + +TEST(X86ManagedRegister, Equals) { + X86ManagedRegister reg_eax = X86ManagedRegister::FromCpuRegister(EAX); + EXPECT_TRUE(reg_eax.Equals(X86ManagedRegister::FromCpuRegister(EAX))); + EXPECT_TRUE(!reg_eax.Equals(X86ManagedRegister::FromCpuRegister(EBX))); + EXPECT_TRUE(!reg_eax.Equals(X86ManagedRegister::FromCpuRegister(EDI))); + EXPECT_TRUE(!reg_eax.Equals(X86ManagedRegister::FromXmmRegister(XMM0))); + EXPECT_TRUE(!reg_eax.Equals(X86ManagedRegister::FromXmmRegister(XMM7))); + EXPECT_TRUE(!reg_eax.Equals(X86ManagedRegister::FromX87Register(ST0))); + EXPECT_TRUE(!reg_eax.Equals(X86ManagedRegister::FromX87Register(ST7))); + EXPECT_TRUE(!reg_eax.Equals(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + EXPECT_TRUE(!reg_eax.Equals(X86ManagedRegister::FromRegisterPair(EBX_EDI))); + + X86ManagedRegister reg_xmm0 = X86ManagedRegister::FromXmmRegister(XMM0); + EXPECT_TRUE(!reg_xmm0.Equals(X86ManagedRegister::FromCpuRegister(EAX))); + EXPECT_TRUE(!reg_xmm0.Equals(X86ManagedRegister::FromCpuRegister(EBX))); + EXPECT_TRUE(!reg_xmm0.Equals(X86ManagedRegister::FromCpuRegister(EDI))); + EXPECT_TRUE(reg_xmm0.Equals(X86ManagedRegister::FromXmmRegister(XMM0))); + EXPECT_TRUE(!reg_xmm0.Equals(X86ManagedRegister::FromXmmRegister(XMM7))); + EXPECT_TRUE(!reg_xmm0.Equals(X86ManagedRegister::FromX87Register(ST0))); + EXPECT_TRUE(!reg_xmm0.Equals(X86ManagedRegister::FromX87Register(ST7))); + EXPECT_TRUE(!reg_xmm0.Equals(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + EXPECT_TRUE(!reg_xmm0.Equals(X86ManagedRegister::FromRegisterPair(EBX_EDI))); + + X86ManagedRegister reg_st0 = X86ManagedRegister::FromX87Register(ST0); + EXPECT_TRUE(!reg_st0.Equals(X86ManagedRegister::FromCpuRegister(EAX))); + EXPECT_TRUE(!reg_st0.Equals(X86ManagedRegister::FromCpuRegister(EBX))); + EXPECT_TRUE(!reg_st0.Equals(X86ManagedRegister::FromCpuRegister(EDI))); + EXPECT_TRUE(!reg_st0.Equals(X86ManagedRegister::FromXmmRegister(XMM0))); + EXPECT_TRUE(!reg_st0.Equals(X86ManagedRegister::FromXmmRegister(XMM7))); + EXPECT_TRUE(reg_st0.Equals(X86ManagedRegister::FromX87Register(ST0))); + EXPECT_TRUE(!reg_st0.Equals(X86ManagedRegister::FromX87Register(ST7))); + EXPECT_TRUE(!reg_st0.Equals(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + EXPECT_TRUE(!reg_st0.Equals(X86ManagedRegister::FromRegisterPair(EBX_EDI))); + + X86ManagedRegister reg_pair = X86ManagedRegister::FromRegisterPair(EAX_EDX); + EXPECT_TRUE(!reg_pair.Equals(X86ManagedRegister::FromCpuRegister(EAX))); + EXPECT_TRUE(!reg_pair.Equals(X86ManagedRegister::FromCpuRegister(EBX))); + EXPECT_TRUE(!reg_pair.Equals(X86ManagedRegister::FromCpuRegister(EDI))); + EXPECT_TRUE(!reg_pair.Equals(X86ManagedRegister::FromXmmRegister(XMM0))); + EXPECT_TRUE(!reg_pair.Equals(X86ManagedRegister::FromXmmRegister(XMM7))); + EXPECT_TRUE(!reg_pair.Equals(X86ManagedRegister::FromX87Register(ST0))); + EXPECT_TRUE(!reg_pair.Equals(X86ManagedRegister::FromX87Register(ST7))); + EXPECT_TRUE(reg_pair.Equals(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + EXPECT_TRUE(!reg_pair.Equals(X86ManagedRegister::FromRegisterPair(EBX_EDI))); +} + +TEST(X86ManagedRegister, Overlaps) { + X86ManagedRegister reg = X86ManagedRegister::FromCpuRegister(EAX); + EXPECT_TRUE(reg.Overlaps(X86ManagedRegister::FromCpuRegister(EAX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EBX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EDI))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromXmmRegister(XMM0))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromXmmRegister(XMM7))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromX87Register(ST0))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromX87Register(ST7))); + EXPECT_TRUE(reg.Overlaps(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromRegisterPair(EBX_EDI))); + + reg = X86ManagedRegister::FromCpuRegister(EDX); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EAX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EBX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EDI))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromXmmRegister(XMM0))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromXmmRegister(XMM7))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromX87Register(ST0))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromX87Register(ST7))); + EXPECT_TRUE(reg.Overlaps(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromRegisterPair(EBX_EDI))); + + reg = X86ManagedRegister::FromCpuRegister(EDI); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EAX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EBX))); + EXPECT_TRUE(reg.Overlaps(X86ManagedRegister::FromCpuRegister(EDI))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromXmmRegister(XMM0))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromXmmRegister(XMM7))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromX87Register(ST0))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromX87Register(ST7))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + EXPECT_TRUE(reg.Overlaps(X86ManagedRegister::FromRegisterPair(EBX_EDI))); + + reg = X86ManagedRegister::FromCpuRegister(EBX); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EAX))); + EXPECT_TRUE(reg.Overlaps(X86ManagedRegister::FromCpuRegister(EBX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EDI))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromXmmRegister(XMM0))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromXmmRegister(XMM7))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromX87Register(ST0))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromX87Register(ST7))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + EXPECT_TRUE(reg.Overlaps(X86ManagedRegister::FromRegisterPair(EBX_EDI))); + + reg = X86ManagedRegister::FromXmmRegister(XMM0); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EAX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EBX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EDI))); + EXPECT_TRUE(reg.Overlaps(X86ManagedRegister::FromXmmRegister(XMM0))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromXmmRegister(XMM7))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromX87Register(ST0))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromX87Register(ST7))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromRegisterPair(EBX_EDI))); + + reg = X86ManagedRegister::FromX87Register(ST0); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EAX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EBX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EDI))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromXmmRegister(XMM0))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromXmmRegister(XMM7))); + EXPECT_TRUE(reg.Overlaps(X86ManagedRegister::FromX87Register(ST0))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromX87Register(ST7))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromRegisterPair(EBX_EDI))); + + reg = X86ManagedRegister::FromRegisterPair(EAX_EDX); + EXPECT_TRUE(reg.Overlaps(X86ManagedRegister::FromCpuRegister(EAX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EBX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EDI))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromXmmRegister(XMM0))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromXmmRegister(XMM7))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromX87Register(ST0))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromX87Register(ST7))); + EXPECT_TRUE(reg.Overlaps(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + EXPECT_TRUE(reg.Overlaps(X86ManagedRegister::FromRegisterPair(EDX_ECX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromRegisterPair(EBX_EDI))); + + reg = X86ManagedRegister::FromRegisterPair(EBX_EDI); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EAX))); + EXPECT_TRUE(reg.Overlaps(X86ManagedRegister::FromCpuRegister(EBX))); + EXPECT_TRUE(reg.Overlaps(X86ManagedRegister::FromCpuRegister(EDI))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromXmmRegister(XMM0))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromXmmRegister(XMM7))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromX87Register(ST0))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromX87Register(ST7))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + EXPECT_TRUE(reg.Overlaps(X86ManagedRegister::FromRegisterPair(EBX_EDI))); + EXPECT_TRUE(reg.Overlaps(X86ManagedRegister::FromRegisterPair(EDX_EBX))); + + reg = X86ManagedRegister::FromRegisterPair(EDX_ECX); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EAX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EBX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromCpuRegister(EDI))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromXmmRegister(XMM0))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromXmmRegister(XMM7))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromX87Register(ST0))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromX87Register(ST7))); + EXPECT_TRUE(reg.Overlaps(X86ManagedRegister::FromRegisterPair(EAX_EDX))); + EXPECT_TRUE(!reg.Overlaps(X86ManagedRegister::FromRegisterPair(EBX_EDI))); + EXPECT_TRUE(reg.Overlaps(X86ManagedRegister::FromRegisterPair(EDX_EBX))); +} + +} // namespace x86 +} // namespace art |