diff options
144 files changed, 7131 insertions, 7254 deletions
diff --git a/Android.mk b/Android.mk index 216e86585f..93603556bc 100644 --- a/Android.mk +++ b/Android.mk @@ -42,27 +42,7 @@ clean-oat: clean-oat-host clean-oat-target .PHONY: clean-oat-host clean-oat-host: - rm -f $(HOST_CORE_IMG_OUTS) - rm -f $(HOST_CORE_OAT_OUTS) - rm -f $(HOST_OUT_JAVA_LIBRARIES)/$(ART_HOST_ARCH)/*.odex -ifneq ($(HOST_PREFER_32_BIT),true) - rm -f $(HOST_OUT_JAVA_LIBRARIES)/$(2ND_ART_HOST_ARCH)/*.odex -endif - rm -f $(TARGET_CORE_IMG_OUTS) - rm -f $(TARGET_CORE_OAT_OUTS) - rm -rf $(DEXPREOPT_PRODUCT_DIR_FULL_PATH) - rm -f $(TARGET_OUT_UNSTRIPPED)/system/framework/*.odex - rm -f $(TARGET_OUT_UNSTRIPPED)/system/framework/*/*.oat - rm -f $(TARGET_OUT_UNSTRIPPED)/system/framework/*/*.art - rm -f $(TARGET_OUT)/framework/*/*.oat - rm -f $(TARGET_OUT)/framework/*/*.art - rm -f $(TARGET_OUT_APPS)/*.odex - rm -f $(TARGET_OUT_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/javalib.odex - rm -f $(TARGET_OUT_INTERMEDIATES)/APPS/*_intermediates/*.odex -ifdef TARGET_2ND_ARCH - rm -f $(2ND_TARGET_OUT_INTERMEDIATES)/JAVA_LIBRARIES/*_intermediates/javalib.odex - rm -f $(2ND_TARGET_OUT_INTERMEDIATES)/APPS/*_intermediates/*.odex -endif + find $(OUT_DIR) -name "*.oat" -o -name "*.odex" -o -name "*.art" | xargs rm -f ifneq ($(TMPDIR),) rm -rf $(TMPDIR)/$(USER)/test-*/dalvik-cache/* rm -rf $(TMPDIR)/android-data/dalvik-cache/* diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 91998fa69c..c5669c05e2 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -60,7 +60,7 @@ $(ART_TEST_TARGET_GTEST_MainStripped_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX) $(call dexpreopt-remove-classes.dex,$@) # Dex file dependencies for each gtest. -ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MyClass Nested Statics StaticsFromCode +ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MultiDex MyClass Nested Statics StaticsFromCode ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle diff --git a/compiler/Android.mk b/compiler/Android.mk index 6b0e6ff121..904f117a5a 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -48,12 +48,6 @@ LIBART_COMPILER_SRC_FILES := \ dex/quick/mips/int_mips.cc \ dex/quick/mips/target_mips.cc \ dex/quick/mips/utility_mips.cc \ - dex/quick/mips64/assemble_mips64.cc \ - dex/quick/mips64/call_mips64.cc \ - dex/quick/mips64/fp_mips64.cc \ - dex/quick/mips64/int_mips64.cc \ - dex/quick/mips64/target_mips64.cc \ - dex/quick/mips64/utility_mips64.cc \ dex/quick/mir_to_lir.cc \ dex/quick/quick_compiler.cc \ dex/quick/ralloc_util.cc \ @@ -112,6 +106,7 @@ LIBART_COMPILER_SRC_FILES := \ optimizing/intrinsics.cc \ optimizing/intrinsics_arm.cc \ optimizing/intrinsics_arm64.cc \ + optimizing/intrinsics_x86.cc \ optimizing/intrinsics_x86_64.cc \ optimizing/licm.cc \ optimizing/locations.cc \ @@ -162,7 +157,6 @@ LIBART_COMPILER_ENUM_OPERATOR_OUT_HEADER_FILES := \ dex/quick/arm/arm_lir.h \ dex/quick/arm64/arm64_lir.h \ dex/quick/mips/mips_lir.h \ - dex/quick/mips64/mips64_lir.h \ dex/quick/resource_mask.h \ dex/compiler_enums.h \ dex/global_value_numbering.h \ diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index 09be4372a8..1d0aad5425 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -178,8 +178,8 @@ void CommonCompilerTest::SetUpRuntimeOptions(RuntimeOptions* options) { verification_results_.reset(new VerificationResults(compiler_options_.get())); method_inliner_map_.reset(new DexFileToMethodInlinerMap); callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(), - method_inliner_map_.get())); - options->push_back(std::make_pair("compilercallbacks", callbacks_.get())); + method_inliner_map_.get(), + CompilerCallbacks::CallbackMode::kCompileApp)); } void CommonCompilerTest::TearDown() { diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index 9cffbc86f3..d7b210d571 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -78,7 +78,6 @@ class CommonCompilerTest : public CommonRuntimeTest { std::unique_ptr<CompilerOptions> compiler_options_; std::unique_ptr<VerificationResults> verification_results_; std::unique_ptr<DexFileToMethodInlinerMap> method_inliner_map_; - std::unique_ptr<CompilerCallbacks> callbacks_; std::unique_ptr<CompilerDriver> compiler_driver_; std::unique_ptr<CumulativeLogger> timer_; std::unique_ptr<const InstructionSetFeatures> instruction_set_features_; diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index bd479bef5b..df72830801 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -1378,11 +1378,16 @@ void Mir2Lir::InitReferenceVRegs(BasicBlock* bb, BitVector* references) { // In Mir2Lir::MethodBlockCodeGen() we have artificially moved the throwing // instruction to the previous block. However, the MIRGraph data used above // doesn't reflect that, so we still need to process that MIR insn here. - DCHECK_EQ(bb->predecessors.size(), 1u); - BasicBlock* pred_bb = mir_graph_->GetBasicBlock(bb->predecessors[0]); - DCHECK(pred_bb != nullptr); - DCHECK(pred_bb->last_mir_insn != nullptr); - UpdateReferenceVRegsLocal(nullptr, pred_bb->last_mir_insn, references); + MIR* mir = nullptr; + BasicBlock* pred_bb = bb; + // Traverse empty blocks. + while (mir == nullptr && pred_bb->predecessors.size() == 1u) { + pred_bb = mir_graph_->GetBasicBlock(bb->predecessors[0]); + DCHECK(pred_bb != nullptr); + mir = pred_bb->last_mir_insn; + } + DCHECK(mir != nullptr); + UpdateReferenceVRegsLocal(nullptr, mir, references); } } diff --git a/compiler/dex/quick/mips/assemble_mips.cc b/compiler/dex/quick/mips/assemble_mips.cc index ed72d676b7..936ff42c8c 100644 --- a/compiler/dex/quick/mips/assemble_mips.cc +++ b/compiler/dex/quick/mips/assemble_mips.cc @@ -77,7 +77,7 @@ namespace art { * * [!] escape. To insert "!", use "!!" */ -/* NOTE: must be kept in sync with enum MipsOpcode from LIR.h */ +/* NOTE: must be kept in sync with enum MipsOpcode from mips_lir.h */ /* * TUNING: We're currently punting on the branch delay slots. All branch * instructions in this map are given a size of 8, which during assembly @@ -85,6 +85,7 @@ namespace art { * an assembler pass to fill those slots when possible. */ const MipsEncodingMap MipsMir2Lir::EncodingMap[kMipsLast] = { + // The following are common mips32r2, mips32r6 and mips64r6 instructions. ENCODING_MAP(kMips32BitData, 0x00000000, kFmtBitBlt, 31, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP, @@ -117,7 +118,7 @@ const MipsEncodingMap MipsMir2Lir::EncodingMap[kMipsLast] = { kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_USE01 | NEEDS_FIXUP, "beq", "!0r,!1r,!2t!0N", 8), - ENCODING_MAP(kMipsBeqz, 0x10000000, /* same as beq above with t = $zero */ + ENCODING_MAP(kMipsBeqz, 0x10000000, // Same as beq above with t = $zero. kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 | NEEDS_FIXUP, "beqz", "!0r,!1t!0N", 8), @@ -137,7 +138,7 @@ const MipsEncodingMap MipsMir2Lir::EncodingMap[kMipsLast] = { kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 | NEEDS_FIXUP, "bltz", "!0r,!1t!0N", 8), - ENCODING_MAP(kMipsBnez, 0x14000000, /* same as bne below with t = $zero */ + ENCODING_MAP(kMipsBnez, 0x14000000, // Same as bne below with t = $zero. kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 | NEEDS_FIXUP, "bnez", "!0r,!1t!0N", 8), @@ -145,14 +146,98 @@ const MipsEncodingMap MipsMir2Lir::EncodingMap[kMipsLast] = { kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_USE01 | NEEDS_FIXUP, "bne", "!0r,!1r,!2t!0N", 8), - ENCODING_MAP(kMipsDiv, 0x0000001a, - kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF_HI | REG_DEF_LO | REG_USE01, - "div", "!0r,!1r", 4), ENCODING_MAP(kMipsExt, 0x7c000000, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 10, 6, kFmtBitBlt, 15, 11, IS_QUAD_OP | REG_DEF0 | REG_USE1, "ext", "!0r,!1r,!2d,!3D", 4), + ENCODING_MAP(kMipsFaddd, 0x46200000, + kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "add.d", "!0S,!1S,!2S", 4), + ENCODING_MAP(kMipsFadds, 0x46000000, + kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "add.s", "!0s,!1s,!2s", 4), + ENCODING_MAP(kMipsFsubd, 0x46200001, + kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "sub.d", "!0S,!1S,!2S", 4), + ENCODING_MAP(kMipsFsubs, 0x46000001, + kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "sub.s", "!0s,!1s,!2s", 4), + ENCODING_MAP(kMipsFdivd, 0x46200003, + kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "div.d", "!0S,!1S,!2S", 4), + ENCODING_MAP(kMipsFdivs, 0x46000003, + kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "div.s", "!0s,!1s,!2s", 4), + ENCODING_MAP(kMipsFmuld, 0x46200002, + kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "mul.d", "!0S,!1S,!2S", 4), + ENCODING_MAP(kMipsFmuls, 0x46000002, + kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "mul.s", "!0s,!1s,!2s", 4), + ENCODING_MAP(kMipsFcvtsd, 0x46200020, + kFmtSfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "cvt.s.d", "!0s,!1S", 4), + ENCODING_MAP(kMipsFcvtsw, 0x46800020, + kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "cvt.s.w", "!0s,!1s", 4), + ENCODING_MAP(kMipsFcvtds, 0x46000021, + kFmtDfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "cvt.d.s", "!0S,!1s", 4), + ENCODING_MAP(kMipsFcvtdw, 0x46800021, + kFmtDfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "cvt.d.w", "!0S,!1s", 4), + ENCODING_MAP(kMipsFcvtwd, 0x46200024, + kFmtSfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "cvt.w.d", "!0s,!1S", 4), + ENCODING_MAP(kMipsFcvtws, 0x46000024, + kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "cvt.w.s", "!0s,!1s", 4), + ENCODING_MAP(kMipsFmovd, 0x46200006, + kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "mov.d", "!0S,!1S", 4), + ENCODING_MAP(kMipsFmovs, 0x46000006, + kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "mov.s", "!0s,!1s", 4), + ENCODING_MAP(kMipsFnegd, 0x46200007, + kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "neg.d", "!0S,!1S", 4), + ENCODING_MAP(kMipsFnegs, 0x46000007, + kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "neg.s", "!0s,!1s", 4), + ENCODING_MAP(kMipsFldc1, 0xd4000000, + kFmtDfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, + "ldc1", "!0S,!1d(!2r)", 4), + ENCODING_MAP(kMipsFlwc1, 0xc4000000, + kFmtSfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, + "lwc1", "!0s,!1d(!2r)", 4), + ENCODING_MAP(kMipsFsdc1, 0xf4000000, + kFmtDfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE, + "sdc1", "!0S,!1d(!2r)", 4), + ENCODING_MAP(kMipsFswc1, 0xe4000000, + kFmtSfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE, + "swc1", "!0s,!1d(!2r)", 4), ENCODING_MAP(kMipsJal, 0x0c000000, kFmtBitBlt, 25, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR, @@ -197,31 +282,31 @@ const MipsEncodingMap MipsMir2Lir::EncodingMap[kMipsLast] = { kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, "lw", "!0r,!1d(!2r)", 4), - ENCODING_MAP(kMipsMfhi, 0x00000010, - kFmtBitBlt, 15, 11, kFmtUnused, -1, -1, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF0 | REG_USE_HI, - "mfhi", "!0r", 4), - ENCODING_MAP(kMipsMflo, 0x00000012, - kFmtBitBlt, 15, 11, kFmtUnused, -1, -1, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF0 | REG_USE_LO, - "mflo", "!0r", 4), - ENCODING_MAP(kMipsMove, 0x00000025, /* or using zero reg */ + ENCODING_MAP(kMipsMove, 0x00000025, // Or using zero reg. kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, "move", "!0r,!1r", 4), - ENCODING_MAP(kMipsMovz, 0x0000000a, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "movz", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMipsMul, 0x70000002, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "mul", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMipsMfc1, 0x44000000, + kFmtBitBlt, 20, 16, kFmtSfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "mfc1", "!0r,!1s", 4), + ENCODING_MAP(kMipsMtc1, 0x44800000, + kFmtBitBlt, 20, 16, kFmtSfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | REG_DEF1, + "mtc1", "!0r,!1s", 4), + ENCODING_MAP(kMipsMfhc1, 0x44600000, + kFmtBitBlt, 20, 16, kFmtSfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, + "mfhc1", "!0r,!1s", 4), + ENCODING_MAP(kMipsMthc1, 0x44e00000, + kFmtBitBlt, 20, 16, kFmtSfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | REG_DEF1, + "mthc1", "!0r,!1s", 4), ENCODING_MAP(kMipsNop, 0x00000000, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, NO_OPERAND, "nop", ";", 4), - ENCODING_MAP(kMipsNor, 0x00000027, /* used for "not" too */ + ENCODING_MAP(kMipsNor, 0x00000027, // Used for "not" too. kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, "nor", "!0r,!1r,!2r", 4), @@ -289,7 +374,7 @@ const MipsEncodingMap MipsMir2Lir::EncodingMap[kMipsLast] = { kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, "srlv", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMipsSubu, 0x00000023, /* used for "neg" too */ + ENCODING_MAP(kMipsSubu, 0x00000023, // Used for "neg" too. kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, "subu", "!0r,!1r,!2r", 4), @@ -297,6 +382,10 @@ const MipsEncodingMap MipsMir2Lir::EncodingMap[kMipsLast] = { kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE, "sw", "!0r,!1d(!2r)", 4), + ENCODING_MAP(kMipsSync, 0x0000000f, + kFmtBitBlt, 10, 6, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_UNARY_OP, + "sync", ";", 4), ENCODING_MAP(kMipsXor, 0x00000026, kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, @@ -305,103 +394,143 @@ const MipsEncodingMap MipsMir2Lir::EncodingMap[kMipsLast] = { kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, "xori", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMipsFadds, 0x46000000, - kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16, + + // The following are mips32r2 instructions. + ENCODING_MAP(kMipsR2Div, 0x0000001a, + kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF_HI | REG_DEF_LO | REG_USE01, + "div", "!0r,!1r", 4), + ENCODING_MAP(kMipsR2Mul, 0x70000002, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "add.s", "!0s,!1s,!2s", 4), - ENCODING_MAP(kMipsFsubs, 0x46000001, - kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16, + "mul", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMipsR2Mfhi, 0x00000010, + kFmtBitBlt, 15, 11, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF0 | REG_USE_HI, + "mfhi", "!0r", 4), + ENCODING_MAP(kMipsR2Mflo, 0x00000012, + kFmtBitBlt, 15, 11, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_UNARY_OP | REG_DEF0 | REG_USE_LO, + "mflo", "!0r", 4), + ENCODING_MAP(kMipsR2Movz, 0x0000000a, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "sub.s", "!0s,!1s,!2s", 4), - ENCODING_MAP(kMipsFmuls, 0x46000002, - kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16, + "movz", "!0r,!1r,!2r", 4), + + // The following are mips32r6 and mips64r6 instructions. + ENCODING_MAP(kMipsR6Div, 0x0000009a, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "mul.s", "!0s,!1s,!2s", 4), - ENCODING_MAP(kMipsFdivs, 0x46000003, - kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16, + "div", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMipsR6Mod, 0x000000da, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "div.s", "!0s,!1s,!2s", 4), - ENCODING_MAP(kMipsFaddd, 0x46200000, - kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16, + "mod", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMipsR6Mul, 0x00000098, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "add.d", "!0S,!1S,!2S", 4), - ENCODING_MAP(kMipsFsubd, 0x46200001, - kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16, + "mul", "!0r,!1r,!2r", 4), + + // The following are mips64r6 instructions. + ENCODING_MAP(kMips64Daddiu, 0x64000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + "daddiu", "!0r,!1r,0x!2h(!2d)", 4), + ENCODING_MAP(kMips64Daddu, 0x0000002d, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "sub.d", "!0S,!1S,!2S", 4), - ENCODING_MAP(kMipsFmuld, 0x46200002, - kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16, + "daddu", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMips64Dahi, 0x04060000, + kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE0, + "dahi", "!0r,0x!1h(!1d)", 4), + ENCODING_MAP(kMips64Dati, 0x041E0000, + kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE0, + "dati", "!0r,0x!1h(!1d)", 4), + ENCODING_MAP(kMips64Daui, 0x74000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + "daui", "!0r,!1r,0x!2h(!2d)", 4), + ENCODING_MAP(kMips64Ddiv, 0x0000009e, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "mul.d", "!0S,!1S,!2S", 4), - ENCODING_MAP(kMipsFdivd, 0x46200003, - kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16, + "ddiv", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMips64Dmod, 0x000000de, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "div.d", "!0S,!1S,!2S", 4), - ENCODING_MAP(kMipsFcvtsd, 0x46200020, - kFmtSfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "cvt.s.d", "!0s,!1S", 4), - ENCODING_MAP(kMipsFcvtsw, 0x46800020, - kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "cvt.s.w", "!0s,!1s", 4), - ENCODING_MAP(kMipsFcvtds, 0x46000021, - kFmtDfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "cvt.d.s", "!0S,!1s", 4), - ENCODING_MAP(kMipsFcvtdw, 0x46800021, - kFmtDfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "cvt.d.w", "!0S,!1s", 4), - ENCODING_MAP(kMipsFcvtws, 0x46000024, - kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "cvt.w.s", "!0s,!1s", 4), - ENCODING_MAP(kMipsFcvtwd, 0x46200024, - kFmtSfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "cvt.w.d", "!0s,!1S", 4), - ENCODING_MAP(kMipsFmovs, 0x46000006, - kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "mov.s", "!0s,!1s", 4), - ENCODING_MAP(kMipsFmovd, 0x46200006, - kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1, + "dmod", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMips64Dmul, 0x0000009c, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "dmul", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMips64Dmfc1, 0x44200000, + kFmtBitBlt, 20, 16, kFmtDfp, 15, 11, kFmtUnused, -1, -1, kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "mov.d", "!0S,!1S", 4), - ENCODING_MAP(kMipsFlwc1, 0xC4000000, - kFmtSfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + "dmfc1", "!0r,!1s", 4), + ENCODING_MAP(kMips64Dmtc1, 0x44a00000, + kFmtBitBlt, 20, 16, kFmtDfp, 15, 11, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | REG_DEF1, + "dmtc1", "!0r,!1s", 4), + ENCODING_MAP(kMips64Drotr32, 0x0000003e | (1 << 21), + kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + "drotr32", "!0r,!1r,0x!2h(!2d)", 4), + ENCODING_MAP(kMips64Dsll, 0x00000038, + kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + "dsll", "!0r,!1r,0x!2h(!2d)", 4), + ENCODING_MAP(kMips64Dsll32, 0x0000003c, + kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + "dsll32", "!0r,!1r,0x!2h(!2d)", 4), + ENCODING_MAP(kMips64Dsrl, 0x0000003a, + kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + "dsrl", "!0r,!1r,0x!2h(!2d)", 4), + ENCODING_MAP(kMips64Dsrl32, 0x0000003e, + kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + "dsrl32", "!0r,!1r,0x!2h(!2d)", 4), + ENCODING_MAP(kMips64Dsra, 0x0000003b, + kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + "dsra", "!0r,!1r,0x!2h(!2d)", 4), + ENCODING_MAP(kMips64Dsra32, 0x0000003f, + kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, + "dsra32", "!0r,!1r,0x!2h(!2d)", 4), + ENCODING_MAP(kMips64Dsllv, 0x00000014, + kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "dsllv", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMips64Dsrlv, 0x00000016, + kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "dsrlv", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMips64Dsrav, 0x00000017, + kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "dsrav", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMips64Dsubu, 0x0000002f, + kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "dsubu", "!0r,!1r,!2r", 4), + ENCODING_MAP(kMips64Ld, 0xdc000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, - "lwc1", "!0s,!1d(!2r)", 4), - ENCODING_MAP(kMipsFldc1, 0xD4000000, - kFmtDfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + "ld", "!0r,!1d(!2r)", 4), + ENCODING_MAP(kMips64Lwu, 0x9c000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, - "ldc1", "!0S,!1d(!2r)", 4), - ENCODING_MAP(kMipsFswc1, 0xE4000000, - kFmtSfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE, - "swc1", "!0s,!1d(!2r)", 4), - ENCODING_MAP(kMipsFsdc1, 0xF4000000, - kFmtDfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, + "lwu", "!0r,!1d(!2r)", 4), + ENCODING_MAP(kMips64Sd, 0xfc000000, + kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE, - "sdc1", "!0S,!1d(!2r)", 4), - ENCODING_MAP(kMipsMfc1, 0x44000000, - kFmtBitBlt, 20, 16, kFmtSfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "mfc1", "!0r,!1s", 4), - ENCODING_MAP(kMipsMtc1, 0x44800000, - kFmtBitBlt, 20, 16, kFmtSfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | REG_DEF1, - "mtc1", "!0r,!1s", 4), - ENCODING_MAP(kMipsMfhc1, 0x44600000, - kFmtBitBlt, 20, 16, kFmtSfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "mfhc1", "!0r,!1s", 4), - ENCODING_MAP(kMipsMthc1, 0x44e00000, - kFmtBitBlt, 20, 16, kFmtSfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | REG_DEF1, - "mthc1", "!0r,!1s", 4), - ENCODING_MAP(kMipsDelta, 0x27e00000, + "sd", "!0r,!1d(!2r)", 4), + + // The following are pseudoinstructions. + ENCODING_MAP(kMipsDelta, 0x27e00000, // It is implemented as daddiu for mips64. kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, 15, 0, kFmtUnused, -1, -1, IS_QUAD_OP | REG_DEF0 | REG_USE_LR | NEEDS_FIXUP, "addiu", "!0r,ra,0x!1h(!1d)", 4), @@ -417,25 +546,6 @@ const MipsEncodingMap MipsMir2Lir::EncodingMap[kMipsLast] = { kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, NO_OPERAND | IS_BRANCH | REG_DEF_LR, "addiu", "ra,pc,8", 4), - ENCODING_MAP(kMipsSync, 0x0000000f, - kFmtBitBlt, 10, 6, kFmtUnused, -1, -1, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_UNARY_OP, - "sync", ";", 4), - - // The following are mips32r6 instructions. - ENCODING_MAP(kMipsR6Div, 0x0000009a, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "div", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMipsR6Mod, 0x000000da, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "mod", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMipsR6Mul, 0x00000098, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "mul", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMipsUndefined, 0x64000000, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, NO_OPERAND, @@ -538,14 +648,13 @@ void MipsMir2Lir::ConvertShortToLongBranch(LIR* lir) { */ AssemblerStatus MipsMir2Lir::AssembleInstructions(CodeOffset start_addr) { LIR *lir; - AssemblerStatus res = kSuccess; // Assume success + AssemblerStatus res = kSuccess; // Assume success. for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) { if (lir->opcode < 0) { continue; } - if (lir->flags.is_nop) { continue; } @@ -567,23 +676,31 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(CodeOffset start_addr) { int offset2 = tab_rec ? tab_rec->offset : lir->target->offset; int delta = offset2 - offset1; if ((delta & 0xffff) == delta && ((delta & 0x8000) == 0)) { - // Fits + // Fits. lir->operands[1] = delta; + if (cu_->target64) { + LIR *new_addiu = RawLIR(lir->dalvik_offset, kMips64Daddiu, lir->operands[0], rRAd, + delta); + InsertLIRBefore(lir, new_addiu); + NopLIR(lir); + res = kRetryAll; + } } else { - // Doesn't fit - must expand to kMipsDelta[Hi|Lo] pair - LIR *new_delta_hi = - RawLIR(lir->dalvik_offset, kMipsDeltaHi, - lir->operands[0], 0, lir->operands[2], - lir->operands[3], 0, lir->target); + // Doesn't fit - must expand to kMipsDelta[Hi|Lo] pair. + LIR *new_delta_hi = RawLIR(lir->dalvik_offset, kMipsDeltaHi, lir->operands[0], 0, + lir->operands[2], lir->operands[3], 0, lir->target); InsertLIRBefore(lir, new_delta_hi); - LIR *new_delta_lo = - RawLIR(lir->dalvik_offset, kMipsDeltaLo, - lir->operands[0], 0, lir->operands[2], - lir->operands[3], 0, lir->target); + LIR *new_delta_lo = RawLIR(lir->dalvik_offset, kMipsDeltaLo, lir->operands[0], 0, + lir->operands[2], lir->operands[3], 0, lir->target); InsertLIRBefore(lir, new_delta_lo); - LIR *new_addu = - RawLIR(lir->dalvik_offset, kMipsAddu, - lir->operands[0], lir->operands[0], rRA); + LIR *new_addu; + if (cu_->target64) { + new_addu = RawLIR(lir->dalvik_offset, kMips64Daddu, lir->operands[0], lir->operands[0], + rRAd); + } else { + new_addu = RawLIR(lir->dalvik_offset, kMipsAddu, lir->operands[0], lir->operands[0], + rRA); + } InsertLIRBefore(lir, new_addu); NopLIR(lir); res = kRetryAll; @@ -698,7 +815,9 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(CodeOffset start_addr) { case kFmtDfp: { // TODO: do we need to adjust now that we're using 64BitSolo? DCHECK(RegStorage::IsDouble(operand)) << ", Operand = 0x" << std::hex << operand; - DCHECK_EQ((operand & 0x1), 0U); + if (!cu_->target64) { + DCHECK_EQ((operand & 0x1), 0U); // May only use even numbered registers for mips32. + } value = (RegStorage::RegNum(operand) << encoder->field_loc[i].start) & ((1 << (encoder->field_loc[i].end + 1)) - 1); bits |= value; @@ -719,7 +838,7 @@ AssemblerStatus MipsMir2Lir::AssembleInstructions(CodeOffset start_addr) { code_buffer_.push_back((bits >> 8) & 0xff); code_buffer_.push_back((bits >> 16) & 0xff); code_buffer_.push_back((bits >> 24) & 0xff); - // TUNING: replace with proper delay slot handling + // TUNING: replace with proper delay slot handling. if (encoder->size == 8) { DCHECK(!IsPseudoLirOp(lir->opcode)); const MipsEncodingMap *encoder2 = &EncodingMap[kMipsNop]; @@ -758,7 +877,7 @@ int MipsMir2Lir::AssignInsnOffsets() { lir->operands[0] = 0; } } - /* Pseudo opcodes don't consume space */ + // Pseudo opcodes don't consume space. } return offset; } @@ -771,10 +890,10 @@ int MipsMir2Lir::AssignInsnOffsets() { void MipsMir2Lir::AssignOffsets() { int offset = AssignInsnOffsets(); - /* Const values have to be word aligned */ + // Const values have to be word aligned. offset = RoundUp(offset, 4); - /* Set up offsets for literals */ + // Set up offsets for literals. data_offset_ = offset; offset = AssignLiteralOffset(offset); @@ -811,19 +930,19 @@ void MipsMir2Lir::AssembleLIR() { CodegenDump(); LOG(FATAL) << "Assembler error - too many retries"; } - // Redo offsets and try again + // Redo offsets and try again. AssignOffsets(); code_buffer_.clear(); } } - // Install literals + // Install literals. InstallLiteralPools(); - // Install switch tables + // Install switch tables. InstallSwitchTables(); - // Install fill array data + // Install fill array data. InstallFillArrayData(); // Create the mapping table and native offset to reference map. diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc index b067221c27..de66b35418 100644 --- a/compiler/dex/quick/mips/call_mips.cc +++ b/compiler/dex/quick/mips/call_mips.cc @@ -68,7 +68,7 @@ bool MipsMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& s */ void MipsMir2Lir::GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) { const uint16_t* table = mir_graph_->GetTable(mir, table_offset); - // Add the table to the list - we'll process it later + // Add the table to the list - we'll process it later. SwitchTable* tab_rec = static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData)); tab_rec->switch_mir = mir; @@ -77,39 +77,39 @@ void MipsMir2Lir::GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLoca int elements = table[1]; switch_tables_.push_back(tab_rec); - // The table is composed of 8-byte key/disp pairs + // The table is composed of 8-byte key/disp pairs. int byte_size = elements * 8; int size_hi = byte_size >> 16; int size_lo = byte_size & 0xffff; - RegStorage r_end = AllocTemp(); + RegStorage r_end = AllocPtrSizeTemp(); if (size_hi) { NewLIR2(kMipsLui, r_end.GetReg(), size_hi); } - // Must prevent code motion for the curr pc pair + // Must prevent code motion for the curr pc pair. GenBarrier(); // Scheduling barrier - NewLIR0(kMipsCurrPC); // Really a jal to .+8 - // Now, fill the branch delay slot + NewLIR0(kMipsCurrPC); // Really a jal to .+8. + // Now, fill the branch delay slot. if (size_hi) { NewLIR3(kMipsOri, r_end.GetReg(), r_end.GetReg(), size_lo); } else { NewLIR3(kMipsOri, r_end.GetReg(), rZERO, size_lo); } - GenBarrier(); // Scheduling barrier + GenBarrier(); // Scheduling barrier. - // Construct BaseLabel and set up table base register + // Construct BaseLabel and set up table base register. LIR* base_label = NewLIR0(kPseudoTargetLabel); - // Remember base label so offsets can be computed later + // Remember base label so offsets can be computed later. tab_rec->anchor = base_label; - RegStorage r_base = AllocTemp(); + RegStorage r_base = AllocPtrSizeTemp(); NewLIR4(kMipsDelta, r_base.GetReg(), 0, WrapPointer(base_label), WrapPointer(tab_rec)); OpRegRegReg(kOpAdd, r_end, r_end, r_base); - // Grab switch test value + // Grab switch test value. rl_src = LoadValue(rl_src, kCoreReg); - // Test loop + // Test loop. RegStorage r_key = AllocTemp(); LIR* loop_label = NewLIR0(kPseudoTargetLabel); LIR* exit_branch = OpCmpBranch(kCondEq, r_base, r_end, NULL); @@ -118,10 +118,10 @@ void MipsMir2Lir::GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLoca OpCmpBranch(kCondNe, rl_src.reg, r_key, loop_label); RegStorage r_disp = AllocTemp(); Load32Disp(r_base, -4, r_disp); - OpRegRegReg(kOpAdd, rs_rRA, rs_rRA, r_disp); - OpReg(kOpBx, rs_rRA); - - // Loop exit + const RegStorage rs_ra = TargetPtrReg(kLr); + OpRegRegReg(kOpAdd, rs_ra, rs_ra, r_disp); + OpReg(kOpBx, rs_ra); + // Loop exit. LIR* exit_label = NewLIR0(kPseudoTargetLabel); exit_branch->target = exit_label; } @@ -141,7 +141,7 @@ void MipsMir2Lir::GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLoca */ void MipsMir2Lir::GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) { const uint16_t* table = mir_graph_->GetTable(mir, table_offset); - // Add the table to the list - we'll process it later + // Add the table to the list - we'll process it later. SwitchTable* tab_rec = static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData)); tab_rec->switch_mir = mir; @@ -150,10 +150,10 @@ void MipsMir2Lir::GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLoca int size = table[1]; switch_tables_.push_back(tab_rec); - // Get the switch value + // Get the switch value. rl_src = LoadValue(rl_src, kCoreReg); - // Prepare the bias. If too big, handle 1st stage here + // Prepare the bias. If too big, handle 1st stage here. int low_key = s4FromSwitchData(&table[2]); bool large_bias = false; RegStorage r_key; @@ -167,10 +167,10 @@ void MipsMir2Lir::GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLoca r_key = AllocTemp(); } - // Must prevent code motion for the curr pc pair + // Must prevent code motion for the curr pc pair. GenBarrier(); - NewLIR0(kMipsCurrPC); // Really a jal to .+8 - // Now, fill the branch delay slot with bias strip + NewLIR0(kMipsCurrPC); // Really a jal to .+8. + // Now, fill the branch delay slot with bias strip. if (low_key == 0) { NewLIR0(kMipsNop); } else { @@ -180,51 +180,60 @@ void MipsMir2Lir::GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLoca OpRegRegImm(kOpSub, r_key, rl_src.reg, low_key); } } - GenBarrier(); // Scheduling barrier + GenBarrier(); // Scheduling barrier. - // Construct BaseLabel and set up table base register + // Construct BaseLabel and set up table base register. LIR* base_label = NewLIR0(kPseudoTargetLabel); - // Remember base label so offsets can be computed later + // Remember base label so offsets can be computed later. tab_rec->anchor = base_label; - // Bounds check - if < 0 or >= size continue following switch + // Bounds check - if < 0 or >= size continue following switch. LIR* branch_over = OpCmpImmBranch(kCondHi, r_key, size-1, NULL); - // Materialize the table base pointer - RegStorage r_base = AllocTemp(); + // Materialize the table base pointer. + RegStorage r_base = AllocPtrSizeTemp(); NewLIR4(kMipsDelta, r_base.GetReg(), 0, WrapPointer(base_label), WrapPointer(tab_rec)); - // Load the displacement from the switch table + // Load the displacement from the switch table. RegStorage r_disp = AllocTemp(); LoadBaseIndexed(r_base, r_key, r_disp, 2, k32); - // Add to rAP and go - OpRegRegReg(kOpAdd, rs_rRA, rs_rRA, r_disp); - OpReg(kOpBx, rs_rRA); + // Add to rRA and go. + const RegStorage rs_ra = TargetPtrReg(kLr); + OpRegRegReg(kOpAdd, rs_ra, rs_ra, r_disp); + OpReg(kOpBx, rs_ra); - /* branch_over target here */ + // Branch_over target here. LIR* target = NewLIR0(kPseudoTargetLabel); branch_over->target = target; } void MipsMir2Lir::GenMoveException(RegLocation rl_dest) { - int ex_offset = Thread::ExceptionOffset<4>().Int32Value(); + int ex_offset = cu_->target64 ? Thread::ExceptionOffset<8>().Int32Value() : + Thread::ExceptionOffset<4>().Int32Value(); RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true); RegStorage reset_reg = AllocTempRef(); - LoadRefDisp(rs_rMIPS_SELF, ex_offset, rl_result.reg, kNotVolatile); + LoadRefDisp(TargetPtrReg(kSelf), ex_offset, rl_result.reg, kNotVolatile); LoadConstant(reset_reg, 0); - StoreRefDisp(rs_rMIPS_SELF, ex_offset, reset_reg, kNotVolatile); + StoreRefDisp(TargetPtrReg(kSelf), ex_offset, reset_reg, kNotVolatile); FreeTemp(reset_reg); StoreValue(rl_dest, rl_result); } void MipsMir2Lir::UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) { - RegStorage reg_card_base = AllocTemp(); - RegStorage reg_card_no = AllocTemp(); - // NOTE: native pointer. - LoadWordDisp(rs_rMIPS_SELF, Thread::CardTableOffset<4>().Int32Value(), reg_card_base); - OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift); - StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, kUnsignedByte); + RegStorage reg_card_base = AllocPtrSizeTemp(); + RegStorage reg_card_no = AllocPtrSizeTemp(); + if (cu_->target64) { + // NOTE: native pointer. + LoadWordDisp(TargetPtrReg(kSelf), Thread::CardTableOffset<8>().Int32Value(), reg_card_base); + OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift); + StoreBaseIndexed(reg_card_base, reg_card_no, As32BitReg(reg_card_base), 0, kUnsignedByte); + } else { + // NOTE: native pointer. + LoadWordDisp(TargetPtrReg(kSelf), Thread::CardTableOffset<4>().Int32Value(), reg_card_base); + OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift); + StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, kUnsignedByte); + } FreeTemp(reg_card_base); FreeTemp(reg_card_no); } @@ -232,33 +241,57 @@ void MipsMir2Lir::UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) { void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { int spill_count = num_core_spills_ + num_fp_spills_; /* - * On entry, rMIPS_ARG0, rMIPS_ARG1, rMIPS_ARG2 & rMIPS_ARG3 are live. Let the register - * allocation mechanism know so it doesn't try to use any of them when - * expanding the frame or flushing. This leaves the utility - * code with a single temp: r12. This should be enough. + * On entry, A0, A1, A2 & A3 are live. On Mips64, A4, A5, A6 & A7 are also live. + * Let the register allocation mechanism know so it doesn't try to use any of them when + * expanding the frame or flushing. */ - LockTemp(rs_rMIPS_ARG0); - LockTemp(rs_rMIPS_ARG1); - LockTemp(rs_rMIPS_ARG2); - LockTemp(rs_rMIPS_ARG3); + const RegStorage arg0 = TargetReg(kArg0); + const RegStorage arg1 = TargetReg(kArg1); + const RegStorage arg2 = TargetReg(kArg2); + const RegStorage arg3 = TargetReg(kArg3); + const RegStorage arg4 = TargetReg(kArg4); + const RegStorage arg5 = TargetReg(kArg5); + const RegStorage arg6 = TargetReg(kArg6); + const RegStorage arg7 = TargetReg(kArg7); + + LockTemp(arg0); + LockTemp(arg1); + LockTemp(arg2); + LockTemp(arg3); + if (cu_->target64) { + LockTemp(arg4); + LockTemp(arg5); + LockTemp(arg6); + LockTemp(arg7); + } + + bool skip_overflow_check; + InstructionSet target = (cu_->target64) ? kMips64 : kMips; + int ptr_size = cu_->target64 ? 8 : 4; /* * We can safely skip the stack overflow check if we're * a leaf *and* our frame size < fudge factor. */ - bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, kMips); + + skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, target); NewLIR0(kPseudoMethodEntry); - RegStorage check_reg = AllocTemp(); - RegStorage new_sp = AllocTemp(); + RegStorage check_reg = AllocPtrSizeTemp(); + RegStorage new_sp = AllocPtrSizeTemp(); + const RegStorage rs_sp = TargetPtrReg(kSp); if (!skip_overflow_check) { - /* Load stack limit */ - Load32Disp(rs_rMIPS_SELF, Thread::StackEndOffset<4>().Int32Value(), check_reg); + // Load stack limit. + if (cu_->target64) { + LoadWordDisp(TargetPtrReg(kSelf), Thread::StackEndOffset<8>().Int32Value(), check_reg); + } else { + Load32Disp(TargetPtrReg(kSelf), Thread::StackEndOffset<4>().Int32Value(), check_reg); + } } - /* Spill core callee saves */ + // Spill core callee saves. SpillCoreRegs(); - /* NOTE: promotion of FP regs currently unsupported, thus no FP spill */ + // NOTE: promotion of FP regs currently unsupported, thus no FP spill. DCHECK_EQ(num_fp_spills_, 0); - const int frame_sub = frame_size_ - spill_count * 4; + const int frame_sub = frame_size_ - spill_count * ptr_size; if (!skip_overflow_check) { class StackOverflowSlowPath : public LIRSlowPath { public: @@ -269,9 +302,9 @@ void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) m2l_->ResetRegPool(); m2l_->ResetDefTracking(); GenerateTargetLabel(kPseudoThrowTarget); - // LR is offset 0 since we push in reverse order. - m2l_->Load32Disp(rs_rMIPS_SP, 0, rs_rRA); - m2l_->OpRegImm(kOpAdd, rs_rMIPS_SP, sp_displace_); + // RA is offset 0 since we push in reverse order. + m2l_->LoadWordDisp(m2l_->TargetPtrReg(kSp), 0, m2l_->TargetPtrReg(kLr)); + m2l_->OpRegImm(kOpAdd, m2l_->TargetPtrReg(kSp), sp_displace_); m2l_->ClobberCallerSave(); RegStorage r_tgt = m2l_->CallHelperSetup(kQuickThrowStackOverflow); // Doesn't clobber LR. m2l_->CallHelper(r_tgt, kQuickThrowStackOverflow, false /* MarkSafepointPC */, @@ -281,21 +314,27 @@ void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) private: const size_t sp_displace_; }; - OpRegRegImm(kOpSub, new_sp, rs_rMIPS_SP, frame_sub); + OpRegRegImm(kOpSub, new_sp, rs_sp, frame_sub); LIR* branch = OpCmpBranch(kCondUlt, new_sp, check_reg, nullptr); - AddSlowPath(new(arena_)StackOverflowSlowPath(this, branch, spill_count * 4)); + AddSlowPath(new(arena_)StackOverflowSlowPath(this, branch, spill_count * ptr_size)); // TODO: avoid copy for small frame sizes. - OpRegCopy(rs_rMIPS_SP, new_sp); // Establish stack + OpRegCopy(rs_sp, new_sp); // Establish stack. } else { - OpRegImm(kOpSub, rs_rMIPS_SP, frame_sub); + OpRegImm(kOpSub, rs_sp, frame_sub); } FlushIns(ArgLocs, rl_method); - FreeTemp(rs_rMIPS_ARG0); - FreeTemp(rs_rMIPS_ARG1); - FreeTemp(rs_rMIPS_ARG2); - FreeTemp(rs_rMIPS_ARG3); + FreeTemp(arg0); + FreeTemp(arg1); + FreeTemp(arg2); + FreeTemp(arg3); + if (cu_->target64) { + FreeTemp(arg4); + FreeTemp(arg5); + FreeTemp(arg6); + FreeTemp(arg7); + } } void MipsMir2Lir::GenExitSequence() { @@ -303,58 +342,67 @@ void MipsMir2Lir::GenExitSequence() { * In the exit path, rMIPS_RET0/rMIPS_RET1 are live - make sure they aren't * allocated by the register utilities as temps. */ - LockTemp(rs_rMIPS_RET0); - LockTemp(rs_rMIPS_RET1); + LockTemp(TargetPtrReg(kRet0)); + LockTemp(TargetPtrReg(kRet1)); NewLIR0(kPseudoMethodExit); UnSpillCoreRegs(); - OpReg(kOpBx, rs_rRA); + OpReg(kOpBx, TargetPtrReg(kLr)); } void MipsMir2Lir::GenSpecialExitSequence() { - OpReg(kOpBx, rs_rRA); + OpReg(kOpBx, TargetPtrReg(kLr)); } void MipsMir2Lir::GenSpecialEntryForSuspend() { - // Keep 16-byte stack alignment - push A0, i.e. ArtMethod*, 2 filler words and RA. - core_spill_mask_ = (1u << rs_rRA.GetRegNum()); + // Keep 16-byte stack alignment - push A0, i.e. ArtMethod*, 2 filler words and RA for mips32, + // but A0 and RA for mips64. + core_spill_mask_ = (1u << TargetPtrReg(kLr).GetRegNum()); num_core_spills_ = 1u; fp_spill_mask_ = 0u; num_fp_spills_ = 0u; frame_size_ = 16u; core_vmap_table_.clear(); fp_vmap_table_.clear(); - OpRegImm(kOpSub, rs_rMIPS_SP, frame_size_); - Store32Disp(rs_rMIPS_SP, frame_size_ - 4, rs_rRA); - Store32Disp(rs_rMIPS_SP, 0, rs_rA0); + const RegStorage rs_sp = TargetPtrReg(kSp); + OpRegImm(kOpSub, rs_sp, frame_size_); + StoreWordDisp(rs_sp, frame_size_ - (cu_->target64 ? 8 : 4), TargetPtrReg(kLr)); + StoreWordDisp(rs_sp, 0, TargetPtrReg(kArg0)); } void MipsMir2Lir::GenSpecialExitForSuspend() { // Pop the frame. Don't pop ArtMethod*, it's no longer needed. - Load32Disp(rs_rMIPS_SP, frame_size_ - 4, rs_rRA); - OpRegImm(kOpAdd, rs_rMIPS_SP, frame_size_); + const RegStorage rs_sp = TargetPtrReg(kSp); + LoadWordDisp(rs_sp, frame_size_ - (cu_->target64 ? 8 : 4), TargetPtrReg(kLr)); + OpRegImm(kOpAdd, rs_sp, frame_size_); } /* * Bit of a hack here - in the absence of a real scheduling pass, * emit the next instruction in static & direct invoke sequences. */ -static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSED, - int state, const MethodReference& target_method, - uint32_t, - uintptr_t direct_code, uintptr_t direct_method, - InvokeType type) { +static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSED, int state, + const MethodReference& target_method, uint32_t, uintptr_t direct_code, + uintptr_t direct_method, InvokeType type) { Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); if (direct_code != 0 && direct_method != 0) { switch (state) { case 0: // Get the current Method* [sets kArg0] if (direct_code != static_cast<uintptr_t>(-1)) { - cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code); + if (cu->target64) { + cg->LoadConstantWide(cg->TargetPtrReg(kInvokeTgt), direct_code); + } else { + cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code); + } } else { cg->LoadCodeAddress(target_method, type, kInvokeTgt); } if (direct_method != static_cast<uintptr_t>(-1)) { - cg->LoadConstant(cg->TargetReg(kArg0, kRef), direct_method); + if (cu->target64) { + cg->LoadConstantWide(cg->TargetReg(kArg0, kRef), direct_method); + } else { + cg->LoadConstant(cg->TargetReg(kArg0, kRef), direct_method); + } } else { cg->LoadMethodAddress(target_method, type, kArg0); } @@ -377,7 +425,11 @@ static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSED, // Set up direct code if known. if (direct_code != 0) { if (direct_code != static_cast<uintptr_t>(-1)) { - cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code); + if (cu->target64) { + cg->LoadConstantWide(cg->TargetPtrReg(kInvokeTgt), direct_code); + } else { + cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code); + } } else { CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds()); cg->LoadCodeAddress(target_method, type, kInvokeTgt); diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h index 649b6c9417..713264e0d9 100644 --- a/compiler/dex/quick/mips/codegen_mips.h +++ b/compiler/dex/quick/mips/codegen_mips.h @@ -17,6 +17,7 @@ #ifndef ART_COMPILER_DEX_QUICK_MIPS_CODEGEN_MIPS_H_ #define ART_COMPILER_DEX_QUICK_MIPS_CODEGEN_MIPS_H_ +#include "dex/compiler_ir.h" #include "dex/quick/mir_to_lir.h" #include "mips_lir.h" @@ -39,215 +40,303 @@ class MipsMir2Lir FINAL : public Mir2Lir { size_t cur_core_reg_; }; + class InToRegStorageMips64Mapper : public InToRegStorageMapper { + public: + explicit InToRegStorageMips64Mapper(Mir2Lir* m2l) : m2l_(m2l), cur_arg_reg_(0) {} + virtual RegStorage GetNextReg(ShortyArg arg); + virtual void Reset() OVERRIDE { + cur_arg_reg_ = 0; + } + protected: + Mir2Lir* m2l_; + private: + size_t cur_arg_reg_; + }; + + InToRegStorageMips64Mapper in_to_reg_storage_mips64_mapper_; InToRegStorageMipsMapper in_to_reg_storage_mips_mapper_; InToRegStorageMapper* GetResetedInToRegStorageMapper() OVERRIDE { - in_to_reg_storage_mips_mapper_.Reset(); - return &in_to_reg_storage_mips_mapper_; + InToRegStorageMapper* res; + if (cu_->target64) { + res = &in_to_reg_storage_mips64_mapper_; + } else { + res = &in_to_reg_storage_mips_mapper_; + } + res->Reset(); + return res; } - public: - MipsMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena); - - // Required for target - codegen utilities. - bool SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, RegLocation rl_src, - RegLocation rl_dest, int lit); - bool EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) OVERRIDE; - void GenMultiplyByConstantFloat(RegLocation rl_dest, RegLocation rl_src1, - int32_t constant) OVERRIDE; - void GenMultiplyByConstantDouble(RegLocation rl_dest, RegLocation rl_src1, - int64_t constant) OVERRIDE; - LIR* CheckSuspendUsingLoad() OVERRIDE; - RegStorage LoadHelper(QuickEntrypointEnum trampoline) OVERRIDE; - LIR* LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size, VolatileKind is_volatile) OVERRIDE; - LIR* LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest, int scale, - OpSize size) OVERRIDE; - LIR* LoadConstantNoClobber(RegStorage r_dest, int value); - LIR* LoadConstantWide(RegStorage r_dest, int64_t value); - LIR* StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size, VolatileKind is_volatile) OVERRIDE; - LIR* StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale, - OpSize size) OVERRIDE; - LIR* GenAtomic64Load(RegStorage r_base, int displacement, RegStorage r_dest); - LIR* GenAtomic64Store(RegStorage r_base, int displacement, RegStorage r_src); - - /// @copydoc Mir2Lir::UnconditionallyMarkGCCard(RegStorage) - void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) OVERRIDE; - - // Required for target - register utilities. - RegStorage Solo64ToPair64(RegStorage reg); - RegStorage Fp64ToSolo32(RegStorage reg); - RegStorage TargetReg(SpecialTargetRegister reg); - RegStorage TargetReg(SpecialTargetRegister reg, WideKind wide_kind) OVERRIDE; - RegLocation GetReturnAlt(); - RegLocation GetReturnWideAlt(); - RegLocation LocCReturn(); - RegLocation LocCReturnRef(); - RegLocation LocCReturnDouble(); - RegLocation LocCReturnFloat(); - RegLocation LocCReturnWide(); - ResourceMask GetRegMaskCommon(const RegStorage& reg) const OVERRIDE; - void AdjustSpillMask(); - void ClobberCallerSave(); - void FreeCallTemps(); - void LockCallTemps(); - void CompilerInitializeRegAlloc(); - - // Required for target - miscellaneous. - void AssembleLIR(); - int AssignInsnOffsets(); - void AssignOffsets(); - AssemblerStatus AssembleInstructions(CodeOffset start_addr); - void DumpResourceMask(LIR* lir, const ResourceMask& mask, const char* prefix) OVERRIDE; - void SetupTargetResourceMasks(LIR* lir, uint64_t flags, - ResourceMask* use_mask, ResourceMask* def_mask) OVERRIDE; - const char* GetTargetInstFmt(int opcode); - const char* GetTargetInstName(int opcode); - std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr); - ResourceMask GetPCUseDefEncoding() const OVERRIDE; - uint64_t GetTargetInstFlags(int opcode); - size_t GetInsnSize(LIR* lir) OVERRIDE; - bool IsUnconditionalBranch(LIR* lir); - - // Get the register class for load/store of a field. - RegisterClass RegClassForFieldLoadStore(OpSize size, bool is_volatile) OVERRIDE; - - // Required for target - Dalvik-level generators. - void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_src2, int flags); - void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_dest, int scale); - void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_src, int scale, bool card_mark); - void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_shift, int flags); - void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2); - void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2); - void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2); - void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src); - bool GenInlinedAbsFloat(CallInfo* info) OVERRIDE; - bool GenInlinedAbsDouble(CallInfo* info) OVERRIDE; - bool GenInlinedCas(CallInfo* info, bool is_long, bool is_object); - bool GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long); - bool GenInlinedSqrt(CallInfo* info); - bool GenInlinedPeek(CallInfo* info, OpSize size); - bool GenInlinedPoke(CallInfo* info, OpSize size); - void GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2, int flags) OVERRIDE; - RegLocation GenDivRem(RegLocation rl_dest, RegStorage reg_lo, RegStorage reg_hi, bool is_div); - RegLocation GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit, bool is_div); - void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenDivZeroCheckWide(RegStorage reg); - void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method); - void GenExitSequence(); - void GenSpecialExitSequence() OVERRIDE; - void GenSpecialEntryForSuspend() OVERRIDE; - void GenSpecialExitForSuspend() OVERRIDE; - void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double); - void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir); - void GenSelect(BasicBlock* bb, MIR* mir); - void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code, - int32_t true_val, int32_t false_val, RegStorage rs_dest, - RegisterClass dest_reg_class) OVERRIDE; - bool GenMemBarrier(MemBarrierKind barrier_kind); - void GenMoveException(RegLocation rl_dest); - void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit, - int first_bit, int second_bit); - void GenNegDouble(RegLocation rl_dest, RegLocation rl_src); - void GenNegFloat(RegLocation rl_dest, RegLocation rl_src); - void GenLargePackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); - void GenLargeSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); - bool GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special); - - // Required for target - single operation generators. - LIR* OpUnconditionalBranch(LIR* target); - LIR* OpCmpBranch(ConditionCode cond, RegStorage src1, RegStorage src2, LIR* target); - LIR* OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_value, LIR* target); - LIR* OpCondBranch(ConditionCode cc, LIR* target); - LIR* OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* target); - LIR* OpFpRegCopy(RegStorage r_dest, RegStorage r_src); - LIR* OpIT(ConditionCode cond, const char* guide); - void OpEndIT(LIR* it); - LIR* OpMem(OpKind op, RegStorage r_base, int disp); - void OpPcRelLoad(RegStorage reg, LIR* target); - LIR* OpReg(OpKind op, RegStorage r_dest_src); - void OpRegCopy(RegStorage r_dest, RegStorage r_src); - LIR* OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src); - LIR* OpRegImm(OpKind op, RegStorage r_dest_src1, int value); - LIR* OpRegReg(OpKind op, RegStorage r_dest_src1, RegStorage r_src2); - LIR* OpMovRegMem(RegStorage r_dest, RegStorage r_base, int offset, MoveType move_type); - LIR* OpMovMemReg(RegStorage r_base, int offset, RegStorage r_src, MoveType move_type); - LIR* OpCondRegReg(OpKind op, ConditionCode cc, RegStorage r_dest, RegStorage r_src); - LIR* OpRegRegImm(OpKind op, RegStorage r_dest, RegStorage r_src1, int value); - LIR* OpRegRegReg(OpKind op, RegStorage r_dest, RegStorage r_src1, RegStorage r_src2); - LIR* OpTestSuspend(LIR* target); - LIR* OpVldm(RegStorage r_base, int count); - LIR* OpVstm(RegStorage r_base, int count); - void OpRegCopyWide(RegStorage dest, RegStorage src); - - // TODO: collapse r_dest. - LIR* LoadBaseDispBody(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size); - // TODO: collapse r_src. - LIR* StoreBaseDispBody(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size); - void SpillCoreRegs(); - void UnSpillCoreRegs(); - static const MipsEncodingMap EncodingMap[kMipsLast]; - bool InexpensiveConstantInt(int32_t value); - bool InexpensiveConstantFloat(int32_t value); - bool InexpensiveConstantLong(int64_t value); - bool InexpensiveConstantDouble(int64_t value); - - bool WideGPRsAreAliases() const OVERRIDE { - return false; // Wide GPRs are formed by pairing. - } - bool WideFPRsAreAliases() const OVERRIDE { - return false; // Wide FPRs are formed by pairing. - } + public: + MipsMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena); - LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) OVERRIDE; + // Required for target - codegen utilities. + bool SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, RegLocation rl_src, + RegLocation rl_dest, int lit); + bool EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) OVERRIDE; + void GenMultiplyByConstantFloat(RegLocation rl_dest, RegLocation rl_src1, int32_t constant) + OVERRIDE; + void GenMultiplyByConstantDouble(RegLocation rl_dest, RegLocation rl_src1, int64_t constant) + OVERRIDE; + LIR* CheckSuspendUsingLoad() OVERRIDE; + RegStorage LoadHelper(QuickEntrypointEnum trampoline) OVERRIDE; + LIR* LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, OpSize size, + VolatileKind is_volatile) OVERRIDE; + LIR* LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest, int scale, + OpSize size) OVERRIDE; + LIR* LoadConstantNoClobber(RegStorage r_dest, int value); + LIR* LoadConstantWideNoClobber(RegStorage r_dest, int64_t value); + LIR* LoadConstantWide(RegStorage r_dest, int64_t value); + LIR* StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, OpSize size, + VolatileKind is_volatile) OVERRIDE; + LIR* StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale, + OpSize size) OVERRIDE; + LIR* GenAtomic64Load(RegStorage r_base, int displacement, RegStorage r_dest); + LIR* GenAtomic64Store(RegStorage r_base, int displacement, RegStorage r_src); - RegLocation GenDivRem(RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2, bool is_div, int flags) OVERRIDE; - RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) - OVERRIDE; + /// @copydoc Mir2Lir::UnconditionallyMarkGCCard(RegStorage) + void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) OVERRIDE; - NextCallInsn GetNextSDCallInsn() OVERRIDE; - LIR* GenCallInsn(const MirMethodLoweringInfo& method_info) OVERRIDE; + // Required for target - register utilities. + RegStorage Solo64ToPair64(RegStorage reg); + RegStorage Fp64ToSolo32(RegStorage reg); + RegStorage TargetReg(SpecialTargetRegister reg); + RegStorage TargetReg(SpecialTargetRegister reg, WideKind wide_kind) OVERRIDE; + RegStorage TargetPtrReg(SpecialTargetRegister reg) OVERRIDE { + return TargetReg(reg, cu_->target64 ? kWide : kNotWide); + } + RegLocation GetReturnAlt(); + RegLocation GetReturnWideAlt(); + RegLocation LocCReturn(); + RegLocation LocCReturnRef(); + RegLocation LocCReturnDouble(); + RegLocation LocCReturnFloat(); + RegLocation LocCReturnWide(); + ResourceMask GetRegMaskCommon(const RegStorage& reg) const OVERRIDE; + void AdjustSpillMask(); + void ClobberCallerSave(); + void FreeCallTemps(); + void LockCallTemps(); + void CompilerInitializeRegAlloc(); - // Unimplemented intrinsics. - bool GenInlinedCharAt(CallInfo* info ATTRIBUTE_UNUSED) OVERRIDE { - return false; - } - bool GenInlinedAbsInt(CallInfo* info ATTRIBUTE_UNUSED) OVERRIDE { - return false; - } - bool GenInlinedAbsLong(CallInfo* info ATTRIBUTE_UNUSED) OVERRIDE { - return false; - } - bool GenInlinedIndexOf(CallInfo* info ATTRIBUTE_UNUSED, bool zero_based ATTRIBUTE_UNUSED) - OVERRIDE { - return false; - } + // Required for target - miscellaneous. + void AssembleLIR(); + int AssignInsnOffsets(); + void AssignOffsets(); + AssemblerStatus AssembleInstructions(CodeOffset start_addr); + void DumpResourceMask(LIR* lir, const ResourceMask& mask, const char* prefix) OVERRIDE; + void SetupTargetResourceMasks(LIR* lir, uint64_t flags, ResourceMask* use_mask, + ResourceMask* def_mask) OVERRIDE; + const char* GetTargetInstFmt(int opcode); + const char* GetTargetInstName(int opcode); + std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr); + ResourceMask GetPCUseDefEncoding() const OVERRIDE; + uint64_t GetTargetInstFlags(int opcode); + size_t GetInsnSize(LIR* lir) OVERRIDE; + bool IsUnconditionalBranch(LIR* lir); + + // Get the register class for load/store of a field. + RegisterClass RegClassForFieldLoadStore(OpSize size, bool is_volatile) OVERRIDE; + + // Required for target - Dalvik-level generators. + void GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, + RegLocation lr_shift); + void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2, int flags); + void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index, + RegLocation rl_dest, int scale); + void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index, + RegLocation rl_src, int scale, bool card_mark); + void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_shift, int flags); + void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2); + void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2); + void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2); + void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src); + bool GenInlinedAbsFloat(CallInfo* info) OVERRIDE; + bool GenInlinedAbsDouble(CallInfo* info) OVERRIDE; + bool GenInlinedCas(CallInfo* info, bool is_long, bool is_object); + bool GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long); + bool GenInlinedSqrt(CallInfo* info); + bool GenInlinedPeek(CallInfo* info, OpSize size); + bool GenInlinedPoke(CallInfo* info, OpSize size); + void GenIntToLong(RegLocation rl_dest, RegLocation rl_src) OVERRIDE; + void GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2, int flags) OVERRIDE; + RegLocation GenDivRem(RegLocation rl_dest, RegStorage reg_lo, RegStorage reg_hi, bool is_div); + RegLocation GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit, bool is_div); + void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenDivZeroCheckWide(RegStorage reg); + void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method); + void GenExitSequence(); + void GenSpecialExitSequence() OVERRIDE; + void GenSpecialEntryForSuspend() OVERRIDE; + void GenSpecialExitForSuspend() OVERRIDE; + void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double); + void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir); + void GenSelect(BasicBlock* bb, MIR* mir); + void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code, + int32_t true_val, int32_t false_val, RegStorage rs_dest, + RegisterClass dest_reg_class) OVERRIDE; + bool GenMemBarrier(MemBarrierKind barrier_kind); + void GenMoveException(RegLocation rl_dest); + void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit, + int first_bit, int second_bit); + void GenNegDouble(RegLocation rl_dest, RegLocation rl_src); + void GenNegFloat(RegLocation rl_dest, RegLocation rl_src); + void GenLargePackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); + void GenLargeSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); + bool GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special); - // True if isa is rev R6. - const bool isaIsR6_; + // Required for target - single operation generators. + LIR* OpUnconditionalBranch(LIR* target); + LIR* OpCmpBranch(ConditionCode cond, RegStorage src1, RegStorage src2, LIR* target); + LIR* OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_value, LIR* target); + LIR* OpCondBranch(ConditionCode cc, LIR* target); + LIR* OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* target); + LIR* OpFpRegCopy(RegStorage r_dest, RegStorage r_src); + LIR* OpIT(ConditionCode cond, const char* guide); + void OpEndIT(LIR* it); + LIR* OpMem(OpKind op, RegStorage r_base, int disp); + void OpPcRelLoad(RegStorage reg, LIR* target); + LIR* OpReg(OpKind op, RegStorage r_dest_src); + void OpRegCopy(RegStorage r_dest, RegStorage r_src); + LIR* OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src); + LIR* OpRegImm(OpKind op, RegStorage r_dest_src1, int value); + LIR* OpRegReg(OpKind op, RegStorage r_dest_src1, RegStorage r_src2); + LIR* OpMovRegMem(RegStorage r_dest, RegStorage r_base, int offset, MoveType move_type); + LIR* OpMovMemReg(RegStorage r_base, int offset, RegStorage r_src, MoveType move_type); + LIR* OpCondRegReg(OpKind op, ConditionCode cc, RegStorage r_dest, RegStorage r_src); + LIR* OpRegRegImm(OpKind op, RegStorage r_dest, RegStorage r_src1, int value); + LIR* OpRegRegReg(OpKind op, RegStorage r_dest, RegStorage r_src1, RegStorage r_src2); + LIR* OpTestSuspend(LIR* target); + LIR* OpVldm(RegStorage r_base, int count); + LIR* OpVstm(RegStorage r_base, int count); + void OpRegCopyWide(RegStorage dest, RegStorage src); - // True if floating point unit is 32bits. - const bool fpuIs32Bit_; + // TODO: collapse r_dest. + LIR* LoadBaseDispBody(RegStorage r_base, int displacement, RegStorage r_dest, OpSize size); + // TODO: collapse r_src. + LIR* StoreBaseDispBody(RegStorage r_base, int displacement, RegStorage r_src, OpSize size); + void SpillCoreRegs(); + void UnSpillCoreRegs(); + static const MipsEncodingMap EncodingMap[kMipsLast]; + bool InexpensiveConstantInt(int32_t value); + bool InexpensiveConstantFloat(int32_t value); + bool InexpensiveConstantLong(int64_t value); + bool InexpensiveConstantDouble(int64_t value); - private: - void GenNegLong(RegLocation rl_dest, RegLocation rl_src); - void GenAddLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2); - void GenSubLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2); + bool WideGPRsAreAliases() const OVERRIDE { + return cu_->target64; // Wide GPRs are formed by pairing on mips32. + } + bool WideFPRsAreAliases() const OVERRIDE { + return cu_->target64; // Wide FPRs are formed by pairing on mips32. + } - void ConvertShortToLongBranch(LIR* lir); + LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) OVERRIDE; + + RegLocation GenDivRem(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2, bool is_div, + int flags) OVERRIDE; + RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) OVERRIDE; + NextCallInsn GetNextSDCallInsn() OVERRIDE; + LIR* GenCallInsn(const MirMethodLoweringInfo& method_info) OVERRIDE; + + // Unimplemented intrinsics. + bool GenInlinedCharAt(CallInfo* info ATTRIBUTE_UNUSED) OVERRIDE { + return false; + } + bool GenInlinedAbsInt(CallInfo* info ATTRIBUTE_UNUSED) OVERRIDE { + return false; + } + bool GenInlinedAbsLong(CallInfo* info ATTRIBUTE_UNUSED) OVERRIDE { + return false; + } + bool GenInlinedIndexOf(CallInfo* info ATTRIBUTE_UNUSED, bool zero_based ATTRIBUTE_UNUSED) + OVERRIDE { + return false; + } + + // True if isa is rev R6. + const bool isaIsR6_; + + // True if floating point unit is 32bits. + const bool fpuIs32Bit_; + + private: + void GenNegLong(RegLocation rl_dest, RegLocation rl_src); + void GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + + void ConvertShortToLongBranch(LIR* lir); + + // Mips64 specific long gen methods: + void GenLongOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenNotLong(RegLocation rl_dest, RegLocation rl_src); + void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); + void GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2, bool is_div, int flags); + void GenConversionCall(QuickEntrypointEnum trampoline, RegLocation rl_dest, RegLocation rl_src, + RegisterClass reg_class); + RegStorage AllocPtrSizeTemp(bool required = true); + + /** + * @param reg #RegStorage containing a Solo64 input register (e.g. @c a1 or @c d0). + * @return A Solo32 with the same register number as the @p reg (e.g. @c a1 or @c f0). + * @see As64BitReg + */ + RegStorage As32BitReg(RegStorage reg) { + DCHECK(!reg.IsPair()); + if ((kFailOnSizeError || kReportSizeError) && !reg.Is64Bit()) { + if (kFailOnSizeError) { + LOG(FATAL) << "Expected 64b register"; + } else { + LOG(WARNING) << "Expected 64b register"; + return reg; + } + } + RegStorage ret_val = RegStorage(RegStorage::k32BitSolo, + reg.GetRawBits() & RegStorage::kRegTypeMask); + DCHECK_EQ(GetRegInfo(reg)->FindMatchingView(RegisterInfo::k32SoloStorageMask) + ->GetReg().GetReg(), + ret_val.GetReg()); + return ret_val; + } + + /** + * @param reg #RegStorage containing a Solo32 input register (e.g. @c a1 or @c f0). + * @return A Solo64 with the same register number as the @p reg (e.g. @c a1 or @c d0). + */ + RegStorage As64BitReg(RegStorage reg) { + DCHECK(!reg.IsPair()); + if ((kFailOnSizeError || kReportSizeError) && !reg.Is32Bit()) { + if (kFailOnSizeError) { + LOG(FATAL) << "Expected 32b register"; + } else { + LOG(WARNING) << "Expected 32b register"; + return reg; + } + } + RegStorage ret_val = RegStorage(RegStorage::k64BitSolo, + reg.GetRawBits() & RegStorage::kRegTypeMask); + DCHECK_EQ(GetRegInfo(reg)->FindMatchingView(RegisterInfo::k64SoloStorageMask) + ->GetReg().GetReg(), + ret_val.GetReg()); + return ret_val; + } + + RegStorage Check64BitReg(RegStorage reg) { + if ((kFailOnSizeError || kReportSizeError) && !reg.Is64Bit()) { + if (kFailOnSizeError) { + LOG(FATAL) << "Checked for 64b register"; + } else { + LOG(WARNING) << "Checked for 64b register"; + return As64BitReg(reg); + } + } + return reg; + } }; } // namespace art diff --git a/compiler/dex/quick/mips/fp_mips.cc b/compiler/dex/quick/mips/fp_mips.cc index 37bf1a6b9a..45fd1a9433 100644 --- a/compiler/dex/quick/mips/fp_mips.cc +++ b/compiler/dex/quick/mips/fp_mips.cc @@ -23,8 +23,8 @@ namespace art { -void MipsMir2Lir::GenArithOpFloat(Instruction::Code opcode, - RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { +void MipsMir2Lir::GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2) { int op = kMipsNop; RegLocation rl_result; @@ -51,7 +51,7 @@ void MipsMir2Lir::GenArithOpFloat(Instruction::Code opcode, break; case Instruction::REM_FLOAT_2ADDR: case Instruction::REM_FLOAT: - FlushAllRegs(); // Send everything to home location + FlushAllRegs(); // Send everything to home location. CallRuntimeHelperRegLocationRegLocation(kQuickFmodf, rl_src1, rl_src2, false); rl_result = GetReturn(kFPReg); StoreValue(rl_dest, rl_result); @@ -69,8 +69,8 @@ void MipsMir2Lir::GenArithOpFloat(Instruction::Code opcode, StoreValue(rl_dest, rl_result); } -void MipsMir2Lir::GenArithOpDouble(Instruction::Code opcode, - RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { +void MipsMir2Lir::GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2) { int op = kMipsNop; RegLocation rl_result; @@ -93,7 +93,7 @@ void MipsMir2Lir::GenArithOpDouble(Instruction::Code opcode, break; case Instruction::REM_DOUBLE_2ADDR: case Instruction::REM_DOUBLE: - FlushAllRegs(); // Send everything to home location + FlushAllRegs(); // Send everything to home location. CallRuntimeHelperRegLocationRegLocation(kQuickFmod, rl_src1, rl_src2, false); rl_result = GetReturnWide(kFPReg); StoreValueWide(rl_dest, rl_result); @@ -147,22 +147,22 @@ void MipsMir2Lir::GenConversion(Instruction::Code opcode, RegLocation rl_dest, op = kMipsFcvtdw; break; case Instruction::FLOAT_TO_INT: - GenConversionCall(kQuickF2iz, rl_dest, rl_src); + GenConversionCall(kQuickF2iz, rl_dest, rl_src, kCoreReg); return; case Instruction::DOUBLE_TO_INT: - GenConversionCall(kQuickD2iz, rl_dest, rl_src); + GenConversionCall(kQuickD2iz, rl_dest, rl_src, kCoreReg); return; case Instruction::LONG_TO_DOUBLE: - GenConversionCall(kQuickL2d, rl_dest, rl_src); + GenConversionCall(kQuickL2d, rl_dest, rl_src, kFPReg); return; case Instruction::FLOAT_TO_LONG: - GenConversionCall(kQuickF2l, rl_dest, rl_src); + GenConversionCall(kQuickF2l, rl_dest, rl_src, kCoreReg); return; case Instruction::LONG_TO_FLOAT: - GenConversionCall(kQuickL2f, rl_dest, rl_src); + GenConversionCall(kQuickL2f, rl_dest, rl_src, kFPReg); return; case Instruction::DOUBLE_TO_LONG: - GenConversionCall(kQuickD2l, rl_dest, rl_src); + GenConversionCall(kQuickD2l, rl_dest, rl_src, kCoreReg); return; default: LOG(FATAL) << "Unexpected opcode: " << opcode; @@ -189,24 +189,24 @@ static RegStorage GetWideArgFP(bool fpuIs32Bit, size_t base) { if (fpuIs32Bit) { switch (base) { case 0: - return RegStorage(RegStorage::k64BitPair, rMIPS_FARG0, rMIPS_FARG1); + return RegStorage(RegStorage::k64BitPair, rFARG0, rFARG1); case 2: - return RegStorage(RegStorage::k64BitPair, rMIPS_FARG2, rMIPS_FARG3); + return RegStorage(RegStorage::k64BitPair, rFARG2, rFARG3); } } else { switch (base) { case 0: - return RegStorage(RegStorage::k64BitSolo, rMIPS_FARG0); + return RegStorage(RegStorage::k64BitSolo, rFARG0); case 2: - return RegStorage(RegStorage::k64BitSolo, rMIPS_FARG2); + return RegStorage(RegStorage::k64BitSolo, rFARG2); } } LOG(FATAL) << "Unsupported Mips.GetWideFP: " << fpuIs32Bit << " " << base; UNREACHABLE(); } -void MipsMir2Lir::GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_src2) { +void MipsMir2Lir::GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2) { bool wide = true; QuickEntrypointEnum target; @@ -232,16 +232,23 @@ void MipsMir2Lir::GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, FlushAllRegs(); LockCallTemps(); if (wide) { - RegStorage r_tmp1 = GetWideArgFP(fpuIs32Bit_, 0); - RegStorage r_tmp2 = GetWideArgFP(fpuIs32Bit_, 2); + RegStorage r_tmp1; + RegStorage r_tmp2; + if (cu_->target64) { + r_tmp1 = RegStorage(RegStorage::k64BitSolo, rFARG0); + r_tmp2 = RegStorage(RegStorage::k64BitSolo, rFARG1); + } else { + r_tmp1 = GetWideArgFP(fpuIs32Bit_, 0); + r_tmp2 = GetWideArgFP(fpuIs32Bit_, 2); + } LoadValueDirectWideFixed(rl_src1, r_tmp1); LoadValueDirectWideFixed(rl_src2, r_tmp2); } else { - LoadValueDirectFixed(rl_src1, rs_rMIPS_FARG0); - LoadValueDirectFixed(rl_src2, rs_rMIPS_FARG2); + LoadValueDirectFixed(rl_src1, rs_rFARG0); + LoadValueDirectFixed(rl_src2, cu_->target64 ? rs_rFARG1 : rs_rFARG2); } RegStorage r_tgt = LoadHelper(target); - // NOTE: not a safepoint + // NOTE: not a safepoint. OpReg(kOpBlx, r_tgt); RegLocation rl_result = GetReturn(kCoreReg); StoreValue(rl_dest, rl_result); @@ -254,18 +261,30 @@ void MipsMir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bo void MipsMir2Lir::GenNegFloat(RegLocation rl_dest, RegLocation rl_src) { RegLocation rl_result; - rl_src = LoadValue(rl_src, kCoreReg); - rl_result = EvalLoc(rl_dest, kCoreReg, true); - OpRegRegImm(kOpAdd, rl_result.reg, rl_src.reg, 0x80000000); + if (cu_->target64) { + rl_src = LoadValue(rl_src, kFPReg); + rl_result = EvalLoc(rl_dest, kFPReg, true); + NewLIR2(kMipsFnegs, rl_result.reg.GetReg(), rl_src.reg.GetReg()); + } else { + rl_src = LoadValue(rl_src, kCoreReg); + rl_result = EvalLoc(rl_dest, kCoreReg, true); + OpRegRegImm(kOpAdd, rl_result.reg, rl_src.reg, 0x80000000); + } StoreValue(rl_dest, rl_result); } void MipsMir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src) { RegLocation rl_result; - rl_src = LoadValueWide(rl_src, kCoreReg); - rl_result = EvalLoc(rl_dest, kCoreReg, true); - OpRegRegImm(kOpAdd, rl_result.reg.GetHigh(), rl_src.reg.GetHigh(), 0x80000000); - OpRegCopy(rl_result.reg, rl_src.reg); + if (cu_->target64) { + rl_src = LoadValueWide(rl_src, kFPReg); + rl_result = EvalLocWide(rl_dest, kFPReg, true); + NewLIR2(kMipsFnegd, rl_result.reg.GetReg(), rl_src.reg.GetReg()); + } else { + rl_src = LoadValueWide(rl_src, kCoreReg); + rl_result = EvalLoc(rl_dest, kCoreReg, true); + OpRegRegImm(kOpAdd, rl_result.reg.GetHigh(), rl_src.reg.GetHigh(), 0x80000000); + OpRegCopy(rl_result.reg, rl_src.reg); + } StoreValueWide(rl_dest, rl_result); } diff --git a/compiler/dex/quick/mips/int_mips.cc b/compiler/dex/quick/mips/int_mips.cc index 8093c9772c..626b36ea28 100644 --- a/compiler/dex/quick/mips/int_mips.cc +++ b/compiler/dex/quick/mips/int_mips.cc @@ -34,6 +34,7 @@ namespace art { * x < y return -1 * x > y return 1 * + * Mips32 implementation * slt t0, x.hi, y.hi; # (x.hi < y.hi) ? 1:0 * sgt t1, x.hi, y.hi; # (y.hi > x.hi) ? 1:0 * subu res, t0, t1 # res = -1:1:0 for [ < > = ] @@ -43,26 +44,40 @@ namespace art { * subu res, t0, t1 * finish: * + * Mips64 implementation + * slt temp, x, y; # (x < y) ? 1:0 + * slt res, y, x; # (x > y) ? 1:0 + * subu res, res, temp; # res = -1:1:0 for [ < > = ] + * */ -void MipsMir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2) { +void MipsMir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { rl_src1 = LoadValueWide(rl_src1, kCoreReg); rl_src2 = LoadValueWide(rl_src2, kCoreReg); - RegStorage t0 = AllocTemp(); - RegStorage t1 = AllocTemp(); - RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - NewLIR3(kMipsSlt, t0.GetReg(), rl_src1.reg.GetHighReg(), rl_src2.reg.GetHighReg()); - NewLIR3(kMipsSlt, t1.GetReg(), rl_src2.reg.GetHighReg(), rl_src1.reg.GetHighReg()); - NewLIR3(kMipsSubu, rl_result.reg.GetReg(), t1.GetReg(), t0.GetReg()); - LIR* branch = OpCmpImmBranch(kCondNe, rl_result.reg, 0, NULL); - NewLIR3(kMipsSltu, t0.GetReg(), rl_src1.reg.GetLowReg(), rl_src2.reg.GetLowReg()); - NewLIR3(kMipsSltu, t1.GetReg(), rl_src2.reg.GetLowReg(), rl_src1.reg.GetLowReg()); - NewLIR3(kMipsSubu, rl_result.reg.GetReg(), t1.GetReg(), t0.GetReg()); - FreeTemp(t0); - FreeTemp(t1); - LIR* target = NewLIR0(kPseudoTargetLabel); - branch->target = target; - StoreValue(rl_dest, rl_result); + if (cu_->target64) { + RegStorage temp = AllocTempWide(); + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + NewLIR3(kMipsSlt, temp.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); + NewLIR3(kMipsSlt, rl_result.reg.GetReg(), rl_src2.reg.GetReg(), rl_src1.reg.GetReg()); + NewLIR3(kMipsSubu, rl_result.reg.GetReg(), rl_result.reg.GetReg(), temp.GetReg()); + FreeTemp(temp); + StoreValue(rl_dest, rl_result); + } else { + RegStorage t0 = AllocTemp(); + RegStorage t1 = AllocTemp(); + RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); + NewLIR3(kMipsSlt, t0.GetReg(), rl_src1.reg.GetHighReg(), rl_src2.reg.GetHighReg()); + NewLIR3(kMipsSlt, t1.GetReg(), rl_src2.reg.GetHighReg(), rl_src1.reg.GetHighReg()); + NewLIR3(kMipsSubu, rl_result.reg.GetReg(), t1.GetReg(), t0.GetReg()); + LIR* branch = OpCmpImmBranch(kCondNe, rl_result.reg, 0, NULL); + NewLIR3(kMipsSltu, t0.GetReg(), rl_src1.reg.GetLowReg(), rl_src2.reg.GetLowReg()); + NewLIR3(kMipsSltu, t1.GetReg(), rl_src2.reg.GetLowReg(), rl_src1.reg.GetLowReg()); + NewLIR3(kMipsSubu, rl_result.reg.GetReg(), t1.GetReg(), t0.GetReg()); + FreeTemp(t0); + FreeTemp(t1); + LIR* target = NewLIR0(kPseudoTargetLabel); + branch->target = target; + StoreValue(rl_dest, rl_result); + } } LIR* MipsMir2Lir::OpCmpBranch(ConditionCode cond, RegStorage src1, RegStorage src2, LIR* target) { @@ -134,7 +149,7 @@ LIR* MipsMir2Lir::OpCmpBranch(ConditionCode cond, RegStorage src1, RegStorage sr LIR* MipsMir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_value, LIR* target) { LIR* branch; if (check_value != 0) { - // TUNING: handle s16 & kCondLt/Mi case using slti + // TUNING: handle s16 & kCondLt/Mi case using slti. RegStorage t_reg = AllocTemp(); LoadConstant(t_reg, check_value); branch = OpCmpBranch(cond, reg, t_reg, target); @@ -164,17 +179,34 @@ LIR* MipsMir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_v } LIR* MipsMir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) { - // If src or dest is a pair, we'll be using low reg. - if (r_dest.IsPair()) { - r_dest = r_dest.GetLow(); - } - if (r_src.IsPair()) { - r_src = r_src.GetLow(); + LIR* res; + MipsOpCode opcode; + + if (!cu_->target64) { + // If src or dest is a pair, we'll be using low reg. + if (r_dest.IsPair()) { + r_dest = r_dest.GetLow(); + } + if (r_src.IsPair()) { + r_src = r_src.GetLow(); + } + } else { + DCHECK(!r_dest.IsPair() && !r_src.IsPair()); } + if (r_dest.IsFloat() || r_src.IsFloat()) return OpFpRegCopy(r_dest, r_src); - LIR* res = RawLIR(current_dalvik_offset_, kMipsMove, - r_dest.GetReg(), r_src.GetReg()); + if (cu_->target64) { + // TODO: Check that r_src and r_dest are both 32 or both 64 bits length on Mips64. + if (r_dest.Is64Bit() || r_src.Is64Bit()) { + opcode = kMipsMove; + } else { + opcode = kMipsSll; + } + } else { + opcode = kMipsMove; + } + res = RawLIR(current_dalvik_offset_, opcode, r_dest.GetReg(), r_src.GetReg()); if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) { res->flags.is_nop = true; } @@ -189,6 +221,10 @@ void MipsMir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) { } void MipsMir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) { + if (cu_->target64) { + OpRegCopy(r_dest, r_src); + return; + } if (r_dest != r_src) { bool dest_fp = r_dest.IsFloat(); bool src_fp = r_src.IsFloat(); @@ -213,16 +249,16 @@ void MipsMir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) { if (src_fp) { // Here if dest is core reg and src is fp reg. if (fpuIs32Bit_) { - NewLIR2(kMipsMfc1, r_dest.GetLowReg(), r_src.GetLowReg()); - NewLIR2(kMipsMfc1, r_dest.GetHighReg(), r_src.GetHighReg()); + NewLIR2(kMipsMfc1, r_dest.GetLowReg(), r_src.GetLowReg()); + NewLIR2(kMipsMfc1, r_dest.GetHighReg(), r_src.GetHighReg()); } else { - r_src = Fp64ToSolo32(r_src); - NewLIR2(kMipsMfc1, r_dest.GetLowReg(), r_src.GetReg()); - NewLIR2(kMipsMfhc1, r_dest.GetHighReg(), r_src.GetReg()); + r_src = Fp64ToSolo32(r_src); + NewLIR2(kMipsMfc1, r_dest.GetLowReg(), r_src.GetReg()); + NewLIR2(kMipsMfhc1, r_dest.GetHighReg(), r_src.GetReg()); } } else { // Here if both src and dest are core registers. - // Handle overlap + // Handle overlap. if (r_src.GetHighReg() == r_dest.GetLowReg()) { OpRegCopy(r_dest.GetHigh(), r_src.GetHigh()); OpRegCopy(r_dest.GetLow(), r_src.GetLow()); @@ -263,17 +299,15 @@ RegLocation MipsMir2Lir::GenDivRem(RegLocation rl_dest, RegStorage reg1, RegStor RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); if (isaIsR6_) { - NewLIR3(is_div ? kMipsR6Div : kMipsR6Mod, - rl_result.reg.GetReg(), reg1.GetReg(), reg2.GetReg()); + NewLIR3(is_div ? kMipsR6Div : kMipsR6Mod, rl_result.reg.GetReg(), reg1.GetReg(), reg2.GetReg()); } else { - NewLIR2(kMipsDiv, reg1.GetReg(), reg2.GetReg()); - NewLIR1(is_div ? kMipsMflo : kMipsMfhi, rl_result.reg.GetReg()); + NewLIR2(kMipsR2Div, reg1.GetReg(), reg2.GetReg()); + NewLIR1(is_div ? kMipsR2Mflo : kMipsR2Mfhi, rl_result.reg.GetReg()); } return rl_result; } -RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int lit, - bool is_div) { +RegLocation MipsMir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int lit, bool is_div) { RegStorage t_reg = AllocTemp(); NewLIR3(kMipsAddiu, t_reg.GetReg(), rZERO, lit); RegLocation rl_result = GenDivRem(rl_dest, reg1, t_reg, is_div); @@ -322,10 +356,17 @@ bool MipsMir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) { // MIPS supports only aligned access. Defer unaligned access to JNI implementation. return false; } - RegLocation rl_src_address = info->args[0]; // long address - rl_src_address = NarrowRegLoc(rl_src_address); // ignore high half in info->args[1] + RegLocation rl_src_address = info->args[0]; // Long address. + if (!cu_->target64) { + rl_src_address = NarrowRegLoc(rl_src_address); // Ignore high half in info->args[1]. + } RegLocation rl_dest = InlineTarget(info); - RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); + RegLocation rl_address; + if (cu_->target64) { + rl_address = LoadValueWide(rl_src_address, kCoreReg); + } else { + rl_address = LoadValue(rl_src_address, kCoreReg); + } RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); DCHECK(size == kSignedByte); LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile); @@ -338,10 +379,17 @@ bool MipsMir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) { // MIPS supports only aligned access. Defer unaligned access to JNI implementation. return false; } - RegLocation rl_src_address = info->args[0]; // long address - rl_src_address = NarrowRegLoc(rl_src_address); // ignore high half in info->args[1] - RegLocation rl_src_value = info->args[2]; // [size] value - RegLocation rl_address = LoadValue(rl_src_address, kCoreReg); + RegLocation rl_src_address = info->args[0]; // Long address. + if (!cu_->target64) { + rl_src_address = NarrowRegLoc(rl_src_address); // Ignore high half in info->args[1]. + } + RegLocation rl_src_value = info->args[2]; // [size] value. + RegLocation rl_address; + if (cu_->target64) { + rl_address = LoadValueWide(rl_src_address, kCoreReg); + } else { + rl_address = LoadValue(rl_src_address, kCoreReg); + } DCHECK(size == kSignedByte); RegLocation rl_value = LoadValue(rl_src_value, kCoreReg); StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size, kNotVolatile); @@ -366,8 +414,7 @@ LIR* MipsMir2Lir::OpVstm(RegStorage r_base, int count) { UNREACHABLE(); } -void MipsMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src, - RegLocation rl_result, int lit, +void MipsMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit, int first_bit, int second_bit) { UNUSED(lit); RegStorage t_reg = AllocTemp(); @@ -380,20 +427,24 @@ void MipsMir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src, } void MipsMir2Lir::GenDivZeroCheckWide(RegStorage reg) { - DCHECK(reg.IsPair()); // TODO: support k64BitSolo. - RegStorage t_reg = AllocTemp(); - OpRegRegReg(kOpOr, t_reg, reg.GetLow(), reg.GetHigh()); - GenDivZeroCheck(t_reg); - FreeTemp(t_reg); + if (cu_->target64) { + GenDivZeroCheck(reg); + } else { + DCHECK(reg.IsPair()); // TODO: support k64BitSolo. + RegStorage t_reg = AllocTemp(); + OpRegRegReg(kOpOr, t_reg, reg.GetLow(), reg.GetHigh()); + GenDivZeroCheck(t_reg); + FreeTemp(t_reg); + } } -// Test suspend flag, return target of taken suspend branch +// Test suspend flag, return target of taken suspend branch. LIR* MipsMir2Lir::OpTestSuspend(LIR* target) { - OpRegImm(kOpSub, rs_rMIPS_SUSPEND, 1); - return OpCmpImmBranch((target == NULL) ? kCondEq : kCondNe, rs_rMIPS_SUSPEND, 0, target); + OpRegImm(kOpSub, TargetPtrReg(kSuspend), 1); + return OpCmpImmBranch((target == NULL) ? kCondEq : kCondNe, TargetPtrReg(kSuspend), 0, target); } -// Decrement register and branch on condition +// Decrement register and branch on condition. LIR* MipsMir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* target) { OpRegImm(kOpSub, reg, 1); return OpCmpImmBranch(c_code, reg, 0, target); @@ -423,9 +474,7 @@ void MipsMir2Lir::OpEndIT(LIR* it) { LOG(FATAL) << "Unexpected use of OpEndIT in Mips"; } -void MipsMir2Lir::GenAddLong(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_src2) { - UNUSED(opcode); +void MipsMir2Lir::GenAddLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { rl_src1 = LoadValueWide(rl_src1, kCoreReg); rl_src2 = LoadValueWide(rl_src2, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); @@ -440,15 +489,14 @@ void MipsMir2Lir::GenAddLong(Instruction::Code opcode, RegLocation rl_dest, OpRegRegReg(kOpAdd, rl_result.reg.GetLow(), rl_src2.reg.GetLow(), rl_src1.reg.GetLow()); RegStorage t_reg = AllocTemp(); OpRegRegReg(kOpAdd, t_reg, rl_src2.reg.GetHigh(), rl_src1.reg.GetHigh()); - NewLIR3(kMipsSltu, rl_result.reg.GetHighReg(), rl_result.reg.GetLowReg(), rl_src2.reg.GetLowReg()); + NewLIR3(kMipsSltu, rl_result.reg.GetHighReg(), rl_result.reg.GetLowReg(), + rl_src2.reg.GetLowReg()); OpRegRegReg(kOpAdd, rl_result.reg.GetHigh(), rl_result.reg.GetHigh(), t_reg); FreeTemp(t_reg); StoreValueWide(rl_dest, rl_result); } -void MipsMir2Lir::GenSubLong(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_src2) { - UNUSED(opcode); +void MipsMir2Lir::GenSubLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { rl_src1 = LoadValueWide(rl_src1, kCoreReg); rl_src2 = LoadValueWide(rl_src2, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); @@ -471,47 +519,136 @@ void MipsMir2Lir::GenSubLong(Instruction::Code opcode, RegLocation rl_dest, void MipsMir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2, int flags) { - switch (opcode) { - case Instruction::ADD_LONG: - case Instruction::ADD_LONG_2ADDR: - GenAddLong(opcode, rl_dest, rl_src1, rl_src2); - return; - case Instruction::SUB_LONG: - case Instruction::SUB_LONG_2ADDR: - GenSubLong(opcode, rl_dest, rl_src1, rl_src2); - return; - case Instruction::NEG_LONG: - GenNegLong(rl_dest, rl_src2); - return; - - default: - break; + if (cu_->target64) { + switch (opcode) { + case Instruction::NOT_LONG: + GenNotLong(rl_dest, rl_src2); + return; + case Instruction::ADD_LONG: + case Instruction::ADD_LONG_2ADDR: + GenLongOp(kOpAdd, rl_dest, rl_src1, rl_src2); + return; + case Instruction::SUB_LONG: + case Instruction::SUB_LONG_2ADDR: + GenLongOp(kOpSub, rl_dest, rl_src1, rl_src2); + return; + case Instruction::MUL_LONG: + case Instruction::MUL_LONG_2ADDR: + GenMulLong(rl_dest, rl_src1, rl_src2); + return; + case Instruction::DIV_LONG: + case Instruction::DIV_LONG_2ADDR: + GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ true, flags); + return; + case Instruction::REM_LONG: + case Instruction::REM_LONG_2ADDR: + GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ false, flags); + return; + case Instruction::AND_LONG: + case Instruction::AND_LONG_2ADDR: + GenLongOp(kOpAnd, rl_dest, rl_src1, rl_src2); + return; + case Instruction::OR_LONG: + case Instruction::OR_LONG_2ADDR: + GenLongOp(kOpOr, rl_dest, rl_src1, rl_src2); + return; + case Instruction::XOR_LONG: + case Instruction::XOR_LONG_2ADDR: + GenLongOp(kOpXor, rl_dest, rl_src1, rl_src2); + return; + case Instruction::NEG_LONG: + GenNegLong(rl_dest, rl_src2); + return; + + default: + LOG(FATAL) << "Invalid long arith op"; + return; + } + } else { + switch (opcode) { + case Instruction::ADD_LONG: + case Instruction::ADD_LONG_2ADDR: + GenAddLong(rl_dest, rl_src1, rl_src2); + return; + case Instruction::SUB_LONG: + case Instruction::SUB_LONG_2ADDR: + GenSubLong(rl_dest, rl_src1, rl_src2); + return; + case Instruction::NEG_LONG: + GenNegLong(rl_dest, rl_src2); + return; + default: + break; + } + // Fallback for all other ops. + Mir2Lir::GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags); } +} - // Fallback for all other ops. - Mir2Lir::GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags); +void MipsMir2Lir::GenLongOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2) { + rl_src1 = LoadValueWide(rl_src1, kCoreReg); + rl_src2 = LoadValueWide(rl_src2, kCoreReg); + RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); + OpRegRegReg(op, rl_result.reg, rl_src1.reg, rl_src2.reg); + StoreValueWide(rl_dest, rl_result); } -void MipsMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) { +void MipsMir2Lir::GenNotLong(RegLocation rl_dest, RegLocation rl_src) { rl_src = LoadValueWide(rl_src, kCoreReg); - RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - /* - * [v1 v0] = -[a1 a0] - * negu v0,a0 - * negu v1,a1 - * sltu t1,r_zero - * subu v1,v1,t1 - */ + RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); + OpRegReg(kOpMvn, rl_result.reg, rl_src.reg); + StoreValueWide(rl_dest, rl_result); +} - OpRegReg(kOpNeg, rl_result.reg.GetLow(), rl_src.reg.GetLow()); - OpRegReg(kOpNeg, rl_result.reg.GetHigh(), rl_src.reg.GetHigh()); - RegStorage t_reg = AllocTemp(); - NewLIR3(kMipsSltu, t_reg.GetReg(), rZERO, rl_result.reg.GetLowReg()); - OpRegRegReg(kOpSub, rl_result.reg.GetHigh(), rl_result.reg.GetHigh(), t_reg); - FreeTemp(t_reg); +void MipsMir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { + rl_src1 = LoadValueWide(rl_src1, kCoreReg); + rl_src2 = LoadValueWide(rl_src2, kCoreReg); + RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); + NewLIR3(kMips64Dmul, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); + StoreValueWide(rl_dest, rl_result); +} + +void MipsMir2Lir::GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_src2, bool is_div, int flags) { + UNUSED(opcode); + // TODO: Implement easy div/rem? + rl_src1 = LoadValueWide(rl_src1, kCoreReg); + rl_src2 = LoadValueWide(rl_src2, kCoreReg); + if ((flags & MIR_IGNORE_DIV_ZERO_CHECK) == 0) { + GenDivZeroCheckWide(rl_src2.reg); + } + RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); + NewLIR3(is_div ? kMips64Ddiv : kMips64Dmod, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), + rl_src2.reg.GetReg()); StoreValueWide(rl_dest, rl_result); } +void MipsMir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) { + rl_src = LoadValueWide(rl_src, kCoreReg); + RegLocation rl_result; + + if (cu_->target64) { + rl_result = EvalLocWide(rl_dest, kCoreReg, true); + OpRegReg(kOpNeg, rl_result.reg, rl_src.reg); + StoreValueWide(rl_dest, rl_result); + } else { + rl_result = EvalLoc(rl_dest, kCoreReg, true); + // [v1 v0] = -[a1 a0] + // negu v0,a0 + // negu v1,a1 + // sltu t1,r_zero + // subu v1,v1,t1 + OpRegReg(kOpNeg, rl_result.reg.GetLow(), rl_src.reg.GetLow()); + OpRegReg(kOpNeg, rl_result.reg.GetHigh(), rl_src.reg.GetHigh()); + RegStorage t_reg = AllocTemp(); + NewLIR3(kMipsSltu, t_reg.GetReg(), rZERO, rl_result.reg.GetLowReg()); + OpRegRegReg(kOpSub, rl_result.reg.GetHigh(), rl_result.reg.GetHigh(), t_reg); + FreeTemp(t_reg); + StoreValueWide(rl_dest, rl_result); + } +} + /* * Generate array load */ @@ -532,18 +669,18 @@ void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); } - /* null object? */ + // Null object? GenNullCheck(rl_array.reg, opt_flags); - RegStorage reg_ptr = AllocTemp(); + RegStorage reg_ptr = (cu_->target64) ? AllocTempRef() : AllocTemp(); bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK)); RegStorage reg_len; if (needs_range_check) { reg_len = AllocTemp(); - /* Get len */ + // Get len. Load32Disp(rl_array.reg, len_offset, reg_len); } - /* reg_ptr -> array data */ + // reg_ptr -> array data. OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset); FreeTemp(rl_array.reg); if ((size == k64) || (size == kDouble)) { @@ -573,7 +710,17 @@ void MipsMir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } - LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size); + + if (cu_->target64) { + if (rl_result.ref) { + LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), As32BitReg(rl_result.reg), scale, + kReference); + } else { + LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_result.reg, scale, size); + } + } else { + LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size); + } FreeTemp(reg_ptr); StoreValue(rl_dest, rl_result); @@ -612,7 +759,7 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, allocated_reg_ptr_temp = true; } - /* null object? */ + // Null object? GenNullCheck(rl_array.reg, opt_flags); bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK)); @@ -620,14 +767,14 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, if (needs_range_check) { reg_len = AllocTemp(); // NOTE: max live temps(4) here. - /* Get len */ + // Get len. Load32Disp(rl_array.reg, len_offset, reg_len); } - /* reg_ptr -> array data */ + // reg_ptr -> array data. OpRegImm(kOpAdd, reg_ptr, data_offset); - /* at this point, reg_ptr points to array, 2 live temps */ + // At this point, reg_ptr points to array, 2 live temps. if ((size == k64) || (size == kDouble)) { - // TUNING: specific wide routine that can handle fp regs + // TUNING: specific wide routine that can handle fp regs. if (scale) { RegStorage r_new_index = AllocTemp(); OpRegRegImm(kOpLsl, r_new_index, rl_index.reg, scale); @@ -660,18 +807,104 @@ void MipsMir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, } } +void MipsMir2Lir::GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, + RegLocation rl_shift) { + if (!cu_->target64) { + Mir2Lir::GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift); + return; + } + OpKind op = kOpBkpt; + switch (opcode) { + case Instruction::SHL_LONG: + case Instruction::SHL_LONG_2ADDR: + op = kOpLsl; + break; + case Instruction::SHR_LONG: + case Instruction::SHR_LONG_2ADDR: + op = kOpAsr; + break; + case Instruction::USHR_LONG: + case Instruction::USHR_LONG_2ADDR: + op = kOpLsr; + break; + default: + LOG(FATAL) << "Unexpected case: " << opcode; + } + rl_shift = LoadValue(rl_shift, kCoreReg); + rl_src1 = LoadValueWide(rl_src1, kCoreReg); + RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); + OpRegRegReg(op, rl_result.reg, rl_src1.reg, As64BitReg(rl_shift.reg)); + StoreValueWide(rl_dest, rl_result); +} + void MipsMir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_shift, int flags) { UNUSED(flags); - // Default implementation is just to ignore the constant case. - GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift); + if (!cu_->target64) { + // Default implementation is just to ignore the constant case. + GenShiftOpLong(opcode, rl_dest, rl_src1, rl_shift); + return; + } + OpKind op = kOpBkpt; + // Per spec, we only care about low 6 bits of shift amount. + int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f; + rl_src1 = LoadValueWide(rl_src1, kCoreReg); + if (shift_amount == 0) { + StoreValueWide(rl_dest, rl_src1); + return; + } + + RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); + switch (opcode) { + case Instruction::SHL_LONG: + case Instruction::SHL_LONG_2ADDR: + op = kOpLsl; + break; + case Instruction::SHR_LONG: + case Instruction::SHR_LONG_2ADDR: + op = kOpAsr; + break; + case Instruction::USHR_LONG: + case Instruction::USHR_LONG_2ADDR: + op = kOpLsr; + break; + default: + LOG(FATAL) << "Unexpected case"; + } + OpRegRegImm(op, rl_result.reg, rl_src1.reg, shift_amount); + StoreValueWide(rl_dest, rl_result); } -void MipsMir2Lir::GenArithImmOpLong(Instruction::Code opcode, - RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2, - int flags) { +void MipsMir2Lir::GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, + RegLocation rl_src1, RegLocation rl_src2, int flags) { // Default - bail to non-const handler. GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags); } +void MipsMir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) { + if (!cu_->target64) { + Mir2Lir::GenIntToLong(rl_dest, rl_src); + return; + } + rl_src = LoadValue(rl_src, kCoreReg); + RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); + NewLIR3(kMipsSll, rl_result.reg.GetReg(), As64BitReg(rl_src.reg).GetReg(), 0); + StoreValueWide(rl_dest, rl_result); +} + +void MipsMir2Lir::GenConversionCall(QuickEntrypointEnum trampoline, RegLocation rl_dest, + RegLocation rl_src, RegisterClass reg_class) { + FlushAllRegs(); // Send everything to home location. + CallRuntimeHelperRegLocation(trampoline, rl_src, false); + if (rl_dest.wide) { + RegLocation rl_result; + rl_result = GetReturnWide(reg_class); + StoreValueWide(rl_dest, rl_result); + } else { + RegLocation rl_result; + rl_result = GetReturn(reg_class); + StoreValue(rl_dest, rl_result); + } +} + } // namespace art diff --git a/compiler/dex/quick/mips/mips_lir.h b/compiler/dex/quick/mips/mips_lir.h index 70370559bc..078ac0a2ad 100644 --- a/compiler/dex/quick/mips/mips_lir.h +++ b/compiler/dex/quick/mips/mips_lir.h @@ -25,25 +25,29 @@ namespace art { /* * Runtime register conventions. * - * zero is always the value 0 - * at is scratch (normally used as temp reg by assembler) - * v0, v1 are scratch (normally hold subroutine return values) - * a0-a3 are scratch (normally hold subroutine arguments) - * t0-t8 are scratch - * t9 is scratch (normally used for function calls) - * s0 (rMIPS_SUSPEND) is reserved [holds suspend-check counter] - * s1 (rMIPS_SELF) is reserved [holds current &Thread] - * s2-s7 are callee save (promotion target) - * k0, k1 are reserved for use by interrupt handlers - * gp is reserved for global pointer - * sp is reserved - * s8 is callee save (promotion target) - * ra is scratch (normally holds the return addr) + * mips32 | mips64 + * $0: zero is always the value 0 + * $1: at is scratch (normally used as temp reg by assembler) + * $2,$3: v0, v1 are scratch (normally hold subroutine return values) + * $4-$7: a0-a3 are scratch (normally hold subroutine arguments) + * $8-$11: t0-t3 are scratch | a4-a7 are scratch (normally hold subroutine arguments) + * $12-$15: t4-t7 are scratch | t0-t3 are scratch + * $16: s0 (rSUSPEND) is reserved [holds suspend-check counter] + * $17: s1 (rSELF) is reserved [holds current &Thread] + * $18-$23: s2-s7 are callee save (promotion target) + * $24: t8 is scratch + * $25: t9 is scratch (normally used for function calls) + * $26,$27: k0, k1 are reserved for use by interrupt handlers + * $28: gp is reserved for global pointer + * $29: sp is reserved + * $30: s8 is callee save (promotion target) + * $31: ra is scratch (normally holds the return addr) * * Preserved across C calls: s0-s8 - * Trashed across C calls: at, v0-v1, a0-a3, t0-t9, gp, ra + * Trashed across C calls (mips32): at, v0-v1, a0-a3, t0-t9, gp, ra + * Trashed across C calls (mips64): at, v0-v1, a0-a7, t0-t3, t8, t9, gp, ra * - * Floating pointer registers + * Floating pointer registers (mips32) * NOTE: there are 32 fp registers (16 df pairs), but currently * only support 16 fp registers (8 df pairs). * f0-f15 @@ -51,14 +55,23 @@ namespace art { * * f0-f15 (df0-df7) trashed across C calls * + * Floating pointer registers (mips64) + * NOTE: there are 32 fp registers. + * f0-f31 + * * For mips32 code use: * a0-a3 to hold operands * v0-v1 to hold results * t0-t9 for temps * + * For mips64 code use: + * a0-a7 to hold operands + * v0-v1 to hold results + * t0-t3, t8-t9 for temps + * * All jump/branch instructions have a delay slot after it. * - * Stack frame diagram (stack grows down, higher addresses at top): + * Stack frame diagram (stack grows down, higher addresses at top): * * +------------------------+ * | IN[ins-1] | {Note: resides in caller's frame} @@ -90,18 +103,6 @@ namespace art { #define LOWORD_OFFSET 0 #define HIWORD_OFFSET 4 -#define rARG0 rA0 -#define rs_rARG0 rs_rA0 -#define rARG1 rA1 -#define rs_rARG1 rs_rA1 -#define rARG2 rA2 -#define rs_rARG2 rs_rA2 -#define rARG3 rA3 -#define rs_rARG3 rs_rA3 -#define rRESULT0 rV0 -#define rs_rRESULT0 rs_rV0 -#define rRESULT1 rV1 -#define rs_rRESULT1 rs_rV1 #define rFARG0 rF12 #define rs_rFARG0 rs_rF12 @@ -111,14 +112,6 @@ namespace art { #define rs_rFARG2 rs_rF14 #define rFARG3 rF15 #define rs_rFARG3 rs_rF15 -#define rFRESULT0 rF0 -#define rs_rFRESULT0 rs_rF0 -#define rFRESULT1 rF1 -#define rs_rFRESULT1 rs_rF1 - -// Regs not used for Mips. -#define rMIPS_LR RegStorage::kInvalidRegVal -#define rMIPS_PC RegStorage::kInvalidRegVal enum MipsResourceEncodingPos { kMipsGPReg0 = 0, @@ -130,6 +123,10 @@ enum MipsResourceEncodingPos { kMipsRegLO, kMipsRegPC, kMipsRegEnd = 51, + // Mips64 related: + kMips64FPRegEnd = 64, + kMips64RegPC = kMips64FPRegEnd, + kMips64RegEnd = 65, }; #define ENCODE_MIPS_REG_LIST(N) (static_cast<uint64_t>(N)) @@ -144,38 +141,78 @@ enum MipsResourceEncodingPos { #define FR_BIT 0 enum MipsNativeRegisterPool { // private marker to avoid generate-operator-out.py from processing. - rZERO = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 0, - rAT = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 1, - rV0 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 2, - rV1 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 3, - rA0 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 4, - rA1 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 5, - rA2 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 6, - rA3 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 7, - rT0 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 8, - rT1 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 9, - rT2 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 10, - rT3 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 11, - rT4 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 12, - rT5 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 13, - rT6 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 14, - rT7 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 15, - rS0 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 16, - rS1 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 17, - rS2 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 18, - rS3 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 19, - rS4 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 20, - rS5 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 21, - rS6 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 22, - rS7 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 23, - rT8 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 24, - rT9 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 25, - rK0 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 26, - rK1 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 27, - rGP = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 28, - rSP = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 29, - rFP = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 30, - rRA = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 31, + rZERO = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 0, + rZEROd = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 0, + rAT = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 1, + rATd = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 1, + rV0 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 2, + rV0d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 2, + rV1 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 3, + rV1d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 3, + rA0 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 4, + rA0d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 4, + rA1 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 5, + rA1d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 5, + rA2 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 6, + rA2d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 6, + rA3 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 7, + rA3d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 7, + rT0_32 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 8, + rA4 = rT0_32, + rA4d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 8, + rT1_32 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 9, + rA5 = rT1_32, + rA5d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 9, + rT2_32 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 10, + rA6 = rT2_32, + rA6d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 10, + rT3_32 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 11, + rA7 = rT3_32, + rA7d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 11, + rT4_32 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 12, + rT0 = rT4_32, + rT0d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 12, + rT5_32 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 13, + rT1 = rT5_32, + rT1d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 13, + rT6_32 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 14, + rT2 = rT6_32, + rT2d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 14, + rT7_32 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 15, + rT3 = rT7_32, + rT3d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 15, + rS0 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 16, + rS0d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 16, + rS1 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 17, + rS1d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 17, + rS2 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 18, + rS2d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 18, + rS3 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 19, + rS3d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 19, + rS4 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 20, + rS4d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 20, + rS5 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 21, + rS5d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 21, + rS6 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 22, + rS6d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 22, + rS7 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 23, + rS7d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 23, + rT8 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 24, + rT8d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 24, + rT9 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 25, + rT9d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 25, + rK0 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 26, + rK0d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 26, + rK1 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 27, + rK1d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 27, + rGP = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 28, + rGPd = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 28, + rSP = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 29, + rSPd = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 29, + rFP = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 30, + rFPd = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 30, + rRA = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 31, + rRAd = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 31, rF0 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 0, rF1 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 1, @@ -193,6 +230,24 @@ enum MipsNativeRegisterPool { // private marker to avoid generate-operator-out. rF13 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 13, rF14 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 14, rF15 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 15, + + rF16 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 16, + rF17 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 17, + rF18 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 18, + rF19 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 19, + rF20 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 20, + rF21 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 21, + rF22 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 22, + rF23 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 23, + rF24 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 24, + rF25 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 25, + rF26 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 26, + rF27 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 27, + rF28 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 28, + rF29 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 29, + rF30 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 30, + rF31 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 31, + #if 0 /* * TODO: The shared resource mask doesn't have enough bit positions to describe all @@ -253,6 +308,39 @@ enum MipsNativeRegisterPool { // private marker to avoid generate-operator-out. rD14_fr1 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 28, rD15_fr1 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 30, #endif + + rD0 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 0, + rD1 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 1, + rD2 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 2, + rD3 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 3, + rD4 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 4, + rD5 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 5, + rD6 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 6, + rD7 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 7, + rD8 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 8, + rD9 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 9, + rD10 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 10, + rD11 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 11, + rD12 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 12, + rD13 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 13, + rD14 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 14, + rD15 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 15, + rD16 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 16, + rD17 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 17, + rD18 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 18, + rD19 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 19, + rD20 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 20, + rD21 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 21, + rD22 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 22, + rD23 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 23, + rD24 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 24, + rD25 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 25, + rD26 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 26, + rD27 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 27, + rD28 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 28, + rD29 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 29, + rD30 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 30, + rD31 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 31, }; constexpr RegStorage rs_rZERO(RegStorage::kValid | rZERO); @@ -263,14 +351,22 @@ constexpr RegStorage rs_rA0(RegStorage::kValid | rA0); constexpr RegStorage rs_rA1(RegStorage::kValid | rA1); constexpr RegStorage rs_rA2(RegStorage::kValid | rA2); constexpr RegStorage rs_rA3(RegStorage::kValid | rA3); -constexpr RegStorage rs_rT0(RegStorage::kValid | rT0); -constexpr RegStorage rs_rT1(RegStorage::kValid | rT1); -constexpr RegStorage rs_rT2(RegStorage::kValid | rT2); -constexpr RegStorage rs_rT3(RegStorage::kValid | rT3); -constexpr RegStorage rs_rT4(RegStorage::kValid | rT4); -constexpr RegStorage rs_rT5(RegStorage::kValid | rT5); -constexpr RegStorage rs_rT6(RegStorage::kValid | rT6); -constexpr RegStorage rs_rT7(RegStorage::kValid | rT7); +constexpr RegStorage rs_rT0_32(RegStorage::kValid | rT0_32); +constexpr RegStorage rs_rA4 = rs_rT0_32; +constexpr RegStorage rs_rT1_32(RegStorage::kValid | rT1_32); +constexpr RegStorage rs_rA5 = rs_rT1_32; +constexpr RegStorage rs_rT2_32(RegStorage::kValid | rT2_32); +constexpr RegStorage rs_rA6 = rs_rT2_32; +constexpr RegStorage rs_rT3_32(RegStorage::kValid | rT3_32); +constexpr RegStorage rs_rA7 = rs_rT3_32; +constexpr RegStorage rs_rT4_32(RegStorage::kValid | rT4_32); +constexpr RegStorage rs_rT0 = rs_rT4_32; +constexpr RegStorage rs_rT5_32(RegStorage::kValid | rT5_32); +constexpr RegStorage rs_rT1 = rs_rT5_32; +constexpr RegStorage rs_rT6_32(RegStorage::kValid | rT6_32); +constexpr RegStorage rs_rT2 = rs_rT6_32; +constexpr RegStorage rs_rT7_32(RegStorage::kValid | rT7_32); +constexpr RegStorage rs_rT3 = rs_rT7_32; constexpr RegStorage rs_rS0(RegStorage::kValid | rS0); constexpr RegStorage rs_rS1(RegStorage::kValid | rS1); constexpr RegStorage rs_rS2(RegStorage::kValid | rS2); @@ -288,9 +384,38 @@ constexpr RegStorage rs_rSP(RegStorage::kValid | rSP); constexpr RegStorage rs_rFP(RegStorage::kValid | rFP); constexpr RegStorage rs_rRA(RegStorage::kValid | rRA); -constexpr RegStorage rs_rMIPS_LR(RegStorage::kInvalid); // Not used for MIPS. -constexpr RegStorage rs_rMIPS_PC(RegStorage::kInvalid); // Not used for MIPS. -constexpr RegStorage rs_rMIPS_COUNT(RegStorage::kInvalid); // Not used for MIPS. +constexpr RegStorage rs_rZEROd(RegStorage::kValid | rZEROd); +constexpr RegStorage rs_rATd(RegStorage::kValid | rATd); +constexpr RegStorage rs_rV0d(RegStorage::kValid | rV0d); +constexpr RegStorage rs_rV1d(RegStorage::kValid | rV1d); +constexpr RegStorage rs_rA0d(RegStorage::kValid | rA0d); +constexpr RegStorage rs_rA1d(RegStorage::kValid | rA1d); +constexpr RegStorage rs_rA2d(RegStorage::kValid | rA2d); +constexpr RegStorage rs_rA3d(RegStorage::kValid | rA3d); +constexpr RegStorage rs_rA4d(RegStorage::kValid | rA4d); +constexpr RegStorage rs_rA5d(RegStorage::kValid | rA5d); +constexpr RegStorage rs_rA6d(RegStorage::kValid | rA6d); +constexpr RegStorage rs_rA7d(RegStorage::kValid | rA7d); +constexpr RegStorage rs_rT0d(RegStorage::kValid | rT0d); +constexpr RegStorage rs_rT1d(RegStorage::kValid | rT1d); +constexpr RegStorage rs_rT2d(RegStorage::kValid | rT2d); +constexpr RegStorage rs_rT3d(RegStorage::kValid | rT3d); +constexpr RegStorage rs_rS0d(RegStorage::kValid | rS0d); +constexpr RegStorage rs_rS1d(RegStorage::kValid | rS1d); +constexpr RegStorage rs_rS2d(RegStorage::kValid | rS2d); +constexpr RegStorage rs_rS3d(RegStorage::kValid | rS3d); +constexpr RegStorage rs_rS4d(RegStorage::kValid | rS4d); +constexpr RegStorage rs_rS5d(RegStorage::kValid | rS5d); +constexpr RegStorage rs_rS6d(RegStorage::kValid | rS6d); +constexpr RegStorage rs_rS7d(RegStorage::kValid | rS7d); +constexpr RegStorage rs_rT8d(RegStorage::kValid | rT8d); +constexpr RegStorage rs_rT9d(RegStorage::kValid | rT9d); +constexpr RegStorage rs_rK0d(RegStorage::kValid | rK0d); +constexpr RegStorage rs_rK1d(RegStorage::kValid | rK1d); +constexpr RegStorage rs_rGPd(RegStorage::kValid | rGPd); +constexpr RegStorage rs_rSPd(RegStorage::kValid | rSPd); +constexpr RegStorage rs_rFPd(RegStorage::kValid | rFPd); +constexpr RegStorage rs_rRAd(RegStorage::kValid | rRAd); constexpr RegStorage rs_rF0(RegStorage::kValid | rF0); constexpr RegStorage rs_rF1(RegStorage::kValid | rF1); @@ -309,6 +434,23 @@ constexpr RegStorage rs_rF13(RegStorage::kValid | rF13); constexpr RegStorage rs_rF14(RegStorage::kValid | rF14); constexpr RegStorage rs_rF15(RegStorage::kValid | rF15); +constexpr RegStorage rs_rF16(RegStorage::kValid | rF16); +constexpr RegStorage rs_rF17(RegStorage::kValid | rF17); +constexpr RegStorage rs_rF18(RegStorage::kValid | rF18); +constexpr RegStorage rs_rF19(RegStorage::kValid | rF19); +constexpr RegStorage rs_rF20(RegStorage::kValid | rF20); +constexpr RegStorage rs_rF21(RegStorage::kValid | rF21); +constexpr RegStorage rs_rF22(RegStorage::kValid | rF22); +constexpr RegStorage rs_rF23(RegStorage::kValid | rF23); +constexpr RegStorage rs_rF24(RegStorage::kValid | rF24); +constexpr RegStorage rs_rF25(RegStorage::kValid | rF25); +constexpr RegStorage rs_rF26(RegStorage::kValid | rF26); +constexpr RegStorage rs_rF27(RegStorage::kValid | rF27); +constexpr RegStorage rs_rF28(RegStorage::kValid | rF28); +constexpr RegStorage rs_rF29(RegStorage::kValid | rF29); +constexpr RegStorage rs_rF30(RegStorage::kValid | rF30); +constexpr RegStorage rs_rF31(RegStorage::kValid | rF31); + constexpr RegStorage rs_rD0_fr0(RegStorage::kValid | rD0_fr0); constexpr RegStorage rs_rD1_fr0(RegStorage::kValid | rD1_fr0); constexpr RegStorage rs_rD2_fr0(RegStorage::kValid | rD2_fr0); @@ -327,53 +469,65 @@ constexpr RegStorage rs_rD5_fr1(RegStorage::kValid | rD5_fr1); constexpr RegStorage rs_rD6_fr1(RegStorage::kValid | rD6_fr1); constexpr RegStorage rs_rD7_fr1(RegStorage::kValid | rD7_fr1); -// TODO: reduce/eliminate use of these. -#define rMIPS_SUSPEND rS0 -#define rs_rMIPS_SUSPEND rs_rS0 -#define rMIPS_SELF rS1 -#define rs_rMIPS_SELF rs_rS1 -#define rMIPS_SP rSP -#define rs_rMIPS_SP rs_rSP -#define rMIPS_ARG0 rARG0 -#define rs_rMIPS_ARG0 rs_rARG0 -#define rMIPS_ARG1 rARG1 -#define rs_rMIPS_ARG1 rs_rARG1 -#define rMIPS_ARG2 rARG2 -#define rs_rMIPS_ARG2 rs_rARG2 -#define rMIPS_ARG3 rARG3 -#define rs_rMIPS_ARG3 rs_rARG3 -#define rMIPS_FARG0 rFARG0 -#define rs_rMIPS_FARG0 rs_rFARG0 -#define rMIPS_FARG1 rFARG1 -#define rs_rMIPS_FARG1 rs_rFARG1 -#define rMIPS_FARG2 rFARG2 -#define rs_rMIPS_FARG2 rs_rFARG2 -#define rMIPS_FARG3 rFARG3 -#define rs_rMIPS_FARG3 rs_rFARG3 -#define rMIPS_RET0 rRESULT0 -#define rs_rMIPS_RET0 rs_rRESULT0 -#define rMIPS_RET1 rRESULT1 -#define rs_rMIPS_RET1 rs_rRESULT1 -#define rMIPS_INVOKE_TGT rT9 -#define rs_rMIPS_INVOKE_TGT rs_rT9 -#define rMIPS_COUNT RegStorage::kInvalidRegVal +constexpr RegStorage rs_rD0(RegStorage::kValid | rD0); +constexpr RegStorage rs_rD1(RegStorage::kValid | rD1); +constexpr RegStorage rs_rD2(RegStorage::kValid | rD2); +constexpr RegStorage rs_rD3(RegStorage::kValid | rD3); +constexpr RegStorage rs_rD4(RegStorage::kValid | rD4); +constexpr RegStorage rs_rD5(RegStorage::kValid | rD5); +constexpr RegStorage rs_rD6(RegStorage::kValid | rD6); +constexpr RegStorage rs_rD7(RegStorage::kValid | rD7); +constexpr RegStorage rs_rD8(RegStorage::kValid | rD8); +constexpr RegStorage rs_rD9(RegStorage::kValid | rD9); +constexpr RegStorage rs_rD10(RegStorage::kValid | rD10); +constexpr RegStorage rs_rD11(RegStorage::kValid | rD11); +constexpr RegStorage rs_rD12(RegStorage::kValid | rD12); +constexpr RegStorage rs_rD13(RegStorage::kValid | rD13); +constexpr RegStorage rs_rD14(RegStorage::kValid | rD14); +constexpr RegStorage rs_rD15(RegStorage::kValid | rD15); +constexpr RegStorage rs_rD16(RegStorage::kValid | rD16); +constexpr RegStorage rs_rD17(RegStorage::kValid | rD17); +constexpr RegStorage rs_rD18(RegStorage::kValid | rD18); +constexpr RegStorage rs_rD19(RegStorage::kValid | rD19); +constexpr RegStorage rs_rD20(RegStorage::kValid | rD20); +constexpr RegStorage rs_rD21(RegStorage::kValid | rD21); +constexpr RegStorage rs_rD22(RegStorage::kValid | rD22); +constexpr RegStorage rs_rD23(RegStorage::kValid | rD23); +constexpr RegStorage rs_rD24(RegStorage::kValid | rD24); +constexpr RegStorage rs_rD25(RegStorage::kValid | rD25); +constexpr RegStorage rs_rD26(RegStorage::kValid | rD26); +constexpr RegStorage rs_rD27(RegStorage::kValid | rD27); +constexpr RegStorage rs_rD28(RegStorage::kValid | rD28); +constexpr RegStorage rs_rD29(RegStorage::kValid | rD29); +constexpr RegStorage rs_rD30(RegStorage::kValid | rD30); +constexpr RegStorage rs_rD31(RegStorage::kValid | rD31); // RegisterLocation templates return values (r_V0, or r_V0/r_V1). const RegLocation mips_loc_c_return {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, RegStorage(RegStorage::k32BitSolo, rV0), INVALID_SREG, INVALID_SREG}; +const RegLocation mips64_loc_c_return_ref + {kLocPhysReg, 0, 0, 0, 0, 0, 1, 0, 1, + RegStorage(RegStorage::k64BitSolo, rV0d), INVALID_SREG, INVALID_SREG}; const RegLocation mips_loc_c_return_wide {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, RegStorage(RegStorage::k64BitPair, rV0, rV1), INVALID_SREG, INVALID_SREG}; +const RegLocation mips64_loc_c_return_wide + {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, + RegStorage(RegStorage::k64BitSolo, rV0d), INVALID_SREG, INVALID_SREG}; const RegLocation mips_loc_c_return_float {kLocPhysReg, 0, 0, 0, 1, 0, 0, 0, 1, RegStorage(RegStorage::k32BitSolo, rF0), INVALID_SREG, INVALID_SREG}; +// FIXME: move MIPS to k64Bitsolo for doubles const RegLocation mips_loc_c_return_double_fr0 {kLocPhysReg, 1, 0, 0, 1, 0, 0, 0, 1, RegStorage(RegStorage::k64BitPair, rF0, rF1), INVALID_SREG, INVALID_SREG}; const RegLocation mips_loc_c_return_double_fr1 {kLocPhysReg, 1, 0, 0, 1, 0, 0, 0, 1, RegStorage(RegStorage::k64BitSolo, rF0), INVALID_SREG, INVALID_SREG}; +const RegLocation mips64_loc_c_return_double + {kLocPhysReg, 1, 0, 0, 1, 0, 0, 0, 1, + RegStorage(RegStorage::k64BitSolo, rD0), INVALID_SREG, INVALID_SREG}; enum MipsShiftEncodings { kMipsLsl = 0x0, @@ -395,104 +549,136 @@ enum MipsShiftEncodings { #define kSY kSYNC0 /* - * The following enum defines the list of supported Thumb instructions by the + * The following enum defines the list of supported mips instructions by the * assembler. Their corresponding EncodingMap positions will be defined in - * Assemble.cc. + * assemble_mips.cc. */ enum MipsOpCode { kMipsFirst = 0, + // The following are common mips32r2, mips32r6 and mips64r6 instructions. kMips32BitData = kMipsFirst, // data [31..0]. - kMipsAddiu, // addiu t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0]. - kMipsAddu, // add d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100001]. - kMipsAnd, // and d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100100]. - kMipsAndi, // andi t,s,imm16 [001100] s[25..21] t[20..16] imm16[15..0]. - kMipsB, // b o [0001000000000000] o[15..0]. - kMipsBal, // bal o [0000010000010001] o[15..0]. - // NOTE: the code tests the range kMipsBeq thru kMipsBne, so adding an instruction in this - // range may require updates. - kMipsBeq, // beq s,t,o [000100] s[25..21] t[20..16] o[15..0]. - kMipsBeqz, // beqz s,o [000100] s[25..21] [00000] o[15..0]. - kMipsBgez, // bgez s,o [000001] s[25..21] [00001] o[15..0]. - kMipsBgtz, // bgtz s,o [000111] s[25..21] [00000] o[15..0]. - kMipsBlez, // blez s,o [000110] s[25..21] [00000] o[15..0]. - kMipsBltz, // bltz s,o [000001] s[25..21] [00000] o[15..0]. - kMipsBnez, // bnez s,o [000101] s[25..21] [00000] o[15..0]. - kMipsBne, // bne s,t,o [000101] s[25..21] t[20..16] o[15..0]. - kMipsDiv, // div s,t [000000] s[25..21] t[20..16] [0000000000011010]. - kMipsExt, // ext t,s,p,z [011111] s[25..21] t[20..16] z[15..11] p[10..6] [000000]. - kMipsJal, // jal t [000011] t[25..0]. - kMipsJalr, // jalr d,s [000000] s[25..21] [00000] d[15..11] hint[10..6] [001001]. - kMipsJr, // jr s [000000] s[25..21] [0000000000] hint[10..6] [001000]. - kMipsLahi, // lui t,imm16 [00111100000] t[20..16] imm16[15..0] load addr hi. - kMipsLalo, // ori t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0] load addr lo. - kMipsLui, // lui t,imm16 [00111100000] t[20..16] imm16[15..0]. - kMipsLb, // lb t,o(b) [100000] b[25..21] t[20..16] o[15..0]. - kMipsLbu, // lbu t,o(b) [100100] b[25..21] t[20..16] o[15..0]. - kMipsLh, // lh t,o(b) [100001] b[25..21] t[20..16] o[15..0]. - kMipsLhu, // lhu t,o(b) [100101] b[25..21] t[20..16] o[15..0]. - kMipsLw, // lw t,o(b) [100011] b[25..21] t[20..16] o[15..0]. - kMipsMfhi, // mfhi d [0000000000000000] d[15..11] [00000010000]. - kMipsMflo, // mflo d [0000000000000000] d[15..11] [00000010010]. - kMipsMove, // move d,s [000000] s[25..21] [00000] d[15..11] [00000100101]. - kMipsMovz, // movz d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000001010]. - kMipsMul, // mul d,s,t [011100] s[25..21] t[20..16] d[15..11] [00000000010]. - kMipsNop, // nop [00000000000000000000000000000000]. - kMipsNor, // nor d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100111]. - kMipsOr, // or d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100101]. - kMipsOri, // ori t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0]. - kMipsPref, // pref h,o(b) [101011] b[25..21] h[20..16] o[15..0]. - kMipsSb, // sb t,o(b) [101000] b[25..21] t[20..16] o[15..0]. - kMipsSeb, // seb d,t [01111100000] t[20..16] d[15..11] [10000100000]. - kMipsSeh, // seh d,t [01111100000] t[20..16] d[15..11] [11000100000]. - kMipsSh, // sh t,o(b) [101001] b[25..21] t[20..16] o[15..0]. - kMipsSll, // sll d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [000000]. - kMipsSllv, // sllv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000100]. - kMipsSlt, // slt d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000101010]. - kMipsSlti, // slti t,s,imm16 [001010] s[25..21] t[20..16] imm16[15..0]. - kMipsSltu, // sltu d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000101011]. - kMipsSra, // sra d,s,imm5 [00000000000] t[20..16] d[15..11] imm5[10..6] [000011]. - kMipsSrav, // srav d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000111]. - kMipsSrl, // srl d,t,a [00000000000] t[20..16] d[20..16] a[10..6] [000010]. - kMipsSrlv, // srlv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000110]. - kMipsSubu, // subu d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100011]. - kMipsSw, // sw t,o(b) [101011] b[25..21] t[20..16] o[15..0]. - kMipsXor, // xor d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100110]. - kMipsXori, // xori t,s,imm16 [001110] s[25..21] t[20..16] imm16[15..0]. - kMipsFadds, // add.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000000]. - kMipsFsubs, // sub.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000001]. - kMipsFmuls, // mul.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000010]. - kMipsFdivs, // div.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000011]. - kMipsFaddd, // add.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000000]. - kMipsFsubd, // sub.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000001]. - kMipsFmuld, // mul.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000010]. - kMipsFdivd, // div.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000011]. - kMipsFcvtsd, // cvt.s.d d,s [01000110001] [00000] s[15..11] d[10..6] [100000]. - kMipsFcvtsw, // cvt.s.w d,s [01000110100] [00000] s[15..11] d[10..6] [100000]. - kMipsFcvtds, // cvt.d.s d,s [01000110000] [00000] s[15..11] d[10..6] [100001]. - kMipsFcvtdw, // cvt.d.w d,s [01000110100] [00000] s[15..11] d[10..6] [100001]. - kMipsFcvtws, // cvt.w.d d,s [01000110000] [00000] s[15..11] d[10..6] [100100]. - kMipsFcvtwd, // cvt.w.d d,s [01000110001] [00000] s[15..11] d[10..6] [100100]. - kMipsFmovs, // mov.s d,s [01000110000] [00000] s[15..11] d[10..6] [000110]. - kMipsFmovd, // mov.d d,s [01000110001] [00000] s[15..11] d[10..6] [000110]. - kMipsFlwc1, // lwc1 t,o(b) [110001] b[25..21] t[20..16] o[15..0]. - kMipsFldc1, // ldc1 t,o(b) [110101] b[25..21] t[20..16] o[15..0]. - kMipsFswc1, // swc1 t,o(b) [111001] b[25..21] t[20..16] o[15..0]. - kMipsFsdc1, // sdc1 t,o(b) [111101] b[25..21] t[20..16] o[15..0]. - kMipsMfc1, // mfc1 t,s [01000100000] t[20..16] s[15..11] [00000000000]. - kMipsMtc1, // mtc1 t,s [01000100100] t[20..16] s[15..11] [00000000000]. - kMipsMfhc1, // mfhc1 t,s [01000100011] t[20..16] s[15..11] [00000000000]. - kMipsMthc1, // mthc1 t,s [01000100111] t[20..16] s[15..11] [00000000000]. - kMipsDelta, // Psuedo for ori t, s, <label>-<label>. - kMipsDeltaHi, // Pseudo for lui t, high16(<label>-<label>). - kMipsDeltaLo, // Pseudo for ori t, s, low16(<label>-<label>). - kMipsCurrPC, // jal to .+8 to materialize pc. - kMipsSync, // sync kind [000000] [0000000000000000] s[10..6] [001111]. - - // The following are mips32r6 instructions. - kMipsR6Div, // div d,s,t [000000] s[25..21] t[20..16] d[15..11] [00010011010]. - kMipsR6Mod, // mod d,s,t [000000] s[25..21] t[20..16] d[15..11] [00011011010]. - kMipsR6Mul, // mul d,s,t [000000] s[25..21] t[20..16] d[15..11] [00010011000]. - + kMipsAddiu, // addiu t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0]. + kMipsAddu, // add d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100001]. + kMipsAnd, // and d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100100]. + kMipsAndi, // andi t,s,imm16 [001100] s[25..21] t[20..16] imm16[15..0]. + kMipsB, // b o [0001000000000000] o[15..0]. + kMipsBal, // bal o [0000010000010001] o[15..0]. + // NOTE : the code tests the range kMipsBeq thru kMipsBne, so adding an instruction in this + // range may require updates. + kMipsBeq, // beq s,t,o [000100] s[25..21] t[20..16] o[15..0]. + kMipsBeqz, // beqz s,o [000100] s[25..21] [00000] o[15..0]. + kMipsBgez, // bgez s,o [000001] s[25..21] [00001] o[15..0]. + kMipsBgtz, // bgtz s,o [000111] s[25..21] [00000] o[15..0]. + kMipsBlez, // blez s,o [000110] s[25..21] [00000] o[15..0]. + kMipsBltz, // bltz s,o [000001] s[25..21] [00000] o[15..0]. + kMipsBnez, // bnez s,o [000101] s[25..21] [00000] o[15..0]. + kMipsBne, // bne s,t,o [000101] s[25..21] t[20..16] o[15..0]. + kMipsExt, // ext t,s,p,z [011111] s[25..21] t[20..16] z[15..11] p[10..6] [000000]. + kMipsFaddd, // add.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000000]. + kMipsFadds, // add.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000000]. + kMipsFsubd, // sub.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000001]. + kMipsFsubs, // sub.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000001]. + kMipsFdivd, // div.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000011]. + kMipsFdivs, // div.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000011]. + kMipsFmuld, // mul.d d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000010]. + kMipsFmuls, // mul.s d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000010]. + kMipsFcvtsd, // cvt.s.d d,s [01000110001] [00000] s[15..11] d[10..6] [100000]. + kMipsFcvtsw, // cvt.s.w d,s [01000110100] [00000] s[15..11] d[10..6] [100000]. + kMipsFcvtds, // cvt.d.s d,s [01000110000] [00000] s[15..11] d[10..6] [100001]. + kMipsFcvtdw, // cvt.d.w d,s [01000110100] [00000] s[15..11] d[10..6] [100001]. + kMipsFcvtwd, // cvt.w.d d,s [01000110001] [00000] s[15..11] d[10..6] [100100]. + kMipsFcvtws, // cvt.w.s d,s [01000110000] [00000] s[15..11] d[10..6] [100100]. + kMipsFmovd, // mov.d d,s [01000110001] [00000] s[15..11] d[10..6] [000110]. + kMipsFmovs, // mov.s d,s [01000110000] [00000] s[15..11] d[10..6] [000110]. + kMipsFnegd, // neg.d d,s [01000110001] [00000] s[15..11] d[10..6] [000111]. + kMipsFnegs, // neg.s d,s [01000110000] [00000] s[15..11] d[10..6] [000111]. + kMipsFldc1, // ldc1 t,o(b) [110101] b[25..21] t[20..16] o[15..0]. + kMipsFlwc1, // lwc1 t,o(b) [110001] b[25..21] t[20..16] o[15..0]. + kMipsFsdc1, // sdc1 t,o(b) [111101] b[25..21] t[20..16] o[15..0]. + kMipsFswc1, // swc1 t,o(b) [111001] b[25..21] t[20..16] o[15..0]. + kMipsJal, // jal t [000011] t[25..0]. + kMipsJalr, // jalr d,s [000000] s[25..21] [00000] d[15..11] hint[10..6] [001001]. + kMipsJr, // jr s [000000] s[25..21] [0000000000] hint[10..6] [001000]. + kMipsLahi, // lui t,imm16 [00111100000] t[20..16] imm16[15..0] load addr hi. + kMipsLalo, // ori t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0] load addr lo. + kMipsLui, // lui t,imm16 [00111100000] t[20..16] imm16[15..0]. + kMipsLb, // lb t,o(b) [100000] b[25..21] t[20..16] o[15..0]. + kMipsLbu, // lbu t,o(b) [100100] b[25..21] t[20..16] o[15..0]. + kMipsLh, // lh t,o(b) [100001] b[25..21] t[20..16] o[15..0]. + kMipsLhu, // lhu t,o(b) [100101] b[25..21] t[20..16] o[15..0]. + kMipsLw, // lw t,o(b) [100011] b[25..21] t[20..16] o[15..0]. + kMipsMove, // move d,s [000000] s[25..21] [00000] d[15..11] [00000100101]. + kMipsMfc1, // mfc1 t,s [01000100000] t[20..16] s[15..11] [00000000000]. + kMipsMtc1, // mtc1 t,s [01000100100] t[20..16] s[15..11] [00000000000]. + kMipsMfhc1, // mfhc1 t,s [01000100011] t[20..16] s[15..11] [00000000000]. + kMipsMthc1, // mthc1 t,s [01000100111] t[20..16] s[15..11] [00000000000]. + kMipsNop, // nop [00000000000000000000000000000000]. + kMipsNor, // nor d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100111]. + kMipsOr, // or d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100101]. + kMipsOri, // ori t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0]. + kMipsPref, // pref h,o(b) [101011] b[25..21] h[20..16] o[15..0]. + kMipsSb, // sb t,o(b) [101000] b[25..21] t[20..16] o[15..0]. + kMipsSeb, // seb d,t [01111100000] t[20..16] d[15..11] [10000100000]. + kMipsSeh, // seh d,t [01111100000] t[20..16] d[15..11] [11000100000]. + kMipsSh, // sh t,o(b) [101001] b[25..21] t[20..16] o[15..0]. + kMipsSll, // sll d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [000000]. + kMipsSllv, // sllv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000100]. + kMipsSlt, // slt d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000101010]. + kMipsSlti, // slti t,s,imm16 [001010] s[25..21] t[20..16] imm16[15..0]. + kMipsSltu, // sltu d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000101011]. + kMipsSra, // sra d,s,imm5 [00000000000] t[20..16] d[15..11] imm5[10..6] [000011]. + kMipsSrav, // srav d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000111]. + kMipsSrl, // srl d,t,a [00000000000] t[20..16] d[20..16] a[10..6] [000010]. + kMipsSrlv, // srlv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000110]. + kMipsSubu, // subu d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100011]. + kMipsSw, // sw t,o(b) [101011] b[25..21] t[20..16] o[15..0]. + kMipsSync, // sync kind [000000] [0000000000000000] s[10..6] [001111]. + kMipsXor, // xor d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100110]. + kMipsXori, // xori t,s,imm16 [001110] s[25..21] t[20..16] imm16[15..0]. + + // The following are mips32r2 instructions. + kMipsR2Div, // div s,t [000000] s[25..21] t[20..16] [0000000000011010]. + kMipsR2Mul, // mul d,s,t [011100] s[25..21] t[20..16] d[15..11] [00000000010]. + kMipsR2Mfhi, // mfhi d [0000000000000000] d[15..11] [00000010000]. + kMipsR2Mflo, // mflo d [0000000000000000] d[15..11] [00000010010]. + kMipsR2Movz, // movz d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000001010]. + + // The following are mips32r6 and mips64r6 instructions. + kMipsR6Div, // div d,s,t [000000] s[25..21] t[20..16] d[15..11] [00010011010]. + kMipsR6Mod, // mod d,s,t [000000] s[25..21] t[20..16] d[15..11] [00011011010]. + kMipsR6Mul, // mul d,s,t [000000] s[25..21] t[20..16] d[15..11] [00010011000]. + + // The following are mips64r6 instructions. + kMips64Daddiu, // daddiu t,s,imm16 [011001] s[25..21] t[20..16] imm16[15..11]. + kMips64Daddu, // daddu d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000101101]. + kMips64Dahi, // dahi s,imm16 [000001] s[25..21] [00110] imm16[15..11]. + kMips64Dati, // dati s,imm16 [000001] s[25..21] [11110] imm16[15..11]. + kMips64Daui, // daui t,s,imm16 [011101] s[25..21] t[20..16] imm16[15..11]. + kMips64Ddiv, // ddiv d,s,t [000000] s[25..21] t[20..16] d[15..11] [00010011110]. + kMips64Dmod, // dmod d,s,t [000000] s[25..21] t[20..16] d[15..11] [00011011110]. + kMips64Dmul, // dmul d,s,t [000000] s[25..21] t[20..16] d[15..11] [00010011100]. + kMips64Dmfc1, // dmfc1 t,s [01000100001] t[20..16] s[15..11] [00000000000]. + kMips64Dmtc1, // dmtc1 t,s [01000100101] t[20..16] s[15..11] [00000000000]. + kMips64Drotr32, // drotr32 d,t,a [00000000001] t[20..16] d[15..11] a[10..6] [111110]. + kMips64Dsll, // dsll d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [111000]. + kMips64Dsll32, // dsll32 d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [111100]. + kMips64Dsrl, // dsrl d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [111010]. + kMips64Dsrl32, // dsrl32 d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [111110]. + kMips64Dsra, // dsra d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [111011]. + kMips64Dsra32, // dsra32 d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [111111]. + kMips64Dsllv, // dsllv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000010100]. + kMips64Dsrlv, // dsrlv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000010110]. + kMips64Dsrav, // dsrav d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000010111]. + kMips64Dsubu, // dsubu d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000101111]. + kMips64Ld, // ld t,o(b) [110111] b[25..21] t[20..16] o[15..0]. + kMips64Lwu, // lwu t,o(b) [100111] b[25..21] t[20..16] o[15..0]. + kMips64Sd, // sd t,o(b) [111111] b[25..21] t[20..16] o[15..0]. + + // The following are pseudoinstructions. + kMipsDelta, // Psuedo for ori t, s, <label>-<label>. + kMipsDeltaHi, // Pseudo for lui t, high16(<label>-<label>). + kMipsDeltaLo, // Pseudo for ori t, s, low16(<label>-<label>). + kMipsCurrPC, // jal to .+8 to materialize pc. kMipsUndefined, // undefined [011001xxxxxxxxxxxxxxxx]. kMipsLast }; @@ -503,7 +689,7 @@ enum MipsEncodingKind { kFmtUnused, kFmtBitBlt, // Bit string using end/start. kFmtDfp, // Double FP reg. - kFmtSfp, // Single FP reg + kFmtSfp, // Single FP reg. kFmtBlt5_2, // Same 5-bit field to 2 locations. }; std::ostream& operator<<(std::ostream& os, const MipsEncodingKind& rhs); diff --git a/compiler/dex/quick/mips/target_mips.cc b/compiler/dex/quick/mips/target_mips.cc index 830f63ac5f..a94fad7534 100644 --- a/compiler/dex/quick/mips/target_mips.cc +++ b/compiler/dex/quick/mips/target_mips.cc @@ -30,55 +30,131 @@ namespace art { -static constexpr RegStorage core_regs_arr[] = - {rs_rZERO, rs_rAT, rs_rV0, rs_rV1, rs_rA0, rs_rA1, rs_rA2, rs_rA3, rs_rT0, rs_rT1, rs_rT2, - rs_rT3, rs_rT4, rs_rT5, rs_rT6, rs_rT7, rs_rS0, rs_rS1, rs_rS2, rs_rS3, rs_rS4, rs_rS5, - rs_rS6, rs_rS7, rs_rT8, rs_rT9, rs_rK0, rs_rK1, rs_rGP, rs_rSP, rs_rFP, rs_rRA}; -static constexpr RegStorage sp_regs_arr[] = +static constexpr RegStorage core_regs_arr_32[] = + {rs_rZERO, rs_rAT, rs_rV0, rs_rV1, rs_rA0, rs_rA1, rs_rA2, rs_rA3, rs_rT0_32, rs_rT1_32, + rs_rT2_32, rs_rT3_32, rs_rT4_32, rs_rT5_32, rs_rT6_32, rs_rT7_32, rs_rS0, rs_rS1, rs_rS2, + rs_rS3, rs_rS4, rs_rS5, rs_rS6, rs_rS7, rs_rT8, rs_rT9, rs_rK0, rs_rK1, rs_rGP, rs_rSP, rs_rFP, + rs_rRA}; +static constexpr RegStorage sp_regs_arr_32[] = {rs_rF0, rs_rF1, rs_rF2, rs_rF3, rs_rF4, rs_rF5, rs_rF6, rs_rF7, rs_rF8, rs_rF9, rs_rF10, rs_rF11, rs_rF12, rs_rF13, rs_rF14, rs_rF15}; -static constexpr RegStorage dp_fr0_regs_arr[] = +static constexpr RegStorage dp_fr0_regs_arr_32[] = {rs_rD0_fr0, rs_rD1_fr0, rs_rD2_fr0, rs_rD3_fr0, rs_rD4_fr0, rs_rD5_fr0, rs_rD6_fr0, rs_rD7_fr0}; -static constexpr RegStorage dp_fr1_regs_arr[] = +static constexpr RegStorage dp_fr1_regs_arr_32[] = {rs_rD0_fr1, rs_rD1_fr1, rs_rD2_fr1, rs_rD3_fr1, rs_rD4_fr1, rs_rD5_fr1, rs_rD6_fr1, rs_rD7_fr1}; -static constexpr RegStorage reserved_regs_arr[] = +static constexpr RegStorage reserved_regs_arr_32[] = {rs_rZERO, rs_rAT, rs_rS0, rs_rS1, rs_rK0, rs_rK1, rs_rGP, rs_rSP, rs_rRA}; -static constexpr RegStorage core_temps_arr[] = - {rs_rV0, rs_rV1, rs_rA0, rs_rA1, rs_rA2, rs_rA3, rs_rT0, rs_rT1, rs_rT2, rs_rT3, rs_rT4, - rs_rT5, rs_rT6, rs_rT7, rs_rT8}; -static constexpr RegStorage sp_temps_arr[] = +static constexpr RegStorage core_temps_arr_32[] = + {rs_rV0, rs_rV1, rs_rA0, rs_rA1, rs_rA2, rs_rA3, rs_rT0_32, rs_rT1_32, rs_rT2_32, rs_rT3_32, + rs_rT4_32, rs_rT5_32, rs_rT6_32, rs_rT7_32, rs_rT8}; +static constexpr RegStorage sp_temps_arr_32[] = {rs_rF0, rs_rF1, rs_rF2, rs_rF3, rs_rF4, rs_rF5, rs_rF6, rs_rF7, rs_rF8, rs_rF9, rs_rF10, rs_rF11, rs_rF12, rs_rF13, rs_rF14, rs_rF15}; -static constexpr RegStorage dp_fr0_temps_arr[] = +static constexpr RegStorage dp_fr0_temps_arr_32[] = {rs_rD0_fr0, rs_rD1_fr0, rs_rD2_fr0, rs_rD3_fr0, rs_rD4_fr0, rs_rD5_fr0, rs_rD6_fr0, rs_rD7_fr0}; -static constexpr RegStorage dp_fr1_temps_arr[] = +static constexpr RegStorage dp_fr1_temps_arr_32[] = {rs_rD0_fr1, rs_rD1_fr1, rs_rD2_fr1, rs_rD3_fr1, rs_rD4_fr1, rs_rD5_fr1, rs_rD6_fr1, rs_rD7_fr1}; +static constexpr RegStorage core_regs_arr_64[] = + {rs_rZERO, rs_rAT, rs_rV0, rs_rV1, rs_rA0, rs_rA1, rs_rA2, rs_rA3, rs_rA4, rs_rA5, rs_rA6, + rs_rA7, rs_rT0, rs_rT1, rs_rT2, rs_rT3, rs_rS0, rs_rS1, rs_rS2, rs_rS3, rs_rS4, rs_rS5, rs_rS6, + rs_rS7, rs_rT8, rs_rT9, rs_rK0, rs_rK1, rs_rGP, rs_rSP, rs_rFP, rs_rRA}; +static constexpr RegStorage core_regs_arr_64d[] = + {rs_rZEROd, rs_rATd, rs_rV0d, rs_rV1d, rs_rA0d, rs_rA1d, rs_rA2d, rs_rA3d, rs_rA4d, rs_rA5d, + rs_rA6d, rs_rA7d, rs_rT0d, rs_rT1d, rs_rT2d, rs_rT3d, rs_rS0d, rs_rS1d, rs_rS2d, rs_rS3d, + rs_rS4d, rs_rS5d, rs_rS6d, rs_rS7d, rs_rT8d, rs_rT9d, rs_rK0d, rs_rK1d, rs_rGPd, rs_rSPd, + rs_rFPd, rs_rRAd}; +#if 0 +// TODO: f24-f31 must be saved before calls and restored after. +static constexpr RegStorage sp_regs_arr_64[] = + {rs_rF0, rs_rF1, rs_rF2, rs_rF3, rs_rF4, rs_rF5, rs_rF6, rs_rF7, rs_rF8, rs_rF9, rs_rF10, + rs_rF11, rs_rF12, rs_rF13, rs_rF14, rs_rF15, rs_rF16, rs_rF17, rs_rF18, rs_rF19, rs_rF20, + rs_rF21, rs_rF22, rs_rF23, rs_rF24, rs_rF25, rs_rF26, rs_rF27, rs_rF28, rs_rF29, rs_rF30, + rs_rF31}; +static constexpr RegStorage dp_regs_arr_64[] = + {rs_rD0, rs_rD1, rs_rD2, rs_rD3, rs_rD4, rs_rD5, rs_rD6, rs_rD7, rs_rD8, rs_rD9, rs_rD10, + rs_rD11, rs_rD12, rs_rD13, rs_rD14, rs_rD15, rs_rD16, rs_rD17, rs_rD18, rs_rD19, rs_rD20, + rs_rD21, rs_rD22, rs_rD23, rs_rD24, rs_rD25, rs_rD26, rs_rD27, rs_rD28, rs_rD29, rs_rD30, + rs_rD31}; +#else +static constexpr RegStorage sp_regs_arr_64[] = + {rs_rF0, rs_rF1, rs_rF2, rs_rF3, rs_rF4, rs_rF5, rs_rF6, rs_rF7, rs_rF8, rs_rF9, rs_rF10, + rs_rF11, rs_rF12, rs_rF13, rs_rF14, rs_rF15, rs_rF16, rs_rF17, rs_rF18, rs_rF19, rs_rF20, + rs_rF21, rs_rF22, rs_rF23}; +static constexpr RegStorage dp_regs_arr_64[] = + {rs_rD0, rs_rD1, rs_rD2, rs_rD3, rs_rD4, rs_rD5, rs_rD6, rs_rD7, rs_rD8, rs_rD9, rs_rD10, + rs_rD11, rs_rD12, rs_rD13, rs_rD14, rs_rD15, rs_rD16, rs_rD17, rs_rD18, rs_rD19, rs_rD20, + rs_rD21, rs_rD22, rs_rD23}; +#endif +static constexpr RegStorage reserved_regs_arr_64[] = + {rs_rZERO, rs_rAT, rs_rS0, rs_rS1, rs_rT9, rs_rK0, rs_rK1, rs_rGP, rs_rSP, rs_rRA}; +static constexpr RegStorage reserved_regs_arr_64d[] = + {rs_rZEROd, rs_rATd, rs_rS0d, rs_rS1d, rs_rT9d, rs_rK0d, rs_rK1d, rs_rGPd, rs_rSPd, rs_rRAd}; +static constexpr RegStorage core_temps_arr_64[] = + {rs_rV0, rs_rV1, rs_rA0, rs_rA1, rs_rA2, rs_rA3, rs_rA4, rs_rA5, rs_rA6, rs_rA7, rs_rT0, rs_rT1, + rs_rT2, rs_rT3, rs_rT8}; +static constexpr RegStorage core_temps_arr_64d[] = + {rs_rV0d, rs_rV1d, rs_rA0d, rs_rA1d, rs_rA2d, rs_rA3d, rs_rA4d, rs_rA5d, rs_rA6d, rs_rA7d, + rs_rT0d, rs_rT1d, rs_rT2d, rs_rT3d, rs_rT8d}; +#if 0 +// TODO: f24-f31 must be saved before calls and restored after. +static constexpr RegStorage sp_temps_arr_64[] = + {rs_rF0, rs_rF1, rs_rF2, rs_rF3, rs_rF4, rs_rF5, rs_rF6, rs_rF7, rs_rF8, rs_rF9, rs_rF10, + rs_rF11, rs_rF12, rs_rF13, rs_rF14, rs_rF15, rs_rF16, rs_rF17, rs_rF18, rs_rF19, rs_rF20, + rs_rF21, rs_rF22, rs_rF23, rs_rF24, rs_rF25, rs_rF26, rs_rF27, rs_rF28, rs_rF29, rs_rF30, + rs_rF31}; +static constexpr RegStorage dp_temps_arr_64[] = + {rs_rD0, rs_rD1, rs_rD2, rs_rD3, rs_rD4, rs_rD5, rs_rD6, rs_rD7, rs_rD8, rs_rD9, rs_rD10, + rs_rD11, rs_rD12, rs_rD13, rs_rD14, rs_rD15, rs_rD16, rs_rD17, rs_rD18, rs_rD19, rs_rD20, + rs_rD21, rs_rD22, rs_rD23, rs_rD24, rs_rD25, rs_rD26, rs_rD27, rs_rD28, rs_rD29, rs_rD30, + rs_rD31}; +#else +static constexpr RegStorage sp_temps_arr_64[] = + {rs_rF0, rs_rF1, rs_rF2, rs_rF3, rs_rF4, rs_rF5, rs_rF6, rs_rF7, rs_rF8, rs_rF9, rs_rF10, + rs_rF11, rs_rF12, rs_rF13, rs_rF14, rs_rF15, rs_rF16, rs_rF17, rs_rF18, rs_rF19, rs_rF20, + rs_rF21, rs_rF22, rs_rF23}; +static constexpr RegStorage dp_temps_arr_64[] = + {rs_rD0, rs_rD1, rs_rD2, rs_rD3, rs_rD4, rs_rD5, rs_rD6, rs_rD7, rs_rD8, rs_rD9, rs_rD10, + rs_rD11, rs_rD12, rs_rD13, rs_rD14, rs_rD15, rs_rD16, rs_rD17, rs_rD18, rs_rD19, rs_rD20, + rs_rD21, rs_rD22, rs_rD23}; +#endif + static constexpr ArrayRef<const RegStorage> empty_pool; -static constexpr ArrayRef<const RegStorage> core_regs(core_regs_arr); -static constexpr ArrayRef<const RegStorage> sp_regs(sp_regs_arr); -static constexpr ArrayRef<const RegStorage> dp_fr0_regs(dp_fr0_regs_arr); -static constexpr ArrayRef<const RegStorage> dp_fr1_regs(dp_fr1_regs_arr); -static constexpr ArrayRef<const RegStorage> reserved_regs(reserved_regs_arr); -static constexpr ArrayRef<const RegStorage> core_temps(core_temps_arr); -static constexpr ArrayRef<const RegStorage> sp_temps(sp_temps_arr); -static constexpr ArrayRef<const RegStorage> dp_fr0_temps(dp_fr0_temps_arr); -static constexpr ArrayRef<const RegStorage> dp_fr1_temps(dp_fr1_temps_arr); +static constexpr ArrayRef<const RegStorage> core_regs_32(core_regs_arr_32); +static constexpr ArrayRef<const RegStorage> sp_regs_32(sp_regs_arr_32); +static constexpr ArrayRef<const RegStorage> dp_fr0_regs_32(dp_fr0_regs_arr_32); +static constexpr ArrayRef<const RegStorage> dp_fr1_regs_32(dp_fr1_regs_arr_32); +static constexpr ArrayRef<const RegStorage> reserved_regs_32(reserved_regs_arr_32); +static constexpr ArrayRef<const RegStorage> core_temps_32(core_temps_arr_32); +static constexpr ArrayRef<const RegStorage> sp_temps_32(sp_temps_arr_32); +static constexpr ArrayRef<const RegStorage> dp_fr0_temps_32(dp_fr0_temps_arr_32); +static constexpr ArrayRef<const RegStorage> dp_fr1_temps_32(dp_fr1_temps_arr_32); + +static constexpr ArrayRef<const RegStorage> core_regs_64(core_regs_arr_64); +static constexpr ArrayRef<const RegStorage> core_regs_64d(core_regs_arr_64d); +static constexpr ArrayRef<const RegStorage> sp_regs_64(sp_regs_arr_64); +static constexpr ArrayRef<const RegStorage> dp_regs_64(dp_regs_arr_64); +static constexpr ArrayRef<const RegStorage> reserved_regs_64(reserved_regs_arr_64); +static constexpr ArrayRef<const RegStorage> reserved_regs_64d(reserved_regs_arr_64d); +static constexpr ArrayRef<const RegStorage> core_temps_64(core_temps_arr_64); +static constexpr ArrayRef<const RegStorage> core_temps_64d(core_temps_arr_64d); +static constexpr ArrayRef<const RegStorage> sp_temps_64(sp_temps_arr_64); +static constexpr ArrayRef<const RegStorage> dp_temps_64(dp_temps_arr_64); RegLocation MipsMir2Lir::LocCReturn() { return mips_loc_c_return; } RegLocation MipsMir2Lir::LocCReturnRef() { - return mips_loc_c_return; + return cu_->target64 ? mips64_loc_c_return_ref : mips_loc_c_return; } RegLocation MipsMir2Lir::LocCReturnWide() { - return mips_loc_c_return_wide; + return cu_->target64 ? mips64_loc_c_return_wide : mips_loc_c_return_wide; } RegLocation MipsMir2Lir::LocCReturnFloat() { @@ -86,14 +162,16 @@ RegLocation MipsMir2Lir::LocCReturnFloat() { } RegLocation MipsMir2Lir::LocCReturnDouble() { - if (fpuIs32Bit_) { - return mips_loc_c_return_double_fr0; + if (cu_->target64) { + return mips64_loc_c_return_double; + } else if (fpuIs32Bit_) { + return mips_loc_c_return_double_fr0; } else { - return mips_loc_c_return_double_fr1; + return mips_loc_c_return_double_fr1; } } -// Convert k64BitSolo into k64BitPair +// Convert k64BitSolo into k64BitPair. RegStorage MipsMir2Lir::Solo64ToPair64(RegStorage reg) { DCHECK(reg.IsDouble()); DCHECK_EQ(reg.GetRegNum() & 1, 0); @@ -113,16 +191,18 @@ RegStorage MipsMir2Lir::Fp64ToSolo32(RegStorage reg) { // Return a target-dependent special register. RegStorage MipsMir2Lir::TargetReg(SpecialTargetRegister reg, WideKind wide_kind) { - if (wide_kind == kWide) { - DCHECK((kArg0 <= reg && reg < kArg7) || (kFArg0 <= reg && reg < kFArg15) || (kRet0 == reg)); - RegStorage ret_reg = RegStorage::MakeRegPair(TargetReg(reg), - TargetReg(static_cast<SpecialTargetRegister>(reg + 1))); - if (!fpuIs32Bit_ && ret_reg.IsFloat()) { - // convert 64BitPair to 64BitSolo for 64bit FPUs. - RegStorage low = ret_reg.GetLow(); - ret_reg = RegStorage::FloatSolo64(low.GetRegNum()); - } - return ret_reg; + if (!cu_->target64 && wide_kind == kWide) { + DCHECK((kArg0 <= reg && reg < kArg7) || (kFArg0 <= reg && reg < kFArg15) || (kRet0 == reg)); + RegStorage ret_reg = RegStorage::MakeRegPair(TargetReg(reg), + TargetReg(static_cast<SpecialTargetRegister>(reg + 1))); + if (!fpuIs32Bit_ && ret_reg.IsFloat()) { + // convert 64BitPair to 64BitSolo for 64bit FPUs. + RegStorage low = ret_reg.GetLow(); + ret_reg = RegStorage::FloatSolo64(low.GetRegNum()); + } + return ret_reg; + } else if (cu_->target64 && (wide_kind == kWide || wide_kind == kRef)) { + return As64BitReg(TargetReg(reg)); } else { return TargetReg(reg); } @@ -132,25 +212,33 @@ RegStorage MipsMir2Lir::TargetReg(SpecialTargetRegister reg, WideKind wide_kind) RegStorage MipsMir2Lir::TargetReg(SpecialTargetRegister reg) { RegStorage res_reg; switch (reg) { - case kSelf: res_reg = rs_rMIPS_SELF; break; - case kSuspend: res_reg = rs_rMIPS_SUSPEND; break; - case kLr: res_reg = rs_rMIPS_LR; break; - case kPc: res_reg = rs_rMIPS_PC; break; - case kSp: res_reg = rs_rMIPS_SP; break; - case kArg0: res_reg = rs_rMIPS_ARG0; break; - case kArg1: res_reg = rs_rMIPS_ARG1; break; - case kArg2: res_reg = rs_rMIPS_ARG2; break; - case kArg3: res_reg = rs_rMIPS_ARG3; break; - case kFArg0: res_reg = rs_rMIPS_FARG0; break; - case kFArg1: res_reg = rs_rMIPS_FARG1; break; - case kFArg2: res_reg = rs_rMIPS_FARG2; break; - case kFArg3: res_reg = rs_rMIPS_FARG3; break; - case kRet0: res_reg = rs_rMIPS_RET0; break; - case kRet1: res_reg = rs_rMIPS_RET1; break; - case kInvokeTgt: res_reg = rs_rMIPS_INVOKE_TGT; break; - case kHiddenArg: res_reg = rs_rT0; break; + case kSelf: res_reg = rs_rS1; break; + case kSuspend: res_reg = rs_rS0; break; + case kLr: res_reg = rs_rRA; break; + case kPc: res_reg = RegStorage::InvalidReg(); break; + case kSp: res_reg = rs_rSP; break; + case kArg0: res_reg = rs_rA0; break; + case kArg1: res_reg = rs_rA1; break; + case kArg2: res_reg = rs_rA2; break; + case kArg3: res_reg = rs_rA3; break; + case kArg4: res_reg = cu_->target64 ? rs_rA4 : RegStorage::InvalidReg(); break; + case kArg5: res_reg = cu_->target64 ? rs_rA5 : RegStorage::InvalidReg(); break; + case kArg6: res_reg = cu_->target64 ? rs_rA6 : RegStorage::InvalidReg(); break; + case kArg7: res_reg = cu_->target64 ? rs_rA7 : RegStorage::InvalidReg(); break; + case kFArg0: res_reg = rs_rF12; break; + case kFArg1: res_reg = rs_rF13; break; + case kFArg2: res_reg = rs_rF14; break; + case kFArg3: res_reg = rs_rF15; break; + case kFArg4: res_reg = cu_->target64 ? rs_rF16 : RegStorage::InvalidReg(); break; + case kFArg5: res_reg = cu_->target64 ? rs_rF17 : RegStorage::InvalidReg(); break; + case kFArg6: res_reg = cu_->target64 ? rs_rF18 : RegStorage::InvalidReg(); break; + case kFArg7: res_reg = cu_->target64 ? rs_rF19 : RegStorage::InvalidReg(); break; + case kRet0: res_reg = rs_rV0; break; + case kRet1: res_reg = rs_rV1; break; + case kInvokeTgt: res_reg = rs_rT9; break; + case kHiddenArg: res_reg = cu_->target64 ? rs_rT0 : rs_rT0_32; break; case kHiddenFpArg: res_reg = RegStorage::InvalidReg(); break; - case kCount: res_reg = rs_rMIPS_COUNT; break; + case kCount: res_reg = RegStorage::InvalidReg(); break; default: res_reg = RegStorage::InvalidReg(); } return res_reg; @@ -172,27 +260,54 @@ RegStorage MipsMir2Lir::InToRegStorageMipsMapper::GetNextReg(ShortyArg arg) { return result; } +RegStorage MipsMir2Lir::InToRegStorageMips64Mapper::GetNextReg(ShortyArg arg) { + const SpecialTargetRegister coreArgMappingToPhysicalReg[] = + {kArg1, kArg2, kArg3, kArg4, kArg5, kArg6, kArg7}; + const size_t coreArgMappingToPhysicalRegSize = arraysize(coreArgMappingToPhysicalReg); + const SpecialTargetRegister fpArgMappingToPhysicalReg[] = + {kFArg1, kFArg2, kFArg3, kFArg4, kFArg5, kFArg6, kFArg7}; + const size_t fpArgMappingToPhysicalRegSize = arraysize(fpArgMappingToPhysicalReg); + + RegStorage result = RegStorage::InvalidReg(); + if (arg.IsFP()) { + if (cur_arg_reg_ < fpArgMappingToPhysicalRegSize) { + DCHECK(!arg.IsRef()); + result = m2l_->TargetReg(fpArgMappingToPhysicalReg[cur_arg_reg_++], + arg.IsWide() ? kWide : kNotWide); + } + } else { + if (cur_arg_reg_ < coreArgMappingToPhysicalRegSize) { + DCHECK(!(arg.IsWide() && arg.IsRef())); + result = m2l_->TargetReg(coreArgMappingToPhysicalReg[cur_arg_reg_++], + arg.IsRef() ? kRef : (arg.IsWide() ? kWide : kNotWide)); + } + } + return result; +} + /* * Decode the register id. */ ResourceMask MipsMir2Lir::GetRegMaskCommon(const RegStorage& reg) const { - if (reg.IsDouble()) { - return ResourceMask::TwoBits((reg.GetRegNum() & ~1) + kMipsFPReg0); - } else if (reg.IsSingle()) { - return ResourceMask::Bit(reg.GetRegNum() + kMipsFPReg0); + if (cu_->target64) { + return ResourceMask::Bit((reg.IsFloat() ? kMipsFPReg0 : 0) + reg.GetRegNum()); } else { - return ResourceMask::Bit(reg.GetRegNum()); + if (reg.IsDouble()) { + return ResourceMask::TwoBits((reg.GetRegNum() & ~1) + kMipsFPReg0); + } else if (reg.IsSingle()) { + return ResourceMask::Bit(reg.GetRegNum() + kMipsFPReg0); + } else { + return ResourceMask::Bit(reg.GetRegNum()); + } } } ResourceMask MipsMir2Lir::GetPCUseDefEncoding() const { - return ResourceMask::Bit(kMipsRegPC); + return cu_->target64 ? ResourceMask::Bit(kMips64RegPC) : ResourceMask::Bit(kMipsRegPC); } - -void MipsMir2Lir::SetupTargetResourceMasks(LIR* lir, uint64_t flags, - ResourceMask* use_mask, ResourceMask* def_mask) { - DCHECK_EQ(cu_->instruction_set, kMips); +void MipsMir2Lir::SetupTargetResourceMasks(LIR* lir, uint64_t flags, ResourceMask* use_mask, + ResourceMask* def_mask) { DCHECK(!lir->flags.use_def_invalid); // Mips-specific resource map setup here. @@ -208,20 +323,22 @@ void MipsMir2Lir::SetupTargetResourceMasks(LIR* lir, uint64_t flags, def_mask->SetBit(kMipsRegLR); } - if (flags & REG_DEF_HI) { - def_mask->SetBit(kMipsRegHI); - } + if (!cu_->target64) { + if (flags & REG_DEF_HI) { + def_mask->SetBit(kMipsRegHI); + } - if (flags & REG_DEF_LO) { - def_mask->SetBit(kMipsRegLO); - } + if (flags & REG_DEF_LO) { + def_mask->SetBit(kMipsRegLO); + } - if (flags & REG_USE_HI) { - use_mask->SetBit(kMipsRegHI); - } + if (flags & REG_USE_HI) { + use_mask->SetBit(kMipsRegHI); + } - if (flags & REG_USE_LO) { - use_mask->SetBit(kMipsRegLO); + if (flags & REG_USE_LO) { + use_mask->SetBit(kMipsRegLO); + } } } @@ -234,9 +351,16 @@ static const char *mips_reg_name[MIPS_REG_COUNT] = { "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra" }; +static const char *mips64_reg_name[MIPS_REG_COUNT] = { + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra" +}; + /* * Interpret a format string and build a string no longer than size - * See format key in Assemble.c. + * See format key in assemble_mips.cc. */ std::string MipsMir2Lir::BuildInsnString(const char *fmt, LIR *lir, unsigned char* base_addr) { std::string buf; @@ -311,7 +435,11 @@ std::string MipsMir2Lir::BuildInsnString(const char *fmt, LIR *lir, unsigned cha break; case 'r': DCHECK(operand >= 0 && operand < MIPS_REG_COUNT); - strcpy(tbuf, mips_reg_name[operand]); + if (cu_->target64) { + strcpy(tbuf, mips64_reg_name[operand]); + } else { + strcpy(tbuf, mips_reg_name[operand]); + } break; case 'N': // Placeholder for delay slot handling @@ -330,7 +458,7 @@ std::string MipsMir2Lir::BuildInsnString(const char *fmt, LIR *lir, unsigned cha return buf; } -// FIXME: need to redo resource maps for MIPS - fix this at that time +// FIXME: need to redo resource maps for MIPS - fix this at that time. void MipsMir2Lir::DumpResourceMask(LIR *mips_lir, const ResourceMask& mask, const char *prefix) { char buf[256]; buf[0] = 0; @@ -341,7 +469,7 @@ void MipsMir2Lir::DumpResourceMask(LIR *mips_lir, const ResourceMask& mask, cons char num[8]; int i; - for (i = 0; i < kMipsRegEnd; i++) { + for (i = 0; i < (cu_->target64 ? kMips64RegEnd : kMipsRegEnd); i++) { if (mask.HasBit(i)) { snprintf(num, arraysize(num), "%d ", i); strcat(buf, num); @@ -354,7 +482,7 @@ void MipsMir2Lir::DumpResourceMask(LIR *mips_lir, const ResourceMask& mask, cons if (mask.HasBit(ResourceMask::kFPStatus)) { strcat(buf, "fpcc "); } - /* Memory bits */ + // Memory bits. if (mips_lir && (mask.HasBit(ResourceMask::kDalvikReg))) { snprintf(buf + strlen(buf), arraysize(buf) - strlen(buf), "dr%d%s", DECODE_ALIAS_INFO_REG(mips_lir->flags.alias_info), @@ -389,63 +517,114 @@ void MipsMir2Lir::AdjustSpillMask() { /* Clobber all regs that might be used by an external C call */ void MipsMir2Lir::ClobberCallerSave() { - Clobber(rs_rZERO); - Clobber(rs_rAT); - Clobber(rs_rV0); - Clobber(rs_rV1); - Clobber(rs_rA0); - Clobber(rs_rA1); - Clobber(rs_rA2); - Clobber(rs_rA3); - Clobber(rs_rT0); - Clobber(rs_rT1); - Clobber(rs_rT2); - Clobber(rs_rT3); - Clobber(rs_rT4); - Clobber(rs_rT5); - Clobber(rs_rT6); - Clobber(rs_rT7); - Clobber(rs_rT8); - Clobber(rs_rT9); - Clobber(rs_rK0); - Clobber(rs_rK1); - Clobber(rs_rGP); - Clobber(rs_rFP); - Clobber(rs_rRA); - Clobber(rs_rF0); - Clobber(rs_rF1); - Clobber(rs_rF2); - Clobber(rs_rF3); - Clobber(rs_rF4); - Clobber(rs_rF5); - Clobber(rs_rF6); - Clobber(rs_rF7); - Clobber(rs_rF8); - Clobber(rs_rF9); - Clobber(rs_rF10); - Clobber(rs_rF11); - Clobber(rs_rF12); - Clobber(rs_rF13); - Clobber(rs_rF14); - Clobber(rs_rF15); - if (fpuIs32Bit_) { - Clobber(rs_rD0_fr0); - Clobber(rs_rD1_fr0); - Clobber(rs_rD2_fr0); - Clobber(rs_rD3_fr0); - Clobber(rs_rD4_fr0); - Clobber(rs_rD5_fr0); - Clobber(rs_rD6_fr0); - Clobber(rs_rD7_fr0); + if (cu_->target64) { + Clobber(rs_rZEROd); + Clobber(rs_rATd); + Clobber(rs_rV0d); + Clobber(rs_rV1d); + Clobber(rs_rA0d); + Clobber(rs_rA1d); + Clobber(rs_rA2d); + Clobber(rs_rA3d); + Clobber(rs_rA4d); + Clobber(rs_rA5d); + Clobber(rs_rA6d); + Clobber(rs_rA7d); + Clobber(rs_rT0d); + Clobber(rs_rT1d); + Clobber(rs_rT2d); + Clobber(rs_rT3d); + Clobber(rs_rT8d); + Clobber(rs_rT9d); + Clobber(rs_rK0d); + Clobber(rs_rK1d); + Clobber(rs_rGPd); + Clobber(rs_rFPd); + Clobber(rs_rRAd); + + Clobber(rs_rF0); + Clobber(rs_rF1); + Clobber(rs_rF2); + Clobber(rs_rF3); + Clobber(rs_rF4); + Clobber(rs_rF5); + Clobber(rs_rF6); + Clobber(rs_rF7); + Clobber(rs_rF8); + Clobber(rs_rF9); + Clobber(rs_rF10); + Clobber(rs_rF11); + Clobber(rs_rF12); + Clobber(rs_rF13); + Clobber(rs_rF14); + Clobber(rs_rF15); + Clobber(rs_rD0); + Clobber(rs_rD1); + Clobber(rs_rD2); + Clobber(rs_rD3); + Clobber(rs_rD4); + Clobber(rs_rD5); + Clobber(rs_rD6); + Clobber(rs_rD7); } else { - Clobber(rs_rD0_fr1); - Clobber(rs_rD1_fr1); - Clobber(rs_rD2_fr1); - Clobber(rs_rD3_fr1); - Clobber(rs_rD4_fr1); - Clobber(rs_rD5_fr1); - Clobber(rs_rD6_fr1); - Clobber(rs_rD7_fr1); + Clobber(rs_rZERO); + Clobber(rs_rAT); + Clobber(rs_rV0); + Clobber(rs_rV1); + Clobber(rs_rA0); + Clobber(rs_rA1); + Clobber(rs_rA2); + Clobber(rs_rA3); + Clobber(rs_rT0_32); + Clobber(rs_rT1_32); + Clobber(rs_rT2_32); + Clobber(rs_rT3_32); + Clobber(rs_rT4_32); + Clobber(rs_rT5_32); + Clobber(rs_rT6_32); + Clobber(rs_rT7_32); + Clobber(rs_rT8); + Clobber(rs_rT9); + Clobber(rs_rK0); + Clobber(rs_rK1); + Clobber(rs_rGP); + Clobber(rs_rFP); + Clobber(rs_rRA); + Clobber(rs_rF0); + Clobber(rs_rF1); + Clobber(rs_rF2); + Clobber(rs_rF3); + Clobber(rs_rF4); + Clobber(rs_rF5); + Clobber(rs_rF6); + Clobber(rs_rF7); + Clobber(rs_rF8); + Clobber(rs_rF9); + Clobber(rs_rF10); + Clobber(rs_rF11); + Clobber(rs_rF12); + Clobber(rs_rF13); + Clobber(rs_rF14); + Clobber(rs_rF15); + if (fpuIs32Bit_) { + Clobber(rs_rD0_fr0); + Clobber(rs_rD1_fr0); + Clobber(rs_rD2_fr0); + Clobber(rs_rD3_fr0); + Clobber(rs_rD4_fr0); + Clobber(rs_rD5_fr0); + Clobber(rs_rD6_fr0); + Clobber(rs_rD7_fr0); + } else { + Clobber(rs_rD0_fr1); + Clobber(rs_rD1_fr1); + Clobber(rs_rD2_fr1); + Clobber(rs_rD3_fr1); + Clobber(rs_rD4_fr1); + Clobber(rs_rD5_fr1); + Clobber(rs_rD6_fr1); + Clobber(rs_rD7_fr1); + } } } @@ -463,18 +642,30 @@ RegLocation MipsMir2Lir::GetReturnAlt() { /* To be used when explicitly managing register use */ void MipsMir2Lir::LockCallTemps() { - LockTemp(rs_rMIPS_ARG0); - LockTemp(rs_rMIPS_ARG1); - LockTemp(rs_rMIPS_ARG2); - LockTemp(rs_rMIPS_ARG3); + LockTemp(TargetReg(kArg0)); + LockTemp(TargetReg(kArg1)); + LockTemp(TargetReg(kArg2)); + LockTemp(TargetReg(kArg3)); + if (cu_->target64) { + LockTemp(TargetReg(kArg4)); + LockTemp(TargetReg(kArg5)); + LockTemp(TargetReg(kArg6)); + LockTemp(TargetReg(kArg7)); + } } /* To be used when explicitly managing register use */ void MipsMir2Lir::FreeCallTemps() { - FreeTemp(rs_rMIPS_ARG0); - FreeTemp(rs_rMIPS_ARG1); - FreeTemp(rs_rMIPS_ARG2); - FreeTemp(rs_rMIPS_ARG3); + FreeTemp(TargetReg(kArg0)); + FreeTemp(TargetReg(kArg1)); + FreeTemp(TargetReg(kArg2)); + FreeTemp(TargetReg(kArg3)); + if (cu_->target64) { + FreeTemp(TargetReg(kArg4)); + FreeTemp(TargetReg(kArg5)); + FreeTemp(TargetReg(kArg6)); + FreeTemp(TargetReg(kArg7)); + } FreeTemp(TargetReg(kHiddenArg)); } @@ -488,31 +679,63 @@ bool MipsMir2Lir::GenMemBarrier(MemBarrierKind barrier_kind ATTRIBUTE_UNUSED) { } void MipsMir2Lir::CompilerInitializeRegAlloc() { - reg_pool_.reset(new (arena_) RegisterPool(this, arena_, core_regs, empty_pool /* core64 */, - sp_regs, - fpuIs32Bit_ ? dp_fr0_regs : dp_fr1_regs, - reserved_regs, empty_pool /* reserved64 */, - core_temps, empty_pool /* core64_temps */, - sp_temps, - fpuIs32Bit_ ? dp_fr0_temps : dp_fr1_temps)); - - // Target-specific adjustments. - - // Alias single precision floats to appropriate half of overlapping double. - for (RegisterInfo* info : reg_pool_->sp_regs_) { - int sp_reg_num = info->GetReg().GetRegNum(); - int dp_reg_num = sp_reg_num & ~1; - RegStorage dp_reg = RegStorage::Solo64(RegStorage::kFloatingPoint | dp_reg_num); - RegisterInfo* dp_reg_info = GetRegInfo(dp_reg); - // Double precision register's master storage should refer to itself. - DCHECK_EQ(dp_reg_info, dp_reg_info->Master()); - // Redirect single precision's master storage to master. - info->SetMaster(dp_reg_info); - // Singles should show a single 32-bit mask bit, at first referring to the low half. - DCHECK_EQ(info->StorageMask(), 0x1U); - if (sp_reg_num & 1) { - // For odd singles, change to user the high word of the backing double. - info->SetStorageMask(0x2); + if (cu_->target64) { + reg_pool_.reset(new (arena_) RegisterPool(this, arena_, core_regs_64, core_regs_64d, sp_regs_64, + dp_regs_64, reserved_regs_64, reserved_regs_64d, + core_temps_64, core_temps_64d, sp_temps_64, + dp_temps_64)); + + // Alias single precision floats to appropriate half of overlapping double. + for (RegisterInfo* info : reg_pool_->sp_regs_) { + int sp_reg_num = info->GetReg().GetRegNum(); + int dp_reg_num = sp_reg_num; + RegStorage dp_reg = RegStorage::Solo64(RegStorage::kFloatingPoint | dp_reg_num); + RegisterInfo* dp_reg_info = GetRegInfo(dp_reg); + // Double precision register's master storage should refer to itself. + DCHECK_EQ(dp_reg_info, dp_reg_info->Master()); + // Redirect single precision's master storage to master. + info->SetMaster(dp_reg_info); + // Singles should show a single 32-bit mask bit, at first referring to the low half. + DCHECK_EQ(info->StorageMask(), 0x1U); + } + + // Alias 32bit W registers to corresponding 64bit X registers. + for (RegisterInfo* info : reg_pool_->core_regs_) { + int d_reg_num = info->GetReg().GetRegNum(); + RegStorage d_reg = RegStorage::Solo64(d_reg_num); + RegisterInfo* d_reg_info = GetRegInfo(d_reg); + // 64bit D register's master storage should refer to itself. + DCHECK_EQ(d_reg_info, d_reg_info->Master()); + // Redirect 32bit master storage to 64bit D. + info->SetMaster(d_reg_info); + // 32bit should show a single 32-bit mask bit, at first referring to the low half. + DCHECK_EQ(info->StorageMask(), 0x1U); + } + } else { + reg_pool_.reset(new (arena_) RegisterPool(this, arena_, core_regs_32, empty_pool, // core64 + sp_regs_32, + fpuIs32Bit_ ? dp_fr0_regs_32 : dp_fr1_regs_32, + reserved_regs_32, empty_pool, // reserved64 + core_temps_32, empty_pool, // core64_temps + sp_temps_32, + fpuIs32Bit_ ? dp_fr0_temps_32 : dp_fr1_temps_32)); + + // Alias single precision floats to appropriate half of overlapping double. + for (RegisterInfo* info : reg_pool_->sp_regs_) { + int sp_reg_num = info->GetReg().GetRegNum(); + int dp_reg_num = sp_reg_num & ~1; + RegStorage dp_reg = RegStorage::Solo64(RegStorage::kFloatingPoint | dp_reg_num); + RegisterInfo* dp_reg_info = GetRegInfo(dp_reg); + // Double precision register's master storage should refer to itself. + DCHECK_EQ(dp_reg_info, dp_reg_info->Master()); + // Redirect single precision's master storage to master. + info->SetMaster(dp_reg_info); + // Singles should show a single 32-bit mask bit, at first referring to the low half. + DCHECK_EQ(info->StorageMask(), 0x1U); + if (sp_reg_num & 1) { + // For odd singles, change to user the high word of the backing double. + info->SetStorageMask(0x2); + } } } @@ -520,7 +743,11 @@ void MipsMir2Lir::CompilerInitializeRegAlloc() { // TODO: adjust when we roll to hard float calling convention. reg_pool_->next_core_reg_ = 2; reg_pool_->next_sp_reg_ = 2; - reg_pool_->next_dp_reg_ = 2; + if (cu_->target64) { + reg_pool_->next_dp_reg_ = 1; + } else { + reg_pool_->next_dp_reg_ = 2; + } } /* @@ -531,14 +758,24 @@ void MipsMir2Lir::CompilerInitializeRegAlloc() { */ RegStorage MipsMir2Lir::LoadHelper(QuickEntrypointEnum trampoline) { // NOTE: native pointer. - LoadWordDisp(rs_rMIPS_SELF, GetThreadOffset<4>(trampoline).Int32Value(), rs_rT9); - return rs_rT9; + if (cu_->target64) { + LoadWordDisp(TargetPtrReg(kSelf), GetThreadOffset<8>(trampoline).Int32Value(), + TargetPtrReg(kInvokeTgt)); + } else { + LoadWordDisp(TargetPtrReg(kSelf), GetThreadOffset<4>(trampoline).Int32Value(), + TargetPtrReg(kInvokeTgt)); + } + return TargetPtrReg(kInvokeTgt); } LIR* MipsMir2Lir::CheckSuspendUsingLoad() { RegStorage tmp = AllocTemp(); // NOTE: native pointer. - LoadWordDisp(rs_rMIPS_SELF, Thread::ThreadSuspendTriggerOffset<4>().Int32Value(), tmp); + if (cu_->target64) { + LoadWordDisp(TargetPtrReg(kSelf), Thread::ThreadSuspendTriggerOffset<8>().Int32Value(), tmp); + } else { + LoadWordDisp(TargetPtrReg(kSelf), Thread::ThreadSuspendTriggerOffset<4>().Int32Value(), tmp); + } LIR *inst = LoadWordDisp(tmp, 0, tmp); FreeTemp(tmp); return inst; @@ -546,31 +783,47 @@ LIR* MipsMir2Lir::CheckSuspendUsingLoad() { LIR* MipsMir2Lir::GenAtomic64Load(RegStorage r_base, int displacement, RegStorage r_dest) { DCHECK(!r_dest.IsFloat()); // See RegClassForFieldLoadStore(). - DCHECK(r_dest.IsPair()); + if (!cu_->target64) { + DCHECK(r_dest.IsPair()); + } ClobberCallerSave(); - LockCallTemps(); // Using fixed registers + LockCallTemps(); // Using fixed registers. RegStorage reg_ptr = TargetReg(kArg0); OpRegRegImm(kOpAdd, reg_ptr, r_base, displacement); RegStorage r_tgt = LoadHelper(kQuickA64Load); LIR *ret = OpReg(kOpBlx, r_tgt); - RegStorage reg_ret = RegStorage::MakeRegPair(TargetReg(kRet0), TargetReg(kRet1)); - OpRegCopyWide(r_dest, reg_ret); + RegStorage reg_ret; + if (cu_->target64) { + OpRegCopy(r_dest, TargetReg(kRet0)); + } else { + reg_ret = RegStorage::MakeRegPair(TargetReg(kRet0), TargetReg(kRet1)); + OpRegCopyWide(r_dest, reg_ret); + } return ret; } LIR* MipsMir2Lir::GenAtomic64Store(RegStorage r_base, int displacement, RegStorage r_src) { DCHECK(!r_src.IsFloat()); // See RegClassForFieldLoadStore(). - DCHECK(r_src.IsPair()); + if (cu_->target64) { + DCHECK(!r_src.IsPair()); + } else { + DCHECK(r_src.IsPair()); + } ClobberCallerSave(); - LockCallTemps(); // Using fixed registers + LockCallTemps(); // Using fixed registers. RegStorage temp_ptr = AllocTemp(); OpRegRegImm(kOpAdd, temp_ptr, r_base, displacement); RegStorage temp_value = AllocTempWide(); OpRegCopyWide(temp_value, r_src); - RegStorage reg_ptr = TargetReg(kArg0); - OpRegCopy(reg_ptr, temp_ptr); - RegStorage reg_value = RegStorage::MakeRegPair(TargetReg(kArg2), TargetReg(kArg3)); - OpRegCopyWide(reg_value, temp_value); + if (cu_->target64) { + OpRegCopyWide(TargetReg(kArg0, kWide), temp_ptr); + OpRegCopyWide(TargetReg(kArg1, kWide), temp_value); + } else { + RegStorage reg_ptr = TargetReg(kArg0); + OpRegCopy(reg_ptr, temp_ptr); + RegStorage reg_value = RegStorage::MakeRegPair(TargetReg(kArg2), TargetReg(kArg3)); + OpRegCopyWide(reg_value, temp_value); + } FreeTemp(temp_ptr); FreeTemp(temp_value); RegStorage r_tgt = LoadHelper(kQuickA64Store); @@ -582,12 +835,15 @@ void MipsMir2Lir::SpillCoreRegs() { return; } uint32_t mask = core_spill_mask_; - int offset = num_core_spills_ * 4; - OpRegImm(kOpSub, rs_rSP, offset); + int ptr_size = cu_->target64 ? 8 : 4; + int offset = num_core_spills_ * ptr_size; + const RegStorage rs_sp = TargetPtrReg(kSp); + OpRegImm(kOpSub, rs_sp, offset); for (int reg = 0; mask; mask >>= 1, reg++) { if (mask & 0x1) { - offset -= 4; - Store32Disp(rs_rMIPS_SP, offset, RegStorage::Solo32(reg)); + offset -= ptr_size; + StoreWordDisp(rs_sp, offset, + cu_->target64 ? RegStorage::Solo64(reg) : RegStorage::Solo32(reg)); } } } @@ -597,14 +853,17 @@ void MipsMir2Lir::UnSpillCoreRegs() { return; } uint32_t mask = core_spill_mask_; - int offset = frame_size_; + int offset = frame_size_; + int ptr_size = cu_->target64 ? 8 : 4; + const RegStorage rs_sp = TargetPtrReg(kSp); for (int reg = 0; mask; mask >>= 1, reg++) { if (mask & 0x1) { - offset -= 4; - Load32Disp(rs_rMIPS_SP, offset, RegStorage::Solo32(reg)); + offset -= ptr_size; + LoadWordDisp(rs_sp, offset, + cu_->target64 ? RegStorage::Solo64(reg) : RegStorage::Solo32(reg)); } } - OpRegImm(kOpAdd, rs_rSP, frame_size_); + OpRegImm(kOpAdd, rs_sp, frame_size_); } bool MipsMir2Lir::IsUnconditionalBranch(LIR* lir) { @@ -624,11 +883,12 @@ RegisterClass MipsMir2Lir::RegClassForFieldLoadStore(OpSize size, bool is_volati } MipsMir2Lir::MipsMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena) - : Mir2Lir(cu, mir_graph, arena), in_to_reg_storage_mips_mapper_(this), - isaIsR6_(cu->compiler_driver->GetInstructionSetFeatures() - ->AsMipsInstructionSetFeatures()->IsR6()), - fpuIs32Bit_(cu->compiler_driver->GetInstructionSetFeatures() - ->AsMipsInstructionSetFeatures()->Is32BitFloatingPoint()) { + : Mir2Lir(cu, mir_graph, arena), in_to_reg_storage_mips64_mapper_(this), + in_to_reg_storage_mips_mapper_(this), + isaIsR6_(cu_->target64 ? true : cu->compiler_driver->GetInstructionSetFeatures() + ->AsMipsInstructionSetFeatures()->IsR6()), + fpuIs32Bit_(cu_->target64 ? false : cu->compiler_driver->GetInstructionSetFeatures() + ->AsMipsInstructionSetFeatures()->Is32BitFloatingPoint()) { for (int i = 0; i < kMipsLast; i++) { DCHECK_EQ(MipsMir2Lir::EncodingMap[i].opcode, i) << "Encoding order for " << MipsMir2Lir::EncodingMap[i].name diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc index 3b7e0ed23b..bf0e0fc78b 100644 --- a/compiler/dex/quick/mips/utility_mips.cc +++ b/compiler/dex/quick/mips/utility_mips.cc @@ -26,30 +26,70 @@ namespace art { -/* This file contains codegen for the MIPS32 ISA. */ +/* This file contains codegen for the Mips ISA */ LIR* MipsMir2Lir::OpFpRegCopy(RegStorage r_dest, RegStorage r_src) { int opcode; - /* must be both DOUBLE or both not DOUBLE */ - DCHECK_EQ(r_dest.IsDouble(), r_src.IsDouble()); - if (r_dest.IsDouble()) { - opcode = kMipsFmovd; - } else { - if (r_dest.IsSingle()) { - if (r_src.IsSingle()) { - opcode = kMipsFmovs; + if (cu_->target64) { + DCHECK_EQ(r_dest.Is64Bit(), r_src.Is64Bit()); + if (r_dest.Is64Bit()) { + if (r_dest.IsDouble()) { + if (r_src.IsDouble()) { + opcode = kMipsFmovd; + } else { + // Note the operands are swapped for the dmtc1 instr. + RegStorage t_opnd = r_src; + r_src = r_dest; + r_dest = t_opnd; + opcode = kMips64Dmtc1; + } } else { - /* note the operands are swapped for the mtc1 instr */ - RegStorage t_opnd = r_src; - r_src = r_dest; - r_dest = t_opnd; - opcode = kMipsMtc1; + DCHECK(r_src.IsDouble()); + opcode = kMips64Dmfc1; } } else { - DCHECK(r_src.IsSingle()); - opcode = kMipsMfc1; + if (r_dest.IsSingle()) { + if (r_src.IsSingle()) { + opcode = kMipsFmovs; + } else { + // Note the operands are swapped for the mtc1 instr. + RegStorage t_opnd = r_src; + r_src = r_dest; + r_dest = t_opnd; + opcode = kMipsMtc1; + } + } else { + DCHECK(r_src.IsSingle()); + opcode = kMipsMfc1; + } } + } else { + // Must be both DOUBLE or both not DOUBLE. + DCHECK_EQ(r_dest.IsDouble(), r_src.IsDouble()); + if (r_dest.IsDouble()) { + opcode = kMipsFmovd; + } else { + if (r_dest.IsSingle()) { + if (r_src.IsSingle()) { + opcode = kMipsFmovs; + } else { + // Note the operands are swapped for the mtc1 instr. + RegStorage t_opnd = r_src; + r_src = r_dest; + r_dest = t_opnd; + opcode = kMipsMtc1; + } + } else { + DCHECK(r_src.IsSingle()); + opcode = kMipsMfc1; + } + } + } + LIR* res; + if (cu_->target64) { + res = RawLIR(current_dalvik_offset_, opcode, r_dest.GetReg(), r_src.GetReg()); + } else { + res = RawLIR(current_dalvik_offset_, opcode, r_src.GetReg(), r_dest.GetReg()); } - LIR* res = RawLIR(current_dalvik_offset_, opcode, r_src.GetReg(), r_dest.GetReg()); if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) { res->flags.is_nop = true; } @@ -95,7 +135,7 @@ LIR* MipsMir2Lir::LoadConstantNoClobber(RegStorage r_dest, int value) { r_dest = AllocTemp(); } - /* See if the value can be constructed cheaply */ + // See if the value can be constructed cheaply. if (value == 0) { res = NewLIR2(kMipsMove, r_dest.GetReg(), rZERO); } else if (IsUint<16>(value)) { @@ -118,6 +158,117 @@ LIR* MipsMir2Lir::LoadConstantNoClobber(RegStorage r_dest, int value) { return res; } +LIR* MipsMir2Lir::LoadConstantWideNoClobber(RegStorage r_dest, int64_t value) { + LIR* res = nullptr; + DCHECK(r_dest.Is64Bit()); + RegStorage r_dest_save = r_dest; + int is_fp_reg = r_dest.IsFloat(); + if (is_fp_reg) { + DCHECK(r_dest.IsDouble()); + r_dest = AllocTemp(); + } + + int bit31 = (value & UINT64_C(0x80000000)) != 0; + + // Loads with 1 instruction. + if (IsUint<16>(value)) { + res = NewLIR3(kMipsOri, r_dest.GetReg(), rZEROd, value); + } else if (IsInt<16>(value)) { + res = NewLIR3(kMips64Daddiu, r_dest.GetReg(), rZEROd, value); + } else if ((value & 0xFFFF) == 0 && IsInt<16>(value >> 16)) { + res = NewLIR2(kMipsLui, r_dest.GetReg(), value >> 16); + } else if (IsInt<32>(value)) { + // Loads with 2 instructions. + res = NewLIR2(kMipsLui, r_dest.GetReg(), value >> 16); + NewLIR3(kMipsOri, r_dest.GetReg(), r_dest.GetReg(), value); + } else if ((value & 0xFFFF0000) == 0 && IsInt<16>(value >> 32)) { + res = NewLIR3(kMipsOri, r_dest.GetReg(), rZEROd, value); + NewLIR2(kMips64Dahi, r_dest.GetReg(), value >> 32); + } else if ((value & UINT64_C(0xFFFFFFFF0000)) == 0) { + res = NewLIR3(kMipsOri, r_dest.GetReg(), rZEROd, value); + NewLIR2(kMips64Dati, r_dest.GetReg(), value >> 48); + } else if ((value & 0xFFFF) == 0 && (value >> 32) >= (-32768 - bit31) && + (value >> 32) <= (32767 - bit31)) { + res = NewLIR2(kMipsLui, r_dest.GetReg(), value >> 16); + NewLIR2(kMips64Dahi, r_dest.GetReg(), (value >> 32) + bit31); + } else if ((value & 0xFFFF) == 0 && ((value >> 31) & 0x1FFFF) == ((0x20000 - bit31) & 0x1FFFF)) { + res = NewLIR2(kMipsLui, r_dest.GetReg(), value >> 16); + NewLIR2(kMips64Dati, r_dest.GetReg(), (value >> 48) + bit31); + } else { + int64_t tmp = value; + int shift_cnt = 0; + while ((tmp & 1) == 0) { + tmp >>= 1; + shift_cnt++; + } + + if (IsUint<16>(tmp)) { + res = NewLIR3(kMipsOri, r_dest.GetReg(), rZEROd, tmp); + NewLIR3((shift_cnt < 32) ? kMips64Dsll : kMips64Dsll32, r_dest.GetReg(), r_dest.GetReg(), + shift_cnt & 0x1F); + } else if (IsInt<16>(tmp)) { + res = NewLIR3(kMips64Daddiu, r_dest.GetReg(), rZEROd, tmp); + NewLIR3((shift_cnt < 32) ? kMips64Dsll : kMips64Dsll32, r_dest.GetReg(), r_dest.GetReg(), + shift_cnt & 0x1F); + } else if (IsInt<32>(tmp)) { + // Loads with 3 instructions. + res = NewLIR2(kMipsLui, r_dest.GetReg(), tmp >> 16); + NewLIR3(kMipsOri, r_dest.GetReg(), r_dest.GetReg(), tmp); + NewLIR3((shift_cnt < 32) ? kMips64Dsll : kMips64Dsll32, r_dest.GetReg(), r_dest.GetReg(), + shift_cnt & 0x1F); + } else { + tmp = value >> 16; + shift_cnt = 16; + while ((tmp & 1) == 0) { + tmp >>= 1; + shift_cnt++; + } + + if (IsUint<16>(tmp)) { + res = NewLIR3(kMipsOri, r_dest.GetReg(), rZEROd, tmp); + NewLIR3((shift_cnt < 32) ? kMips64Dsll : kMips64Dsll32, r_dest.GetReg(), r_dest.GetReg(), + shift_cnt & 0x1F); + NewLIR3(kMipsOri, r_dest.GetReg(), r_dest.GetReg(), value); + } else if (IsInt<16>(tmp)) { + res = NewLIR3(kMips64Daddiu, r_dest.GetReg(), rZEROd, tmp); + NewLIR3((shift_cnt < 32) ? kMips64Dsll : kMips64Dsll32, r_dest.GetReg(), r_dest.GetReg(), + shift_cnt & 0x1F); + NewLIR3(kMipsOri, r_dest.GetReg(), r_dest.GetReg(), value); + } else { + // Loads with 3-4 instructions. + uint64_t tmp2 = value; + if (((tmp2 >> 16) & 0xFFFF) != 0 || (tmp2 & 0xFFFFFFFF) == 0) { + res = NewLIR2(kMipsLui, r_dest.GetReg(), tmp2 >> 16); + } + if ((tmp2 & 0xFFFF) != 0) { + if (res) + NewLIR3(kMipsOri, r_dest.GetReg(), r_dest.GetReg(), tmp2); + else + res = NewLIR3(kMipsOri, r_dest.GetReg(), rZEROd, tmp2); + } + if (bit31) { + tmp2 += UINT64_C(0x100000000); + } + if (((tmp2 >> 32) & 0xFFFF) != 0) { + NewLIR2(kMips64Dahi, r_dest.GetReg(), tmp2 >> 32); + } + if (tmp2 & UINT64_C(0x800000000000)) { + tmp2 += UINT64_C(0x1000000000000); + } + if ((tmp2 >> 48) != 0) { + NewLIR2(kMips64Dati, r_dest.GetReg(), tmp2 >> 48); + } + } + } + } + + if (is_fp_reg) { + NewLIR2(kMips64Dmtc1, r_dest.GetReg(), r_dest_save.GetReg()); + FreeTemp(r_dest); + } + return res; +} + LIR* MipsMir2Lir::OpUnconditionalBranch(LIR* target) { LIR* res = NewLIR1(kMipsB, 0 /* offset to be patched during assembly*/); res->target = target; @@ -136,57 +287,33 @@ LIR* MipsMir2Lir::OpReg(OpKind op, RegStorage r_dest_src) { default: LOG(FATAL) << "Bad case in OpReg"; } - return NewLIR2(opcode, rRA, r_dest_src.GetReg()); + return NewLIR2(opcode, cu_->target64 ? rRAd : rRA, r_dest_src.GetReg()); } LIR* MipsMir2Lir::OpRegImm(OpKind op, RegStorage r_dest_src1, int value) { - LIR *res; - bool neg = (value < 0); - int abs_value = (neg) ? -value : value; - bool short_form = (abs_value & 0xff) == abs_value; - MipsOpCode opcode = kMipsNop; - switch (op) { - case kOpAdd: - return OpRegRegImm(op, r_dest_src1, r_dest_src1, value); - break; - case kOpSub: - return OpRegRegImm(op, r_dest_src1, r_dest_src1, value); - break; - default: - LOG(FATAL) << "Bad case in OpRegImm"; - break; - } - if (short_form) { - res = NewLIR2(opcode, r_dest_src1.GetReg(), abs_value); + if ((op == kOpAdd) || (op == kOpSub)) { + return OpRegRegImm(op, r_dest_src1, r_dest_src1, value); } else { - RegStorage r_scratch = AllocTemp(); - res = LoadConstant(r_scratch, value); - if (op == kOpCmp) - NewLIR2(opcode, r_dest_src1.GetReg(), r_scratch.GetReg()); - else - NewLIR3(opcode, r_dest_src1.GetReg(), r_dest_src1.GetReg(), r_scratch.GetReg()); + LOG(FATAL) << "Bad case in OpRegImm"; } - return res; + UNREACHABLE(); } LIR* MipsMir2Lir::OpRegRegReg(OpKind op, RegStorage r_dest, RegStorage r_src1, RegStorage r_src2) { MipsOpCode opcode = kMipsNop; + bool is64bit = cu_->target64 && (r_dest.Is64Bit() || r_src1.Is64Bit() || r_src2.Is64Bit()); switch (op) { case kOpAdd: - opcode = kMipsAddu; + opcode = is64bit ? kMips64Daddu : kMipsAddu; break; case kOpSub: - opcode = kMipsSubu; + opcode = is64bit ? kMips64Dsubu : kMipsSubu; break; case kOpAnd: opcode = kMipsAnd; break; case kOpMul: - if (isaIsR6_) { - opcode = kMipsR6Mul; - } else { - opcode = kMipsMul; - } + opcode = isaIsR6_ ? kMipsR6Mul : kMipsR2Mul; break; case kOpOr: opcode = kMipsOr; @@ -195,20 +322,20 @@ LIR* MipsMir2Lir::OpRegRegReg(OpKind op, RegStorage r_dest, RegStorage r_src1, R opcode = kMipsXor; break; case kOpLsl: - opcode = kMipsSllv; + opcode = is64bit ? kMips64Dsllv : kMipsSllv; break; case kOpLsr: - opcode = kMipsSrlv; + opcode = is64bit ? kMips64Dsrlv : kMipsSrlv; break; case kOpAsr: - opcode = kMipsSrav; + opcode = is64bit ? kMips64Dsrav : kMipsSrav; break; case kOpAdc: case kOpSbc: LOG(FATAL) << "No carry bit on MIPS"; break; default: - LOG(FATAL) << "bad case in OpRegRegReg"; + LOG(FATAL) << "Bad case in OpRegRegReg"; break; } return NewLIR3(opcode, r_dest.GetReg(), r_src1.GetReg(), r_src2.GetReg()); @@ -218,36 +345,67 @@ LIR* MipsMir2Lir::OpRegRegImm(OpKind op, RegStorage r_dest, RegStorage r_src1, i LIR *res; MipsOpCode opcode = kMipsNop; bool short_form = true; + bool is64bit = cu_->target64 && (r_dest.Is64Bit() || r_src1.Is64Bit()); switch (op) { case kOpAdd: if (IS_SIMM16(value)) { - opcode = kMipsAddiu; + opcode = is64bit ? kMips64Daddiu : kMipsAddiu; } else { short_form = false; - opcode = kMipsAddu; + opcode = is64bit ? kMips64Daddu : kMipsAddu; } break; case kOpSub: if (IS_SIMM16((-value))) { value = -value; - opcode = kMipsAddiu; + opcode = is64bit ? kMips64Daddiu : kMipsAddiu; } else { short_form = false; - opcode = kMipsSubu; + opcode = is64bit ? kMips64Dsubu : kMipsSubu; } break; case kOpLsl: - DCHECK(value >= 0 && value <= 31); - opcode = kMipsSll; + if (is64bit) { + DCHECK(value >= 0 && value <= 63); + if (value >= 0 && value <= 31) { + opcode = kMips64Dsll; + } else { + opcode = kMips64Dsll32; + value = value - 32; + } + } else { + DCHECK(value >= 0 && value <= 31); + opcode = kMipsSll; + } break; case kOpLsr: - DCHECK(value >= 0 && value <= 31); - opcode = kMipsSrl; + if (is64bit) { + DCHECK(value >= 0 && value <= 63); + if (value >= 0 && value <= 31) { + opcode = kMips64Dsrl; + } else { + opcode = kMips64Dsrl32; + value = value - 32; + } + } else { + DCHECK(value >= 0 && value <= 31); + opcode = kMipsSrl; + } break; case kOpAsr: - DCHECK(value >= 0 && value <= 31); - opcode = kMipsSra; + if (is64bit) { + DCHECK(value >= 0 && value <= 63); + if (value >= 0 && value <= 31) { + opcode = kMips64Dsra; + } else { + opcode = kMips64Dsra32; + value = value - 32; + } + } else { + DCHECK(value >= 0 && value <= 31); + opcode = kMipsSra; + } break; case kOpAnd: if (IS_UIMM16((value))) { @@ -275,11 +433,7 @@ LIR* MipsMir2Lir::OpRegRegImm(OpKind op, RegStorage r_dest, RegStorage r_src1, i break; case kOpMul: short_form = false; - if (isaIsR6_) { - opcode = kMipsR6Mul; - } else { - opcode = kMipsMul; - } + opcode = isaIsR6_ ? kMipsR6Mul : kMipsR2Mul; break; default: LOG(FATAL) << "Bad case in OpRegRegImm"; @@ -293,8 +447,14 @@ LIR* MipsMir2Lir::OpRegRegImm(OpKind op, RegStorage r_dest, RegStorage r_src1, i res = LoadConstant(r_dest, value); NewLIR3(opcode, r_dest.GetReg(), r_src1.GetReg(), r_dest.GetReg()); } else { - RegStorage r_scratch = AllocTemp(); - res = LoadConstant(r_scratch, value); + RegStorage r_scratch; + if (is64bit) { + r_scratch = AllocTempWide(); + res = LoadConstantWide(r_scratch, value); + } else { + r_scratch = AllocTemp(); + res = LoadConstant(r_scratch, value); + } NewLIR3(opcode, r_dest.GetReg(), r_src1.GetReg(), r_scratch.GetReg()); } } @@ -311,7 +471,11 @@ LIR* MipsMir2Lir::OpRegReg(OpKind op, RegStorage r_dest_src1, RegStorage r_src2) case kOpMvn: return NewLIR3(kMipsNor, r_dest_src1.GetReg(), r_src2.GetReg(), rZERO); case kOpNeg: - return NewLIR3(kMipsSubu, r_dest_src1.GetReg(), rZERO, r_src2.GetReg()); + if (cu_->target64 && r_dest_src1.Is64Bit()) { + return NewLIR3(kMips64Dsubu, r_dest_src1.GetReg(), rZEROd, r_src2.GetReg()); + } else { + return NewLIR3(kMipsSubu, r_dest_src1.GetReg(), rZERO, r_src2.GetReg()); + } case kOpAdd: case kOpAnd: case kOpMul: @@ -320,21 +484,29 @@ LIR* MipsMir2Lir::OpRegReg(OpKind op, RegStorage r_dest_src1, RegStorage r_src2) case kOpXor: return OpRegRegReg(op, r_dest_src1, r_dest_src1, r_src2); case kOp2Byte: - if (cu_->compiler_driver->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures() - ->IsMipsIsaRevGreaterThanEqual2()) { + if (cu_->target64) { res = NewLIR2(kMipsSeb, r_dest_src1.GetReg(), r_src2.GetReg()); } else { - res = OpRegRegImm(kOpLsl, r_dest_src1, r_src2, 24); - OpRegRegImm(kOpAsr, r_dest_src1, r_dest_src1, 24); + if (cu_->compiler_driver->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures() + ->IsMipsIsaRevGreaterThanEqual2()) { + res = NewLIR2(kMipsSeb, r_dest_src1.GetReg(), r_src2.GetReg()); + } else { + res = OpRegRegImm(kOpLsl, r_dest_src1, r_src2, 24); + OpRegRegImm(kOpAsr, r_dest_src1, r_dest_src1, 24); + } } return res; case kOp2Short: - if (cu_->compiler_driver->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures() - ->IsMipsIsaRevGreaterThanEqual2()) { + if (cu_->target64) { res = NewLIR2(kMipsSeh, r_dest_src1.GetReg(), r_src2.GetReg()); } else { - res = OpRegRegImm(kOpLsl, r_dest_src1, r_src2, 16); - OpRegRegImm(kOpAsr, r_dest_src1, r_dest_src1, 16); + if (cu_->compiler_driver->GetInstructionSetFeatures()->AsMipsInstructionSetFeatures() + ->IsMipsIsaRevGreaterThanEqual2()) { + res = NewLIR2(kMipsSeh, r_dest_src1.GetReg(), r_src2.GetReg()); + } else { + res = OpRegRegImm(kOpLsl, r_dest_src1, r_src2, 16); + OpRegRegImm(kOpAsr, r_dest_src1, r_dest_src1, 16); + } } return res; case kOp2Char: @@ -367,10 +539,14 @@ LIR* MipsMir2Lir::OpCondRegReg(OpKind op, ConditionCode cc, RegStorage r_dest, R LIR* MipsMir2Lir::LoadConstantWide(RegStorage r_dest, int64_t value) { LIR *res; + if (cu_->target64) { + res = LoadConstantWideNoClobber(r_dest, value); + return res; + } if (fpuIs32Bit_ || !r_dest.IsFloat()) { // 32bit FPU (pairs) or loading into GPR. if (!r_dest.IsPair()) { - // Form 64-bit pair + // Form 64-bit pair. r_dest = Solo64ToPair64(r_dest); } res = LoadConstantNoClobber(r_dest.GetLow(), Low32Bits(value)); @@ -393,7 +569,8 @@ LIR* MipsMir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStor LIR *first = NULL; LIR *res; MipsOpCode opcode = kMipsNop; - RegStorage t_reg = AllocTemp(); + bool is64bit = cu_->target64 && r_dest.Is64Bit(); + RegStorage t_reg = is64bit ? AllocTempWide() : AllocTemp(); if (r_dest.IsFloat()) { DCHECK(r_dest.IsSingle()); @@ -404,14 +581,34 @@ LIR* MipsMir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStor size = k32; } - if (!scale) { - first = NewLIR3(kMipsAddu, t_reg.GetReg() , r_base.GetReg(), r_index.GetReg()); + if (cu_->target64) { + if (!scale) { + if (is64bit) { + first = NewLIR3(kMips64Daddu, t_reg.GetReg() , r_base.GetReg(), r_index.GetReg()); + } else { + first = NewLIR3(kMipsAddu, t_reg.GetReg() , r_base.GetReg(), r_index.GetReg()); + } + } else { + first = OpRegRegImm(kOpLsl, t_reg, r_index, scale); + NewLIR3(kMips64Daddu, t_reg.GetReg() , r_base.GetReg(), t_reg.GetReg()); + } } else { - first = OpRegRegImm(kOpLsl, t_reg, r_index, scale); - NewLIR3(kMipsAddu, t_reg.GetReg() , r_base.GetReg(), t_reg.GetReg()); + if (!scale) { + first = NewLIR3(kMipsAddu, t_reg.GetReg() , r_base.GetReg(), r_index.GetReg()); + } else { + first = OpRegRegImm(kOpLsl, t_reg, r_index, scale); + NewLIR3(kMipsAddu, t_reg.GetReg() , r_base.GetReg(), t_reg.GetReg()); + } } switch (size) { + case k64: + if (cu_->target64) { + opcode = kMips64Ld; + } else { + LOG(FATAL) << "Bad case in LoadBaseIndexed"; + } + break; case kSingle: opcode = kMipsFlwc1; break; @@ -440,7 +637,7 @@ LIR* MipsMir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStor return (first) ? first : res; } -/* store value base base + scaled index. */ +// Store value base base + scaled index. LIR* MipsMir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale, OpSize size) { LIR *first = NULL; @@ -456,11 +653,12 @@ LIR* MipsMir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegSto size = k32; } + MipsOpCode add_opcode = cu_->target64 ? kMips64Daddu : kMipsAddu; if (!scale) { - first = NewLIR3(kMipsAddu, t_reg.GetReg() , r_base.GetReg(), r_index.GetReg()); + first = NewLIR3(add_opcode, t_reg.GetReg() , r_base.GetReg(), r_index.GetReg()); } else { first = OpRegRegImm(kOpLsl, t_reg, r_index, scale); - NewLIR3(kMipsAddu, t_reg.GetReg() , r_base.GetReg(), t_reg.GetReg()); + NewLIR3(add_opcode, t_reg.GetReg() , r_base.GetReg(), t_reg.GetReg()); } switch (size) { @@ -507,9 +705,19 @@ LIR* MipsMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStora switch (size) { case k64: case kDouble: + if (cu_->target64) { + r_dest = Check64BitReg(r_dest); + if (!r_dest.IsFloat()) { + opcode = kMips64Ld; + } else { + opcode = kMipsFldc1; + } + DCHECK_EQ((displacement & 0x3), 0); + break; + } is64bit = true; if (fpuIs32Bit_ && !r_dest.IsPair()) { - // Form 64-bit pair + // Form 64-bit pair. r_dest = Solo64ToPair64(r_dest); } short_form = IS_SIMM16_2WORD(displacement); @@ -546,20 +754,40 @@ LIR* MipsMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStora LOG(FATAL) << "Bad case in LoadBaseIndexedBody"; } + if (cu_->target64) { + if (short_form) { + load = res = NewLIR3(opcode, r_dest.GetReg(), displacement, r_base.GetReg()); + } else { + RegStorage r_tmp = (r_base == r_dest) ? AllocTemp() : r_dest; + res = OpRegRegImm(kOpAdd, r_tmp, r_base, displacement); + load = NewLIR3(opcode, r_dest.GetReg(), 0, r_tmp.GetReg()); + if (r_tmp != r_dest) + FreeTemp(r_tmp); + } + + if (mem_ref_type_ == ResourceMask::kDalvikReg) { + DCHECK_EQ(r_base, TargetPtrReg(kSp)); + AnnotateDalvikRegAccess(load, displacement >> 2, true /* is_load */, r_dest.Is64Bit()); + } + return res; + } + if (short_form) { if (!is64bit) { load = res = NewLIR3(opcode, r_dest.GetReg(), displacement, r_base.GetReg()); } else { if (fpuIs32Bit_ || !r_dest.IsFloat()) { DCHECK(r_dest.IsPair()); - load = res = NewLIR3(opcode, r_dest.GetLowReg(), displacement + LOWORD_OFFSET, r_base.GetReg()); + load = res = NewLIR3(opcode, r_dest.GetLowReg(), displacement + LOWORD_OFFSET, + r_base.GetReg()); load2 = NewLIR3(opcode, r_dest.GetHighReg(), displacement + HIWORD_OFFSET, r_base.GetReg()); } else { // Here if 64bit fpu and r_dest is a 64bit fp register. RegStorage r_tmp = AllocTemp(); // FIXME: why is r_dest a 64BitPair here??? r_dest = Fp64ToSolo32(r_dest); - load = res = NewLIR3(kMipsFlwc1, r_dest.GetReg(), displacement + LOWORD_OFFSET, r_base.GetReg()); + load = res = NewLIR3(kMipsFlwc1, r_dest.GetReg(), displacement + LOWORD_OFFSET, + r_base.GetReg()); load2 = NewLIR3(kMipsLw, r_tmp.GetReg(), displacement + HIWORD_OFFSET, r_base.GetReg()); NewLIR2(kMipsMthc1, r_tmp.GetReg(), r_dest.GetReg()); FreeTemp(r_tmp); @@ -591,7 +819,7 @@ LIR* MipsMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStora } if (mem_ref_type_ == ResourceMask::kDalvikReg) { - DCHECK_EQ(r_base, rs_rMIPS_SP); + DCHECK_EQ(r_base, TargetPtrReg(kSp)); AnnotateDalvikRegAccess(load, (displacement + (is64bit ? LOWORD_OFFSET : 0)) >> 2, true /* is_load */, is64bit /* is64bit */); if (is64bit) { @@ -599,19 +827,21 @@ LIR* MipsMir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStora true /* is_load */, is64bit /* is64bit */); } } - return load; + return res; } -LIR* MipsMir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size, VolatileKind is_volatile) { - if (UNLIKELY(is_volatile == kVolatile && (size == k64 || size == kDouble))) { +LIR* MipsMir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, OpSize size, + VolatileKind is_volatile) { + if (UNLIKELY(is_volatile == kVolatile && (size == k64 || size == kDouble)) + && (!cu_->target64 || displacement & 0x7)) { + // TODO: use lld/scd instructions for Mips64. // Do atomic 64-bit load. return GenAtomic64Load(r_base, displacement, r_dest); } // TODO: base this on target. if (size == kWord) { - size = k32; + size = cu_->target64 ? k64 : k32; } LIR* load; load = LoadBaseDispBody(r_base, displacement, r_dest, size); @@ -624,8 +854,8 @@ LIR* MipsMir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r } // FIXME: don't split r_dest into 2 containers. -LIR* MipsMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, - RegStorage r_src, OpSize size) { +LIR* MipsMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegStorage r_src, + OpSize size) { LIR *res; LIR *store = NULL; LIR *store2 = NULL; @@ -636,9 +866,19 @@ LIR* MipsMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, switch (size) { case k64: case kDouble: + if (cu_->target64) { + r_src = Check64BitReg(r_src); + if (!r_src.IsFloat()) { + opcode = kMips64Sd; + } else { + opcode = kMipsFsdc1; + } + DCHECK_EQ((displacement & 0x3), 0); + break; + } is64bit = true; if (fpuIs32Bit_ && !r_src.IsPair()) { - // Form 64-bit pair + // Form 64-bit pair. r_src = Solo64ToPair64(r_src); } short_form = IS_SIMM16_2WORD(displacement); @@ -670,19 +910,38 @@ LIR* MipsMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, LOG(FATAL) << "Bad case in StoreBaseDispBody"; } + if (cu_->target64) { + if (short_form) { + store = res = NewLIR3(opcode, r_src.GetReg(), displacement, r_base.GetReg()); + } else { + RegStorage r_scratch = AllocTemp(); + res = OpRegRegImm(kOpAdd, r_scratch, r_base, displacement); + store = NewLIR3(opcode, r_src.GetReg(), 0, r_scratch.GetReg()); + FreeTemp(r_scratch); + } + + if (mem_ref_type_ == ResourceMask::kDalvikReg) { + DCHECK_EQ(r_base, TargetPtrReg(kSp)); + AnnotateDalvikRegAccess(store, displacement >> 2, false /* is_load */, r_src.Is64Bit()); + } + return res; + } + if (short_form) { if (!is64bit) { store = res = NewLIR3(opcode, r_src.GetReg(), displacement, r_base.GetReg()); } else { if (fpuIs32Bit_ || !r_src.IsFloat()) { DCHECK(r_src.IsPair()); - store = res = NewLIR3(opcode, r_src.GetLowReg(), displacement + LOWORD_OFFSET, r_base.GetReg()); + store = res = NewLIR3(opcode, r_src.GetLowReg(), displacement + LOWORD_OFFSET, + r_base.GetReg()); store2 = NewLIR3(opcode, r_src.GetHighReg(), displacement + HIWORD_OFFSET, r_base.GetReg()); } else { // Here if 64bit fpu and r_src is a 64bit fp register RegStorage r_tmp = AllocTemp(); r_src = Fp64ToSolo32(r_src); - store = res = NewLIR3(kMipsFswc1, r_src.GetReg(), displacement + LOWORD_OFFSET, r_base.GetReg()); + store = res = NewLIR3(kMipsFswc1, r_src.GetReg(), displacement + LOWORD_OFFSET, + r_base.GetReg()); NewLIR2(kMipsMfhc1, r_tmp.GetReg(), r_src.GetReg()); store2 = NewLIR3(kMipsSw, r_tmp.GetReg(), displacement + HIWORD_OFFSET, r_base.GetReg()); FreeTemp(r_tmp); @@ -712,7 +971,7 @@ LIR* MipsMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, } if (mem_ref_type_ == ResourceMask::kDalvikReg) { - DCHECK_EQ(r_base, rs_rMIPS_SP); + DCHECK_EQ(r_base, TargetPtrReg(kSp)); AnnotateDalvikRegAccess(store, (displacement + (is64bit ? LOWORD_OFFSET : 0)) >> 2, false /* is_load */, is64bit /* is64bit */); if (is64bit) { @@ -724,21 +983,23 @@ LIR* MipsMir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, return res; } -LIR* MipsMir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size, VolatileKind is_volatile) { +LIR* MipsMir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, OpSize size, + VolatileKind is_volatile) { if (is_volatile == kVolatile) { // Ensure that prior accesses become visible to other threads first. GenMemBarrier(kAnyStore); } LIR* store; - if (UNLIKELY(is_volatile == kVolatile && (size == k64 || size == kDouble))) { + if (UNLIKELY(is_volatile == kVolatile && (size == k64 || size == kDouble) && + (!cu_->target64 || displacement & 0x7))) { + // TODO: use lld/scd instructions for Mips64. // Do atomic 64-bit load. store = GenAtomic64Store(r_base, displacement, r_src); } else { // TODO: base this on target. if (size == kWord) { - size = k32; + size = cu_->target64 ? k64 : k32; } store = StoreBaseDispBody(r_base, displacement, r_src, size); } @@ -765,7 +1026,7 @@ LIR* MipsMir2Lir::OpCondBranch(ConditionCode cc, LIR* target) { } LIR* MipsMir2Lir::InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) { - if (IsDirectEntrypoint(trampoline)) { + if (!cu_->target64 && IsDirectEntrypoint(trampoline)) { // Reserve argument space on stack (for $a0-$a3) for // entrypoints that directly reference native implementations. // This is not safe in general, as it violates the frame size @@ -780,4 +1041,8 @@ LIR* MipsMir2Lir::InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointE return OpReg(op, r_tgt); } +RegStorage MipsMir2Lir::AllocPtrSizeTemp(bool required) { + return cu_->target64 ? AllocTempWide(required) : AllocTemp(required); +} + } // namespace art diff --git a/compiler/dex/quick/mips64/assemble_mips64.cc b/compiler/dex/quick/mips64/assemble_mips64.cc deleted file mode 100644 index d96561bba4..0000000000 --- a/compiler/dex/quick/mips64/assemble_mips64.cc +++ /dev/null @@ -1,898 +0,0 @@ -/* - * Copyright (C) 2015 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 "codegen_mips64.h" - -#include "base/logging.h" -#include "dex/compiler_ir.h" -#include "dex/quick/mir_to_lir-inl.h" -#include "mips64_lir.h" - -namespace art { - -#define MAX_ASSEMBLER_RETRIES 50 - -/* - * opcode: Mips64OpCode enum - * skeleton: pre-designated bit-pattern for this opcode - * k0: key to applying ds/de - * ds: dest start bit position - * de: dest end bit position - * k1: key to applying s1s/s1e - * s1s: src1 start bit position - * s1e: src1 end bit position - * k2: key to applying s2s/s2e - * s2s: src2 start bit position - * s2e: src2 end bit position - * operands: number of operands (for sanity check purposes) - * name: mnemonic name - * fmt: for pretty-printing - */ -#define ENCODING_MAP(opcode, skeleton, k0, ds, de, k1, s1s, s1e, k2, s2s, s2e, \ - k3, k3s, k3e, flags, name, fmt, size) \ - {skeleton, {{k0, ds, de}, {k1, s1s, s1e}, {k2, s2s, s2e}, \ - {k3, k3s, k3e}}, opcode, flags, name, fmt, size} - -/* Instruction dump string format keys: !pf, where "!" is the start - * of the key, "p" is which numeric operand to use and "f" is the - * print format. - * - * [p]ositions: - * 0 -> operands[0] (dest) - * 1 -> operands[1] (src1) - * 2 -> operands[2] (src2) - * 3 -> operands[3] (extra) - * - * [f]ormats: - * h -> 4-digit hex - * d -> decimal - * E -> decimal*4 - * F -> decimal*2 - * c -> branch condition (beq, bne, etc.) - * t -> pc-relative target - * T -> pc-region target - * u -> 1st half of bl[x] target - * v -> 2nd half ob bl[x] target - * R -> register list - * s -> single precision floating point register - * S -> double precision floating point register - * m -> Thumb2 modified immediate - * n -> complimented Thumb2 modified immediate - * M -> Thumb2 16-bit zero-extended immediate - * b -> 4-digit binary - * N -> append a NOP - * - * [!] escape. To insert "!", use "!!" - */ -/* NOTE: must be kept in sync with enum Mips64Opcode from mips64_lir.h */ -/* - * TUNING: We're currently punting on the branch delay slots. All branch - * instructions in this map are given a size of 8, which during assembly - * is expanded to include a nop. This scheme should be replaced with - * an assembler pass to fill those slots when possible. - */ -const Mips64EncodingMap Mips64Mir2Lir::EncodingMap[kMips64Last] = { - ENCODING_MAP(kMips6432BitData, 0x00000000, - kFmtBitBlt, 31, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_UNARY_OP, - "data", "0x!0h(!0d)", 4), - ENCODING_MAP(kMips64Addiu, 0x24000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "addiu", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMips64Addu, 0x00000021, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "addu", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64And, 0x00000024, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "and", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Andi, 0x30000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "andi", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMips64B, 0x10000000, - kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | NEEDS_FIXUP, - "b", "!0t!0N", 8), - ENCODING_MAP(kMips64Bal, 0x04110000, - kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR | - NEEDS_FIXUP, "bal", "!0t!0N", 8), - ENCODING_MAP(kMips64Beq, 0x10000000, - kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, - kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_USE01 | - NEEDS_FIXUP, "beq", "!0r,!1r,!2t!0N", 8), - ENCODING_MAP(kMips64Beqz, 0x10000000, // Same as beq above with t = $zero. - kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 | - NEEDS_FIXUP, "beqz", "!0r,!1t!0N", 8), - ENCODING_MAP(kMips64Bgez, 0x04010000, - kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 | - NEEDS_FIXUP, "bgez", "!0r,!1t!0N", 8), - ENCODING_MAP(kMips64Bgtz, 0x1c000000, - kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 | - NEEDS_FIXUP, "bgtz", "!0r,!1t!0N", 8), - ENCODING_MAP(kMips64Blez, 0x18000000, - kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 | - NEEDS_FIXUP, "blez", "!0r,!1t!0N", 8), - ENCODING_MAP(kMips64Bltz, 0x04000000, - kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 | - NEEDS_FIXUP, "bltz", "!0r,!1t!0N", 8), - ENCODING_MAP(kMips64Bnez, 0x14000000, // Same as bne below with t = $zero. - kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_USE0 | - NEEDS_FIXUP, "bnez", "!0r,!1t!0N", 8), - ENCODING_MAP(kMips64Bne, 0x14000000, - kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, - kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_USE01 | - NEEDS_FIXUP, "bne", "!0r,!1r,!2t!0N", 8), - ENCODING_MAP(kMips64Break, 0x0000000d, - kFmtBitBlt, 25, 6, kFmtUnused, -1, -1, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_UNARY_OP, "break", "!0d", 4), - ENCODING_MAP(kMips64Daddiu, 0x64000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "daddiu", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMips64Daddu, 0x0000002d, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "daddu", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Dahi, 0x04060000, - kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE0, - "dahi", "!0r,0x!1h(!1d)", 4), - ENCODING_MAP(kMips64Dati, 0x041E0000, - kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE0, - "dati", "!0r,0x!1h(!1d)", 4), - ENCODING_MAP(kMips64Daui, 0x74000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "daui", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMips64Ddiv, 0x0000009e, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "ddiv", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Div, 0x0000009a, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "div", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Dmod, 0x000000de, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "dmod", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Dmul, 0x0000009c, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "dmul", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Dmfc1, 0x44200000, - kFmtBitBlt, 20, 16, kFmtDfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "dmfc1", "!0r,!1s", 4), - ENCODING_MAP(kMips64Dmtc1, 0x44a00000, - kFmtBitBlt, 20, 16, kFmtDfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | REG_DEF1, - "dmtc1", "!0r,!1s", 4), - ENCODING_MAP(kMips64Drotr32, 0x0000003e | (1 << 21), - kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "drotr32", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMips64Dsll, 0x00000038, - kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "dsll", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMips64Dsll32, 0x0000003c, - kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "dsll32", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMips64Dsrl, 0x0000003a, - kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "dsrl", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMips64Dsrl32, 0x0000003e, - kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "dsrl32", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMips64Dsra, 0x0000003b, - kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "dsra", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMips64Dsra32, 0x0000003f, - kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "dsra32", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMips64Dsllv, 0x00000014, - kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "dsllv", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Dsrlv, 0x00000016, - kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "dsrlv", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Dsrav, 0x00000017, - kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "dsrav", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Dsubu, 0x0000002f, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "dsubu", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Ext, 0x7c000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 10, 6, - kFmtBitBlt, 15, 11, IS_QUAD_OP | REG_DEF0 | REG_USE1, - "ext", "!0r,!1r,!2d,!3D", 4), - ENCODING_MAP(kMips64Faddd, 0x46200000, - kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "add.d", "!0S,!1S,!2S", 4), - ENCODING_MAP(kMips64Fadds, 0x46000000, - kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "add.s", "!0s,!1s,!2s", 4), - ENCODING_MAP(kMips64Fdivd, 0x46200003, - kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "div.d", "!0S,!1S,!2S", 4), - ENCODING_MAP(kMips64Fdivs, 0x46000003, - kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "div.s", "!0s,!1s,!2s", 4), - ENCODING_MAP(kMips64Fmuld, 0x46200002, - kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "mul.d", "!0S,!1S,!2S", 4), - ENCODING_MAP(kMips64Fmuls, 0x46000002, - kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "mul.s", "!0s,!1s,!2s", 4), - ENCODING_MAP(kMips64Fsubd, 0x46200001, - kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtDfp, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "sub.d", "!0S,!1S,!2S", 4), - ENCODING_MAP(kMips64Fsubs, 0x46000001, - kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtSfp, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "sub.s", "!0s,!1s,!2s", 4), - ENCODING_MAP(kMips64Fcvtsd, 0x46200020, - kFmtSfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "cvt.s.d", "!0s,!1S", 4), - ENCODING_MAP(kMips64Fcvtsw, 0x46800020, - kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "cvt.s.w", "!0s,!1s", 4), - ENCODING_MAP(kMips64Fcvtds, 0x46000021, - kFmtDfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "cvt.d.s", "!0S,!1s", 4), - ENCODING_MAP(kMips64Fcvtdw, 0x46800021, - kFmtDfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "cvt.d.w", "!0S,!1s", 4), - ENCODING_MAP(kMips64Fcvtws, 0x46000024, - kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "cvt.w.s", "!0s,!1s", 4), - ENCODING_MAP(kMips64Fcvtwd, 0x46200024, - kFmtSfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "cvt.w.d", "!0s,!1S", 4), - ENCODING_MAP(kMips64Fmovd, 0x46200006, - kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "mov.d", "!0S,!1S", 4), - ENCODING_MAP(kMips64Fmovs, 0x46000006, - kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "mov.s", "!0s,!1s", 4), - ENCODING_MAP(kMips64Fnegd, 0x46200007, - kFmtDfp, 10, 6, kFmtDfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "neg.d", "!0S,!1S", 4), - ENCODING_MAP(kMips64Fnegs, 0x46000007, - kFmtSfp, 10, 6, kFmtSfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "neg.s", "!0s,!1s", 4), - ENCODING_MAP(kMips64Fldc1, 0xd4000000, - kFmtDfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, - "ldc1", "!0S,!1d(!2r)", 4), - ENCODING_MAP(kMips64Flwc1, 0xc4000000, - kFmtSfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, - "lwc1", "!0s,!1d(!2r)", 4), - ENCODING_MAP(kMips64Fsdc1, 0xf4000000, - kFmtDfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE, - "sdc1", "!0S,!1d(!2r)", 4), - ENCODING_MAP(kMips64Fswc1, 0xe4000000, - kFmtSfp, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE, - "swc1", "!0s,!1d(!2r)", 4), - ENCODING_MAP(kMips64Jal, 0x0c000000, - kFmtBitBlt, 25, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_UNARY_OP | IS_BRANCH | REG_DEF_LR, - "jal", "!0T(!0E)!0N", 8), - ENCODING_MAP(kMips64Jalr, 0x00000009, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | IS_BRANCH | REG_DEF0_USE1, - "jalr", "!0r,!1r!0N", 8), - ENCODING_MAP(kMips64Lahi, 0x3c000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0, - "lahi/lui", "!0r,0x!1h(!1d)", 4), - ENCODING_MAP(kMips64Lalo, 0x34000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "lalo/ori", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMips64Lb, 0x80000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, - "lb", "!0r,!1d(!2r)", 4), - ENCODING_MAP(kMips64Lbu, 0x90000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, - "lbu", "!0r,!1d(!2r)", 4), - ENCODING_MAP(kMips64Ld, 0xdc000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, - "ld", "!0r,!1d(!2r)", 4), - ENCODING_MAP(kMips64Lh, 0x84000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, - "lh", "!0r,!1d(!2r)", 4), - ENCODING_MAP(kMips64Lhu, 0x94000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, - "lhu", "!0r,!1d(!2r)", 4), - ENCODING_MAP(kMips64Lui, 0x3c000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0, - "lui", "!0r,0x!1h(!1d)", 4), - ENCODING_MAP(kMips64Lw, 0x8c000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, - "lw", "!0r,!1d(!2r)", 4), - ENCODING_MAP(kMips64Lwu, 0x9c000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE2 | IS_LOAD, - "lwu", "!0r,!1d(!2r)", 4), - ENCODING_MAP(kMips64Mfc1, 0x44000000, - kFmtBitBlt, 20, 16, kFmtSfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "mfc1", "!0r,!1s", 4), - ENCODING_MAP(kMips64Mtc1, 0x44800000, - kFmtBitBlt, 20, 16, kFmtSfp, 15, 11, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_USE0 | REG_DEF1, - "mtc1", "!0r,!1s", 4), - ENCODING_MAP(kMips64Move, 0x0000002d, // Or using zero reg. - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "move", "!0r,!1r", 4), - ENCODING_MAP(kMips64Mod, 0x000000da, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "mod", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Mul, 0x00000098, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "mul", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Nop, 0x00000000, - kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, NO_OPERAND, - "nop", ";", 4), - ENCODING_MAP(kMips64Nor, 0x00000027, // Used for "not" too. - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "nor", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Or, 0x00000025, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "or", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Ori, 0x34000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "ori", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMips64Sb, 0xa0000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE, - "sb", "!0r,!1d(!2r)", 4), - ENCODING_MAP(kMips64Sd, 0xfc000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE, - "sd", "!0r,!1d(!2r)", 4), - ENCODING_MAP(kMips64Seb, 0x7c000420, - kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "seb", "!0r,!1r", 4), - ENCODING_MAP(kMips64Seh, 0x7c000620, - kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_BINARY_OP | REG_DEF0_USE1, - "seh", "!0r,!1r", 4), - ENCODING_MAP(kMips64Sh, 0xa4000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE, - "sh", "!0r,!1d(!2r)", 4), - ENCODING_MAP(kMips64Sll, 0x00000000, - kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "sll", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMips64Sllv, 0x00000004, - kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "sllv", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Slt, 0x0000002a, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "slt", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Slti, 0x28000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "slti", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMips64Sltu, 0x0000002b, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "sltu", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Sra, 0x00000003, - kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "sra", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMips64Srav, 0x00000007, - kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "srav", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Srl, 0x00000002, - kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 10, 6, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "srl", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMips64Srlv, 0x00000006, - kFmtBitBlt, 15, 11, kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "srlv", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Subu, 0x00000023, // Used for "neg" too. - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "subu", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Sw, 0xac000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtBitBlt, 25, 21, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_USE02 | IS_STORE, - "sw", "!0r,!1d(!2r)", 4), - ENCODING_MAP(kMips64Sync, 0x0000000f, - kFmtBitBlt, 10, 6, kFmtUnused, -1, -1, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_UNARY_OP, - "sync", ";", 4), - ENCODING_MAP(kMips64Xor, 0x00000026, - kFmtBitBlt, 15, 11, kFmtBitBlt, 25, 21, kFmtBitBlt, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "xor", "!0r,!1r,!2r", 4), - ENCODING_MAP(kMips64Xori, 0x38000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 25, 21, kFmtBitBlt, 15, 0, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, - "xori", "!0r,!1r,0x!2h(!2d)", 4), - ENCODING_MAP(kMips64CurrPC, 0x04110001, - kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, NO_OPERAND | IS_BRANCH | REG_DEF_LR, - "addiu", "ra,pc,8", 4), - ENCODING_MAP(kMips64Delta, 0x67e00000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, 15, 0, - kFmtUnused, -1, -1, IS_QUAD_OP | REG_DEF0 | REG_USE_LR | - NEEDS_FIXUP, "daddiu", "!0r,ra,0x!1h(!1d)", 4), - ENCODING_MAP(kMips64DeltaHi, 0x3c000000, - kFmtBitBlt, 20, 16, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_QUAD_OP | REG_DEF0 | NEEDS_FIXUP, - "lui", "!0r,0x!1h(!1d)", 4), - ENCODING_MAP(kMips64DeltaLo, 0x34000000, - kFmtBlt5_2, 16, 21, kFmtBitBlt, 15, 0, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, IS_QUAD_OP | REG_DEF0_USE0 | NEEDS_FIXUP, - "ori", "!0r,!0r,0x!1h(!1d)", 4), - ENCODING_MAP(kMips64Undefined, 0x64000000, - kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, NO_OPERAND, - "undefined", "", 4), -}; - - -/* - * Convert a short-form branch to long form. Hopefully, this won't happen - * very often because the PIC sequence is especially unfortunate. - * - * Orig conditional branch - * ----------------------- - * beq rs,rt,target - * - * Long conditional branch - * ----------------------- - * bne rs,rt,hop - * bal .+8 ; rRA <- anchor - * lui rAT, ((target-anchor) >> 16) - * anchor: - * ori rAT, rAT, ((target-anchor) & 0xffff) - * addu rAT, rAT, rRA - * jalr rZERO, rAT - * hop: - * - * Orig unconditional branch - * ------------------------- - * b target - * - * Long unconditional branch - * ----------------------- - * bal .+8 ; rRA <- anchor - * lui rAT, ((target-anchor) >> 16) - * anchor: - * ori rAT, rAT, ((target-anchor) & 0xffff) - * addu rAT, rAT, rRA - * jalr rZERO, rAT - * - * - * NOTE: An out-of-range bal isn't supported because it should - * never happen with the current PIC model. - */ -void Mips64Mir2Lir::ConvertShortToLongBranch(LIR* lir) { - // For conditional branches we'll need to reverse the sense - bool unconditional = false; - int opcode = lir->opcode; - int dalvik_offset = lir->dalvik_offset; - switch (opcode) { - case kMips64Bal: - LOG(FATAL) << "long branch and link unsupported"; - UNREACHABLE(); - case kMips64B: - unconditional = true; - break; - case kMips64Beq: opcode = kMips64Bne; break; - case kMips64Bne: opcode = kMips64Beq; break; - case kMips64Beqz: opcode = kMips64Bnez; break; - case kMips64Bgez: opcode = kMips64Bltz; break; - case kMips64Bgtz: opcode = kMips64Blez; break; - case kMips64Blez: opcode = kMips64Bgtz; break; - case kMips64Bltz: opcode = kMips64Bgez; break; - case kMips64Bnez: opcode = kMips64Beqz; break; - default: - LOG(FATAL) << "Unexpected branch kind " << opcode; - UNREACHABLE(); - } - LIR* hop_target = NULL; - if (!unconditional) { - hop_target = RawLIR(dalvik_offset, kPseudoTargetLabel); - LIR* hop_branch = RawLIR(dalvik_offset, opcode, lir->operands[0], - lir->operands[1], 0, 0, 0, hop_target); - InsertLIRBefore(lir, hop_branch); - } - LIR* curr_pc = RawLIR(dalvik_offset, kMips64CurrPC); - InsertLIRBefore(lir, curr_pc); - LIR* anchor = RawLIR(dalvik_offset, kPseudoTargetLabel); - LIR* delta_hi = RawLIR(dalvik_offset, kMips64DeltaHi, rAT, 0, WrapPointer(anchor), 0, 0, - lir->target); - InsertLIRBefore(lir, delta_hi); - InsertLIRBefore(lir, anchor); - LIR* delta_lo = RawLIR(dalvik_offset, kMips64DeltaLo, rAT, 0, WrapPointer(anchor), 0, 0, - lir->target); - InsertLIRBefore(lir, delta_lo); - LIR* addu = RawLIR(dalvik_offset, kMips64Addu, rAT, rAT, rRA); - InsertLIRBefore(lir, addu); - LIR* jalr = RawLIR(dalvik_offset, kMips64Jalr, rZERO, rAT); - InsertLIRBefore(lir, jalr); - if (!unconditional) { - InsertLIRBefore(lir, hop_target); - } - NopLIR(lir); -} - -/* - * Assemble the LIR into binary instruction format. Note that we may - * discover that pc-relative displacements may not fit the selected - * instruction. In those cases we will try to substitute a new code - * sequence or request that the trace be shortened and retried. - */ -AssemblerStatus Mips64Mir2Lir::AssembleInstructions(CodeOffset start_addr) { - LIR *lir; - AssemblerStatus res = kSuccess; // Assume success. - - for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) { - if (lir->opcode < 0) { - continue; - } - - if (lir->flags.is_nop) { - continue; - } - - if (lir->flags.fixup != kFixupNone) { - if (lir->opcode == kMips64Delta) { - /* - * The "Delta" pseudo-ops load the difference between - * two pc-relative locations into a the target register - * found in operands[0]. The delta is determined by - * (label2 - label1), where label1 is a standard - * kPseudoTargetLabel and is stored in operands[2]. - * If operands[3] is null, then label2 is a kPseudoTargetLabel - * and is found in lir->target. If operands[3] is non-NULL, - * then it is a Switch/Data table. - */ - int offset1 = UnwrapPointer<LIR>(lir->operands[2])->offset; - const EmbeddedData* tab_rec = UnwrapPointer<EmbeddedData>(lir->operands[3]); - int offset2 = tab_rec ? tab_rec->offset : lir->target->offset; - int delta = offset2 - offset1; - if ((delta & 0xffff) == delta && ((delta & 0x8000) == 0)) { - // Fits. - lir->operands[1] = delta; - } else { - // Doesn't fit - must expand to kMips64Delta[Hi|Lo] pair. - LIR *new_delta_hi = RawLIR(lir->dalvik_offset, kMips64DeltaHi, lir->operands[0], 0, - lir->operands[2], lir->operands[3], 0, lir->target); - InsertLIRBefore(lir, new_delta_hi); - LIR *new_delta_lo = RawLIR(lir->dalvik_offset, kMips64DeltaLo, lir->operands[0], 0, - lir->operands[2], lir->operands[3], 0, lir->target); - InsertLIRBefore(lir, new_delta_lo); - LIR *new_addu = RawLIR(lir->dalvik_offset, kMips64Daddu, lir->operands[0], - lir->operands[0], rRAd); - InsertLIRBefore(lir, new_addu); - NopLIR(lir); - res = kRetryAll; - } - } else if (lir->opcode == kMips64DeltaLo) { - int offset1 = UnwrapPointer<LIR>(lir->operands[2])->offset; - const EmbeddedData* tab_rec = UnwrapPointer<EmbeddedData>(lir->operands[3]); - int offset2 = tab_rec ? tab_rec->offset : lir->target->offset; - int delta = offset2 - offset1; - lir->operands[1] = delta & 0xffff; - } else if (lir->opcode == kMips64DeltaHi) { - int offset1 = UnwrapPointer<LIR>(lir->operands[2])->offset; - const EmbeddedData* tab_rec = UnwrapPointer<EmbeddedData>(lir->operands[3]); - int offset2 = tab_rec ? tab_rec->offset : lir->target->offset; - int delta = offset2 - offset1; - lir->operands[1] = (delta >> 16) & 0xffff; - } else if (lir->opcode == kMips64B || lir->opcode == kMips64Bal) { - LIR *target_lir = lir->target; - CodeOffset pc = lir->offset + 4; - CodeOffset target = target_lir->offset; - int delta = target - pc; - if (delta & 0x3) { - LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta; - } - if (delta > 131068 || delta < -131069) { - res = kRetryAll; - ConvertShortToLongBranch(lir); - } else { - lir->operands[0] = delta >> 2; - } - } else if (lir->opcode >= kMips64Beqz && lir->opcode <= kMips64Bnez) { - LIR *target_lir = lir->target; - CodeOffset pc = lir->offset + 4; - CodeOffset target = target_lir->offset; - int delta = target - pc; - if (delta & 0x3) { - LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta; - } - if (delta > 131068 || delta < -131069) { - res = kRetryAll; - ConvertShortToLongBranch(lir); - } else { - lir->operands[1] = delta >> 2; - } - } else if (lir->opcode == kMips64Beq || lir->opcode == kMips64Bne) { - LIR *target_lir = lir->target; - CodeOffset pc = lir->offset + 4; - CodeOffset target = target_lir->offset; - int delta = target - pc; - if (delta & 0x3) { - LOG(FATAL) << "PC-rel offset not multiple of 4: " << delta; - } - if (delta > 131068 || delta < -131069) { - res = kRetryAll; - ConvertShortToLongBranch(lir); - } else { - lir->operands[2] = delta >> 2; - } - } else if (lir->opcode == kMips64Jal) { - CodeOffset cur_pc = (start_addr + lir->offset + 4) & ~3; - CodeOffset target = lir->operands[0]; - /* ensure PC-region branch can be used */ - DCHECK_EQ((cur_pc & 0xF0000000), (target & 0xF0000000)); - if (target & 0x3) { - LOG(FATAL) << "Jump target not multiple of 4: " << target; - } - lir->operands[0] = target >> 2; - } else if (lir->opcode == kMips64Lahi) { /* ld address hi (via lui) */ - LIR *target_lir = lir->target; - CodeOffset target = start_addr + target_lir->offset; - lir->operands[1] = target >> 16; - } else if (lir->opcode == kMips64Lalo) { /* ld address lo (via ori) */ - LIR *target_lir = lir->target; - CodeOffset target = start_addr + target_lir->offset; - lir->operands[2] = lir->operands[2] + target; - } - } - - /* - * If one of the pc-relative instructions expanded we'll have - * to make another pass. Don't bother to fully assemble the - * instruction. - */ - if (res != kSuccess) { - continue; - } - DCHECK(!IsPseudoLirOp(lir->opcode)); - const Mips64EncodingMap *encoder = &EncodingMap[lir->opcode]; - uint32_t bits = encoder->skeleton; - int i; - for (i = 0; i < 4; i++) { - uint32_t operand; - uint32_t value; - operand = lir->operands[i]; - switch (encoder->field_loc[i].kind) { - case kFmtUnused: - break; - case kFmtBitBlt: - if (encoder->field_loc[i].start == 0 && encoder->field_loc[i].end == 31) { - value = operand; - } else { - value = (operand << encoder->field_loc[i].start) & - ((1 << (encoder->field_loc[i].end + 1)) - 1); - } - bits |= value; - break; - case kFmtBlt5_2: - value = (operand & 0x1f); - bits |= (value << encoder->field_loc[i].start); - bits |= (value << encoder->field_loc[i].end); - break; - case kFmtDfp: { - // TODO: do we need to adjust now that we're using 64BitSolo? - DCHECK(RegStorage::IsDouble(operand)) << ", Operand = 0x" << std::hex << operand; - value = (RegStorage::RegNum(operand) << encoder->field_loc[i].start) & - ((1 << (encoder->field_loc[i].end + 1)) - 1); - bits |= value; - break; - } - case kFmtSfp: - DCHECK(RegStorage::IsSingle(operand)) << ", Operand = 0x" << std::hex << operand; - value = (RegStorage::RegNum(operand) << encoder->field_loc[i].start) & - ((1 << (encoder->field_loc[i].end + 1)) - 1); - bits |= value; - break; - default: - LOG(FATAL) << "Bad encoder format: " << encoder->field_loc[i].kind; - } - } - // We only support little-endian MIPS64. - code_buffer_.push_back(bits & 0xff); - code_buffer_.push_back((bits >> 8) & 0xff); - code_buffer_.push_back((bits >> 16) & 0xff); - code_buffer_.push_back((bits >> 24) & 0xff); - // TUNING: replace with proper delay slot handling. - if (encoder->size == 8) { - DCHECK(!IsPseudoLirOp(lir->opcode)); - const Mips64EncodingMap *encoder2 = &EncodingMap[kMips64Nop]; - uint32_t bits2 = encoder2->skeleton; - code_buffer_.push_back(bits2 & 0xff); - code_buffer_.push_back((bits2 >> 8) & 0xff); - code_buffer_.push_back((bits2 >> 16) & 0xff); - code_buffer_.push_back((bits2 >> 24) & 0xff); - } - } - return res; -} - -size_t Mips64Mir2Lir::GetInsnSize(LIR* lir) { - DCHECK(!IsPseudoLirOp(lir->opcode)); - return EncodingMap[lir->opcode].size; -} - -// LIR offset assignment. -// TODO: consolidate w/ Arm assembly mechanism. -int Mips64Mir2Lir::AssignInsnOffsets() { - LIR* lir; - int offset = 0; - - for (lir = first_lir_insn_; lir != NULL; lir = NEXT_LIR(lir)) { - lir->offset = offset; - if (LIKELY(lir->opcode >= 0)) { - if (!lir->flags.is_nop) { - offset += lir->flags.size; - } - } else if (UNLIKELY(lir->opcode == kPseudoPseudoAlign4)) { - if (offset & 0x2) { - offset += 2; - lir->operands[0] = 1; - } else { - lir->operands[0] = 0; - } - } - // Pseudo opcodes don't consume space. - } - return offset; -} - -/* - * Walk the compilation unit and assign offsets to instructions - * and literals and compute the total size of the compiled unit. - * TODO: consolidate w/ Arm assembly mechanism. - */ -void Mips64Mir2Lir::AssignOffsets() { - int offset = AssignInsnOffsets(); - - // Const values have to be word aligned. - offset = RoundUp(offset, 4); - - // Set up offsets for literals. - data_offset_ = offset; - - offset = AssignLiteralOffset(offset); - - offset = AssignSwitchTablesOffset(offset); - - offset = AssignFillArrayDataOffset(offset); - - total_size_ = offset; -} - -/* - * Go over each instruction in the list and calculate the offset from the top - * before sending them off to the assembler. If out-of-range branch distance is - * seen rearrange the instructions a bit to correct it. - * TODO: consolidate w/ Arm assembly mechanism. - */ -void Mips64Mir2Lir::AssembleLIR() { - cu_->NewTimingSplit("Assemble"); - AssignOffsets(); - int assembler_retries = 0; - /* - * Assemble here. Note that we generate code with optimistic assumptions - * and if found now to work, we'll have to redo the sequence and retry. - */ - - while (true) { - AssemblerStatus res = AssembleInstructions(0); - if (res == kSuccess) { - break; - } else { - assembler_retries++; - if (assembler_retries > MAX_ASSEMBLER_RETRIES) { - CodegenDump(); - LOG(FATAL) << "Assembler error - too many retries"; - } - // Redo offsets and try again. - AssignOffsets(); - code_buffer_.clear(); - } - } - - // Install literals. - InstallLiteralPools(); - - // Install switch tables. - InstallSwitchTables(); - - // Install fill array data. - InstallFillArrayData(); - - // Create the mapping table and native offset to reference map. - cu_->NewTimingSplit("PcMappingTable"); - CreateMappingTables(); - - cu_->NewTimingSplit("GcMap"); - CreateNativeGcMap(); -} - -} // namespace art diff --git a/compiler/dex/quick/mips64/call_mips64.cc b/compiler/dex/quick/mips64/call_mips64.cc deleted file mode 100644 index 0e587706cc..0000000000 --- a/compiler/dex/quick/mips64/call_mips64.cc +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -/* This file contains codegen for the Mips64 ISA */ - -#include "codegen_mips64.h" - -#include "base/logging.h" -#include "dex/mir_graph.h" -#include "dex/quick/mir_to_lir-inl.h" -#include "entrypoints/quick/quick_entrypoints.h" -#include "gc/accounting/card_table.h" -#include "mips64_lir.h" -#include "mirror/art_method.h" -#include "mirror/object_array-inl.h" - -namespace art { - -bool Mips64Mir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special) { - // TODO - UNUSED(bb, mir, special); - return false; -} - -/* - * The lack of pc-relative loads on Mips64 presents somewhat of a challenge - * for our PIC switch table strategy. To materialize the current location - * we'll do a dummy JAL and reference our tables using rRA as the - * base register. Note that rRA will be used both as the base to - * locate the switch table data and as the reference base for the switch - * target offsets stored in the table. We'll use a special pseudo-instruction - * to represent the jal and trigger the construction of the - * switch table offsets (which will happen after final assembly and all - * labels are fixed). - * - * The test loop will look something like: - * - * ori r_end, rZERO, #table_size ; size in bytes - * jal BaseLabel ; stores "return address" (BaseLabel) in rRA - * nop ; opportunistically fill - * BaseLabel: - * addiu r_base, rRA, <table> - <BaseLabel> ; table relative to BaseLabel - addu r_end, r_end, r_base ; end of table - * lw r_val, [rSP, v_reg_off] ; Test Value - * loop: - * beq r_base, r_end, done - * lw r_key, 0(r_base) - * addu r_base, 8 - * bne r_val, r_key, loop - * lw r_disp, -4(r_base) - * addu rRA, r_disp - * jalr rZERO, rRA - * done: - * - */ -void Mips64Mir2Lir::GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) { - const uint16_t* table = mir_graph_->GetTable(mir, table_offset); - // Add the table to the list - we'll process it later. - SwitchTable* tab_rec = static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), - kArenaAllocData)); - tab_rec->switch_mir = mir; - tab_rec->table = table; - tab_rec->vaddr = current_dalvik_offset_; - int elements = table[1]; - switch_tables_.push_back(tab_rec); - - // The table is composed of 8-byte key/disp pairs. - int byte_size = elements * 8; - - int size_hi = byte_size >> 16; - int size_lo = byte_size & 0xffff; - - RegStorage r_end = AllocTempWide(); - if (size_hi) { - NewLIR2(kMips64Lui, r_end.GetReg(), size_hi); - } - // Must prevent code motion for the curr pc pair. - GenBarrier(); // Scheduling barrier. - NewLIR0(kMips64CurrPC); // Really a jal to .+8. - // Now, fill the branch delay slot. - if (size_hi) { - NewLIR3(kMips64Ori, r_end.GetReg(), r_end.GetReg(), size_lo); - } else { - NewLIR3(kMips64Ori, r_end.GetReg(), rZERO, size_lo); - } - GenBarrier(); // Scheduling barrier. - - // Construct BaseLabel and set up table base register. - LIR* base_label = NewLIR0(kPseudoTargetLabel); - // Remember base label so offsets can be computed later. - tab_rec->anchor = base_label; - RegStorage r_base = AllocTempWide(); - NewLIR4(kMips64Delta, r_base.GetReg(), 0, WrapPointer(base_label), WrapPointer(tab_rec)); - OpRegRegReg(kOpAdd, r_end, r_end, r_base); - - // Grab switch test value. - rl_src = LoadValue(rl_src, kCoreReg); - - // Test loop. - RegStorage r_key = AllocTemp(); - LIR* loop_label = NewLIR0(kPseudoTargetLabel); - LIR* exit_branch = OpCmpBranch(kCondEq, r_base, r_end, NULL); - Load32Disp(r_base, 0, r_key); - OpRegImm(kOpAdd, r_base, 8); - OpCmpBranch(kCondNe, rl_src.reg, r_key, loop_label); - RegStorage r_disp = AllocTemp(); - Load32Disp(r_base, -4, r_disp); - OpRegRegReg(kOpAdd, TargetReg(kLr, kWide), TargetReg(kLr, kWide), r_disp); - OpReg(kOpBx, TargetReg(kLr, kWide)); - - // Loop exit. - LIR* exit_label = NewLIR0(kPseudoTargetLabel); - exit_branch->target = exit_label; -} - -/* - * Code pattern will look something like: - * - * lw r_val - * jal BaseLabel ; stores "return address" (BaseLabel) in rRA - * nop ; opportunistically fill - * [subiu r_val, bias] ; Remove bias if low_val != 0 - * bound check -> done - * lw r_disp, [rRA, r_val] - * addu rRA, r_disp - * jalr rZERO, rRA - * done: - */ -void Mips64Mir2Lir::GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) { - const uint16_t* table = mir_graph_->GetTable(mir, table_offset); - // Add the table to the list - we'll process it later. - SwitchTable* tab_rec = - static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData)); - tab_rec->switch_mir = mir; - tab_rec->table = table; - tab_rec->vaddr = current_dalvik_offset_; - int size = table[1]; - switch_tables_.push_back(tab_rec); - - // Get the switch value. - rl_src = LoadValue(rl_src, kCoreReg); - - // Prepare the bias. If too big, handle 1st stage here. - int low_key = s4FromSwitchData(&table[2]); - bool large_bias = false; - RegStorage r_key; - if (low_key == 0) { - r_key = rl_src.reg; - } else if ((low_key & 0xffff) != low_key) { - r_key = AllocTemp(); - LoadConstant(r_key, low_key); - large_bias = true; - } else { - r_key = AllocTemp(); - } - - // Must prevent code motion for the curr pc pair. - GenBarrier(); - NewLIR0(kMips64CurrPC); // Really a jal to .+8. - // Now, fill the branch delay slot with bias strip. - if (low_key == 0) { - NewLIR0(kMips64Nop); - } else { - if (large_bias) { - OpRegRegReg(kOpSub, r_key, rl_src.reg, r_key); - } else { - OpRegRegImm(kOpSub, r_key, rl_src.reg, low_key); - } - } - GenBarrier(); // Scheduling barrier. - - // Construct BaseLabel and set up table base register. - LIR* base_label = NewLIR0(kPseudoTargetLabel); - // Remember base label so offsets can be computed later. - tab_rec->anchor = base_label; - - // Bounds check - if < 0 or >= size continue following switch. - LIR* branch_over = OpCmpImmBranch(kCondHi, r_key, size-1, NULL); - - // Materialize the table base pointer. - RegStorage r_base = AllocTempWide(); - NewLIR4(kMips64Delta, r_base.GetReg(), 0, WrapPointer(base_label), WrapPointer(tab_rec)); - - // Load the displacement from the switch table. - RegStorage r_disp = AllocTemp(); - LoadBaseIndexed(r_base, r_key, r_disp, 2, k32); - - // Add to rAP and go. - OpRegRegReg(kOpAdd, TargetReg(kLr, kWide), TargetReg(kLr, kWide), r_disp); - OpReg(kOpBx, TargetReg(kLr, kWide)); - - // Branch_over target here. - LIR* target = NewLIR0(kPseudoTargetLabel); - branch_over->target = target; -} - -void Mips64Mir2Lir::GenMoveException(RegLocation rl_dest) { - int ex_offset = Thread::ExceptionOffset<8>().Int32Value(); - RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true); - RegStorage reset_reg = AllocTempRef(); - LoadRefDisp(rs_rMIPS64_SELF, ex_offset, rl_result.reg, kNotVolatile); - LoadConstant(reset_reg, 0); - StoreRefDisp(rs_rMIPS64_SELF, ex_offset, reset_reg, kNotVolatile); - FreeTemp(reset_reg); - StoreValue(rl_dest, rl_result); -} - -void Mips64Mir2Lir::UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) { - RegStorage reg_card_base = AllocTempWide(); - RegStorage reg_card_no = AllocTempWide(); - // NOTE: native pointer. - LoadWordDisp(rs_rMIPS64_SELF, Thread::CardTableOffset<8>().Int32Value(), reg_card_base); - OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift); - StoreBaseIndexed(reg_card_base, reg_card_no, As32BitReg(reg_card_base), 0, kUnsignedByte); - FreeTemp(reg_card_base); - FreeTemp(reg_card_no); -} - -void Mips64Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { - int spill_count = num_core_spills_ + num_fp_spills_; - /* - * On entry, rMIPS64_ARG0, rMIPS64_ARG1, rMIPS64_ARG2, rMIPS64_ARG3, - * rMIPS64_ARG4, rMIPS64_ARG5, rMIPS64_ARG6 & rMIPS64_ARG7 are live. - * Let the register allocation mechanism know so it doesn't try to - * use any of them when expanding the frame or flushing. - */ - LockTemp(rs_rMIPS64_ARG0); - LockTemp(rs_rMIPS64_ARG1); - LockTemp(rs_rMIPS64_ARG2); - LockTemp(rs_rMIPS64_ARG3); - LockTemp(rs_rMIPS64_ARG4); - LockTemp(rs_rMIPS64_ARG5); - LockTemp(rs_rMIPS64_ARG6); - LockTemp(rs_rMIPS64_ARG7); - - /* - * We can safely skip the stack overflow check if we're - * a leaf *and* our frame size < fudge factor. - */ - bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, - kMips64); - NewLIR0(kPseudoMethodEntry); - RegStorage check_reg = AllocTempWide(); - RegStorage new_sp = AllocTempWide(); - if (!skip_overflow_check) { - // Load stack limit. - LoadWordDisp(rs_rMIPS64_SELF, Thread::StackEndOffset<8>().Int32Value(), check_reg); - } - // Spill core callee saves. - SpillCoreRegs(); - // NOTE: promotion of FP regs currently unsupported, thus no FP spill. - DCHECK_EQ(num_fp_spills_, 0); - const int frame_sub = frame_size_ - spill_count * 8; - if (!skip_overflow_check) { - class StackOverflowSlowPath : public LIRSlowPath { - public: - StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, size_t sp_displace) - : LIRSlowPath(m2l, branch), sp_displace_(sp_displace) { - } - void Compile() OVERRIDE { - m2l_->ResetRegPool(); - m2l_->ResetDefTracking(); - GenerateTargetLabel(kPseudoThrowTarget); - // Load RA from the top of the frame. - m2l_->LoadWordDisp(rs_rMIPS64_SP, sp_displace_ - 8, rs_rRAd); - m2l_->OpRegImm(kOpAdd, rs_rMIPS64_SP, sp_displace_); - m2l_->ClobberCallerSave(); - RegStorage r_tgt = m2l_->CallHelperSetup(kQuickThrowStackOverflow); // Doesn't clobber LR. - m2l_->CallHelper(r_tgt, kQuickThrowStackOverflow, false /* MarkSafepointPC */, - false /* UseLink */); - } - - private: - const size_t sp_displace_; - }; - OpRegRegImm(kOpSub, new_sp, rs_rMIPS64_SP, frame_sub); - LIR* branch = OpCmpBranch(kCondUlt, new_sp, check_reg, nullptr); - AddSlowPath(new(arena_)StackOverflowSlowPath(this, branch, spill_count * 8)); - // TODO: avoid copy for small frame sizes. - OpRegCopy(rs_rMIPS64_SP, new_sp); // Establish stack. - } else { - OpRegImm(kOpSub, rs_rMIPS64_SP, frame_sub); - } - - FlushIns(ArgLocs, rl_method); - - FreeTemp(rs_rMIPS64_ARG0); - FreeTemp(rs_rMIPS64_ARG1); - FreeTemp(rs_rMIPS64_ARG2); - FreeTemp(rs_rMIPS64_ARG3); - FreeTemp(rs_rMIPS64_ARG4); - FreeTemp(rs_rMIPS64_ARG5); - FreeTemp(rs_rMIPS64_ARG6); - FreeTemp(rs_rMIPS64_ARG7); -} - -void Mips64Mir2Lir::GenExitSequence() { - /* - * In the exit path, rMIPS64_RET0/rMIPS64_RET1 are live - make sure they aren't - * allocated by the register utilities as temps. - */ - LockTemp(rs_rMIPS64_RET0); - LockTemp(rs_rMIPS64_RET1); - - NewLIR0(kPseudoMethodExit); - UnSpillCoreRegs(); - OpReg(kOpBx, rs_rRAd); -} - -void Mips64Mir2Lir::GenSpecialExitSequence() { - OpReg(kOpBx, rs_rRAd); -} - -void Mips64Mir2Lir::GenSpecialEntryForSuspend() { - // Keep 16-byte stack alignment - push A0, i.e. ArtMethod* and RA. - core_spill_mask_ = (1u << rs_rRAd.GetRegNum()); - num_core_spills_ = 1u; - fp_spill_mask_ = 0u; - num_fp_spills_ = 0u; - frame_size_ = 16u; - core_vmap_table_.clear(); - fp_vmap_table_.clear(); - OpRegImm(kOpSub, rs_rMIPS64_SP, frame_size_); - StoreWordDisp(rs_rMIPS64_SP, frame_size_ - 8, rs_rRAd); - StoreWordDisp(rs_rMIPS64_SP, 0, rs_rA0d); -} - -void Mips64Mir2Lir::GenSpecialExitForSuspend() { - // Pop the frame. Don't pop ArtMethod*, it's no longer needed. - LoadWordDisp(rs_rMIPS64_SP, frame_size_ - 8, rs_rRAd); - OpRegImm(kOpAdd, rs_rMIPS64_SP, frame_size_); -} - -/* - * Bit of a hack here - in the absence of a real scheduling pass, - * emit the next instruction in static & direct invoke sequences. - */ -static int Mips64NextSDCallInsn(CompilationUnit* cu, CallInfo* info ATTRIBUTE_UNUSED, int state, - const MethodReference& target_method, uint32_t, - uintptr_t direct_code, uintptr_t direct_method, InvokeType type) { - Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get()); - if (direct_code != 0 && direct_method != 0) { - switch (state) { - case 0: // Get the current Method* [sets kArg0] - if (direct_code != static_cast<uintptr_t>(-1)) { - cg->LoadConstantWide(cg->TargetPtrReg(kInvokeTgt), direct_code); - } else { - cg->LoadCodeAddress(target_method, type, kInvokeTgt); - } - if (direct_method != static_cast<uintptr_t>(-1)) { - cg->LoadConstantWide(cg->TargetReg(kArg0, kRef), direct_method); - } else { - cg->LoadMethodAddress(target_method, type, kArg0); - } - break; - default: - return -1; - } - } else { - RegStorage arg0_ref = cg->TargetReg(kArg0, kRef); - switch (state) { - case 0: // Get the current Method* [sets kArg0] - // TUNING: we can save a reg copy if Method* has been promoted. - cg->LoadCurrMethodDirect(arg0_ref); - break; - case 1: // Get method->dex_cache_resolved_methods_ - cg->LoadRefDisp(arg0_ref, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(), - arg0_ref, kNotVolatile); - // Set up direct code if known. - if (direct_code != 0) { - if (direct_code != static_cast<uintptr_t>(-1)) { - cg->LoadConstantWide(cg->TargetPtrReg(kInvokeTgt), direct_code); - } else { - CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds()); - cg->LoadCodeAddress(target_method, type, kInvokeTgt); - } - } - break; - case 2: // Grab target method* - CHECK_EQ(cu->dex_file, target_method.dex_file); - cg->LoadRefDisp(arg0_ref, mirror::ObjectArray<mirror::Object>:: - OffsetOfElement(target_method.dex_method_index).Int32Value(), arg0_ref, - kNotVolatile); - break; - case 3: // Grab the code from the method* - if (direct_code == 0) { - int32_t offset = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset( - InstructionSetPointerSize(cu->instruction_set)).Int32Value(); - // Get the compiled code address [use *alt_from or kArg0, set kInvokeTgt] - cg->LoadWordDisp(arg0_ref, offset, cg->TargetPtrReg(kInvokeTgt)); - } - break; - default: - return -1; - } - } - return state + 1; -} - -NextCallInsn Mips64Mir2Lir::GetNextSDCallInsn() { - return Mips64NextSDCallInsn; -} - -LIR* Mips64Mir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info ATTRIBUTE_UNUSED) { - return OpReg(kOpBlx, TargetPtrReg(kInvokeTgt)); -} - -} // namespace art diff --git a/compiler/dex/quick/mips64/codegen_mips64.h b/compiler/dex/quick/mips64/codegen_mips64.h deleted file mode 100644 index c9fd62f757..0000000000 --- a/compiler/dex/quick/mips64/codegen_mips64.h +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (C) 2015 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_DEX_QUICK_MIPS64_CODEGEN_MIPS64_H_ -#define ART_COMPILER_DEX_QUICK_MIPS64_CODEGEN_MIPS64_H_ - -#include "dex/quick/mir_to_lir.h" -#include "mips64_lir.h" - -namespace art { - -struct CompilationUnit; - -class Mips64Mir2Lir FINAL : public Mir2Lir { - protected: - class InToRegStorageMips64Mapper : public InToRegStorageMapper { - public: - explicit InToRegStorageMips64Mapper(Mir2Lir* m2l) : m2l_(m2l), cur_arg_reg_(0) {} - virtual RegStorage GetNextReg(ShortyArg arg); - virtual void Reset() OVERRIDE { - cur_arg_reg_ = 0; - } - protected: - Mir2Lir* m2l_; - private: - size_t cur_arg_reg_; - }; - - InToRegStorageMips64Mapper in_to_reg_storage_mips64_mapper_; - InToRegStorageMapper* GetResetedInToRegStorageMapper() OVERRIDE { - in_to_reg_storage_mips64_mapper_.Reset(); - return &in_to_reg_storage_mips64_mapper_; - } - - public: - Mips64Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena); - - // Required for target - codegen utilities. - bool SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, RegLocation rl_src, - RegLocation rl_dest, int lit); - bool EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) OVERRIDE; - void GenMultiplyByConstantFloat(RegLocation rl_dest, RegLocation rl_src1, int32_t constant) - OVERRIDE; - void GenMultiplyByConstantDouble(RegLocation rl_dest, RegLocation rl_src1, int64_t constant) - OVERRIDE; - LIR* CheckSuspendUsingLoad() OVERRIDE; - RegStorage LoadHelper(QuickEntrypointEnum trampoline) OVERRIDE; - LIR* LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, OpSize size, - VolatileKind is_volatile) OVERRIDE; - LIR* LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest, int scale, - OpSize size) OVERRIDE; - LIR* LoadConstantNoClobber(RegStorage r_dest, int value); - LIR* LoadConstantWide(RegStorage r_dest, int64_t value); - LIR* StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, OpSize size, - VolatileKind is_volatile) OVERRIDE; - LIR* StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, int scale, - OpSize size) OVERRIDE; - LIR* GenAtomic64Load(RegStorage r_base, int displacement, RegStorage r_dest); - LIR* GenAtomic64Store(RegStorage r_base, int displacement, RegStorage r_src); - - /// @copydoc Mir2Lir::UnconditionallyMarkGCCard(RegStorage) - void UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) OVERRIDE; - - // Required for target - register utilities. - RegStorage TargetReg(SpecialTargetRegister reg) OVERRIDE; - RegStorage TargetReg(SpecialTargetRegister reg, WideKind wide_kind) OVERRIDE { - if (wide_kind == kWide || wide_kind == kRef) { - return As64BitReg(TargetReg(reg)); - } else { - return Check32BitReg(TargetReg(reg)); - } - } - RegStorage TargetPtrReg(SpecialTargetRegister reg) OVERRIDE { - return As64BitReg(TargetReg(reg)); - } - RegLocation GetReturnAlt(); - RegLocation GetReturnWideAlt(); - RegLocation LocCReturn(); - RegLocation LocCReturnRef(); - RegLocation LocCReturnDouble(); - RegLocation LocCReturnFloat(); - RegLocation LocCReturnWide(); - ResourceMask GetRegMaskCommon(const RegStorage& reg) const OVERRIDE; - void AdjustSpillMask(); - void ClobberCallerSave(); - void FreeCallTemps(); - void LockCallTemps(); - void CompilerInitializeRegAlloc(); - - // Required for target - miscellaneous. - void AssembleLIR(); - int AssignInsnOffsets(); - void AssignOffsets(); - AssemblerStatus AssembleInstructions(CodeOffset start_addr); - void DumpResourceMask(LIR* lir, const ResourceMask& mask, const char* prefix) OVERRIDE; - void SetupTargetResourceMasks(LIR* lir, uint64_t flags, ResourceMask* use_mask, - ResourceMask* def_mask) OVERRIDE; - const char* GetTargetInstFmt(int opcode); - const char* GetTargetInstName(int opcode); - std::string BuildInsnString(const char* fmt, LIR* lir, unsigned char* base_addr); - ResourceMask GetPCUseDefEncoding() const OVERRIDE; - uint64_t GetTargetInstFlags(int opcode); - size_t GetInsnSize(LIR* lir) OVERRIDE; - bool IsUnconditionalBranch(LIR* lir); - - // Get the register class for load/store of a field. - RegisterClass RegClassForFieldLoadStore(OpSize size, bool is_volatile) OVERRIDE; - - // Required for target - Dalvik-level generators. - void GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, - RegLocation lr_shift); - void GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2, int flags); - void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index, - RegLocation rl_dest, int scale); - void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, RegLocation rl_index, - RegLocation rl_src, int scale, bool card_mark); - void GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_shift, int flags); - void GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2); - void GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2); - void GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2); - void GenConversion(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src); - bool GenInlinedAbsFloat(CallInfo* info) OVERRIDE; - bool GenInlinedAbsDouble(CallInfo* info) OVERRIDE; - bool GenInlinedCas(CallInfo* info, bool is_long, bool is_object); - bool GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long); - bool GenInlinedSqrt(CallInfo* info); - bool GenInlinedPeek(CallInfo* info, OpSize size); - bool GenInlinedPoke(CallInfo* info, OpSize size); - void GenIntToLong(RegLocation rl_dest, RegLocation rl_src) OVERRIDE; - void GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2, int flags) OVERRIDE; - RegLocation GenDivRem(RegLocation rl_dest, RegStorage reg_lo, RegStorage reg_hi, bool is_div); - RegLocation GenDivRemLit(RegLocation rl_dest, RegStorage reg_lo, int lit, bool is_div); - void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenDivZeroCheckWide(RegStorage reg); - void GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method); - void GenExitSequence(); - void GenSpecialExitSequence() OVERRIDE; - void GenSpecialEntryForSuspend() OVERRIDE; - void GenSpecialExitForSuspend() OVERRIDE; - void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double); - void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir); - void GenSelect(BasicBlock* bb, MIR* mir); - void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code, - int32_t true_val, int32_t false_val, RegStorage rs_dest, - RegisterClass dest_reg_class) OVERRIDE; - bool GenMemBarrier(MemBarrierKind barrier_kind); - void GenMoveException(RegLocation rl_dest); - void GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, int lit, - int first_bit, int second_bit); - void GenNegDouble(RegLocation rl_dest, RegLocation rl_src); - void GenNegFloat(RegLocation rl_dest, RegLocation rl_src); - void GenLargePackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); - void GenLargeSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src); - bool GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special); - - // Required for target - single operation generators. - LIR* OpUnconditionalBranch(LIR* target); - LIR* OpCmpBranch(ConditionCode cond, RegStorage src1, RegStorage src2, LIR* target); - LIR* OpCmpImmBranch(ConditionCode cond, RegStorage reg, int check_value, LIR* target); - LIR* OpCondBranch(ConditionCode cc, LIR* target); - LIR* OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* target); - LIR* OpFpRegCopy(RegStorage r_dest, RegStorage r_src); - LIR* OpIT(ConditionCode cond, const char* guide); - void OpEndIT(LIR* it); - LIR* OpMem(OpKind op, RegStorage r_base, int disp); - void OpPcRelLoad(RegStorage reg, LIR* target); - LIR* OpReg(OpKind op, RegStorage r_dest_src); - void OpRegCopy(RegStorage r_dest, RegStorage r_src); - LIR* OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src); - LIR* OpRegImm(OpKind op, RegStorage r_dest_src1, int value); - LIR* OpRegReg(OpKind op, RegStorage r_dest_src1, RegStorage r_src2); - LIR* OpMovRegMem(RegStorage r_dest, RegStorage r_base, int offset, MoveType move_type); - LIR* OpMovMemReg(RegStorage r_base, int offset, RegStorage r_src, MoveType move_type); - LIR* OpCondRegReg(OpKind op, ConditionCode cc, RegStorage r_dest, RegStorage r_src); - LIR* OpRegRegImm(OpKind op, RegStorage r_dest, RegStorage r_src1, int value); - LIR* OpRegRegReg(OpKind op, RegStorage r_dest, RegStorage r_src1, RegStorage r_src2); - LIR* OpTestSuspend(LIR* target); - LIR* OpVldm(RegStorage r_base, int count); - LIR* OpVstm(RegStorage r_base, int count); - void OpRegCopyWide(RegStorage dest, RegStorage src); - - // TODO: collapse r_dest. - LIR* LoadBaseDispBody(RegStorage r_base, int displacement, RegStorage r_dest, OpSize size); - // TODO: collapse r_src. - LIR* StoreBaseDispBody(RegStorage r_base, int displacement, RegStorage r_src, OpSize size); - void SpillCoreRegs(); - void UnSpillCoreRegs(); - static const Mips64EncodingMap EncodingMap[kMips64Last]; - bool InexpensiveConstantInt(int32_t value); - bool InexpensiveConstantFloat(int32_t value); - bool InexpensiveConstantLong(int64_t value); - bool InexpensiveConstantDouble(int64_t value); - - bool WideGPRsAreAliases() const OVERRIDE { - return true; // 64b architecture. - } - bool WideFPRsAreAliases() const OVERRIDE { - return true; // 64b architecture. - } - - LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) OVERRIDE; - RegLocation GenDivRem(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2, - bool is_div, int flags) OVERRIDE; - RegLocation GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, bool is_div) - OVERRIDE; - NextCallInsn GetNextSDCallInsn() OVERRIDE; - LIR* GenCallInsn(const MirMethodLoweringInfo& method_info) OVERRIDE; - // Unimplemented intrinsics. - bool GenInlinedCharAt(CallInfo* info ATTRIBUTE_UNUSED) OVERRIDE { - return false; - } - bool GenInlinedAbsInt(CallInfo* info ATTRIBUTE_UNUSED) OVERRIDE { - return false; - } - bool GenInlinedAbsLong(CallInfo* info ATTRIBUTE_UNUSED) OVERRIDE { - return false; - } - bool GenInlinedIndexOf(CallInfo* info ATTRIBUTE_UNUSED, bool zero_based ATTRIBUTE_UNUSED) - OVERRIDE { - return false; - } - - private: - void GenLongOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenNotLong(RegLocation rl_dest, RegLocation rl_src); - void GenNegLong(RegLocation rl_dest, RegLocation rl_src); - void GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2); - void GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2, bool is_div, int flags); - void GenConversionCall(QuickEntrypointEnum trampoline, RegLocation rl_dest, RegLocation rl_src, - RegisterClass reg_class); - - void ConvertShortToLongBranch(LIR* lir); - - /** - * @param reg #RegStorage containing a Solo64 input register (e.g. @c a1 or @c d0). - * @return A Solo32 with the same register number as the @p reg (e.g. @c a1 or @c f0). - * @see As64BitReg - */ - RegStorage As32BitReg(RegStorage reg) { - DCHECK(!reg.IsPair()); - if ((kFailOnSizeError || kReportSizeError) && !reg.Is64Bit()) { - if (kFailOnSizeError) { - LOG(FATAL) << "Expected 64b register"; - } else { - LOG(WARNING) << "Expected 64b register"; - return reg; - } - } - RegStorage ret_val = RegStorage(RegStorage::k32BitSolo, - reg.GetRawBits() & RegStorage::kRegTypeMask); - DCHECK_EQ(GetRegInfo(reg)->FindMatchingView(RegisterInfo::k32SoloStorageMask) - ->GetReg().GetReg(), - ret_val.GetReg()); - return ret_val; - } - - RegStorage Check32BitReg(RegStorage reg) { - if ((kFailOnSizeError || kReportSizeError) && !reg.Is32Bit()) { - if (kFailOnSizeError) { - LOG(FATAL) << "Checked for 32b register"; - } else { - LOG(WARNING) << "Checked for 32b register"; - return As32BitReg(reg); - } - } - return reg; - } - - /** - * @param reg #RegStorage containing a Solo32 input register (e.g. @c a1 or @c f0). - * @return A Solo64 with the same register number as the @p reg (e.g. @c a1 or @c d0). - */ - RegStorage As64BitReg(RegStorage reg) { - DCHECK(!reg.IsPair()); - if ((kFailOnSizeError || kReportSizeError) && !reg.Is32Bit()) { - if (kFailOnSizeError) { - LOG(FATAL) << "Expected 32b register"; - } else { - LOG(WARNING) << "Expected 32b register"; - return reg; - } - } - RegStorage ret_val = RegStorage(RegStorage::k64BitSolo, - reg.GetRawBits() & RegStorage::kRegTypeMask); - DCHECK_EQ(GetRegInfo(reg)->FindMatchingView(RegisterInfo::k64SoloStorageMask) - ->GetReg().GetReg(), - ret_val.GetReg()); - return ret_val; - } - - RegStorage Check64BitReg(RegStorage reg) { - if ((kFailOnSizeError || kReportSizeError) && !reg.Is64Bit()) { - if (kFailOnSizeError) { - LOG(FATAL) << "Checked for 64b register"; - } else { - LOG(WARNING) << "Checked for 64b register"; - return As64BitReg(reg); - } - } - return reg; - } - - void GenBreakpoint(int code); -}; - -} // namespace art - -#endif // ART_COMPILER_DEX_QUICK_MIPS64_CODEGEN_MIPS64_H_ diff --git a/compiler/dex/quick/mips64/fp_mips64.cc b/compiler/dex/quick/mips64/fp_mips64.cc deleted file mode 100644 index 5c8ee9ccb8..0000000000 --- a/compiler/dex/quick/mips64/fp_mips64.cc +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (C) 2015 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 "codegen_mips64.h" - -#include "base/logging.h" -#include "dex/quick/mir_to_lir-inl.h" -#include "entrypoints/quick/quick_entrypoints.h" -#include "mips64_lir.h" - -namespace art { - -void Mips64Mir2Lir::GenArithOpFloat(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_src2) { - int op = kMips64Nop; - RegLocation rl_result; - - /* - * Don't attempt to optimize register usage since these opcodes call out to - * the handlers. - */ - switch (opcode) { - case Instruction::ADD_FLOAT_2ADDR: - case Instruction::ADD_FLOAT: - op = kMips64Fadds; - break; - case Instruction::SUB_FLOAT_2ADDR: - case Instruction::SUB_FLOAT: - op = kMips64Fsubs; - break; - case Instruction::DIV_FLOAT_2ADDR: - case Instruction::DIV_FLOAT: - op = kMips64Fdivs; - break; - case Instruction::MUL_FLOAT_2ADDR: - case Instruction::MUL_FLOAT: - op = kMips64Fmuls; - break; - case Instruction::REM_FLOAT_2ADDR: - case Instruction::REM_FLOAT: - FlushAllRegs(); // Send everything to home location. - CallRuntimeHelperRegLocationRegLocation(kQuickFmodf, rl_src1, rl_src2, false); - rl_result = GetReturn(kFPReg); - StoreValue(rl_dest, rl_result); - return; - case Instruction::NEG_FLOAT: - GenNegFloat(rl_dest, rl_src1); - return; - default: - LOG(FATAL) << "Unexpected opcode: " << opcode; - } - rl_src1 = LoadValue(rl_src1, kFPReg); - rl_src2 = LoadValue(rl_src2, kFPReg); - rl_result = EvalLoc(rl_dest, kFPReg, true); - NewLIR3(op, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); - StoreValue(rl_dest, rl_result); -} - -void Mips64Mir2Lir::GenArithOpDouble(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_src2) { - int op = kMips64Nop; - RegLocation rl_result; - - switch (opcode) { - case Instruction::ADD_DOUBLE_2ADDR: - case Instruction::ADD_DOUBLE: - op = kMips64Faddd; - break; - case Instruction::SUB_DOUBLE_2ADDR: - case Instruction::SUB_DOUBLE: - op = kMips64Fsubd; - break; - case Instruction::DIV_DOUBLE_2ADDR: - case Instruction::DIV_DOUBLE: - op = kMips64Fdivd; - break; - case Instruction::MUL_DOUBLE_2ADDR: - case Instruction::MUL_DOUBLE: - op = kMips64Fmuld; - break; - case Instruction::REM_DOUBLE_2ADDR: - case Instruction::REM_DOUBLE: - FlushAllRegs(); // Send everything to home location. - CallRuntimeHelperRegLocationRegLocation(kQuickFmod, rl_src1, rl_src2, false); - rl_result = GetReturnWide(kFPReg); - StoreValueWide(rl_dest, rl_result); - return; - case Instruction::NEG_DOUBLE: - GenNegDouble(rl_dest, rl_src1); - return; - default: - LOG(FATAL) << "Unpexpected opcode: " << opcode; - } - rl_src1 = LoadValueWide(rl_src1, kFPReg); - DCHECK(rl_src1.wide); - rl_src2 = LoadValueWide(rl_src2, kFPReg); - DCHECK(rl_src2.wide); - rl_result = EvalLoc(rl_dest, kFPReg, true); - DCHECK(rl_dest.wide); - DCHECK(rl_result.wide); - NewLIR3(op, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); - StoreValueWide(rl_dest, rl_result); -} - -void Mips64Mir2Lir::GenMultiplyByConstantFloat(RegLocation rl_dest, RegLocation rl_src1, - int32_t constant) { - // TODO: need mips64 implementation. - UNUSED(rl_dest, rl_src1, constant); - LOG(FATAL) << "Unimplemented GenMultiplyByConstantFloat in mips64"; -} - -void Mips64Mir2Lir::GenMultiplyByConstantDouble(RegLocation rl_dest, RegLocation rl_src1, - int64_t constant) { - // TODO: need mips64 implementation. - UNUSED(rl_dest, rl_src1, constant); - LOG(FATAL) << "Unimplemented GenMultiplyByConstantDouble in mips64"; -} - -void Mips64Mir2Lir::GenConversion(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src) { - int op = kMips64Nop; - RegLocation rl_result; - switch (opcode) { - case Instruction::INT_TO_FLOAT: - op = kMips64Fcvtsw; - break; - case Instruction::DOUBLE_TO_FLOAT: - op = kMips64Fcvtsd; - break; - case Instruction::FLOAT_TO_DOUBLE: - op = kMips64Fcvtds; - break; - case Instruction::INT_TO_DOUBLE: - op = kMips64Fcvtdw; - break; - case Instruction::FLOAT_TO_INT: - GenConversionCall(kQuickF2iz, rl_dest, rl_src, kCoreReg); - return; - case Instruction::DOUBLE_TO_INT: - GenConversionCall(kQuickD2iz, rl_dest, rl_src, kCoreReg); - return; - case Instruction::LONG_TO_DOUBLE: - GenConversionCall(kQuickL2d, rl_dest, rl_src, kFPReg); - return; - case Instruction::FLOAT_TO_LONG: - GenConversionCall(kQuickF2l, rl_dest, rl_src, kCoreReg); - return; - case Instruction::LONG_TO_FLOAT: - GenConversionCall(kQuickL2f, rl_dest, rl_src, kFPReg); - return; - case Instruction::DOUBLE_TO_LONG: - GenConversionCall(kQuickD2l, rl_dest, rl_src, kCoreReg); - return; - default: - LOG(FATAL) << "Unexpected opcode: " << opcode; - } - if (rl_src.wide) { - rl_src = LoadValueWide(rl_src, kFPReg); - } else { - rl_src = LoadValue(rl_src, kFPReg); - } - rl_result = EvalLoc(rl_dest, kFPReg, true); - NewLIR2(op, rl_result.reg.GetReg(), rl_src.reg.GetReg()); - if (rl_dest.wide) { - StoreValueWide(rl_dest, rl_result); - } else { - StoreValue(rl_dest, rl_result); - } -} - -void Mips64Mir2Lir::GenCmpFP(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2) { - bool wide = true; - QuickEntrypointEnum target; - - switch (opcode) { - case Instruction::CMPL_FLOAT: - target = kQuickCmplFloat; - wide = false; - break; - case Instruction::CMPG_FLOAT: - target = kQuickCmpgFloat; - wide = false; - break; - case Instruction::CMPL_DOUBLE: - target = kQuickCmplDouble; - break; - case Instruction::CMPG_DOUBLE: - target = kQuickCmpgDouble; - break; - default: - LOG(FATAL) << "Unexpected opcode: " << opcode; - target = kQuickCmplFloat; - } - FlushAllRegs(); - LockCallTemps(); - if (wide) { - RegStorage r_tmp1(RegStorage::k64BitSolo, rMIPS64_FARG0); - RegStorage r_tmp2(RegStorage::k64BitSolo, rMIPS64_FARG1); - LoadValueDirectWideFixed(rl_src1, r_tmp1); - LoadValueDirectWideFixed(rl_src2, r_tmp2); - } else { - LoadValueDirectFixed(rl_src1, rs_rMIPS64_FARG0); - LoadValueDirectFixed(rl_src2, rs_rMIPS64_FARG1); - } - RegStorage r_tgt = LoadHelper(target); - // NOTE: not a safepoint. - OpReg(kOpBlx, r_tgt); - RegLocation rl_result = GetReturn(kCoreReg); - StoreValue(rl_dest, rl_result); -} - -void Mips64Mir2Lir::GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double) { - UNUSED(bb, mir, gt_bias, is_double); - UNIMPLEMENTED(FATAL) << "Need codegen for fused fp cmp branch"; -} - -void Mips64Mir2Lir::GenNegFloat(RegLocation rl_dest, RegLocation rl_src) { - RegLocation rl_result; - rl_src = LoadValue(rl_src, kFPReg); - rl_result = EvalLoc(rl_dest, kFPReg, true); - NewLIR2(kMips64Fnegs, rl_result.reg.GetReg(), rl_src.reg.GetReg()); - StoreValue(rl_dest, rl_result); -} - -void Mips64Mir2Lir::GenNegDouble(RegLocation rl_dest, RegLocation rl_src) { - RegLocation rl_result; - rl_src = LoadValueWide(rl_src, kFPReg); - rl_result = EvalLocWide(rl_dest, kFPReg, true); - NewLIR2(kMips64Fnegd, rl_result.reg.GetReg(), rl_src.reg.GetReg()); - StoreValueWide(rl_dest, rl_result); -} - -bool Mips64Mir2Lir::GenInlinedMinMax(CallInfo* info, bool is_min, bool is_long) { - // TODO: need Mips64 implementation. - UNUSED(info, is_min, is_long); - return false; -} - -} // namespace art diff --git a/compiler/dex/quick/mips64/int_mips64.cc b/compiler/dex/quick/mips64/int_mips64.cc deleted file mode 100644 index 5c545bb824..0000000000 --- a/compiler/dex/quick/mips64/int_mips64.cc +++ /dev/null @@ -1,692 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -/* This file contains codegen for the Mips64 ISA */ - -#include "codegen_mips64.h" - -#include "base/logging.h" -#include "dex/mir_graph.h" -#include "dex/quick/mir_to_lir-inl.h" -#include "dex/reg_storage_eq.h" -#include "entrypoints/quick/quick_entrypoints.h" -#include "mips64_lir.h" -#include "mirror/array-inl.h" - -namespace art { - -/* - * Compare two 64-bit values - * x = y return 0 - * x < y return -1 - * x > y return 1 - * - * slt temp, x, y; # (x < y) ? 1:0 - * slt res, y, x; # (x > y) ? 1:0 - * subu res, res, temp; # res = -1:1:0 for [ < > = ] - * - */ -void Mips64Mir2Lir::GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { - rl_src1 = LoadValueWide(rl_src1, kCoreReg); - rl_src2 = LoadValueWide(rl_src2, kCoreReg); - RegStorage temp = AllocTempWide(); - RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - NewLIR3(kMips64Slt, temp.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); - NewLIR3(kMips64Slt, rl_result.reg.GetReg(), rl_src2.reg.GetReg(), rl_src1.reg.GetReg()); - NewLIR3(kMips64Subu, rl_result.reg.GetReg(), rl_result.reg.GetReg(), temp.GetReg()); - FreeTemp(temp); - StoreValue(rl_dest, rl_result); -} - -LIR* Mips64Mir2Lir::OpCmpBranch(ConditionCode cond, RegStorage src1, RegStorage src2, LIR* target) { - LIR* branch; - Mips64OpCode slt_op; - Mips64OpCode br_op; - bool cmp_zero = false; - bool swapped = false; - switch (cond) { - case kCondEq: - br_op = kMips64Beq; - cmp_zero = true; - break; - case kCondNe: - br_op = kMips64Bne; - cmp_zero = true; - break; - case kCondUlt: - slt_op = kMips64Sltu; - br_op = kMips64Bnez; - break; - case kCondUge: - slt_op = kMips64Sltu; - br_op = kMips64Beqz; - break; - case kCondGe: - slt_op = kMips64Slt; - br_op = kMips64Beqz; - break; - case kCondGt: - slt_op = kMips64Slt; - br_op = kMips64Bnez; - swapped = true; - break; - case kCondLe: - slt_op = kMips64Slt; - br_op = kMips64Beqz; - swapped = true; - break; - case kCondLt: - slt_op = kMips64Slt; - br_op = kMips64Bnez; - break; - case kCondHi: // Gtu - slt_op = kMips64Sltu; - br_op = kMips64Bnez; - swapped = true; - break; - default: - LOG(FATAL) << "No support for ConditionCode: " << cond; - return NULL; - } - if (cmp_zero) { - branch = NewLIR2(br_op, src1.GetReg(), src2.GetReg()); - } else { - RegStorage t_reg = AllocTemp(); - if (swapped) { - NewLIR3(slt_op, t_reg.GetReg(), src2.GetReg(), src1.GetReg()); - } else { - NewLIR3(slt_op, t_reg.GetReg(), src1.GetReg(), src2.GetReg()); - } - branch = NewLIR1(br_op, t_reg.GetReg()); - FreeTemp(t_reg); - } - branch->target = target; - return branch; -} - -LIR* Mips64Mir2Lir::OpCmpImmBranch(ConditionCode cond, RegStorage reg, - int check_value, LIR* target) { - LIR* branch; - if (check_value != 0) { - // TUNING: handle s16 & kCondLt/Mi case using slti. - RegStorage t_reg = AllocTemp(); - LoadConstant(t_reg, check_value); - branch = OpCmpBranch(cond, reg, t_reg, target); - FreeTemp(t_reg); - return branch; - } - Mips64OpCode opc; - switch (cond) { - case kCondEq: opc = kMips64Beqz; break; - case kCondGe: opc = kMips64Bgez; break; - case kCondGt: opc = kMips64Bgtz; break; - case kCondLe: opc = kMips64Blez; break; - // case KCondMi: - case kCondLt: opc = kMips64Bltz; break; - case kCondNe: opc = kMips64Bnez; break; - default: - // Tuning: use slti when applicable. - RegStorage t_reg = AllocTemp(); - LoadConstant(t_reg, check_value); - branch = OpCmpBranch(cond, reg, t_reg, target); - FreeTemp(t_reg); - return branch; - } - branch = NewLIR1(opc, reg.GetReg()); - branch->target = target; - return branch; -} - -LIR* Mips64Mir2Lir::OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) { - DCHECK(!r_dest.IsPair() && !r_src.IsPair()); - if (r_dest.IsFloat() || r_src.IsFloat()) - return OpFpRegCopy(r_dest, r_src); - // TODO: Check that r_src and r_dest are both 32 or both 64 bits length. - LIR* res; - if (r_dest.Is64Bit() || r_src.Is64Bit()) { - res = RawLIR(current_dalvik_offset_, kMips64Move, r_dest.GetReg(), r_src.GetReg()); - } else { - res = RawLIR(current_dalvik_offset_, kMips64Sll, r_dest.GetReg(), r_src.GetReg(), 0); - } - if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) { - res->flags.is_nop = true; - } - return res; -} - -void Mips64Mir2Lir::OpRegCopy(RegStorage r_dest, RegStorage r_src) { - if (r_dest != r_src) { - LIR *res = OpRegCopyNoInsert(r_dest, r_src); - AppendLIR(res); - } -} - -void Mips64Mir2Lir::OpRegCopyWide(RegStorage r_dest, RegStorage r_src) { - OpRegCopy(r_dest, r_src); -} - -void Mips64Mir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code, - int32_t true_val, int32_t false_val, RegStorage rs_dest, - RegisterClass dest_reg_class) { - UNUSED(dest_reg_class); - // Implement as a branch-over. - // TODO: Conditional move? - LoadConstant(rs_dest, true_val); - LIR* ne_branchover = OpCmpBranch(code, left_op, right_op, NULL); - LoadConstant(rs_dest, false_val); - LIR* target_label = NewLIR0(kPseudoTargetLabel); - ne_branchover->target = target_label; -} - -void Mips64Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) { - UNUSED(bb, mir); - UNIMPLEMENTED(FATAL) << "Need codegen for select"; -} - -void Mips64Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) { - UNUSED(bb, mir); - UNIMPLEMENTED(FATAL) << "Need codegen for fused long cmp branch"; -} - -RegLocation Mips64Mir2Lir::GenDivRem(RegLocation rl_dest, RegStorage reg1, RegStorage reg2, - bool is_div) { - RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - NewLIR3(is_div ? kMips64Div : kMips64Mod, rl_result.reg.GetReg(), reg1.GetReg(), reg2.GetReg()); - return rl_result; -} - -RegLocation Mips64Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegStorage reg1, int lit, - bool is_div) { - RegStorage t_reg = AllocTemp(); - NewLIR3(kMips64Addiu, t_reg.GetReg(), rZERO, lit); - RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - NewLIR3(is_div ? kMips64Div : kMips64Mod, rl_result.reg.GetReg(), reg1.GetReg(), t_reg.GetReg()); - FreeTemp(t_reg); - return rl_result; -} - -RegLocation Mips64Mir2Lir::GenDivRem(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2, - bool is_div, int flags) { - UNUSED(rl_dest, rl_src1, rl_src2, is_div, flags); - LOG(FATAL) << "Unexpected use of GenDivRem for Mips64"; - UNREACHABLE(); -} - -RegLocation Mips64Mir2Lir::GenDivRemLit(RegLocation rl_dest, RegLocation rl_src1, int lit, - bool is_div) { - UNUSED(rl_dest, rl_src1, lit, is_div); - LOG(FATAL) << "Unexpected use of GenDivRemLit for Mips64"; - UNREACHABLE(); -} - -bool Mips64Mir2Lir::GenInlinedCas(CallInfo* info, bool is_long, bool is_object) { - UNUSED(info, is_long, is_object); - return false; -} - -bool Mips64Mir2Lir::GenInlinedAbsFloat(CallInfo* info) { - UNUSED(info); - // TODO: add Mips64 implementation. - return false; -} - -bool Mips64Mir2Lir::GenInlinedAbsDouble(CallInfo* info) { - UNUSED(info); - // TODO: add Mips64 implementation. - return false; -} - -bool Mips64Mir2Lir::GenInlinedSqrt(CallInfo* info) { - UNUSED(info); - return false; -} - -bool Mips64Mir2Lir::GenInlinedPeek(CallInfo* info, OpSize size) { - if (size != kSignedByte) { - // MIPS64 supports only aligned access. Defer unaligned access to JNI implementation. - return false; - } - RegLocation rl_src_address = info->args[0]; // Long address. - RegLocation rl_dest = InlineTarget(info); - RegLocation rl_address = LoadValueWide(rl_src_address, kCoreReg); - RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); - DCHECK(size == kSignedByte); - LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile); - StoreValue(rl_dest, rl_result); - return true; -} - -bool Mips64Mir2Lir::GenInlinedPoke(CallInfo* info, OpSize size) { - if (size != kSignedByte) { - // MIPS64 supports only aligned access. Defer unaligned access to JNI implementation. - return false; - } - RegLocation rl_src_address = info->args[0]; // Long address. - RegLocation rl_src_value = info->args[2]; // [size] value. - RegLocation rl_address = LoadValueWide(rl_src_address, kCoreReg); - DCHECK(size == kSignedByte); - RegLocation rl_value = LoadValue(rl_src_value, kCoreReg); - StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size, kNotVolatile); - return true; -} - -void Mips64Mir2Lir::OpPcRelLoad(RegStorage reg, LIR* target) { - UNUSED(reg, target); - LOG(FATAL) << "Unexpected use of OpPcRelLoad for Mips64"; - UNREACHABLE(); -} - -LIR* Mips64Mir2Lir::OpVldm(RegStorage r_base, int count) { - UNUSED(r_base, count); - LOG(FATAL) << "Unexpected use of OpVldm for Mips64"; - UNREACHABLE(); -} - -LIR* Mips64Mir2Lir::OpVstm(RegStorage r_base, int count) { - UNUSED(r_base, count); - LOG(FATAL) << "Unexpected use of OpVstm for Mips64"; - UNREACHABLE(); -} - -void Mips64Mir2Lir::GenMultiplyByTwoBitMultiplier(RegLocation rl_src, RegLocation rl_result, - int lit, int first_bit, int second_bit) { - UNUSED(lit); - RegStorage t_reg = AllocTemp(); - OpRegRegImm(kOpLsl, t_reg, rl_src.reg, second_bit - first_bit); - OpRegRegReg(kOpAdd, rl_result.reg, rl_src.reg, t_reg); - FreeTemp(t_reg); - if (first_bit != 0) { - OpRegRegImm(kOpLsl, rl_result.reg, rl_result.reg, first_bit); - } -} - -void Mips64Mir2Lir::GenDivZeroCheckWide(RegStorage reg) { - GenDivZeroCheck(reg); -} - -// Test suspend flag, return target of taken suspend branch. -LIR* Mips64Mir2Lir::OpTestSuspend(LIR* target) { - OpRegImm(kOpSub, rs_rMIPS64_SUSPEND, 1); - return OpCmpImmBranch((target == NULL) ? kCondEq : kCondNe, rs_rMIPS64_SUSPEND, 0, target); -} - -// Decrement register and branch on condition. -LIR* Mips64Mir2Lir::OpDecAndBranch(ConditionCode c_code, RegStorage reg, LIR* target) { - OpRegImm(kOpSub, reg, 1); - return OpCmpImmBranch(c_code, reg, 0, target); -} - -bool Mips64Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, - RegLocation rl_src, RegLocation rl_dest, int lit) { - UNUSED(dalvik_opcode, is_div, rl_src, rl_dest, lit); - LOG(FATAL) << "Unexpected use of smallLiteralDive in Mips64"; - UNREACHABLE(); -} - -bool Mips64Mir2Lir::EasyMultiply(RegLocation rl_src, RegLocation rl_dest, int lit) { - UNUSED(rl_src, rl_dest, lit); - LOG(FATAL) << "Unexpected use of easyMultiply in Mips64"; - UNREACHABLE(); -} - -LIR* Mips64Mir2Lir::OpIT(ConditionCode cond, const char* guide) { - UNUSED(cond, guide); - LOG(FATAL) << "Unexpected use of OpIT in Mips64"; - UNREACHABLE(); -} - -void Mips64Mir2Lir::OpEndIT(LIR* it) { - UNUSED(it); - LOG(FATAL) << "Unexpected use of OpEndIT in Mips64"; -} - -void Mips64Mir2Lir::GenArithOpLong(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_src2, int flags) { - switch (opcode) { - case Instruction::NOT_LONG: - GenNotLong(rl_dest, rl_src2); - return; - case Instruction::ADD_LONG: - case Instruction::ADD_LONG_2ADDR: - GenLongOp(kOpAdd, rl_dest, rl_src1, rl_src2); - return; - case Instruction::SUB_LONG: - case Instruction::SUB_LONG_2ADDR: - GenLongOp(kOpSub, rl_dest, rl_src1, rl_src2); - return; - case Instruction::MUL_LONG: - case Instruction::MUL_LONG_2ADDR: - GenMulLong(rl_dest, rl_src1, rl_src2); - return; - case Instruction::DIV_LONG: - case Instruction::DIV_LONG_2ADDR: - GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ true, flags); - return; - case Instruction::REM_LONG: - case Instruction::REM_LONG_2ADDR: - GenDivRemLong(opcode, rl_dest, rl_src1, rl_src2, /*is_div*/ false, flags); - return; - case Instruction::AND_LONG: - case Instruction::AND_LONG_2ADDR: - GenLongOp(kOpAnd, rl_dest, rl_src1, rl_src2); - return; - case Instruction::OR_LONG: - case Instruction::OR_LONG_2ADDR: - GenLongOp(kOpOr, rl_dest, rl_src1, rl_src2); - return; - case Instruction::XOR_LONG: - case Instruction::XOR_LONG_2ADDR: - GenLongOp(kOpXor, rl_dest, rl_src1, rl_src2); - return; - case Instruction::NEG_LONG: - GenNegLong(rl_dest, rl_src2); - return; - - default: - LOG(FATAL) << "Invalid long arith op"; - return; - } -} - -void Mips64Mir2Lir::GenLongOp(OpKind op, RegLocation rl_dest, RegLocation rl_src1, - RegLocation rl_src2) { - rl_src1 = LoadValueWide(rl_src1, kCoreReg); - rl_src2 = LoadValueWide(rl_src2, kCoreReg); - RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); - OpRegRegReg(op, rl_result.reg, rl_src1.reg, rl_src2.reg); - StoreValueWide(rl_dest, rl_result); -} - -void Mips64Mir2Lir::GenNotLong(RegLocation rl_dest, RegLocation rl_src) { - rl_src = LoadValueWide(rl_src, kCoreReg); - RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); - OpRegReg(kOpMvn, rl_result.reg, rl_src.reg); - StoreValueWide(rl_dest, rl_result); -} - -void Mips64Mir2Lir::GenNegLong(RegLocation rl_dest, RegLocation rl_src) { - rl_src = LoadValueWide(rl_src, kCoreReg); - RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); - OpRegReg(kOpNeg, rl_result.reg, rl_src.reg); - StoreValueWide(rl_dest, rl_result); -} - -void Mips64Mir2Lir::GenMulLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2) { - rl_src1 = LoadValueWide(rl_src1, kCoreReg); - rl_src2 = LoadValueWide(rl_src2, kCoreReg); - RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); - NewLIR3(kMips64Dmul, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), rl_src2.reg.GetReg()); - StoreValueWide(rl_dest, rl_result); -} - -void Mips64Mir2Lir::GenDivRemLong(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_src2, bool is_div, - int flags) { - UNUSED(opcode); - // TODO: Implement easy div/rem? - rl_src1 = LoadValueWide(rl_src1, kCoreReg); - rl_src2 = LoadValueWide(rl_src2, kCoreReg); - if ((flags & MIR_IGNORE_DIV_ZERO_CHECK) == 0) { - GenDivZeroCheckWide(rl_src2.reg); - } - RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); - NewLIR3(is_div ? kMips64Ddiv : kMips64Dmod, rl_result.reg.GetReg(), rl_src1.reg.GetReg(), - rl_src2.reg.GetReg()); - StoreValueWide(rl_dest, rl_result); -} - -/* - * Generate array load - */ -void Mips64Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_dest, int scale) { - RegisterClass reg_class = RegClassBySize(size); - int len_offset = mirror::Array::LengthOffset().Int32Value(); - int data_offset; - RegLocation rl_result; - rl_array = LoadValue(rl_array, kRefReg); - rl_index = LoadValue(rl_index, kCoreReg); - - // FIXME: need to add support for rl_index.is_const. - - if (size == k64 || size == kDouble) { - data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value(); - } else { - data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); - } - - // Null object? - GenNullCheck(rl_array.reg, opt_flags); - - RegStorage reg_ptr = AllocTempRef(); - bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK)); - RegStorage reg_len; - if (needs_range_check) { - reg_len = AllocTemp(); - // Get len. - Load32Disp(rl_array.reg, len_offset, reg_len); - } - // reg_ptr -> array data. - OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset); - FreeTemp(rl_array.reg); - if ((size == k64) || (size == kDouble)) { - if (scale) { - RegStorage r_new_index = AllocTemp(); - OpRegRegImm(kOpLsl, r_new_index, rl_index.reg, scale); - OpRegReg(kOpAdd, reg_ptr, r_new_index); - FreeTemp(r_new_index); - } else { - OpRegReg(kOpAdd, reg_ptr, rl_index.reg); - } - FreeTemp(rl_index.reg); - rl_result = EvalLoc(rl_dest, reg_class, true); - - if (needs_range_check) { - GenArrayBoundsCheck(rl_index.reg, reg_len); - FreeTemp(reg_len); - } - LoadBaseDisp(reg_ptr, 0, rl_result.reg, size, kNotVolatile); - - FreeTemp(reg_ptr); - StoreValueWide(rl_dest, rl_result); - } else { - rl_result = EvalLoc(rl_dest, reg_class, true); - - if (needs_range_check) { - GenArrayBoundsCheck(rl_index.reg, reg_len); - FreeTemp(reg_len); - } - if (rl_result.ref) { - LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), As32BitReg(rl_result.reg), scale, - kReference); - } else { - LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_result.reg, scale, size); - } - - FreeTemp(reg_ptr); - StoreValue(rl_dest, rl_result); - } -} - -/* - * Generate array store - * - */ -void Mips64Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, - RegLocation rl_index, RegLocation rl_src, int scale, - bool card_mark) { - RegisterClass reg_class = RegClassBySize(size); - int len_offset = mirror::Array::LengthOffset().Int32Value(); - int data_offset; - - if (size == k64 || size == kDouble) { - data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value(); - } else { - data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); - } - - rl_array = LoadValue(rl_array, kRefReg); - rl_index = LoadValue(rl_index, kCoreReg); - - // FIXME: need to add support for rl_index.is_const. - - RegStorage reg_ptr; - bool allocated_reg_ptr_temp = false; - if (IsTemp(rl_array.reg) && !card_mark) { - Clobber(rl_array.reg); - reg_ptr = rl_array.reg; - } else { - reg_ptr = AllocTemp(); - OpRegCopy(reg_ptr, rl_array.reg); - allocated_reg_ptr_temp = true; - } - - // Null object? - GenNullCheck(rl_array.reg, opt_flags); - - bool needs_range_check = (!(opt_flags & MIR_IGNORE_RANGE_CHECK)); - RegStorage reg_len; - if (needs_range_check) { - reg_len = AllocTemp(); - // NOTE: max live temps(4) here. - // Get len. - Load32Disp(rl_array.reg, len_offset, reg_len); - } - // reg_ptr -> array data. - OpRegImm(kOpAdd, reg_ptr, data_offset); - // At this point, reg_ptr points to array, 2 live temps. - if ((size == k64) || (size == kDouble)) { - // TUNING: specific wide routine that can handle fp regs. - if (scale) { - RegStorage r_new_index = AllocTemp(); - OpRegRegImm(kOpLsl, r_new_index, rl_index.reg, scale); - OpRegReg(kOpAdd, reg_ptr, r_new_index); - FreeTemp(r_new_index); - } else { - OpRegReg(kOpAdd, reg_ptr, rl_index.reg); - } - rl_src = LoadValueWide(rl_src, reg_class); - - if (needs_range_check) { - GenArrayBoundsCheck(rl_index.reg, reg_len); - FreeTemp(reg_len); - } - - StoreBaseDisp(reg_ptr, 0, rl_src.reg, size, kNotVolatile); - } else { - rl_src = LoadValue(rl_src, reg_class); - if (needs_range_check) { - GenArrayBoundsCheck(rl_index.reg, reg_len); - FreeTemp(reg_len); - } - StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size); - } - if (allocated_reg_ptr_temp) { - FreeTemp(reg_ptr); - } - if (card_mark) { - MarkGCCard(opt_flags, rl_src.reg, rl_array.reg); - } -} - -void Mips64Mir2Lir::GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_shift) { - OpKind op = kOpBkpt; - switch (opcode) { - case Instruction::SHL_LONG: - case Instruction::SHL_LONG_2ADDR: - op = kOpLsl; - break; - case Instruction::SHR_LONG: - case Instruction::SHR_LONG_2ADDR: - op = kOpAsr; - break; - case Instruction::USHR_LONG: - case Instruction::USHR_LONG_2ADDR: - op = kOpLsr; - break; - default: - LOG(FATAL) << "Unexpected case: " << opcode; - } - rl_shift = LoadValue(rl_shift, kCoreReg); - rl_src1 = LoadValueWide(rl_src1, kCoreReg); - RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); - OpRegRegReg(op, rl_result.reg, rl_src1.reg, As64BitReg(rl_shift.reg)); - StoreValueWide(rl_dest, rl_result); -} - -void Mips64Mir2Lir::GenShiftImmOpLong(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_shift, int flags) { - UNUSED(flags); - OpKind op = kOpBkpt; - // Per spec, we only care about low 6 bits of shift amount. - int shift_amount = mir_graph_->ConstantValue(rl_shift) & 0x3f; - rl_src1 = LoadValueWide(rl_src1, kCoreReg); - if (shift_amount == 0) { - StoreValueWide(rl_dest, rl_src1); - return; - } - - RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); - switch (opcode) { - case Instruction::SHL_LONG: - case Instruction::SHL_LONG_2ADDR: - op = kOpLsl; - break; - case Instruction::SHR_LONG: - case Instruction::SHR_LONG_2ADDR: - op = kOpAsr; - break; - case Instruction::USHR_LONG: - case Instruction::USHR_LONG_2ADDR: - op = kOpLsr; - break; - default: - LOG(FATAL) << "Unexpected case"; - } - OpRegRegImm(op, rl_result.reg, rl_src1.reg, shift_amount); - StoreValueWide(rl_dest, rl_result); -} - -void Mips64Mir2Lir::GenArithImmOpLong(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_src2, int flags) { - // Default - bail to non-const handler. - GenArithOpLong(opcode, rl_dest, rl_src1, rl_src2, flags); -} - -void Mips64Mir2Lir::GenIntToLong(RegLocation rl_dest, RegLocation rl_src) { - rl_src = LoadValue(rl_src, kCoreReg); - RegLocation rl_result = EvalLocWide(rl_dest, kCoreReg, true); - NewLIR3(kMips64Sll, rl_result.reg.GetReg(), As64BitReg(rl_src.reg).GetReg(), 0); - StoreValueWide(rl_dest, rl_result); -} - -void Mips64Mir2Lir::GenConversionCall(QuickEntrypointEnum trampoline, RegLocation rl_dest, - RegLocation rl_src, RegisterClass reg_class) { - FlushAllRegs(); // Send everything to home location. - CallRuntimeHelperRegLocation(trampoline, rl_src, false); - if (rl_dest.wide) { - RegLocation rl_result; - rl_result = GetReturnWide(reg_class); - StoreValueWide(rl_dest, rl_result); - } else { - RegLocation rl_result; - rl_result = GetReturn(reg_class); - StoreValue(rl_dest, rl_result); - } -} - -} // namespace art diff --git a/compiler/dex/quick/mips64/mips64_lir.h b/compiler/dex/quick/mips64/mips64_lir.h deleted file mode 100644 index 4a5c5ce3c8..0000000000 --- a/compiler/dex/quick/mips64/mips64_lir.h +++ /dev/null @@ -1,648 +0,0 @@ -/* - * Copyright (C) 2015 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_DEX_QUICK_MIPS64_MIPS64_LIR_H_ -#define ART_COMPILER_DEX_QUICK_MIPS64_MIPS64_LIR_H_ - -#include "dex/reg_location.h" -#include "dex/reg_storage.h" - -namespace art { - -/* - * Runtime register conventions. - * - * zero is always the value 0 - * at is scratch (normally used as temp reg by assembler) - * v0, v1 are scratch (normally hold subroutine return values) - * a0-a7 are scratch (normally hold subroutine arguments) - * t0-t3, t8 are scratch - * t9 is scratch (normally used for function calls) - * s0 (rMIPS_SUSPEND) is reserved [holds suspend-check counter] - * s1 (rMIPS_SELF) is reserved [holds current &Thread] - * s2-s7 are callee save (promotion target) - * k0, k1 are reserved for use by interrupt handlers - * gp is reserved for global pointer - * sp is reserved - * s8 is callee save (promotion target) - * ra is scratch (normally holds the return addr) - * - * Preserved across C calls: s0-s8 - * Trashed across C calls: at, v0-v1, a0-a7, t0-t3, t8-t9, gp, ra - * - * Floating pointer registers - * NOTE: there are 32 fp registers. - * f0-f31 - * - * f0-f31 trashed across C calls - * - * For mips64 code use: - * a0-a7 to hold operands - * v0-v1 to hold results - * t0-t3, t8-t9 for temps - * - * All jump/branch instructions have a delay slot after it. - * - * Stack frame diagram (stack grows down, higher addresses at top): - * - * +------------------------+ - * | IN[ins-1] | {Note: resides in caller's frame} - * | . | - * | IN[0] | - * | caller's Method* | - * +========================+ {Note: start of callee's frame} - * | spill region | {variable sized - will include lr if non-leaf.} - * +------------------------+ - * | ...filler word... | {Note: used as 2nd word of V[locals-1] if long] - * +------------------------+ - * | V[locals-1] | - * | V[locals-2] | - * | . | - * | . | - * | V[1] | - * | V[0] | - * +------------------------+ - * | 0 to 3 words padding | - * +------------------------+ - * | OUT[outs-1] | - * | OUT[outs-2] | - * | . | - * | OUT[0] | - * | cur_method* | <<== sp w/ 16-byte alignment - * +========================+ - */ - - -#define rARG0 rA0d -#define rs_rARG0 rs_rA0d -#define rARG1 rA1d -#define rs_rARG1 rs_rA1d -#define rARG2 rA2d -#define rs_rARG2 rs_rA2d -#define rARG3 rA3d -#define rs_rARG3 rs_rA3d -#define rARG4 rA4d -#define rs_rARG4 rs_rA4d -#define rARG5 rA5d -#define rs_rARG5 rs_rA5d -#define rARG6 rA6d -#define rs_rARG6 rs_rA6d -#define rARG7 rA7d -#define rs_rARG7 rs_rA7d -#define rRESULT0 rV0d -#define rs_rRESULT0 rs_rV0d -#define rRESULT1 rV1d -#define rs_rRESULT1 rs_rV1d - -#define rFARG0 rF12 -#define rs_rFARG0 rs_rF12 -#define rFARG1 rF13 -#define rs_rFARG1 rs_rF13 -#define rFARG2 rF14 -#define rs_rFARG2 rs_rF14 -#define rFARG3 rF15 -#define rs_rFARG3 rs_rF15 -#define rFARG4 rF16 -#define rs_rFARG4 rs_rF16 -#define rFARG5 rF17 -#define rs_rFARG5 rs_rF17 -#define rFARG6 rF18 -#define rs_rFARG6 rs_rF18 -#define rFARG7 rF19 -#define rs_rFARG7 rs_rF19 -#define rFRESULT0 rF0 -#define rs_rFRESULT0 rs_rF0 -#define rFRESULT1 rF1 -#define rs_rFRESULT1 rs_rF1 - -// Regs not used for Mips64. -#define rMIPS64_LR RegStorage::kInvalidRegVal -#define rMIPS64_PC RegStorage::kInvalidRegVal - -enum Mips64ResourceEncodingPos { - kMips64GPReg0 = 0, - kMips64RegSP = 29, - kMips64RegLR = 31, - kMips64FPReg0 = 32, - kMips64FPRegEnd = 64, - kMips64RegPC = kMips64FPRegEnd, - kMips64RegEnd = 65, -}; - -enum Mips64NativeRegisterPool { // private marker to avoid generate-operator-out.py from processing. - rZERO = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 0, - rZEROd = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 0, - rAT = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 1, - rATd = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 1, - rV0 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 2, - rV0d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 2, - rV1 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 3, - rV1d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 3, - rA0 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 4, - rA0d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 4, - rA1 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 5, - rA1d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 5, - rA2 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 6, - rA2d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 6, - rA3 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 7, - rA3d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 7, - rA4 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 8, - rA4d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 8, - rA5 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 9, - rA5d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 9, - rA6 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 10, - rA6d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 10, - rA7 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 11, - rA7d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 11, - rT0 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 12, - rT0d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 12, - rT1 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 13, - rT1d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 13, - rT2 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 14, - rT2d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 14, - rT3 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 15, - rT3d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 15, - rS0 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 16, - rS0d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 16, - rS1 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 17, - rS1d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 17, - rS2 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 18, - rS2d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 18, - rS3 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 19, - rS3d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 19, - rS4 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 20, - rS4d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 20, - rS5 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 21, - rS5d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 21, - rS6 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 22, - rS6d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 22, - rS7 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 23, - rS7d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 23, - rT8 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 24, - rT8d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 24, - rT9 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 25, - rT9d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 25, - rK0 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 26, - rK0d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 26, - rK1 = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 27, - rK1d = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 27, - rGP = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 28, - rGPd = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 28, - rSP = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 29, - rSPd = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 29, - rFP = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 30, - rFPd = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 30, - rRA = RegStorage::k32BitSolo | RegStorage::kCoreRegister | 31, - rRAd = RegStorage::k64BitSolo | RegStorage::kCoreRegister | 31, - - rF0 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 0, - rF1 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 1, - rF2 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 2, - rF3 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 3, - rF4 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 4, - rF5 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 5, - rF6 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 6, - rF7 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 7, - rF8 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 8, - rF9 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 9, - rF10 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 10, - rF11 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 11, - rF12 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 12, - rF13 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 13, - rF14 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 14, - rF15 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 15, - rF16 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 16, - rF17 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 17, - rF18 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 18, - rF19 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 19, - rF20 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 20, - rF21 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 21, - rF22 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 22, - rF23 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 23, - rF24 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 24, - rF25 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 25, - rF26 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 26, - rF27 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 27, - rF28 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 28, - rF29 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 29, - rF30 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 30, - rF31 = RegStorage::k32BitSolo | RegStorage::kFloatingPoint | 31, - - rD0 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 0, - rD1 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 1, - rD2 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 2, - rD3 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 3, - rD4 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 4, - rD5 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 5, - rD6 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 6, - rD7 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 7, - rD8 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 8, - rD9 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 9, - rD10 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 10, - rD11 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 11, - rD12 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 12, - rD13 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 13, - rD14 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 14, - rD15 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 15, - rD16 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 16, - rD17 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 17, - rD18 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 18, - rD19 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 19, - rD20 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 20, - rD21 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 21, - rD22 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 22, - rD23 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 23, - rD24 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 24, - rD25 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 25, - rD26 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 26, - rD27 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 27, - rD28 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 28, - rD29 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 29, - rD30 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 30, - rD31 = RegStorage::k64BitSolo | RegStorage::kFloatingPoint | 31, -}; - -constexpr RegStorage rs_rZERO(RegStorage::kValid | rZERO); -constexpr RegStorage rs_rZEROd(RegStorage::kValid | rZEROd); -constexpr RegStorage rs_rAT(RegStorage::kValid | rAT); -constexpr RegStorage rs_rATd(RegStorage::kValid | rATd); -constexpr RegStorage rs_rV0(RegStorage::kValid | rV0); -constexpr RegStorage rs_rV0d(RegStorage::kValid | rV0d); -constexpr RegStorage rs_rV1(RegStorage::kValid | rV1); -constexpr RegStorage rs_rV1d(RegStorage::kValid | rV1d); -constexpr RegStorage rs_rA0(RegStorage::kValid | rA0); -constexpr RegStorage rs_rA0d(RegStorage::kValid | rA0d); -constexpr RegStorage rs_rA1(RegStorage::kValid | rA1); -constexpr RegStorage rs_rA1d(RegStorage::kValid | rA1d); -constexpr RegStorage rs_rA2(RegStorage::kValid | rA2); -constexpr RegStorage rs_rA2d(RegStorage::kValid | rA2d); -constexpr RegStorage rs_rA3(RegStorage::kValid | rA3); -constexpr RegStorage rs_rA3d(RegStorage::kValid | rA3d); -constexpr RegStorage rs_rA4(RegStorage::kValid | rA4); -constexpr RegStorage rs_rA4d(RegStorage::kValid | rA4d); -constexpr RegStorage rs_rA5(RegStorage::kValid | rA5); -constexpr RegStorage rs_rA5d(RegStorage::kValid | rA5d); -constexpr RegStorage rs_rA6(RegStorage::kValid | rA6); -constexpr RegStorage rs_rA6d(RegStorage::kValid | rA6d); -constexpr RegStorage rs_rA7(RegStorage::kValid | rA7); -constexpr RegStorage rs_rA7d(RegStorage::kValid | rA7d); -constexpr RegStorage rs_rT0(RegStorage::kValid | rT0); -constexpr RegStorage rs_rT0d(RegStorage::kValid | rT0d); -constexpr RegStorage rs_rT1(RegStorage::kValid | rT1); -constexpr RegStorage rs_rT1d(RegStorage::kValid | rT1d); -constexpr RegStorage rs_rT2(RegStorage::kValid | rT2); -constexpr RegStorage rs_rT2d(RegStorage::kValid | rT2d); -constexpr RegStorage rs_rT3(RegStorage::kValid | rT3); -constexpr RegStorage rs_rT3d(RegStorage::kValid | rT3d); -constexpr RegStorage rs_rS0(RegStorage::kValid | rS0); -constexpr RegStorage rs_rS0d(RegStorage::kValid | rS0d); -constexpr RegStorage rs_rS1(RegStorage::kValid | rS1); -constexpr RegStorage rs_rS1d(RegStorage::kValid | rS1d); -constexpr RegStorage rs_rS2(RegStorage::kValid | rS2); -constexpr RegStorage rs_rS2d(RegStorage::kValid | rS2d); -constexpr RegStorage rs_rS3(RegStorage::kValid | rS3); -constexpr RegStorage rs_rS3d(RegStorage::kValid | rS3d); -constexpr RegStorage rs_rS4(RegStorage::kValid | rS4); -constexpr RegStorage rs_rS4d(RegStorage::kValid | rS4d); -constexpr RegStorage rs_rS5(RegStorage::kValid | rS5); -constexpr RegStorage rs_rS5d(RegStorage::kValid | rS5d); -constexpr RegStorage rs_rS6(RegStorage::kValid | rS6); -constexpr RegStorage rs_rS6d(RegStorage::kValid | rS6d); -constexpr RegStorage rs_rS7(RegStorage::kValid | rS7); -constexpr RegStorage rs_rS7d(RegStorage::kValid | rS7d); -constexpr RegStorage rs_rT8(RegStorage::kValid | rT8); -constexpr RegStorage rs_rT8d(RegStorage::kValid | rT8d); -constexpr RegStorage rs_rT9(RegStorage::kValid | rT9); -constexpr RegStorage rs_rT9d(RegStorage::kValid | rT9d); -constexpr RegStorage rs_rK0(RegStorage::kValid | rK0); -constexpr RegStorage rs_rK0d(RegStorage::kValid | rK0d); -constexpr RegStorage rs_rK1(RegStorage::kValid | rK1); -constexpr RegStorage rs_rK1d(RegStorage::kValid | rK1d); -constexpr RegStorage rs_rGP(RegStorage::kValid | rGP); -constexpr RegStorage rs_rGPd(RegStorage::kValid | rGPd); -constexpr RegStorage rs_rSP(RegStorage::kValid | rSP); -constexpr RegStorage rs_rSPd(RegStorage::kValid | rSPd); -constexpr RegStorage rs_rFP(RegStorage::kValid | rFP); -constexpr RegStorage rs_rFPd(RegStorage::kValid | rFPd); -constexpr RegStorage rs_rRA(RegStorage::kValid | rRA); -constexpr RegStorage rs_rRAd(RegStorage::kValid | rRAd); - -constexpr RegStorage rs_rMIPS64_LR(RegStorage::kInvalid); // Not used for MIPS64. -constexpr RegStorage rs_rMIPS64_PC(RegStorage::kInvalid); // Not used for MIPS64. -constexpr RegStorage rs_rMIPS64_COUNT(RegStorage::kInvalid); // Not used for MIPS64. - -constexpr RegStorage rs_rF0(RegStorage::kValid | rF0); -constexpr RegStorage rs_rF1(RegStorage::kValid | rF1); -constexpr RegStorage rs_rF2(RegStorage::kValid | rF2); -constexpr RegStorage rs_rF3(RegStorage::kValid | rF3); -constexpr RegStorage rs_rF4(RegStorage::kValid | rF4); -constexpr RegStorage rs_rF5(RegStorage::kValid | rF5); -constexpr RegStorage rs_rF6(RegStorage::kValid | rF6); -constexpr RegStorage rs_rF7(RegStorage::kValid | rF7); -constexpr RegStorage rs_rF8(RegStorage::kValid | rF8); -constexpr RegStorage rs_rF9(RegStorage::kValid | rF9); -constexpr RegStorage rs_rF10(RegStorage::kValid | rF10); -constexpr RegStorage rs_rF11(RegStorage::kValid | rF11); -constexpr RegStorage rs_rF12(RegStorage::kValid | rF12); -constexpr RegStorage rs_rF13(RegStorage::kValid | rF13); -constexpr RegStorage rs_rF14(RegStorage::kValid | rF14); -constexpr RegStorage rs_rF15(RegStorage::kValid | rF15); -constexpr RegStorage rs_rF16(RegStorage::kValid | rF16); -constexpr RegStorage rs_rF17(RegStorage::kValid | rF17); -constexpr RegStorage rs_rF18(RegStorage::kValid | rF18); -constexpr RegStorage rs_rF19(RegStorage::kValid | rF19); -constexpr RegStorage rs_rF20(RegStorage::kValid | rF20); -constexpr RegStorage rs_rF21(RegStorage::kValid | rF21); -constexpr RegStorage rs_rF22(RegStorage::kValid | rF22); -constexpr RegStorage rs_rF23(RegStorage::kValid | rF23); -constexpr RegStorage rs_rF24(RegStorage::kValid | rF24); -constexpr RegStorage rs_rF25(RegStorage::kValid | rF25); -constexpr RegStorage rs_rF26(RegStorage::kValid | rF26); -constexpr RegStorage rs_rF27(RegStorage::kValid | rF27); -constexpr RegStorage rs_rF28(RegStorage::kValid | rF28); -constexpr RegStorage rs_rF29(RegStorage::kValid | rF29); -constexpr RegStorage rs_rF30(RegStorage::kValid | rF30); -constexpr RegStorage rs_rF31(RegStorage::kValid | rF31); - -constexpr RegStorage rs_rD0(RegStorage::kValid | rD0); -constexpr RegStorage rs_rD1(RegStorage::kValid | rD1); -constexpr RegStorage rs_rD2(RegStorage::kValid | rD2); -constexpr RegStorage rs_rD3(RegStorage::kValid | rD3); -constexpr RegStorage rs_rD4(RegStorage::kValid | rD4); -constexpr RegStorage rs_rD5(RegStorage::kValid | rD5); -constexpr RegStorage rs_rD6(RegStorage::kValid | rD6); -constexpr RegStorage rs_rD7(RegStorage::kValid | rD7); -constexpr RegStorage rs_rD8(RegStorage::kValid | rD8); -constexpr RegStorage rs_rD9(RegStorage::kValid | rD9); -constexpr RegStorage rs_rD10(RegStorage::kValid | rD10); -constexpr RegStorage rs_rD11(RegStorage::kValid | rD11); -constexpr RegStorage rs_rD12(RegStorage::kValid | rD12); -constexpr RegStorage rs_rD13(RegStorage::kValid | rD13); -constexpr RegStorage rs_rD14(RegStorage::kValid | rD14); -constexpr RegStorage rs_rD15(RegStorage::kValid | rD15); -constexpr RegStorage rs_rD16(RegStorage::kValid | rD16); -constexpr RegStorage rs_rD17(RegStorage::kValid | rD17); -constexpr RegStorage rs_rD18(RegStorage::kValid | rD18); -constexpr RegStorage rs_rD19(RegStorage::kValid | rD19); -constexpr RegStorage rs_rD20(RegStorage::kValid | rD20); -constexpr RegStorage rs_rD21(RegStorage::kValid | rD21); -constexpr RegStorage rs_rD22(RegStorage::kValid | rD22); -constexpr RegStorage rs_rD23(RegStorage::kValid | rD23); -constexpr RegStorage rs_rD24(RegStorage::kValid | rD24); -constexpr RegStorage rs_rD25(RegStorage::kValid | rD25); -constexpr RegStorage rs_rD26(RegStorage::kValid | rD26); -constexpr RegStorage rs_rD27(RegStorage::kValid | rD27); -constexpr RegStorage rs_rD28(RegStorage::kValid | rD28); -constexpr RegStorage rs_rD29(RegStorage::kValid | rD29); -constexpr RegStorage rs_rD30(RegStorage::kValid | rD30); -constexpr RegStorage rs_rD31(RegStorage::kValid | rD31); - -// TODO: reduce/eliminate use of these. -#define rMIPS64_SUSPEND rS0d -#define rs_rMIPS64_SUSPEND rs_rS0d -#define rMIPS64_SELF rS1d -#define rs_rMIPS64_SELF rs_rS1d -#define rMIPS64_SP rSPd -#define rs_rMIPS64_SP rs_rSPd -#define rMIPS64_ARG0 rARG0 -#define rs_rMIPS64_ARG0 rs_rARG0 -#define rMIPS64_ARG1 rARG1 -#define rs_rMIPS64_ARG1 rs_rARG1 -#define rMIPS64_ARG2 rARG2 -#define rs_rMIPS64_ARG2 rs_rARG2 -#define rMIPS64_ARG3 rARG3 -#define rs_rMIPS64_ARG3 rs_rARG3 -#define rMIPS64_ARG4 rARG4 -#define rs_rMIPS64_ARG4 rs_rARG4 -#define rMIPS64_ARG5 rARG5 -#define rs_rMIPS64_ARG5 rs_rARG5 -#define rMIPS64_ARG6 rARG6 -#define rs_rMIPS64_ARG6 rs_rARG6 -#define rMIPS64_ARG7 rARG7 -#define rs_rMIPS64_ARG7 rs_rARG7 -#define rMIPS64_FARG0 rFARG0 -#define rs_rMIPS64_FARG0 rs_rFARG0 -#define rMIPS64_FARG1 rFARG1 -#define rs_rMIPS64_FARG1 rs_rFARG1 -#define rMIPS64_FARG2 rFARG2 -#define rs_rMIPS64_FARG2 rs_rFARG2 -#define rMIPS64_FARG3 rFARG3 -#define rs_rMIPS64_FARG3 rs_rFARG3 -#define rMIPS64_FARG4 rFARG4 -#define rs_rMIPS64_FARG4 rs_rFARG4 -#define rMIPS64_FARG5 rFARG5 -#define rs_rMIPS64_FARG5 rs_rFARG5 -#define rMIPS64_FARG6 rFARG6 -#define rs_rMIPS64_FARG6 rs_rFARG6 -#define rMIPS64_FARG7 rFARG7 -#define rs_rMIPS64_FARG7 rs_rFARG7 -#define rMIPS64_RET0 rRESULT0 -#define rs_rMIPS64_RET0 rs_rRESULT0 -#define rMIPS64_RET1 rRESULT1 -#define rs_rMIPS64_RET1 rs_rRESULT1 -#define rMIPS64_INVOKE_TGT rT9d -#define rs_rMIPS64_INVOKE_TGT rs_rT9d -#define rMIPS64_COUNT RegStorage::kInvalidRegVal - -// RegisterLocation templates return values (r_V0). -const RegLocation mips64_loc_c_return - {kLocPhysReg, 0, 0, 0, 0, 0, 0, 0, 1, - RegStorage(RegStorage::k32BitSolo, rV0), INVALID_SREG, INVALID_SREG}; -const RegLocation mips64_loc_c_return_ref - {kLocPhysReg, 0, 0, 0, 0, 0, 1, 0, 1, - RegStorage(RegStorage::k64BitSolo, rV0d), INVALID_SREG, INVALID_SREG}; -const RegLocation mips64_loc_c_return_wide - {kLocPhysReg, 1, 0, 0, 0, 0, 0, 0, 1, - RegStorage(RegStorage::k64BitSolo, rV0d), INVALID_SREG, INVALID_SREG}; -const RegLocation mips64_loc_c_return_float - {kLocPhysReg, 0, 0, 0, 1, 0, 0, 0, 1, - RegStorage(RegStorage::k32BitSolo, rF0), INVALID_SREG, INVALID_SREG}; -const RegLocation mips64_loc_c_return_double - {kLocPhysReg, 1, 0, 0, 1, 0, 0, 0, 1, - RegStorage(RegStorage::k64BitSolo, rD0), INVALID_SREG, INVALID_SREG}; - -enum Mips64ShiftEncodings { - kMips64Lsl = 0x0, - kMips64Lsr = 0x1, - kMips64Asr = 0x2, - kMips64Ror = 0x3 -}; - -// MIPS64 sync kinds (Note: support for kinds other than kSYNC0 may not exist). -#define kSYNC0 0x00 -#define kSYNC_WMB 0x04 -#define kSYNC_MB 0x01 -#define kSYNC_ACQUIRE 0x11 -#define kSYNC_RELEASE 0x12 -#define kSYNC_RMB 0x13 - -// TODO: Use smaller hammer when appropriate for target CPU. -#define kST kSYNC0 -#define kSY kSYNC0 - -/* - * The following enum defines the list of supported Mips64 instructions by the - * assembler. Their corresponding EncodingMap positions will be defined in - * assemble_mips64.cc. - */ -enum Mips64OpCode { - kMips64First = 0, - kMips6432BitData = kMips64First, // data [31..0]. - kMips64Addiu, // addiu t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0]. - kMips64Addu, // add d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100001]. - kMips64And, // and d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100100]. - kMips64Andi, // andi t,s,imm16 [001100] s[25..21] t[20..16] imm16[15..0]. - kMips64B, // b o [0001000000000000] o[15..0]. - kMips64Bal, // bal o [0000010000010001] o[15..0]. - // NOTE: the code tests the range kMips64Beq thru kMips64Bne, so adding an instruction in this - // range may require updates. - kMips64Beq, // beq s,t,o [000100] s[25..21] t[20..16] o[15..0]. - kMips64Beqz, // beqz s,o [000100] s[25..21] [00000] o[15..0]. - kMips64Bgez, // bgez s,o [000001] s[25..21] [00001] o[15..0]. - kMips64Bgtz, // bgtz s,o [000111] s[25..21] [00000] o[15..0]. - kMips64Blez, // blez s,o [000110] s[25..21] [00000] o[15..0]. - kMips64Bltz, // bltz s,o [000001] s[25..21] [00000] o[15..0]. - kMips64Bnez, // bnez s,o [000101] s[25..21] [00000] o[15..0]. - kMips64Bne, // bne s,t,o [000101] s[25..21] t[20..16] o[15..0]. - kMips64Break, // break code [000000] code[25..6] [001101]. - kMips64Daddiu, // daddiu t,s,imm16 [011001] s[25..21] t[20..16] imm16[15..11]. - kMips64Daddu, // daddu d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000101101]. - kMips64Dahi, // dahi s,imm16 [000001] s[25..21] [00110] imm16[15..11]. - kMips64Dati, // dati s,imm16 [000001] s[25..21] [11110] imm16[15..11]. - kMips64Daui, // daui t,s,imm16 [011101] s[25..21] t[20..16] imm16[15..11]. - kMips64Ddiv, // ddiv d,s,t [000000] s[25..21] t[20..16] d[15..11] [00010011110]. - kMips64Div, // div d,s,t [000000] s[25..21] t[20..16] d[15..11] [00010011010]. - kMips64Dmod, // dmod d,s,t [000000] s[25..21] t[20..16] d[15..11] [00011011110]. - kMips64Dmul, // dmul d,s,t [000000] s[25..21] t[20..16] d[15..11] [00010011100]. - kMips64Dmfc1, // dmfc1 t,s [01000100001] t[20..16] s[15..11] [00000000000]. - kMips64Dmtc1, // dmtc1 t,s [01000100101] t[20..16] s[15..11] [00000000000]. - kMips64Drotr32, // drotr32 d,t,a [00000000001] t[20..16] d[15..11] a[10..6] [111110]. - kMips64Dsll, // dsll d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [111000]. - kMips64Dsll32, // dsll32 d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [111100]. - kMips64Dsrl, // dsrl d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [111010]. - kMips64Dsrl32, // dsrl32 d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [111110]. - kMips64Dsra, // dsra d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [111011]. - kMips64Dsra32, // dsra32 d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [111111]. - kMips64Dsllv, // dsllv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000010100]. - kMips64Dsrlv, // dsrlv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000010110]. - kMips64Dsrav, // dsrav d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000010111]. - kMips64Dsubu, // dsubu d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000101111]. - kMips64Ext, // ext t,s,p,z [011111] s[25..21] t[20..16] z[15..11] p[10..6] [000000]. - kMips64Faddd, // add.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000000]. - kMips64Fadds, // add.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000000]. - kMips64Fdivd, // div.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000011]. - kMips64Fdivs, // div.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000011]. - kMips64Fmuld, // mul.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000010]. - kMips64Fmuls, // mul.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000010]. - kMips64Fsubd, // sub.d d,s,t [01000110001] t[20..16] s[15..11] d[10..6] [000001]. - kMips64Fsubs, // sub.s d,s,t [01000110000] t[20..16] s[15..11] d[10..6] [000001]. - kMips64Fcvtsd, // cvt.s.d d,s [01000110001] [00000] s[15..11] d[10..6] [100000]. - kMips64Fcvtsw, // cvt.s.w d,s [01000110100] [00000] s[15..11] d[10..6] [100000]. - kMips64Fcvtds, // cvt.d.s d,s [01000110000] [00000] s[15..11] d[10..6] [100001]. - kMips64Fcvtdw, // cvt.d.w d,s [01000110100] [00000] s[15..11] d[10..6] [100001]. - kMips64Fcvtws, // cvt.w.d d,s [01000110000] [00000] s[15..11] d[10..6] [100100]. - kMips64Fcvtwd, // cvt.w.d d,s [01000110001] [00000] s[15..11] d[10..6] [100100]. - kMips64Fmovd, // mov.d d,s [01000110001] [00000] s[15..11] d[10..6] [000110]. - kMips64Fmovs, // mov.s d,s [01000110000] [00000] s[15..11] d[10..6] [000110]. - kMips64Fnegd, // neg.d d,s [01000110001] [00000] s[15..11] d[10..6] [000111]. - kMips64Fnegs, // neg.s d,s [01000110000] [00000] s[15..11] d[10..6] [000111]. - kMips64Fldc1, // ldc1 t,o(b) [110101] b[25..21] t[20..16] o[15..0]. - kMips64Flwc1, // lwc1 t,o(b) [110001] b[25..21] t[20..16] o[15..0]. - kMips64Fsdc1, // sdc1 t,o(b) [111101] b[25..21] t[20..16] o[15..0]. - kMips64Fswc1, // swc1 t,o(b) [111001] b[25..21] t[20..16] o[15..0]. - kMips64Jal, // jal t [000011] t[25..0]. - kMips64Jalr, // jalr d,s [000000] s[25..21] [00000] d[15..11] hint[10..6] [001001]. - kMips64Lahi, // lui t,imm16 [00111100000] t[20..16] imm16[15..0] load addr hi. - kMips64Lalo, // ori t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0] load addr lo. - kMips64Lb, // lb t,o(b) [100000] b[25..21] t[20..16] o[15..0]. - kMips64Lbu, // lbu t,o(b) [100100] b[25..21] t[20..16] o[15..0]. - kMips64Ld, // ld t,o(b) [110111] b[25..21] t[20..16] o[15..0]. - kMips64Lh, // lh t,o(b) [100001] b[25..21] t[20..16] o[15..0]. - kMips64Lhu, // lhu t,o(b) [100101] b[25..21] t[20..16] o[15..0]. - kMips64Lui, // lui t,imm16 [00111100000] t[20..16] imm16[15..0]. - kMips64Lw, // lw t,o(b) [100011] b[25..21] t[20..16] o[15..0]. - kMips64Lwu, // lwu t,o(b) [100111] b[25..21] t[20..16] o[15..0]. - kMips64Mfc1, // mfc1 t,s [01000100000] t[20..16] s[15..11] [00000000000]. - kMips64Mtc1, // mtc1 t,s [01000100100] t[20..16] s[15..11] [00000000000]. - kMips64Move, // move d,s [000000] s[25..21] [00000] d[15..11] [00000101101]. - kMips64Mod, // mod d,s,t [000000] s[25..21] t[20..16] d[15..11] [00011011010]. - kMips64Mul, // mul d,s,t [000000] s[25..21] t[20..16] d[15..11] [00010011000]. - kMips64Nop, // nop [00000000000000000000000000000000]. - kMips64Nor, // nor d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100111]. - kMips64Or, // or d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100101]. - kMips64Ori, // ori t,s,imm16 [001001] s[25..21] t[20..16] imm16[15..0]. - kMips64Sb, // sb t,o(b) [101000] b[25..21] t[20..16] o[15..0]. - kMips64Sd, // sd t,o(b) [111111] b[25..21] t[20..16] o[15..0]. - kMips64Seb, // seb d,t [01111100000] t[20..16] d[15..11] [10000100000]. - kMips64Seh, // seh d,t [01111100000] t[20..16] d[15..11] [11000100000]. - kMips64Sh, // sh t,o(b) [101001] b[25..21] t[20..16] o[15..0]. - kMips64Sll, // sll d,t,a [00000000000] t[20..16] d[15..11] a[10..6] [000000]. - kMips64Sllv, // sllv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000100]. - kMips64Slt, // slt d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000101010]. - kMips64Slti, // slti t,s,imm16 [001010] s[25..21] t[20..16] imm16[15..0]. - kMips64Sltu, // sltu d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000101011]. - kMips64Sra, // sra d,s,imm5 [00000000000] t[20..16] d[15..11] imm5[10..6] [000011]. - kMips64Srav, // srav d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000111]. - kMips64Srl, // srl d,t,a [00000000000] t[20..16] d[20..16] a[10..6] [000010]. - kMips64Srlv, // srlv d,t,s [000000] s[25..21] t[20..16] d[15..11] [00000000110]. - kMips64Subu, // subu d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100011]. - kMips64Sw, // sw t,o(b) [101011] b[25..21] t[20..16] o[15..0]. - kMips64Sync, // sync kind [000000] [0000000000000000] s[10..6] [001111]. - kMips64Xor, // xor d,s,t [000000] s[25..21] t[20..16] d[15..11] [00000100110]. - kMips64Xori, // xori t,s,imm16 [001110] s[25..21] t[20..16] imm16[15..0]. - kMips64CurrPC, // jal to .+8 to materialize pc. - kMips64Delta, // Psuedo for ori t, s, <label>-<label>. - kMips64DeltaHi, // Pseudo for lui t, high16(<label>-<label>). - kMips64DeltaLo, // Pseudo for ori t, s, low16(<label>-<label>). - kMips64Undefined, // undefined [011001xxxxxxxxxxxxxxxx]. - kMips64Last -}; -std::ostream& operator<<(std::ostream& os, const Mips64OpCode& rhs); - -// Instruction assembly field_loc kind. -enum Mips64EncodingKind { - kFmtUnused, - kFmtBitBlt, // Bit string using end/start. - kFmtDfp, // Double FP reg. - kFmtSfp, // Single FP reg. - kFmtBlt5_2, // Same 5-bit field to 2 locations. -}; -std::ostream& operator<<(std::ostream& os, const Mips64EncodingKind& rhs); - -// Struct used to define the snippet positions for each MIPS64 opcode. -struct Mips64EncodingMap { - uint32_t skeleton; - struct { - Mips64EncodingKind kind; - int end; // end for kFmtBitBlt, 1-bit slice end for FP regs. - int start; // start for kFmtBitBlt, 4-bit slice end for FP regs. - } field_loc[4]; - Mips64OpCode opcode; - uint64_t flags; - const char *name; - const char* fmt; - int size; // Note: size is in bytes. -}; - -extern Mips64EncodingMap EncodingMap[kMips64Last]; - -#define IS_UIMM16(v) ((0 <= (v)) && ((v) <= 65535)) -#define IS_SIMM16(v) ((-32768 <= (v)) && ((v) <= 32766)) -#define IS_SIMM16_2WORD(v) ((-32764 <= (v)) && ((v) <= 32763)) // 2 offsets must fit. - -} // namespace art - -#endif // ART_COMPILER_DEX_QUICK_MIPS64_MIPS64_LIR_H_ diff --git a/compiler/dex/quick/mips64/target_mips64.cc b/compiler/dex/quick/mips64/target_mips64.cc deleted file mode 100644 index 6ed9617bde..0000000000 --- a/compiler/dex/quick/mips64/target_mips64.cc +++ /dev/null @@ -1,653 +0,0 @@ -/* - * Copyright (C) 2015 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 "codegen_mips64.h" - -#include <inttypes.h> - -#include <string> - -#include "arch/mips64/instruction_set_features_mips64.h" -#include "backend_mips64.h" -#include "base/logging.h" -#include "dex/compiler_ir.h" -#include "dex/quick/mir_to_lir-inl.h" -#include "driver/compiler_driver.h" -#include "mips64_lir.h" - -namespace art { - -static constexpr RegStorage core_regs_arr32[] = - {rs_rZERO, rs_rAT, rs_rV0, rs_rV1, rs_rA0, rs_rA1, rs_rA2, rs_rA3, rs_rA4, rs_rA5, rs_rA6, - rs_rA7, rs_rT0, rs_rT1, rs_rT2, rs_rT3, rs_rS0, rs_rS1, rs_rS2, rs_rS3, rs_rS4, rs_rS5, - rs_rS6, rs_rS7, rs_rT8, rs_rT9, rs_rK0, rs_rK1, rs_rGP, rs_rSP, rs_rFP, rs_rRA}; -static constexpr RegStorage core_regs_arr64[] = - {rs_rZEROd, rs_rATd, rs_rV0d, rs_rV1d, rs_rA0d, rs_rA1d, rs_rA2d, rs_rA3d, rs_rA4d, rs_rA5d, - rs_rA6d, rs_rA7d, rs_rT0d, rs_rT1d, rs_rT2d, rs_rT3d, rs_rS0d, rs_rS1d, rs_rS2d, rs_rS3d, - rs_rS4d, rs_rS5d, rs_rS6d, rs_rS7d, rs_rT8d, rs_rT9d, rs_rK0d, rs_rK1d, rs_rGPd, rs_rSPd, - rs_rFPd, rs_rRAd}; -#if 0 -// TODO: f24-f31 must be saved before calls and restored after. -static constexpr RegStorage sp_regs_arr[] = - {rs_rF0, rs_rF1, rs_rF2, rs_rF3, rs_rF4, rs_rF5, rs_rF6, rs_rF7, rs_rF8, rs_rF9, rs_rF10, - rs_rF11, rs_rF12, rs_rF13, rs_rF14, rs_rF15, rs_rF16, rs_rF17, rs_rF18, rs_rF19, rs_rF20, - rs_rF21, rs_rF22, rs_rF23, rs_rF24, rs_rF25, rs_rF26, rs_rF27, rs_rF28, rs_rF29, rs_rF30, - rs_rF31}; -static constexpr RegStorage dp_regs_arr[] = - {rs_rD0, rs_rD1, rs_rD2, rs_rD3, rs_rD4, rs_rD5, rs_rD6, rs_rD7, rs_rD8, rs_rD9, rs_rD10, - rs_rD11, rs_rD12, rs_rD13, rs_rD14, rs_rD15, rs_rD16, rs_rD17, rs_rD18, rs_rD19, rs_rD20, - rs_rD21, rs_rD22, rs_rD23, rs_rD24, rs_rD25, rs_rD26, rs_rD27, rs_rD28, rs_rD29, rs_rD30, - rs_rD31}; -#else -static constexpr RegStorage sp_regs_arr[] = - {rs_rF0, rs_rF1, rs_rF2, rs_rF3, rs_rF4, rs_rF5, rs_rF6, rs_rF7, rs_rF8, rs_rF9, rs_rF10, - rs_rF11, rs_rF12, rs_rF13, rs_rF14, rs_rF15, rs_rF16, rs_rF17, rs_rF18, rs_rF19, rs_rF20, - rs_rF21, rs_rF22, rs_rF23}; -static constexpr RegStorage dp_regs_arr[] = - {rs_rD0, rs_rD1, rs_rD2, rs_rD3, rs_rD4, rs_rD5, rs_rD6, rs_rD7, rs_rD8, rs_rD9, rs_rD10, - rs_rD11, rs_rD12, rs_rD13, rs_rD14, rs_rD15, rs_rD16, rs_rD17, rs_rD18, rs_rD19, rs_rD20, - rs_rD21, rs_rD22, rs_rD23}; -#endif -static constexpr RegStorage reserved_regs_arr32[] = - {rs_rZERO, rs_rAT, rs_rS0, rs_rS1, rs_rT9, rs_rK0, rs_rK1, rs_rGP, rs_rSP, rs_rRA}; -static constexpr RegStorage reserved_regs_arr64[] = - {rs_rZEROd, rs_rATd, rs_rS0d, rs_rS1d, rs_rT9d, rs_rK0d, rs_rK1d, rs_rGPd, rs_rSPd, rs_rRAd}; -static constexpr RegStorage core_temps_arr32[] = - {rs_rV0, rs_rV1, rs_rA0, rs_rA1, rs_rA2, rs_rA3, rs_rA4, rs_rA5, rs_rA6, rs_rA7, rs_rT0, - rs_rT1, rs_rT2, rs_rT3, rs_rT8}; -static constexpr RegStorage core_temps_arr64[] = - {rs_rV0d, rs_rV1d, rs_rA0d, rs_rA1d, rs_rA2d, rs_rA3d, rs_rA4d, rs_rA5d, rs_rA6d, rs_rA7d, - rs_rT0d, rs_rT1d, rs_rT2d, rs_rT3d, rs_rT8d}; -#if 0 -// TODO: f24-f31 must be saved before calls and restored after. -static constexpr RegStorage sp_temps_arr[] = - {rs_rF0, rs_rF1, rs_rF2, rs_rF3, rs_rF4, rs_rF5, rs_rF6, rs_rF7, rs_rF8, rs_rF9, rs_rF10, - rs_rF11, rs_rF12, rs_rF13, rs_rF14, rs_rF15, rs_rF16, rs_rF17, rs_rF18, rs_rF19, rs_rF20, - rs_rF21, rs_rF22, rs_rF23, rs_rF24, rs_rF25, rs_rF26, rs_rF27, rs_rF28, rs_rF29, rs_rF30, - rs_rF31}; -static constexpr RegStorage dp_temps_arr[] = - {rs_rD0, rs_rD1, rs_rD2, rs_rD3, rs_rD4, rs_rD5, rs_rD6, rs_rD7, rs_rD8, rs_rD9, rs_rD10, - rs_rD11, rs_rD12, rs_rD13, rs_rD14, rs_rD15, rs_rD16, rs_rD17, rs_rD18, rs_rD19, rs_rD20, - rs_rD21, rs_rD22, rs_rD23, rs_rD24, rs_rD25, rs_rD26, rs_rD27, rs_rD28, rs_rD29, rs_rD30, - rs_rD31}; -#else -static constexpr RegStorage sp_temps_arr[] = - {rs_rF0, rs_rF1, rs_rF2, rs_rF3, rs_rF4, rs_rF5, rs_rF6, rs_rF7, rs_rF8, rs_rF9, rs_rF10, - rs_rF11, rs_rF12, rs_rF13, rs_rF14, rs_rF15, rs_rF16, rs_rF17, rs_rF18, rs_rF19, rs_rF20, - rs_rF21, rs_rF22, rs_rF23}; -static constexpr RegStorage dp_temps_arr[] = - {rs_rD0, rs_rD1, rs_rD2, rs_rD3, rs_rD4, rs_rD5, rs_rD6, rs_rD7, rs_rD8, rs_rD9, rs_rD10, - rs_rD11, rs_rD12, rs_rD13, rs_rD14, rs_rD15, rs_rD16, rs_rD17, rs_rD18, rs_rD19, rs_rD20, - rs_rD21, rs_rD22, rs_rD23}; -#endif - -static constexpr ArrayRef<const RegStorage> empty_pool; -static constexpr ArrayRef<const RegStorage> core_regs32(core_regs_arr32); -static constexpr ArrayRef<const RegStorage> core_regs64(core_regs_arr64); -static constexpr ArrayRef<const RegStorage> sp_regs(sp_regs_arr); -static constexpr ArrayRef<const RegStorage> dp_regs(dp_regs_arr); -static constexpr ArrayRef<const RegStorage> reserved_regs32(reserved_regs_arr32); -static constexpr ArrayRef<const RegStorage> reserved_regs64(reserved_regs_arr64); -static constexpr ArrayRef<const RegStorage> core_temps32(core_temps_arr32); -static constexpr ArrayRef<const RegStorage> core_temps64(core_temps_arr64); -static constexpr ArrayRef<const RegStorage> sp_temps(sp_temps_arr); -static constexpr ArrayRef<const RegStorage> dp_temps(dp_temps_arr); - -RegLocation Mips64Mir2Lir::LocCReturn() { - return mips64_loc_c_return; -} - -RegLocation Mips64Mir2Lir::LocCReturnRef() { - return mips64_loc_c_return_ref; -} - -RegLocation Mips64Mir2Lir::LocCReturnWide() { - return mips64_loc_c_return_wide; -} - -RegLocation Mips64Mir2Lir::LocCReturnFloat() { - return mips64_loc_c_return_float; -} - -RegLocation Mips64Mir2Lir::LocCReturnDouble() { - return mips64_loc_c_return_double; -} - -// Return a target-dependent special register. -RegStorage Mips64Mir2Lir::TargetReg(SpecialTargetRegister reg) { - RegStorage res_reg; - switch (reg) { - case kSelf: res_reg = rs_rS1; break; - case kSuspend: res_reg = rs_rS0; break; - case kLr: res_reg = rs_rRA; break; - case kPc: res_reg = RegStorage::InvalidReg(); break; - case kSp: res_reg = rs_rSP; break; - case kArg0: res_reg = rs_rA0; break; - case kArg1: res_reg = rs_rA1; break; - case kArg2: res_reg = rs_rA2; break; - case kArg3: res_reg = rs_rA3; break; - case kArg4: res_reg = rs_rA4; break; - case kArg5: res_reg = rs_rA5; break; - case kArg6: res_reg = rs_rA6; break; - case kArg7: res_reg = rs_rA7; break; - case kFArg0: res_reg = rs_rF12; break; - case kFArg1: res_reg = rs_rF13; break; - case kFArg2: res_reg = rs_rF14; break; - case kFArg3: res_reg = rs_rF15; break; - case kFArg4: res_reg = rs_rF16; break; - case kFArg5: res_reg = rs_rF17; break; - case kFArg6: res_reg = rs_rF18; break; - case kFArg7: res_reg = rs_rF19; break; - case kRet0: res_reg = rs_rV0; break; - case kRet1: res_reg = rs_rV1; break; - case kInvokeTgt: res_reg = rs_rT9; break; - case kHiddenArg: res_reg = rs_rT0; break; - case kHiddenFpArg: res_reg = RegStorage::InvalidReg(); break; - case kCount: res_reg = RegStorage::InvalidReg(); break; - default: res_reg = RegStorage::InvalidReg(); - } - return res_reg; -} - -RegStorage Mips64Mir2Lir::InToRegStorageMips64Mapper::GetNextReg(ShortyArg arg) { - const SpecialTargetRegister coreArgMappingToPhysicalReg[] = - {kArg1, kArg2, kArg3, kArg4, kArg5, kArg6, kArg7}; - const size_t coreArgMappingToPhysicalRegSize = arraysize(coreArgMappingToPhysicalReg); - const SpecialTargetRegister fpArgMappingToPhysicalReg[] = - {kFArg1, kFArg2, kFArg3, kFArg4, kFArg5, kFArg6, kFArg7}; - const size_t fpArgMappingToPhysicalRegSize = arraysize(fpArgMappingToPhysicalReg); - - RegStorage result = RegStorage::InvalidReg(); - if (arg.IsFP()) { - if (cur_arg_reg_ < fpArgMappingToPhysicalRegSize) { - DCHECK(!arg.IsRef()); - result = m2l_->TargetReg(fpArgMappingToPhysicalReg[cur_arg_reg_++], - arg.IsWide() ? kWide : kNotWide); - } - } else { - if (cur_arg_reg_ < coreArgMappingToPhysicalRegSize) { - DCHECK(!(arg.IsWide() && arg.IsRef())); - result = m2l_->TargetReg(coreArgMappingToPhysicalReg[cur_arg_reg_++], - arg.IsRef() ? kRef : (arg.IsWide() ? kWide : kNotWide)); - } - } - return result; -} - -/* - * Decode the register id. - */ -ResourceMask Mips64Mir2Lir::GetRegMaskCommon(const RegStorage& reg) const { - return ResourceMask::Bit((reg.IsFloat() ? kMips64FPReg0 : 0) + reg.GetRegNum()); -} - -ResourceMask Mips64Mir2Lir::GetPCUseDefEncoding() const { - return ResourceMask::Bit(kMips64RegPC); -} - - -void Mips64Mir2Lir::SetupTargetResourceMasks(LIR* lir, uint64_t flags, ResourceMask* use_mask, - ResourceMask* def_mask) { - DCHECK(!lir->flags.use_def_invalid); - - // Mips64-specific resource map setup here. - if (flags & REG_DEF_SP) { - def_mask->SetBit(kMips64RegSP); - } - - if (flags & REG_USE_SP) { - use_mask->SetBit(kMips64RegSP); - } - - if (flags & REG_DEF_LR) { - def_mask->SetBit(kMips64RegLR); - } -} - -/* For dumping instructions */ -#define MIPS64_REG_COUNT 32 -static const char *mips64_reg_name[MIPS64_REG_COUNT] = { - "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", - "a4", "a5", "a6", "a7", "t0", "t1", "t2", "t3", - "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", - "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra" -}; - -/* - * Interpret a format string and build a string no longer than size - * See format key in assemble_mips64.cc. - */ -std::string Mips64Mir2Lir::BuildInsnString(const char *fmt, LIR *lir, unsigned char* base_addr) { - std::string buf; - int i; - const char *fmt_end = &fmt[strlen(fmt)]; - char tbuf[256]; - char nc; - while (fmt < fmt_end) { - int operand; - if (*fmt == '!') { - fmt++; - DCHECK_LT(fmt, fmt_end); - nc = *fmt++; - if (nc == '!') { - strcpy(tbuf, "!"); - } else { - DCHECK_LT(fmt, fmt_end); - DCHECK_LT(static_cast<unsigned>(nc-'0'), 4u); - operand = lir->operands[nc-'0']; - switch (*fmt++) { - case 'b': - strcpy(tbuf, "0000"); - for (i = 3; i >= 0; i--) { - tbuf[i] += operand & 1; - operand >>= 1; - } - break; - case 's': - snprintf(tbuf, arraysize(tbuf), "$f%d", RegStorage::RegNum(operand)); - break; - case 'S': - DCHECK_EQ(RegStorage::RegNum(operand) & 1, 0); - snprintf(tbuf, arraysize(tbuf), "$f%d", RegStorage::RegNum(operand)); - break; - case 'h': - snprintf(tbuf, arraysize(tbuf), "%04x", operand); - break; - case 'M': - case 'd': - snprintf(tbuf, arraysize(tbuf), "%d", operand); - break; - case 'D': - snprintf(tbuf, arraysize(tbuf), "%d", operand+1); - break; - case 'E': - snprintf(tbuf, arraysize(tbuf), "%d", operand*4); - break; - case 'F': - snprintf(tbuf, arraysize(tbuf), "%d", operand*2); - break; - case 't': - snprintf(tbuf, arraysize(tbuf), "0x%08" PRIxPTR " (L%p)", - reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4 + (operand << 1), - lir->target); - break; - case 'T': - snprintf(tbuf, arraysize(tbuf), "0x%08x", operand << 2); - break; - case 'u': { - int offset_1 = lir->operands[0]; - int offset_2 = NEXT_LIR(lir)->operands[0]; - uintptr_t target = - (((reinterpret_cast<uintptr_t>(base_addr) + lir->offset + 4) & ~3) + - (offset_1 << 21 >> 9) + (offset_2 << 1)) & 0xfffffffc; - snprintf(tbuf, arraysize(tbuf), "%p", reinterpret_cast<void*>(target)); - break; - } - - /* Nothing to print for BLX_2 */ - case 'v': - strcpy(tbuf, "see above"); - break; - case 'r': - DCHECK(operand >= 0 && operand < MIPS64_REG_COUNT); - strcpy(tbuf, mips64_reg_name[operand]); - break; - case 'N': - // Placeholder for delay slot handling - strcpy(tbuf, "; nop"); - break; - default: - strcpy(tbuf, "DecodeError"); - break; - } - buf += tbuf; - } - } else { - buf += *fmt++; - } - } - return buf; -} - -// FIXME: need to redo resource maps for MIPS64 - fix this at that time. -void Mips64Mir2Lir::DumpResourceMask(LIR *mips64_lir, const ResourceMask& mask, const char *prefix) { - char buf[256]; - buf[0] = 0; - - if (mask.Equals(kEncodeAll)) { - strcpy(buf, "all"); - } else { - char num[8]; - int i; - - for (i = 0; i < kMips64RegEnd; i++) { - if (mask.HasBit(i)) { - snprintf(num, arraysize(num), "%d ", i); - strcat(buf, num); - } - } - - if (mask.HasBit(ResourceMask::kCCode)) { - strcat(buf, "cc "); - } - if (mask.HasBit(ResourceMask::kFPStatus)) { - strcat(buf, "fpcc "); - } - // Memory bits. - if (mips64_lir && (mask.HasBit(ResourceMask::kDalvikReg))) { - snprintf(buf + strlen(buf), arraysize(buf) - strlen(buf), "dr%d%s", - DECODE_ALIAS_INFO_REG(mips64_lir->flags.alias_info), - DECODE_ALIAS_INFO_WIDE(mips64_lir->flags.alias_info) ? "(+1)" : ""); - } - if (mask.HasBit(ResourceMask::kLiteral)) { - strcat(buf, "lit "); - } - - if (mask.HasBit(ResourceMask::kHeapRef)) { - strcat(buf, "heap "); - } - if (mask.HasBit(ResourceMask::kMustNotAlias)) { - strcat(buf, "noalias "); - } - } - if (buf[0]) { - LOG(INFO) << prefix << ": " << buf; - } -} - -/* - * TUNING: is true leaf? Can't just use METHOD_IS_LEAF to determine as some - * instructions might call out to C/assembly helper functions. Until - * machinery is in place, always spill lr. - */ - -void Mips64Mir2Lir::AdjustSpillMask() { - core_spill_mask_ |= (1 << rs_rRA.GetRegNum()); - num_core_spills_++; -} - -/* Clobber all regs that might be used by an external C call */ -void Mips64Mir2Lir::ClobberCallerSave() { - Clobber(rs_rZEROd); - Clobber(rs_rATd); - Clobber(rs_rV0d); - Clobber(rs_rV1d); - Clobber(rs_rA0d); - Clobber(rs_rA1d); - Clobber(rs_rA2d); - Clobber(rs_rA3d); - Clobber(rs_rA4d); - Clobber(rs_rA5d); - Clobber(rs_rA6d); - Clobber(rs_rA7d); - Clobber(rs_rT0d); - Clobber(rs_rT1d); - Clobber(rs_rT2d); - Clobber(rs_rT3d); - Clobber(rs_rT8d); - Clobber(rs_rT9d); - Clobber(rs_rK0d); - Clobber(rs_rK1d); - Clobber(rs_rGPd); - Clobber(rs_rFPd); - Clobber(rs_rRAd); - - Clobber(rs_rF0); - Clobber(rs_rF1); - Clobber(rs_rF2); - Clobber(rs_rF3); - Clobber(rs_rF4); - Clobber(rs_rF5); - Clobber(rs_rF6); - Clobber(rs_rF7); - Clobber(rs_rF8); - Clobber(rs_rF9); - Clobber(rs_rF10); - Clobber(rs_rF11); - Clobber(rs_rF12); - Clobber(rs_rF13); - Clobber(rs_rF14); - Clobber(rs_rF15); - Clobber(rs_rD0); - Clobber(rs_rD1); - Clobber(rs_rD2); - Clobber(rs_rD3); - Clobber(rs_rD4); - Clobber(rs_rD5); - Clobber(rs_rD6); - Clobber(rs_rD7); -} - -RegLocation Mips64Mir2Lir::GetReturnWideAlt() { - UNIMPLEMENTED(FATAL) << "No GetReturnWideAlt for MIPS64"; - RegLocation res = LocCReturnWide(); - return res; -} - -RegLocation Mips64Mir2Lir::GetReturnAlt() { - UNIMPLEMENTED(FATAL) << "No GetReturnAlt for MIPS64"; - RegLocation res = LocCReturn(); - return res; -} - -/* To be used when explicitly managing register use */ -void Mips64Mir2Lir::LockCallTemps() { - LockTemp(rs_rMIPS64_ARG0); - LockTemp(rs_rMIPS64_ARG1); - LockTemp(rs_rMIPS64_ARG2); - LockTemp(rs_rMIPS64_ARG3); - LockTemp(rs_rMIPS64_ARG4); - LockTemp(rs_rMIPS64_ARG5); - LockTemp(rs_rMIPS64_ARG6); - LockTemp(rs_rMIPS64_ARG7); -} - -/* To be used when explicitly managing register use */ -void Mips64Mir2Lir::FreeCallTemps() { - FreeTemp(rs_rMIPS64_ARG0); - FreeTemp(rs_rMIPS64_ARG1); - FreeTemp(rs_rMIPS64_ARG2); - FreeTemp(rs_rMIPS64_ARG3); - FreeTemp(rs_rMIPS64_ARG4); - FreeTemp(rs_rMIPS64_ARG5); - FreeTemp(rs_rMIPS64_ARG6); - FreeTemp(rs_rMIPS64_ARG7); - FreeTemp(TargetReg(kHiddenArg)); -} - -bool Mips64Mir2Lir::GenMemBarrier(MemBarrierKind barrier_kind ATTRIBUTE_UNUSED) { - if (cu_->compiler_driver->GetInstructionSetFeatures()->IsSmp()) { - NewLIR1(kMips64Sync, 0 /* Only stype currently supported */); - return true; - } else { - return false; - } -} - -void Mips64Mir2Lir::CompilerInitializeRegAlloc() { - reg_pool_.reset(new (arena_) RegisterPool(this, arena_, core_regs32, core_regs64 , sp_regs, - dp_regs, reserved_regs32, reserved_regs64, - core_temps32, core_temps64, sp_temps, - dp_temps)); - - // Target-specific adjustments. - - // Alias single precision floats to appropriate half of overlapping double. - for (RegisterInfo* info : reg_pool_->sp_regs_) { - int sp_reg_num = info->GetReg().GetRegNum(); - int dp_reg_num = sp_reg_num; - RegStorage dp_reg = RegStorage::Solo64(RegStorage::kFloatingPoint | dp_reg_num); - RegisterInfo* dp_reg_info = GetRegInfo(dp_reg); - // Double precision register's master storage should refer to itself. - DCHECK_EQ(dp_reg_info, dp_reg_info->Master()); - // Redirect single precision's master storage to master. - info->SetMaster(dp_reg_info); - // Singles should show a single 32-bit mask bit, at first referring to the low half. - DCHECK_EQ(info->StorageMask(), 0x1U); - } - - // Alias 32bit W registers to corresponding 64bit X registers. - for (RegisterInfo* info : reg_pool_->core_regs_) { - int d_reg_num = info->GetReg().GetRegNum(); - RegStorage d_reg = RegStorage::Solo64(d_reg_num); - RegisterInfo* d_reg_info = GetRegInfo(d_reg); - // 64bit D register's master storage should refer to itself. - DCHECK_EQ(d_reg_info, d_reg_info->Master()); - // Redirect 32bit master storage to 64bit D. - info->SetMaster(d_reg_info); - // 32bit should show a single 32-bit mask bit, at first referring to the low half. - DCHECK_EQ(info->StorageMask(), 0x1U); - } - - // Don't start allocating temps at r0/s0/d0 or you may clobber return regs in early-exit methods. - // TODO: adjust when we roll to hard float calling convention. - reg_pool_->next_core_reg_ = 2; - reg_pool_->next_sp_reg_ = 2; - reg_pool_->next_dp_reg_ = 1; -} - -/* - * In the Arm code a it is typical to use the link register - * to hold the target address. However, for Mips64 we must - * ensure that all branch instructions can be restarted if - * there is a trap in the shadow. Allocate a temp register. - */ -RegStorage Mips64Mir2Lir::LoadHelper(QuickEntrypointEnum trampoline) { - // NOTE: native pointer. - LoadWordDisp(rs_rMIPS64_SELF, GetThreadOffset<8>(trampoline).Int32Value(), rs_rT9d); - return rs_rT9d; -} - -LIR* Mips64Mir2Lir::CheckSuspendUsingLoad() { - RegStorage tmp = AllocTemp(); - // NOTE: native pointer. - LoadWordDisp(rs_rMIPS64_SELF, Thread::ThreadSuspendTriggerOffset<8>().Int32Value(), tmp); - LIR *inst = LoadWordDisp(tmp, 0, tmp); - FreeTemp(tmp); - return inst; -} - -LIR* Mips64Mir2Lir::GenAtomic64Load(RegStorage r_base, int displacement, RegStorage r_dest) { - DCHECK(!r_dest.IsFloat()); // See RegClassForFieldLoadStore(). - ClobberCallerSave(); - LockCallTemps(); // Using fixed registers. - RegStorage reg_ptr = TargetReg(kArg0); - OpRegRegImm(kOpAdd, reg_ptr, r_base, displacement); - RegStorage r_tgt = LoadHelper(kQuickA64Load); - LIR *ret = OpReg(kOpBlx, r_tgt); - OpRegCopy(r_dest, TargetReg(kRet0)); - return ret; -} - -LIR* Mips64Mir2Lir::GenAtomic64Store(RegStorage r_base, int displacement, RegStorage r_src) { - DCHECK(!r_src.IsFloat()); // See RegClassForFieldLoadStore(). - DCHECK(!r_src.IsPair()); - ClobberCallerSave(); - LockCallTemps(); // Using fixed registers. - RegStorage temp_ptr = AllocTemp(); - OpRegRegImm(kOpAdd, temp_ptr, r_base, displacement); - RegStorage temp_value = AllocTemp(); - OpRegCopy(temp_value, r_src); - OpRegCopy(TargetReg(kArg0), temp_ptr); - OpRegCopy(TargetReg(kArg1), temp_value); - FreeTemp(temp_ptr); - FreeTemp(temp_value); - RegStorage r_tgt = LoadHelper(kQuickA64Store); - return OpReg(kOpBlx, r_tgt); -} - -void Mips64Mir2Lir::SpillCoreRegs() { - if (num_core_spills_ == 0) { - return; - } - uint32_t mask = core_spill_mask_; - // Start saving from offset 0 so that ra ends up on the top of the frame. - int offset = 0; - OpRegImm(kOpSub, rs_rSPd, num_core_spills_ * 8); - for (int reg = 0; mask; mask >>= 1, reg++) { - if (mask & 0x1) { - StoreWordDisp(rs_rMIPS64_SP, offset, RegStorage::Solo64(reg)); - offset += 8; - } - } -} - -void Mips64Mir2Lir::UnSpillCoreRegs() { - if (num_core_spills_ == 0) { - return; - } - uint32_t mask = core_spill_mask_; - int offset = frame_size_ - num_core_spills_ * 8; - for (int reg = 0; mask; mask >>= 1, reg++) { - if (mask & 0x1) { - LoadWordDisp(rs_rMIPS64_SP, offset, RegStorage::Solo64(reg)); - offset += 8; - } - } - OpRegImm(kOpAdd, rs_rSPd, frame_size_); -} - -bool Mips64Mir2Lir::IsUnconditionalBranch(LIR* lir) { - return (lir->opcode == kMips64B); -} - -RegisterClass Mips64Mir2Lir::RegClassForFieldLoadStore(OpSize size, bool is_volatile) { - if (UNLIKELY(is_volatile)) { - // On Mips64, atomic 64-bit load/store requires a core register. - // Smaller aligned load/store is atomic for both core and fp registers. - if (size == k64 || size == kDouble) { - return kCoreReg; - } - } - // TODO: Verify that both core and fp registers are suitable for smaller sizes. - return RegClassBySize(size); -} - -Mips64Mir2Lir::Mips64Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena) - : Mir2Lir(cu, mir_graph, arena), in_to_reg_storage_mips64_mapper_(this) { - for (int i = 0; i < kMips64Last; i++) { - DCHECK_EQ(Mips64Mir2Lir::EncodingMap[i].opcode, i) - << "Encoding order for " << Mips64Mir2Lir::EncodingMap[i].name - << " is wrong: expecting " << i << ", seeing " - << static_cast<int>(Mips64Mir2Lir::EncodingMap[i].opcode); - } -} - -Mir2Lir* Mips64CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph, - ArenaAllocator* const arena) { - return new Mips64Mir2Lir(cu, mir_graph, arena); -} - -uint64_t Mips64Mir2Lir::GetTargetInstFlags(int opcode) { - DCHECK(!IsPseudoLirOp(opcode)); - return Mips64Mir2Lir::EncodingMap[opcode].flags; -} - -const char* Mips64Mir2Lir::GetTargetInstName(int opcode) { - DCHECK(!IsPseudoLirOp(opcode)); - return Mips64Mir2Lir::EncodingMap[opcode].name; -} - -const char* Mips64Mir2Lir::GetTargetInstFmt(int opcode) { - DCHECK(!IsPseudoLirOp(opcode)); - return Mips64Mir2Lir::EncodingMap[opcode].fmt; -} - -void Mips64Mir2Lir::GenBreakpoint(int code) { - NewLIR1(kMips64Break, code); -} - -} // namespace art diff --git a/compiler/dex/quick/mips64/utility_mips64.cc b/compiler/dex/quick/mips64/utility_mips64.cc deleted file mode 100644 index 38e354cbde..0000000000 --- a/compiler/dex/quick/mips64/utility_mips64.cc +++ /dev/null @@ -1,875 +0,0 @@ -/* - * Copyright (C) 2015 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 "codegen_mips64.h" - -#include "arch/mips64/instruction_set_features_mips64.h" -#include "base/logging.h" -#include "dex/quick/mir_to_lir-inl.h" -#include "dex/reg_storage_eq.h" -#include "driver/compiler_driver.h" -#include "mips64_lir.h" - -namespace art { - -/* This file contains codegen for the MIPS64 ISA. */ - -LIR* Mips64Mir2Lir::OpFpRegCopy(RegStorage r_dest, RegStorage r_src) { - int opcode; - // Must be both DOUBLE or both not DOUBLE. - DCHECK_EQ(r_dest.Is64Bit(), r_src.Is64Bit()); - if (r_dest.Is64Bit()) { - if (r_dest.IsDouble()) { - if (r_src.IsDouble()) { - opcode = kMips64Fmovd; - } else { - // Note the operands are swapped for the dmtc1 instr. - RegStorage t_opnd = r_src; - r_src = r_dest; - r_dest = t_opnd; - opcode = kMips64Dmtc1; - } - } else { - DCHECK(r_src.IsDouble()); - opcode = kMips64Dmfc1; - } - } else { - if (r_dest.IsSingle()) { - if (r_src.IsSingle()) { - opcode = kMips64Fmovs; - } else { - // Note the operands are swapped for the mtc1 instr. - RegStorage t_opnd = r_src; - r_src = r_dest; - r_dest = t_opnd; - opcode = kMips64Mtc1; - } - } else { - DCHECK(r_src.IsSingle()); - opcode = kMips64Mfc1; - } - } - LIR* res = RawLIR(current_dalvik_offset_, opcode, r_dest.GetReg(), r_src.GetReg()); - if (!(cu_->disable_opt & (1 << kSafeOptimizations)) && r_dest == r_src) { - res->flags.is_nop = true; - } - return res; -} - -bool Mips64Mir2Lir::InexpensiveConstantInt(int32_t value) { - // For encodings, see LoadConstantNoClobber below. - return ((value == 0) || IsUint<16>(value) || IsInt<16>(value)); -} - -bool Mips64Mir2Lir::InexpensiveConstantFloat(int32_t value) { - UNUSED(value); - return false; // TUNING -} - -bool Mips64Mir2Lir::InexpensiveConstantLong(int64_t value) { - UNUSED(value); - return false; // TUNING -} - -bool Mips64Mir2Lir::InexpensiveConstantDouble(int64_t value) { - UNUSED(value); - return false; // TUNING -} - -/* - * Load a immediate using a shortcut if possible; otherwise - * grab from the per-translation literal pool. If target is - * a high register, build constant into a low register and copy. - * - * No additional register clobbering operation performed. Use this version when - * 1) r_dest is freshly returned from AllocTemp or - * 2) The codegen is under fixed register usage - */ -LIR* Mips64Mir2Lir::LoadConstantNoClobber(RegStorage r_dest, int value) { - LIR *res; - - RegStorage r_dest_save = r_dest; - int is_fp_reg = r_dest.IsFloat(); - if (is_fp_reg) { - DCHECK(r_dest.IsSingle()); - r_dest = AllocTemp(); - } - - // See if the value can be constructed cheaply. - if (value == 0) { - res = NewLIR2(kMips64Move, r_dest.GetReg(), rZERO); - } else if (IsUint<16>(value)) { - // Use OR with (unsigned) immediate to encode 16b unsigned int. - res = NewLIR3(kMips64Ori, r_dest.GetReg(), rZERO, value); - } else if (IsInt<16>(value)) { - // Use ADD with (signed) immediate to encode 16b signed int. - res = NewLIR3(kMips64Addiu, r_dest.GetReg(), rZERO, value); - } else { - res = NewLIR2(kMips64Lui, r_dest.GetReg(), value >> 16); - if (value & 0xffff) - NewLIR3(kMips64Ori, r_dest.GetReg(), r_dest.GetReg(), value); - } - - if (is_fp_reg) { - NewLIR2(kMips64Mtc1, r_dest.GetReg(), r_dest_save.GetReg()); - FreeTemp(r_dest); - } - - return res; -} - -LIR* Mips64Mir2Lir::OpUnconditionalBranch(LIR* target) { - LIR* res = NewLIR1(kMips64B, 0 /* offset to be patched during assembly*/); - res->target = target; - return res; -} - -LIR* Mips64Mir2Lir::OpReg(OpKind op, RegStorage r_dest_src) { - Mips64OpCode opcode = kMips64Nop; - switch (op) { - case kOpBlx: - opcode = kMips64Jalr; - break; - case kOpBx: - return NewLIR2(kMips64Jalr, rZERO, r_dest_src.GetReg()); - break; - default: - LOG(FATAL) << "Bad case in OpReg"; - } - return NewLIR2(opcode, rRAd, r_dest_src.GetReg()); -} - -LIR* Mips64Mir2Lir::OpRegImm(OpKind op, RegStorage r_dest_src1, int value) { - LIR *res; - bool neg = (value < 0); - int abs_value = (neg) ? -value : value; - bool short_form = (abs_value & 0xff) == abs_value; - bool is64bit = r_dest_src1.Is64Bit(); - RegStorage r_scratch; - Mips64OpCode opcode = kMips64Nop; - switch (op) { - case kOpAdd: - return OpRegRegImm(op, r_dest_src1, r_dest_src1, value); - case kOpSub: - return OpRegRegImm(op, r_dest_src1, r_dest_src1, value); - default: - LOG(FATAL) << "Bad case in OpRegImm"; - } - if (short_form) { - res = NewLIR2(opcode, r_dest_src1.GetReg(), abs_value); - } else { - if (is64bit) { - r_scratch = AllocTempWide(); - res = LoadConstantWide(r_scratch, value); - } else { - r_scratch = AllocTemp(); - res = LoadConstant(r_scratch, value); - } - if (op == kOpCmp) { - NewLIR2(opcode, r_dest_src1.GetReg(), r_scratch.GetReg()); - } else { - NewLIR3(opcode, r_dest_src1.GetReg(), r_dest_src1.GetReg(), r_scratch.GetReg()); - } - } - return res; -} - -LIR* Mips64Mir2Lir::OpRegRegReg(OpKind op, RegStorage r_dest, - RegStorage r_src1, RegStorage r_src2) { - Mips64OpCode opcode = kMips64Nop; - bool is64bit = r_dest.Is64Bit() || r_src1.Is64Bit() || r_src2.Is64Bit(); - - switch (op) { - case kOpAdd: - if (is64bit) { - opcode = kMips64Daddu; - } else { - opcode = kMips64Addu; - } - break; - case kOpSub: - if (is64bit) { - opcode = kMips64Dsubu; - } else { - opcode = kMips64Subu; - } - break; - case kOpAnd: - opcode = kMips64And; - break; - case kOpMul: - opcode = kMips64Mul; - break; - case kOpOr: - opcode = kMips64Or; - break; - case kOpXor: - opcode = kMips64Xor; - break; - case kOpLsl: - if (is64bit) { - opcode = kMips64Dsllv; - } else { - opcode = kMips64Sllv; - } - break; - case kOpLsr: - if (is64bit) { - opcode = kMips64Dsrlv; - } else { - opcode = kMips64Srlv; - } - break; - case kOpAsr: - if (is64bit) { - opcode = kMips64Dsrav; - } else { - opcode = kMips64Srav; - } - break; - case kOpAdc: - case kOpSbc: - LOG(FATAL) << "No carry bit on MIPS64"; - break; - default: - LOG(FATAL) << "Bad case in OpRegRegReg"; - break; - } - return NewLIR3(opcode, r_dest.GetReg(), r_src1.GetReg(), r_src2.GetReg()); -} - -LIR* Mips64Mir2Lir::OpRegRegImm(OpKind op, RegStorage r_dest, RegStorage r_src1, int value) { - LIR *res; - Mips64OpCode opcode = kMips64Nop; - bool short_form = true; - bool is64bit = r_dest.Is64Bit() || r_src1.Is64Bit(); - - switch (op) { - case kOpAdd: - if (is64bit) { - if (IS_SIMM16(value)) { - opcode = kMips64Daddiu; - } else { - short_form = false; - opcode = kMips64Daddu; - } - } else { - if (IS_SIMM16(value)) { - opcode = kMips64Addiu; - } else { - short_form = false; - opcode = kMips64Addu; - } - } - break; - case kOpSub: - if (is64bit) { - if (IS_SIMM16((-value))) { - value = -value; - opcode = kMips64Daddiu; - } else { - short_form = false; - opcode = kMips64Dsubu; - } - } else { - if (IS_SIMM16((-value))) { - value = -value; - opcode = kMips64Addiu; - } else { - short_form = false; - opcode = kMips64Subu; - } - } - break; - case kOpLsl: - if (is64bit) { - DCHECK(value >= 0 && value <= 63); - if (value >= 0 && value <= 31) { - opcode = kMips64Dsll; - } else { - opcode = kMips64Dsll32; - value = value - 32; - } - } else { - DCHECK(value >= 0 && value <= 31); - opcode = kMips64Sll; - } - break; - case kOpLsr: - if (is64bit) { - DCHECK(value >= 0 && value <= 63); - if (value >= 0 && value <= 31) { - opcode = kMips64Dsrl; - } else { - opcode = kMips64Dsrl32; - value = value - 32; - } - } else { - DCHECK(value >= 0 && value <= 31); - opcode = kMips64Srl; - } - break; - case kOpAsr: - if (is64bit) { - DCHECK(value >= 0 && value <= 63); - if (value >= 0 && value <= 31) { - opcode = kMips64Dsra; - } else { - opcode = kMips64Dsra32; - value = value - 32; - } - } else { - DCHECK(value >= 0 && value <= 31); - opcode = kMips64Sra; - } - break; - case kOpAnd: - if (IS_UIMM16((value))) { - opcode = kMips64Andi; - } else { - short_form = false; - opcode = kMips64And; - } - break; - case kOpOr: - if (IS_UIMM16((value))) { - opcode = kMips64Ori; - } else { - short_form = false; - opcode = kMips64Or; - } - break; - case kOpXor: - if (IS_UIMM16((value))) { - opcode = kMips64Xori; - } else { - short_form = false; - opcode = kMips64Xor; - } - break; - case kOpMul: - short_form = false; - opcode = kMips64Mul; - break; - default: - LOG(FATAL) << "Bad case in OpRegRegImm"; - break; - } - - if (short_form) { - res = NewLIR3(opcode, r_dest.GetReg(), r_src1.GetReg(), value); - } else { - if (r_dest != r_src1) { - res = LoadConstant(r_dest, value); - NewLIR3(opcode, r_dest.GetReg(), r_src1.GetReg(), r_dest.GetReg()); - } else { - if (is64bit) { - RegStorage r_scratch = AllocTempWide(); - res = LoadConstantWide(r_scratch, value); - NewLIR3(opcode, r_dest.GetReg(), r_src1.GetReg(), r_scratch.GetReg()); - } else { - RegStorage r_scratch = AllocTemp(); - res = LoadConstant(r_scratch, value); - NewLIR3(opcode, r_dest.GetReg(), r_src1.GetReg(), r_scratch.GetReg()); - } - } - } - return res; -} - -LIR* Mips64Mir2Lir::OpRegReg(OpKind op, RegStorage r_dest_src1, RegStorage r_src2) { - Mips64OpCode opcode = kMips64Nop; - LIR *res; - switch (op) { - case kOpMov: - opcode = kMips64Move; - break; - case kOpMvn: - return NewLIR3(kMips64Nor, r_dest_src1.GetReg(), r_src2.GetReg(), rZEROd); - case kOpNeg: - if (r_dest_src1.Is64Bit()) - return NewLIR3(kMips64Dsubu, r_dest_src1.GetReg(), rZEROd, r_src2.GetReg()); - else - return NewLIR3(kMips64Subu, r_dest_src1.GetReg(), rZERO, r_src2.GetReg()); - case kOpAdd: - case kOpAnd: - case kOpMul: - case kOpOr: - case kOpSub: - case kOpXor: - return OpRegRegReg(op, r_dest_src1, r_dest_src1, r_src2); - case kOp2Byte: - res = NewLIR2(kMips64Seb, r_dest_src1.GetReg(), r_src2.GetReg()); - return res; - case kOp2Short: - res = NewLIR2(kMips64Seh, r_dest_src1.GetReg(), r_src2.GetReg()); - return res; - case kOp2Char: - return NewLIR3(kMips64Andi, r_dest_src1.GetReg(), r_src2.GetReg(), 0xFFFF); - default: - LOG(FATAL) << "Bad case in OpRegReg"; - UNREACHABLE(); - } - return NewLIR2(opcode, r_dest_src1.GetReg(), r_src2.GetReg()); -} - -LIR* Mips64Mir2Lir::OpMovRegMem(RegStorage r_dest, RegStorage r_base, int offset, - MoveType move_type) { - UNUSED(r_dest, r_base, offset, move_type); - UNIMPLEMENTED(FATAL); - UNREACHABLE(); -} - -LIR* Mips64Mir2Lir::OpMovMemReg(RegStorage r_base, int offset, - RegStorage r_src, MoveType move_type) { - UNUSED(r_base, offset, r_src, move_type); - UNIMPLEMENTED(FATAL); - UNREACHABLE(); -} - -LIR* Mips64Mir2Lir::OpCondRegReg(OpKind op, ConditionCode cc, - RegStorage r_dest, RegStorage r_src) { - UNUSED(op, cc, r_dest, r_src); - LOG(FATAL) << "Unexpected use of OpCondRegReg for MIPS64"; - UNREACHABLE(); -} - -LIR* Mips64Mir2Lir::LoadConstantWide(RegStorage r_dest, int64_t value) { - LIR *res = nullptr; - DCHECK(r_dest.Is64Bit()); - RegStorage r_dest_save = r_dest; - int is_fp_reg = r_dest.IsFloat(); - if (is_fp_reg) { - DCHECK(r_dest.IsDouble()); - r_dest = AllocTemp(); - } - - int bit31 = (value & UINT64_C(0x80000000)) != 0; - - // Loads with 1 instruction. - if (IsUint<16>(value)) { - res = NewLIR3(kMips64Ori, r_dest.GetReg(), rZEROd, value); - } else if (IsInt<16>(value)) { - res = NewLIR3(kMips64Daddiu, r_dest.GetReg(), rZEROd, value); - } else if ((value & 0xFFFF) == 0 && IsInt<16>(value >> 16)) { - res = NewLIR2(kMips64Lui, r_dest.GetReg(), value >> 16); - } else if (IsInt<32>(value)) { - // Loads with 2 instructions. - res = NewLIR2(kMips64Lui, r_dest.GetReg(), value >> 16); - NewLIR3(kMips64Ori, r_dest.GetReg(), r_dest.GetReg(), value); - } else if ((value & 0xFFFF0000) == 0 && IsInt<16>(value >> 32)) { - res = NewLIR3(kMips64Ori, r_dest.GetReg(), rZEROd, value); - NewLIR2(kMips64Dahi, r_dest.GetReg(), value >> 32); - } else if ((value & UINT64_C(0xFFFFFFFF0000)) == 0) { - res = NewLIR3(kMips64Ori, r_dest.GetReg(), rZEROd, value); - NewLIR2(kMips64Dati, r_dest.GetReg(), value >> 48); - } else if ((value & 0xFFFF) == 0 && (value >> 32) >= (-32768 - bit31) && - (value >> 32) <= (32767 - bit31)) { - res = NewLIR2(kMips64Lui, r_dest.GetReg(), value >> 16); - NewLIR2(kMips64Dahi, r_dest.GetReg(), (value >> 32) + bit31); - } else if ((value & 0xFFFF) == 0 && ((value >> 31) & 0x1FFFF) == ((0x20000 - bit31) & 0x1FFFF)) { - res = NewLIR2(kMips64Lui, r_dest.GetReg(), value >> 16); - NewLIR2(kMips64Dati, r_dest.GetReg(), (value >> 48) + bit31); - } else { - int64_t tmp = value; - int shift_cnt = 0; - while ((tmp & 1) == 0) { - tmp >>= 1; - shift_cnt++; - } - - if (IsUint<16>(tmp)) { - res = NewLIR3(kMips64Ori, r_dest.GetReg(), rZEROd, tmp); - NewLIR3((shift_cnt < 32) ? kMips64Dsll : kMips64Dsll32, r_dest.GetReg(), r_dest.GetReg(), - shift_cnt & 0x1F); - } else if (IsInt<16>(tmp)) { - res = NewLIR3(kMips64Daddiu, r_dest.GetReg(), rZEROd, tmp); - NewLIR3((shift_cnt < 32) ? kMips64Dsll : kMips64Dsll32, r_dest.GetReg(), r_dest.GetReg(), - shift_cnt & 0x1F); - } else if (IsInt<32>(tmp)) { - // Loads with 3 instructions. - res = NewLIR2(kMips64Lui, r_dest.GetReg(), tmp >> 16); - NewLIR3(kMips64Ori, r_dest.GetReg(), r_dest.GetReg(), tmp); - NewLIR3((shift_cnt < 32) ? kMips64Dsll : kMips64Dsll32, r_dest.GetReg(), r_dest.GetReg(), - shift_cnt & 0x1F); - } else { - tmp = value >> 16; - shift_cnt = 16; - while ((tmp & 1) == 0) { - tmp >>= 1; - shift_cnt++; - } - - if (IsUint<16>(tmp)) { - res = NewLIR3(kMips64Ori, r_dest.GetReg(), rZEROd, tmp); - NewLIR3((shift_cnt < 32) ? kMips64Dsll : kMips64Dsll32, r_dest.GetReg(), r_dest.GetReg(), - shift_cnt & 0x1F); - NewLIR3(kMips64Ori, r_dest.GetReg(), r_dest.GetReg(), value); - } else if (IsInt<16>(tmp)) { - res = NewLIR3(kMips64Daddiu, r_dest.GetReg(), rZEROd, tmp); - NewLIR3((shift_cnt < 32) ? kMips64Dsll : kMips64Dsll32, r_dest.GetReg(), r_dest.GetReg(), - shift_cnt & 0x1F); - NewLIR3(kMips64Ori, r_dest.GetReg(), r_dest.GetReg(), value); - } else { - // Loads with 3-4 instructions. - uint64_t tmp2 = value; - if (((tmp2 >> 16) & 0xFFFF) != 0 || (tmp2 & 0xFFFFFFFF) == 0) { - res = NewLIR2(kMips64Lui, r_dest.GetReg(), tmp2 >> 16); - } - if ((tmp2 & 0xFFFF) != 0) { - if (res) - NewLIR3(kMips64Ori, r_dest.GetReg(), r_dest.GetReg(), tmp2); - else - res = NewLIR3(kMips64Ori, r_dest.GetReg(), rZEROd, tmp2); - } - if (bit31) { - tmp2 += UINT64_C(0x100000000); - } - if (((tmp2 >> 32) & 0xFFFF) != 0) { - NewLIR2(kMips64Dahi, r_dest.GetReg(), tmp2 >> 32); - } - if (tmp2 & UINT64_C(0x800000000000)) { - tmp2 += UINT64_C(0x1000000000000); - } - if ((tmp2 >> 48) != 0) { - NewLIR2(kMips64Dati, r_dest.GetReg(), tmp2 >> 48); - } - } - } - } - - if (is_fp_reg) { - NewLIR2(kMips64Dmtc1, r_dest.GetReg(), r_dest_save.GetReg()); - FreeTemp(r_dest); - } - - return res; -} - -/* Load value from base + scaled index. */ -LIR* Mips64Mir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_dest, - int scale, OpSize size) { - LIR *first = NULL; - LIR *res; - RegStorage t_reg; - Mips64OpCode opcode = kMips64Nop; - bool is64bit = r_dest.Is64Bit(); - if (is64bit) { - t_reg = AllocTempWide(); - } else { - t_reg = AllocTemp(); - } - - if (r_dest.IsFloat()) { - DCHECK(r_dest.IsSingle()); - DCHECK((size == k32) || (size == kSingle) || (size == kReference)); - size = kSingle; - } else if (is64bit) { - size = k64; - } else { - if (size == kSingle) - size = k32; - } - - if (!scale) { - if (is64bit) { - first = NewLIR3(kMips64Daddu, t_reg.GetReg() , r_base.GetReg(), r_index.GetReg()); - } else { - first = NewLIR3(kMips64Addu, t_reg.GetReg() , r_base.GetReg(), r_index.GetReg()); - } - } else { - first = OpRegRegImm(kOpLsl, t_reg, r_index, scale); - NewLIR3(kMips64Daddu, t_reg.GetReg() , r_base.GetReg(), t_reg.GetReg()); - } - - switch (size) { - case k64: - opcode = kMips64Ld; - break; - case kSingle: - opcode = kMips64Flwc1; - break; - case k32: - case kReference: - opcode = kMips64Lw; - break; - case kUnsignedHalf: - opcode = kMips64Lhu; - break; - case kSignedHalf: - opcode = kMips64Lh; - break; - case kUnsignedByte: - opcode = kMips64Lbu; - break; - case kSignedByte: - opcode = kMips64Lb; - break; - default: - LOG(FATAL) << "Bad case in LoadBaseIndexed"; - } - - res = NewLIR3(opcode, r_dest.GetReg(), 0, t_reg.GetReg()); - FreeTemp(t_reg); - return (first) ? first : res; -} - -/* Store value base base + scaled index. */ -LIR* Mips64Mir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegStorage r_src, - int scale, OpSize size) { - LIR *first = NULL; - Mips64OpCode opcode = kMips64Nop; - RegStorage t_reg = AllocTemp(); - - if (r_src.IsFloat()) { - DCHECK(r_src.IsSingle()); - DCHECK((size == k32) || (size == kSingle) || (size == kReference)); - size = kSingle; - } else { - if (size == kSingle) - size = k32; - } - - if (!scale) { - first = NewLIR3(kMips64Daddu, t_reg.GetReg() , r_base.GetReg(), r_index.GetReg()); - } else { - first = OpRegRegImm(kOpLsl, t_reg, r_index, scale); - NewLIR3(kMips64Daddu, t_reg.GetReg() , r_base.GetReg(), t_reg.GetReg()); - } - - switch (size) { - case kSingle: - opcode = kMips64Fswc1; - break; - case k32: - case kReference: - opcode = kMips64Sw; - break; - case kUnsignedHalf: - case kSignedHalf: - opcode = kMips64Sh; - break; - case kUnsignedByte: - case kSignedByte: - opcode = kMips64Sb; - break; - default: - LOG(FATAL) << "Bad case in StoreBaseIndexed"; - } - NewLIR3(opcode, r_src.GetReg(), 0, t_reg.GetReg()); - return first; -} - -// FIXME: don't split r_dest into 2 containers. -LIR* Mips64Mir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size) { -/* - * Load value from base + displacement. Optionally perform null check - * on base (which must have an associated s_reg and MIR). If not - * performing null check, incoming MIR can be null. IMPORTANT: this - * code must not allocate any new temps. If a new register is needed - * and base and dest are the same, spill some other register to - * rlp and then restore. - */ - LIR *res; - LIR *load = NULL; - Mips64OpCode opcode = kMips64Nop; - bool short_form = IS_SIMM16(displacement); - - switch (size) { - case k64: - case kDouble: - r_dest = Check64BitReg(r_dest); - if (!r_dest.IsFloat()) - opcode = kMips64Ld; - else - opcode = kMips64Fldc1; - DCHECK_EQ((displacement & 0x3), 0); - break; - case k32: - case kSingle: - case kReference: - opcode = kMips64Lw; - if (r_dest.IsFloat()) { - opcode = kMips64Flwc1; - DCHECK(r_dest.IsSingle()); - } - DCHECK_EQ((displacement & 0x3), 0); - break; - case kUnsignedHalf: - opcode = kMips64Lhu; - DCHECK_EQ((displacement & 0x1), 0); - break; - case kSignedHalf: - opcode = kMips64Lh; - DCHECK_EQ((displacement & 0x1), 0); - break; - case kUnsignedByte: - opcode = kMips64Lbu; - break; - case kSignedByte: - opcode = kMips64Lb; - break; - default: - LOG(FATAL) << "Bad case in LoadBaseIndexedBody"; - } - - if (short_form) { - load = res = NewLIR3(opcode, r_dest.GetReg(), displacement, r_base.GetReg()); - } else { - RegStorage r_tmp = (r_base == r_dest) ? AllocTemp() : r_dest; - res = OpRegRegImm(kOpAdd, r_tmp, r_base, displacement); - load = NewLIR3(opcode, r_dest.GetReg(), 0, r_tmp.GetReg()); - if (r_tmp != r_dest) - FreeTemp(r_tmp); - } - - if (mem_ref_type_ == ResourceMask::kDalvikReg) { - DCHECK_EQ(r_base, rs_rMIPS64_SP); - AnnotateDalvikRegAccess(load, displacement >> 2, true /* is_load */, r_dest.Is64Bit()); - } - return res; -} - -LIR* Mips64Mir2Lir::LoadBaseDisp(RegStorage r_base, int displacement, RegStorage r_dest, - OpSize size, VolatileKind is_volatile) { - if (UNLIKELY(is_volatile == kVolatile && (size == k64 || size == kDouble) && - displacement & 0x7)) { - // TODO: use lld/scd instructions for Mips64. - // Do atomic 64-bit load. - return GenAtomic64Load(r_base, displacement, r_dest); - } - - // TODO: base this on target. - if (size == kWord) { - size = k64; - } - LIR* load; - load = LoadBaseDispBody(r_base, displacement, r_dest, size); - - if (UNLIKELY(is_volatile == kVolatile)) { - GenMemBarrier(kLoadAny); - } - - return load; -} - -// FIXME: don't split r_dest into 2 containers. -LIR* Mips64Mir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size) { - LIR *res; - LIR *store = NULL; - Mips64OpCode opcode = kMips64Nop; - bool short_form = IS_SIMM16(displacement); - - switch (size) { - case k64: - case kDouble: - r_src = Check64BitReg(r_src); - if (!r_src.IsFloat()) - opcode = kMips64Sd; - else - opcode = kMips64Fsdc1; - DCHECK_EQ((displacement & 0x3), 0); - break; - case k32: - case kSingle: - case kReference: - opcode = kMips64Sw; - if (r_src.IsFloat()) { - opcode = kMips64Fswc1; - DCHECK(r_src.IsSingle()); - } - DCHECK_EQ((displacement & 0x3), 0); - break; - case kUnsignedHalf: - case kSignedHalf: - opcode = kMips64Sh; - DCHECK_EQ((displacement & 0x1), 0); - break; - case kUnsignedByte: - case kSignedByte: - opcode = kMips64Sb; - break; - default: - LOG(FATAL) << "Bad case in StoreBaseDispBody"; - } - - if (short_form) { - store = res = NewLIR3(opcode, r_src.GetReg(), displacement, r_base.GetReg()); - } else { - RegStorage r_scratch = AllocTemp(); - res = OpRegRegImm(kOpAdd, r_scratch, r_base, displacement); - store = NewLIR3(opcode, r_src.GetReg(), 0, r_scratch.GetReg()); - FreeTemp(r_scratch); - } - - if (mem_ref_type_ == ResourceMask::kDalvikReg) { - DCHECK_EQ(r_base, rs_rMIPS64_SP); - AnnotateDalvikRegAccess(store, displacement >> 2, false /* is_load */, r_src.Is64Bit()); - } - - return res; -} - -LIR* Mips64Mir2Lir::StoreBaseDisp(RegStorage r_base, int displacement, RegStorage r_src, - OpSize size, VolatileKind is_volatile) { - if (is_volatile == kVolatile) { - // Ensure that prior accesses become visible to other threads first. - GenMemBarrier(kAnyStore); - } - - LIR* store; - if (UNLIKELY(is_volatile == kVolatile && (size == k64 || size == kDouble) && - displacement & 0x7)) { - // TODO - use lld/scd instructions for Mips64 - // Do atomic 64-bit load. - store = GenAtomic64Store(r_base, displacement, r_src); - } else { - // TODO: base this on target. - if (size == kWord) { - size = k64; - } - store = StoreBaseDispBody(r_base, displacement, r_src, size); - } - - if (UNLIKELY(is_volatile == kVolatile)) { - // Preserve order with respect to any subsequent volatile loads. - // We need StoreLoad, but that generally requires the most expensive barrier. - GenMemBarrier(kAnyAny); - } - - return store; -} - -LIR* Mips64Mir2Lir::OpMem(OpKind op, RegStorage r_base, int disp) { - UNUSED(op, r_base, disp); - LOG(FATAL) << "Unexpected use of OpMem for MIPS64"; - UNREACHABLE(); -} - -LIR* Mips64Mir2Lir::OpCondBranch(ConditionCode cc, LIR* target) { - UNUSED(cc, target); - LOG(FATAL) << "Unexpected use of OpCondBranch for MIPS64"; - UNREACHABLE(); -} - -LIR* Mips64Mir2Lir::InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) { - UNUSED(trampoline); // The address of the trampoline is already loaded into r_tgt. - return OpReg(op, r_tgt); -} - -} // namespace art diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc index 1673312919..6d289843e8 100644 --- a/compiler/dex/quick/quick_compiler.cc +++ b/compiler/dex/quick/quick_compiler.cc @@ -45,7 +45,6 @@ #include "dex/quick/arm/backend_arm.h" #include "dex/quick/arm64/backend_arm64.h" #include "dex/quick/mips/backend_mips.h" -#include "dex/quick/mips64/backend_mips64.h" #include "dex/quick/x86/backend_x86.h" namespace art { @@ -808,10 +807,9 @@ Mir2Lir* QuickCompiler::GetCodeGenerator(CompilationUnit* cu, void* compilation_ mir_to_lir = Arm64CodeGenerator(cu, cu->mir_graph.get(), &cu->arena); break; case kMips: - mir_to_lir = MipsCodeGenerator(cu, cu->mir_graph.get(), &cu->arena); - break; + // Fall-through. case kMips64: - mir_to_lir = Mips64CodeGenerator(cu, cu->mir_graph.get(), &cu->arena); + mir_to_lir = MipsCodeGenerator(cu, cu->mir_graph.get(), &cu->arena); break; case kX86: // Fall-through. diff --git a/compiler/dex/quick_compiler_callbacks.h b/compiler/dex/quick_compiler_callbacks.h index cdf71b642e..d692d26229 100644 --- a/compiler/dex/quick_compiler_callbacks.h +++ b/compiler/dex/quick_compiler_callbacks.h @@ -27,8 +27,9 @@ class DexFileToMethodInlinerMap; class QuickCompilerCallbacks FINAL : public CompilerCallbacks { public: QuickCompilerCallbacks(VerificationResults* verification_results, - DexFileToMethodInlinerMap* method_inliner_map) - : verification_results_(verification_results), + DexFileToMethodInlinerMap* method_inliner_map, + CompilerCallbacks::CallbackMode mode) + : CompilerCallbacks(mode), verification_results_(verification_results), method_inliner_map_(method_inliner_map) { CHECK(verification_results != nullptr); CHECK(method_inliner_map != nullptr); diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index a02e25edde..5ebc029fcf 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -41,7 +41,7 @@ class CompilerDriverTest : public CommonCompilerTest { TimingLogger timings("CompilerDriverTest::CompileAll", false, false); TimingLogger::ScopedTiming t(__FUNCTION__, &timings); compiler_driver_->CompileAll(class_loader, - Runtime::Current()->GetCompileTimeClassPath(class_loader), + GetDexFiles(class_loader), &timings); t.NewTiming("MakeAllExecutable"); MakeAllExecutable(class_loader); @@ -66,8 +66,7 @@ class CompilerDriverTest : public CommonCompilerTest { } void MakeAllExecutable(jobject class_loader) { - const std::vector<const DexFile*>& class_path - = Runtime::Current()->GetCompileTimeClassPath(class_loader); + const std::vector<const DexFile*> class_path = GetDexFiles(class_loader); for (size_t i = 0; i != class_path.size(); ++i) { const DexFile* dex_file = class_path[i]; CHECK(dex_file != NULL); diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc index fd3a9121ae..8e2d175af9 100644 --- a/compiler/elf_writer_test.cc +++ b/compiler/elf_writer_test.cc @@ -46,11 +46,7 @@ class ElfWriterTest : public CommonCompilerTest { EXPECT_EQ(expected_value, ef->FindDynamicSymbolAddress(symbol_name)); \ } while (false) -#if defined(ART_USE_OPTIMIZING_COMPILER) -TEST_F(ElfWriterTest, DISABLED_dlsym) { -#else TEST_F(ElfWriterTest, dlsym) { -#endif std::string elf_location = GetCoreOatLocation(); std::string elf_filename = GetSystemImageFilename(elf_location.c_str(), kRuntimeISA); LOG(INFO) << "elf_filename=" << elf_filename; diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 8b311542f6..df5d5cca3b 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -89,7 +89,8 @@ JitCompiler::JitCompiler() : total_time_(0) { verification_results_.reset(new VerificationResults(compiler_options_.get())); method_inliner_map_.reset(new DexFileToMethodInlinerMap); callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(), - method_inliner_map_.get())); + method_inliner_map_.get(), + CompilerCallbacks::CallbackMode::kCompileApp)); compiler_driver_.reset(new CompilerDriver( compiler_options_.get(), verification_results_.get(), method_inliner_map_.get(), Compiler::kQuick, instruction_set, instruction_set_features_.get(), false, diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index c426625ae1..afd39e8874 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -85,8 +85,6 @@ TEST_F(OatTest, WriteRead) { compiler_options_.reset(new CompilerOptions); verification_results_.reset(new VerificationResults(compiler_options_.get())); method_inliner_map_.reset(new DexFileToMethodInlinerMap); - callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(), - method_inliner_map_.get())); timer_.reset(new CumulativeLogger("Compilation times")); compiler_driver_.reset(new CompilerDriver(compiler_options_.get(), verification_results_.get(), diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc index 0ecc0d7433..be432c5a20 100644 --- a/compiler/optimizing/boolean_simplifier.cc +++ b/compiler/optimizing/boolean_simplifier.cc @@ -59,21 +59,23 @@ static HInstruction* GetOppositeCondition(HInstruction* cond) { return new (allocator) HGreaterThan(lhs, rhs); } else if (cond->IsGreaterThan()) { return new (allocator) HLessThanOrEqual(lhs, rhs); - } else if (cond->IsGreaterThanOrEqual()) { + } else { + DCHECK(cond->IsGreaterThanOrEqual()); return new (allocator) HLessThan(lhs, rhs); } } else if (cond->IsIntConstant()) { HIntConstant* int_const = cond->AsIntConstant(); if (int_const->IsZero()) { - return graph->GetIntConstant1(); + return graph->GetIntConstant(1); } else { DCHECK(int_const->IsOne()); - return graph->GetIntConstant0(); + return graph->GetIntConstant(0); } + } else { + // General case when 'cond' is another instruction of type boolean. + // Negate with 'cond == 0'. + return new (allocator) HEqual(cond, graph->GetIntConstant(0)); } - - LOG(FATAL) << "Instruction " << cond->DebugName() << " used as a condition"; - UNREACHABLE(); } void HBooleanSimplifier::Run() { diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc index 24fa58317a..b3653fe903 100644 --- a/compiler/optimizing/bounds_check_elimination_test.cc +++ b/compiler/optimizing/bounds_check_elimination_test.cc @@ -52,12 +52,11 @@ TEST(BoundsCheckEliminationTest, NarrowingRangeArrayBoundsElimination) { HParameterValue(0, Primitive::kPrimNot); // array HInstruction* parameter2 = new (&allocator) HParameterValue(0, Primitive::kPrimInt); // i - HInstruction* constant_1 = new (&allocator) HIntConstant(1); - HInstruction* constant_0 = new (&allocator) HIntConstant(0); entry->AddInstruction(parameter1); entry->AddInstruction(parameter2); - entry->AddInstruction(constant_1); - entry->AddInstruction(constant_0); + + HInstruction* constant_1 = graph->GetIntConstant(1); + HInstruction* constant_0 = graph->GetIntConstant(0); HBasicBlock* block1 = new (&allocator) HBasicBlock(graph); graph->AddBlock(block1); @@ -158,14 +157,12 @@ TEST(BoundsCheckEliminationTest, OverflowArrayBoundsElimination) { HParameterValue(0, Primitive::kPrimNot); // array HInstruction* parameter2 = new (&allocator) HParameterValue(0, Primitive::kPrimInt); // i - HInstruction* constant_1 = new (&allocator) HIntConstant(1); - HInstruction* constant_0 = new (&allocator) HIntConstant(0); - HInstruction* constant_max_int = new (&allocator) HIntConstant(INT_MAX); entry->AddInstruction(parameter1); entry->AddInstruction(parameter2); - entry->AddInstruction(constant_1); - entry->AddInstruction(constant_0); - entry->AddInstruction(constant_max_int); + + HInstruction* constant_1 = graph->GetIntConstant(1); + HInstruction* constant_0 = graph->GetIntConstant(0); + HInstruction* constant_max_int = graph->GetIntConstant(INT_MAX); HBasicBlock* block1 = new (&allocator) HBasicBlock(graph); graph->AddBlock(block1); @@ -232,14 +229,12 @@ TEST(BoundsCheckEliminationTest, UnderflowArrayBoundsElimination) { HParameterValue(0, Primitive::kPrimNot); // array HInstruction* parameter2 = new (&allocator) HParameterValue(0, Primitive::kPrimInt); // i - HInstruction* constant_1 = new (&allocator) HIntConstant(1); - HInstruction* constant_0 = new (&allocator) HIntConstant(0); - HInstruction* constant_max_int = new (&allocator) HIntConstant(INT_MAX); entry->AddInstruction(parameter1); entry->AddInstruction(parameter2); - entry->AddInstruction(constant_1); - entry->AddInstruction(constant_0); - entry->AddInstruction(constant_max_int); + + HInstruction* constant_1 = graph->GetIntConstant(1); + HInstruction* constant_0 = graph->GetIntConstant(0); + HInstruction* constant_max_int = graph->GetIntConstant(INT_MAX); HBasicBlock* block1 = new (&allocator) HBasicBlock(graph); graph->AddBlock(block1); @@ -303,15 +298,12 @@ TEST(BoundsCheckEliminationTest, ConstantArrayBoundsElimination) { graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot); - HInstruction* constant_5 = new (&allocator) HIntConstant(5); - HInstruction* constant_4 = new (&allocator) HIntConstant(4); - HInstruction* constant_6 = new (&allocator) HIntConstant(6); - HInstruction* constant_1 = new (&allocator) HIntConstant(1); entry->AddInstruction(parameter); - entry->AddInstruction(constant_5); - entry->AddInstruction(constant_4); - entry->AddInstruction(constant_6); - entry->AddInstruction(constant_1); + + HInstruction* constant_5 = graph->GetIntConstant(5); + HInstruction* constant_4 = graph->GetIntConstant(4); + HInstruction* constant_6 = graph->GetIntConstant(6); + HInstruction* constant_1 = graph->GetIntConstant(1); HBasicBlock* block = new (&allocator) HBasicBlock(graph); graph->AddBlock(block); @@ -379,13 +371,11 @@ static HGraph* BuildSSAGraph1(ArenaAllocator* allocator, graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimNot); - HInstruction* constant_initial = new (allocator) HIntConstant(initial); - HInstruction* constant_increment = new (allocator) HIntConstant(increment); - HInstruction* constant_10 = new (allocator) HIntConstant(10); entry->AddInstruction(parameter); - entry->AddInstruction(constant_initial); - entry->AddInstruction(constant_increment); - entry->AddInstruction(constant_10); + + HInstruction* constant_initial = graph->GetIntConstant(initial); + HInstruction* constant_increment = graph->GetIntConstant(increment); + HInstruction* constant_10 = graph->GetIntConstant(10); HBasicBlock* block = new (allocator) HBasicBlock(graph); graph->AddBlock(block); @@ -518,15 +508,12 @@ static HGraph* BuildSSAGraph2(ArenaAllocator* allocator, graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimNot); - HInstruction* constant_initial = new (allocator) HIntConstant(initial); - HInstruction* constant_increment = new (allocator) HIntConstant(increment); - HInstruction* constant_minus_1 = new (allocator) HIntConstant(-1); - HInstruction* constant_10 = new (allocator) HIntConstant(10); entry->AddInstruction(parameter); - entry->AddInstruction(constant_initial); - entry->AddInstruction(constant_increment); - entry->AddInstruction(constant_minus_1); - entry->AddInstruction(constant_10); + + HInstruction* constant_initial = graph->GetIntConstant(initial); + HInstruction* constant_increment = graph->GetIntConstant(increment); + HInstruction* constant_minus_1 = graph->GetIntConstant(-1); + HInstruction* constant_10 = graph->GetIntConstant(10); HBasicBlock* block = new (allocator) HBasicBlock(graph); graph->AddBlock(block); @@ -651,12 +638,10 @@ static HGraph* BuildSSAGraph3(ArenaAllocator* allocator, HBasicBlock* entry = new (allocator) HBasicBlock(graph); graph->AddBlock(entry); graph->SetEntryBlock(entry); - HInstruction* constant_10 = new (allocator) HIntConstant(10); - HInstruction* constant_initial = new (allocator) HIntConstant(initial); - HInstruction* constant_increment = new (allocator) HIntConstant(increment); - entry->AddInstruction(constant_10); - entry->AddInstruction(constant_initial); - entry->AddInstruction(constant_increment); + + HInstruction* constant_10 = graph->GetIntConstant(10); + HInstruction* constant_initial = graph->GetIntConstant(initial); + HInstruction* constant_increment = graph->GetIntConstant(increment); HBasicBlock* block = new (allocator) HBasicBlock(graph); graph->AddBlock(block); @@ -765,15 +750,12 @@ static HGraph* BuildSSAGraph4(ArenaAllocator* allocator, graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimNot); - HInstruction* constant_initial = new (allocator) HIntConstant(initial); - HInstruction* constant_1 = new (allocator) HIntConstant(1); - HInstruction* constant_10 = new (allocator) HIntConstant(10); - HInstruction* constant_minus_1 = new (allocator) HIntConstant(-1); entry->AddInstruction(parameter); - entry->AddInstruction(constant_initial); - entry->AddInstruction(constant_1); - entry->AddInstruction(constant_10); - entry->AddInstruction(constant_minus_1); + + HInstruction* constant_initial = graph->GetIntConstant(initial); + HInstruction* constant_1 = graph->GetIntConstant(1); + HInstruction* constant_10 = graph->GetIntConstant(10); + HInstruction* constant_minus_1 = graph->GetIntConstant(-1); HBasicBlock* block = new (allocator) HBasicBlock(graph); graph->AddBlock(block); @@ -893,13 +875,11 @@ TEST(BoundsCheckEliminationTest, BubbleSortArrayBoundsElimination) { graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (&allocator) HParameterValue(0, Primitive::kPrimNot); - HInstruction* constant_0 = new (&allocator) HIntConstant(0); - HInstruction* constant_minus_1 = new (&allocator) HIntConstant(-1); - HInstruction* constant_1 = new (&allocator) HIntConstant(1); entry->AddInstruction(parameter); - entry->AddInstruction(constant_0); - entry->AddInstruction(constant_minus_1); - entry->AddInstruction(constant_1); + + HInstruction* constant_0 = graph->GetIntConstant(0); + HInstruction* constant_minus_1 = graph->GetIntConstant(-1); + HInstruction* constant_1 = graph->GetIntConstant(1); HBasicBlock* block = new (&allocator) HBasicBlock(graph); graph->AddBlock(block); diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index f81935a7c6..2cdd5af9f3 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -215,7 +215,7 @@ void HGraphBuilder::If_21t(const Instruction& instruction, uint32_t dex_pc) { DCHECK(fallthrough_target != nullptr); PotentiallyAddSuspendCheck(branch_target, dex_pc); HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt); - T* comparison = new (arena_) T(value, GetIntConstant(0)); + T* comparison = new (arena_) T(value, graph_->GetIntConstant(0)); current_block_->AddInstruction(comparison); HInstruction* ifinst = new (arena_) HIf(comparison); current_block_->AddInstruction(ifinst); @@ -515,7 +515,7 @@ void HGraphBuilder::Binop_12x(const Instruction& instruction, template<typename T> void HGraphBuilder::Binop_22s(const Instruction& instruction, bool reverse) { HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt); - HInstruction* second = GetIntConstant(instruction.VRegC_22s()); + HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22s()); if (reverse) { std::swap(first, second); } @@ -526,7 +526,7 @@ void HGraphBuilder::Binop_22s(const Instruction& instruction, bool reverse) { template<typename T> void HGraphBuilder::Binop_22b(const Instruction& instruction, bool reverse) { HInstruction* first = LoadLocal(instruction.VRegB(), Primitive::kPrimInt); - HInstruction* second = GetIntConstant(instruction.VRegC_22b()); + HInstruction* second = graph_->GetIntConstant(instruction.VRegC_22b()); if (reverse) { std::swap(first, second); } @@ -824,9 +824,9 @@ void HGraphBuilder::BuildCheckedDivRem(uint16_t out_vreg, HInstruction* second = nullptr; if (second_is_constant) { if (type == Primitive::kPrimInt) { - second = GetIntConstant(second_vreg_or_constant); + second = graph_->GetIntConstant(second_vreg_or_constant); } else { - second = GetLongConstant(second_vreg_or_constant); + second = graph_->GetLongConstant(second_vreg_or_constant); } } else { second = LoadLocal(second_vreg_or_constant, type); @@ -890,7 +890,7 @@ void HGraphBuilder::BuildFilledNewArray(uint32_t dex_pc, bool is_range, uint32_t* args, uint32_t register_index) { - HInstruction* length = GetIntConstant(number_of_vreg_arguments); + HInstruction* length = graph_->GetIntConstant(number_of_vreg_arguments); QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index) ? kQuickAllocArrayWithAccessCheck : kQuickAllocArray; @@ -910,7 +910,7 @@ void HGraphBuilder::BuildFilledNewArray(uint32_t dex_pc, temps.Add(object); for (size_t i = 0; i < number_of_vreg_arguments; ++i) { HInstruction* value = LoadLocal(is_range ? register_index + i : args[i], type); - HInstruction* index = GetIntConstant(i); + HInstruction* index = graph_->GetIntConstant(i); current_block_->AddInstruction( new (arena_) HArraySet(object, index, value, type, dex_pc)); } @@ -924,8 +924,8 @@ void HGraphBuilder::BuildFillArrayData(HInstruction* object, Primitive::Type anticipated_type, uint32_t dex_pc) { for (uint32_t i = 0; i < element_count; ++i) { - HInstruction* index = GetIntConstant(i); - HInstruction* value = GetIntConstant(data[i]); + HInstruction* index = graph_->GetIntConstant(i); + HInstruction* value = graph_->GetIntConstant(data[i]); current_block_->AddInstruction(new (arena_) HArraySet( object, index, value, anticipated_type, dex_pc)); } @@ -949,7 +949,7 @@ void HGraphBuilder::BuildFillArrayData(const Instruction& instruction, uint32_t // Implementation of this DEX instruction seems to be that the bounds check is // done before doing any stores. - HInstruction* last_index = GetIntConstant(payload->element_count - 1); + HInstruction* last_index = graph_->GetIntConstant(payload->element_count - 1); current_block_->AddInstruction(new (arena_) HBoundsCheck(last_index, length, dex_pc)); switch (payload->element_width) { @@ -990,8 +990,8 @@ void HGraphBuilder::BuildFillWideArrayData(HInstruction* object, uint32_t element_count, uint32_t dex_pc) { for (uint32_t i = 0; i < element_count; ++i) { - HInstruction* index = GetIntConstant(i); - HInstruction* value = GetLongConstant(data[i]); + HInstruction* index = graph_->GetIntConstant(i); + HInstruction* value = graph_->GetLongConstant(data[i]); current_block_->AddInstruction(new (arena_) HArraySet( object, index, value, Primitive::kPrimLong, dex_pc)); } @@ -1082,7 +1082,7 @@ void HGraphBuilder::BuildSwitchCaseHelper(const Instruction& instruction, size_t PotentiallyAddSuspendCheck(case_target, dex_pc); // The current case's value. - HInstruction* this_case_value = GetIntConstant(case_value_int); + HInstruction* this_case_value = graph_->GetIntConstant(case_value_int); // Compare value and this_case_value. HEqual* comparison = new (arena_) HEqual(value, this_case_value); @@ -1140,28 +1140,28 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 switch (instruction.Opcode()) { case Instruction::CONST_4: { int32_t register_index = instruction.VRegA(); - HIntConstant* constant = GetIntConstant(instruction.VRegB_11n()); + HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_11n()); UpdateLocal(register_index, constant); break; } case Instruction::CONST_16: { int32_t register_index = instruction.VRegA(); - HIntConstant* constant = GetIntConstant(instruction.VRegB_21s()); + HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21s()); UpdateLocal(register_index, constant); break; } case Instruction::CONST: { int32_t register_index = instruction.VRegA(); - HIntConstant* constant = GetIntConstant(instruction.VRegB_31i()); + HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_31i()); UpdateLocal(register_index, constant); break; } case Instruction::CONST_HIGH16: { int32_t register_index = instruction.VRegA(); - HIntConstant* constant = GetIntConstant(instruction.VRegB_21h() << 16); + HIntConstant* constant = graph_->GetIntConstant(instruction.VRegB_21h() << 16); UpdateLocal(register_index, constant); break; } @@ -1172,7 +1172,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 int64_t value = instruction.VRegB_21s(); value <<= 48; value >>= 48; - HLongConstant* constant = GetLongConstant(value); + HLongConstant* constant = graph_->GetLongConstant(value); UpdateLocal(register_index, constant); break; } @@ -1183,14 +1183,14 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 int64_t value = instruction.VRegB_31i(); value <<= 32; value >>= 32; - HLongConstant* constant = GetLongConstant(value); + HLongConstant* constant = graph_->GetLongConstant(value); UpdateLocal(register_index, constant); break; } case Instruction::CONST_WIDE: { int32_t register_index = instruction.VRegA(); - HLongConstant* constant = GetLongConstant(instruction.VRegB_51l()); + HLongConstant* constant = graph_->GetLongConstant(instruction.VRegB_51l()); UpdateLocal(register_index, constant); break; } @@ -1198,7 +1198,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 case Instruction::CONST_WIDE_HIGH16: { int32_t register_index = instruction.VRegA(); int64_t value = static_cast<int64_t>(instruction.VRegB_21h()) << 48; - HLongConstant* constant = GetLongConstant(value); + HLongConstant* constant = graph_->GetLongConstant(value); UpdateLocal(register_index, constant); break; } @@ -2100,24 +2100,6 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 return true; } // NOLINT(readability/fn_size) -HIntConstant* HGraphBuilder::GetIntConstant(int32_t constant) { - switch (constant) { - case 0: return graph_->GetIntConstant0(); - case 1: return graph_->GetIntConstant1(); - default: { - HIntConstant* instruction = new (arena_) HIntConstant(constant); - graph_->AddConstant(instruction); - return instruction; - } - } -} - -HLongConstant* HGraphBuilder::GetLongConstant(int64_t constant) { - HLongConstant* instruction = new (arena_) HLongConstant(constant); - graph_->AddConstant(instruction); - return instruction; -} - HLocal* HGraphBuilder::GetLocalAt(int register_index) const { return locals_.Get(register_index); } diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index b206660fdc..6a0738a7b9 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -96,8 +96,6 @@ class HGraphBuilder : public ValueObject { void MaybeUpdateCurrentBlock(size_t index); HBasicBlock* FindBlockStartingAt(int32_t index) const; - HIntConstant* GetIntConstant(int32_t constant); - HLongConstant* GetLongConstant(int64_t constant); void InitializeLocals(uint16_t count); HLocal* GetLocalAt(int register_index) const; void UpdateLocal(int register_index, HInstruction* instruction) const; diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 97c470b730..1f95041a92 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -41,12 +41,6 @@ static bool ExpectedPairLayout(Location location) { static constexpr int kCurrentMethodStackOffset = 0; -static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1, R2, R3 }; -static constexpr size_t kRuntimeParameterCoreRegistersLength = - arraysize(kRuntimeParameterCoreRegisters); -static constexpr SRegister kRuntimeParameterFpuRegisters[] = { S0, S1, S2, S3 }; -static constexpr size_t kRuntimeParameterFpuRegistersLength = - arraysize(kRuntimeParameterFpuRegisters); // We unconditionally allocate R5 to ensure we can do long operations // with baseline. static constexpr Register kCoreSavedRegisterForBaseline = R5; @@ -59,18 +53,6 @@ static constexpr SRegister kFpuCalleeSaves[] = // S registers. Therefore there is no need to block it. static constexpr DRegister DTMP = D31; -class InvokeRuntimeCallingConvention : public CallingConvention<Register, SRegister> { - public: - InvokeRuntimeCallingConvention() - : CallingConvention(kRuntimeParameterCoreRegisters, - kRuntimeParameterCoreRegistersLength, - kRuntimeParameterFpuRegisters, - kRuntimeParameterFpuRegistersLength) {} - - private: - DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); -}; - #define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())-> #define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmWordSize, x).Int32Value() @@ -2105,16 +2087,32 @@ void InstructionCodeGeneratorARM::VisitMul(HMul* mul) { } void LocationsBuilderARM::VisitDiv(HDiv* div) { - LocationSummary::CallKind call_kind = div->GetResultType() == Primitive::kPrimLong - ? LocationSummary::kCall - : LocationSummary::kNoCall; + LocationSummary::CallKind call_kind = LocationSummary::kNoCall; + if (div->GetResultType() == Primitive::kPrimLong) { + // pLdiv runtime call. + call_kind = LocationSummary::kCall; + } else if (div->GetResultType() == Primitive::kPrimInt && + !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + // pIdivmod runtime call. + call_kind = LocationSummary::kCall; + } + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(div, call_kind); switch (div->GetResultType()) { case Primitive::kPrimInt: { - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + } else { + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but + // we only need the former. + locations->SetOut(Location::RegisterLocation(R0)); + } break; } case Primitive::kPrimLong: { @@ -2147,9 +2145,18 @@ void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) { switch (div->GetResultType()) { case Primitive::kPrimInt: { - __ sdiv(out.AsRegister<Register>(), - first.AsRegister<Register>(), - second.AsRegister<Register>()); + if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + __ sdiv(out.AsRegister<Register>(), + first.AsRegister<Register>(), + second.AsRegister<Register>()); + } else { + InvokeRuntimeCallingConvention calling_convention; + DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegister<Register>()); + DCHECK_EQ(calling_convention.GetRegisterAt(1), second.AsRegister<Register>()); + DCHECK_EQ(R0, out.AsRegister<Register>()); + + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pIdivmod), div, div->GetDexPc(), nullptr); + } break; } @@ -2187,17 +2194,32 @@ void InstructionCodeGeneratorARM::VisitDiv(HDiv* div) { void LocationsBuilderARM::VisitRem(HRem* rem) { Primitive::Type type = rem->GetResultType(); - LocationSummary::CallKind call_kind = type == Primitive::kPrimInt - ? LocationSummary::kNoCall - : LocationSummary::kCall; + + // Most remainders are implemented in the runtime. + LocationSummary::CallKind call_kind = LocationSummary::kCall; + if (rem->GetResultType() == Primitive::kPrimInt && + codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + // Have hardware divide instruction for int, do it with three instructions. + call_kind = LocationSummary::kNoCall; + } + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(rem, call_kind); switch (type) { case Primitive::kPrimInt: { - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); - locations->AddTemp(Location::RequiresRegister()); + if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + locations->AddTemp(Location::RequiresRegister()); + } else { + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + // Note: divrem will compute both the quotient and the remainder as the pair R0 and R1, but + // we only need the latter. + locations->SetOut(Location::RegisterLocation(R1)); + } break; } case Primitive::kPrimLong: { @@ -2242,16 +2264,25 @@ void InstructionCodeGeneratorARM::VisitRem(HRem* rem) { Primitive::Type type = rem->GetResultType(); switch (type) { case Primitive::kPrimInt: { - Register reg1 = first.AsRegister<Register>(); - Register reg2 = second.AsRegister<Register>(); - Register temp = locations->GetTemp(0).AsRegister<Register>(); + if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) { + Register reg1 = first.AsRegister<Register>(); + Register reg2 = second.AsRegister<Register>(); + Register temp = locations->GetTemp(0).AsRegister<Register>(); + + // temp = reg1 / reg2 (integer division) + // temp = temp * reg2 + // dest = reg1 - temp + __ sdiv(temp, reg1, reg2); + __ mul(temp, temp, reg2); + __ sub(out.AsRegister<Register>(), reg1, ShifterOperand(temp)); + } else { + InvokeRuntimeCallingConvention calling_convention; + DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegister<Register>()); + DCHECK_EQ(calling_convention.GetRegisterAt(1), second.AsRegister<Register>()); + DCHECK_EQ(R1, out.AsRegister<Register>()); - // temp = reg1 / reg2 (integer division) - // temp = temp * reg2 - // dest = reg1 - temp - __ sdiv(temp, reg1, reg2); - __ mul(temp, temp, reg2); - __ sub(out.AsRegister<Register>(), reg1, ShifterOperand(temp)); + codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pIdivmod), rem, rem->GetDexPc(), nullptr); + } break; } diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 57e1d2f2f5..bcdea7a639 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -41,6 +41,25 @@ static constexpr size_t kParameterFpuRegistersLength = arraysize(kParameterFpuRe static constexpr Register kArtMethodRegister = R0; +static constexpr Register kRuntimeParameterCoreRegisters[] = { R0, R1, R2, R3 }; +static constexpr size_t kRuntimeParameterCoreRegistersLength = + arraysize(kRuntimeParameterCoreRegisters); +static constexpr SRegister kRuntimeParameterFpuRegisters[] = { S0, S1, S2, S3 }; +static constexpr size_t kRuntimeParameterFpuRegistersLength = + arraysize(kRuntimeParameterFpuRegisters); + +class InvokeRuntimeCallingConvention : public CallingConvention<Register, SRegister> { + public: + InvokeRuntimeCallingConvention() + : CallingConvention(kRuntimeParameterCoreRegisters, + kRuntimeParameterCoreRegistersLength, + kRuntimeParameterFpuRegisters, + kRuntimeParameterFpuRegistersLength) {} + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); +}; + static constexpr DRegister FromLowSToD(SRegister reg) { return DCHECK_CONSTEXPR(reg % 2 == 0, , D0) static_cast<DRegister>(reg / 2); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 9455a918d4..32ada3837e 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -98,29 +98,6 @@ Location ARM64ReturnLocation(Primitive::Type return_type) { } } -static const Register kRuntimeParameterCoreRegisters[] = { x0, x1, x2, x3, x4, x5, x6, x7 }; -static constexpr size_t kRuntimeParameterCoreRegistersLength = - arraysize(kRuntimeParameterCoreRegisters); -static const FPRegister kRuntimeParameterFpuRegisters[] = { d0, d1, d2, d3, d4, d5, d6, d7 }; -static constexpr size_t kRuntimeParameterFpuRegistersLength = - arraysize(kRuntimeParameterCoreRegisters); - -class InvokeRuntimeCallingConvention : public CallingConvention<Register, FPRegister> { - public: - static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters); - - InvokeRuntimeCallingConvention() - : CallingConvention(kRuntimeParameterCoreRegisters, - kRuntimeParameterCoreRegistersLength, - kRuntimeParameterFpuRegisters, - kRuntimeParameterFpuRegistersLength) {} - - Location GetReturnLocation(Primitive::Type return_type); - - private: - DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); -}; - Location InvokeRuntimeCallingConvention::GetReturnLocation(Primitive::Type return_type) { return ARM64ReturnLocation(return_type); } diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index cbb2e5c749..2c624d2926 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -80,6 +80,31 @@ class SlowPathCodeARM64 : public SlowPathCode { DISALLOW_COPY_AND_ASSIGN(SlowPathCodeARM64); }; +static const vixl::Register kRuntimeParameterCoreRegisters[] = + { vixl::x0, vixl::x1, vixl::x2, vixl::x3, vixl::x4, vixl::x5, vixl::x6, vixl::x7 }; +static constexpr size_t kRuntimeParameterCoreRegistersLength = + arraysize(kRuntimeParameterCoreRegisters); +static const vixl::FPRegister kRuntimeParameterFpuRegisters[] = + { vixl::d0, vixl::d1, vixl::d2, vixl::d3, vixl::d4, vixl::d5, vixl::d6, vixl::d7 }; +static constexpr size_t kRuntimeParameterFpuRegistersLength = + arraysize(kRuntimeParameterCoreRegisters); + +class InvokeRuntimeCallingConvention : public CallingConvention<vixl::Register, vixl::FPRegister> { + public: + static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters); + + InvokeRuntimeCallingConvention() + : CallingConvention(kRuntimeParameterCoreRegisters, + kRuntimeParameterCoreRegistersLength, + kRuntimeParameterFpuRegisters, + kRuntimeParameterFpuRegistersLength) {} + + Location GetReturnLocation(Primitive::Type return_type); + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); +}; + class InvokeDexCallingConvention : public CallingConvention<vixl::Register, vixl::FPRegister> { public: InvokeDexCallingConvention() diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 4414a65efa..8d0ca0beb9 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -19,6 +19,8 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "gc/accounting/card_table.h" +#include "intrinsics.h" +#include "intrinsics_x86.h" #include "mirror/array-inl.h" #include "mirror/art_method.h" #include "mirror/class.h" @@ -34,46 +36,14 @@ namespace x86 { static constexpr int kCurrentMethodStackOffset = 0; -static constexpr Register kRuntimeParameterCoreRegisters[] = { EAX, ECX, EDX, EBX }; -static constexpr size_t kRuntimeParameterCoreRegistersLength = - arraysize(kRuntimeParameterCoreRegisters); static constexpr Register kCoreCalleeSaves[] = { EBP, ESI, EDI }; -static constexpr XmmRegister kRuntimeParameterFpuRegisters[] = { XMM0, XMM1, XMM2, XMM3 }; -static constexpr size_t kRuntimeParameterFpuRegistersLength = - arraysize(kRuntimeParameterFpuRegisters); static constexpr int kC2ConditionMask = 0x400; static constexpr int kFakeReturnRegister = Register(8); -class InvokeRuntimeCallingConvention : public CallingConvention<Register, XmmRegister> { - public: - InvokeRuntimeCallingConvention() - : CallingConvention(kRuntimeParameterCoreRegisters, - kRuntimeParameterCoreRegistersLength, - kRuntimeParameterFpuRegisters, - kRuntimeParameterFpuRegistersLength) {} - - private: - DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); -}; - #define __ reinterpret_cast<X86Assembler*>(codegen->GetAssembler())-> -class SlowPathCodeX86 : public SlowPathCode { - public: - SlowPathCodeX86() : entry_label_(), exit_label_() {} - - Label* GetEntryLabel() { return &entry_label_; } - Label* GetExitLabel() { return &exit_label_; } - - private: - Label entry_label_; - Label exit_label_; - - DISALLOW_COPY_AND_ASSIGN(SlowPathCodeX86); -}; - class NullCheckSlowPathX86 : public SlowPathCodeX86 { public: explicit NullCheckSlowPathX86(HNullCheck* instruction) : instruction_(instruction) {} @@ -1140,35 +1110,30 @@ void InstructionCodeGeneratorX86::VisitReturn(HReturn* ret) { } void LocationsBuilderX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { + IntrinsicLocationsBuilderX86 intrinsic(GetGraph()->GetArena()); + if (intrinsic.TryDispatch(invoke)) { + return; + } + HandleInvoke(invoke); } -void InstructionCodeGeneratorX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { - Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>(); - - // TODO: Implement all kinds of calls: - // 1) boot -> boot - // 2) app -> boot - // 3) app -> app - // - // Currently we implement the app -> app logic, which looks up in the resolve cache. +static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorX86* codegen) { + if (invoke->GetLocations()->Intrinsified()) { + IntrinsicCodeGeneratorX86 intrinsic(codegen); + intrinsic.Dispatch(invoke); + return true; + } + return false; +} - // temp = method; - codegen_->LoadCurrentMethod(temp); - if (!invoke->IsRecursive()) { - // temp = temp->dex_cache_resolved_methods_; - __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value())); - // temp = temp[index_in_cache] - __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex()))); - // (temp + offset_of_quick_compiled_code)() - __ call(Address( - temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value())); - } else { - __ call(codegen_->GetFrameEntryLabel()); +void InstructionCodeGeneratorX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { + if (TryGenerateIntrinsicCode(invoke, codegen_)) { + return; } - DCHECK(!codegen_->IsLeafMethod()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + codegen_->GenerateStaticOrDirectCall( + invoke, invoke->GetLocations()->GetTemp(0).AsRegister<Register>()); } void LocationsBuilderX86::VisitInvokeVirtual(HInvokeVirtual* invoke) { @@ -2863,6 +2828,32 @@ void InstructionCodeGeneratorX86::GenerateMemoryBarrier(MemBarrierKind kind) { } +void CodeGeneratorX86::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, + Register temp) { + // TODO: Implement all kinds of calls: + // 1) boot -> boot + // 2) app -> boot + // 3) app -> app + // + // Currently we implement the app -> app logic, which looks up in the resolve cache. + // temp = method; + LoadCurrentMethod(temp); + if (!invoke->IsRecursive()) { + // temp = temp->dex_cache_resolved_methods_; + __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value())); + // temp = temp[index_in_cache] + __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetDexMethodIndex()))); + // (temp + offset_of_quick_compiled_code)() + __ call(Address( + temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset(kX86WordSize).Int32Value())); + } else { + __ call(GetFrameEntryLabel()); + } + + DCHECK(!IsLeafMethod()); + RecordPcInfo(invoke, invoke->GetDexPc()); +} + void CodeGeneratorX86::MarkGCCard(Register temp, Register card, Register object, Register value) { Label is_null; __ testl(value, value); diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index c5763de05e..6a4d42dd01 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -39,6 +39,25 @@ static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCore static constexpr XmmRegister kParameterFpuRegisters[] = { XMM0, XMM1, XMM2, XMM3 }; static constexpr size_t kParameterFpuRegistersLength = arraysize(kParameterFpuRegisters); +static constexpr Register kRuntimeParameterCoreRegisters[] = { EAX, ECX, EDX, EBX }; +static constexpr size_t kRuntimeParameterCoreRegistersLength = + arraysize(kRuntimeParameterCoreRegisters); +static constexpr XmmRegister kRuntimeParameterFpuRegisters[] = { XMM0, XMM1, XMM2, XMM3 }; +static constexpr size_t kRuntimeParameterFpuRegistersLength = + arraysize(kRuntimeParameterFpuRegisters); + +class InvokeRuntimeCallingConvention : public CallingConvention<Register, XmmRegister> { + public: + InvokeRuntimeCallingConvention() + : CallingConvention(kRuntimeParameterCoreRegisters, + kRuntimeParameterCoreRegistersLength, + kRuntimeParameterFpuRegisters, + kRuntimeParameterFpuRegistersLength) {} + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); +}; + class InvokeDexCallingConvention : public CallingConvention<Register, XmmRegister> { public: InvokeDexCallingConvention() : CallingConvention( @@ -228,6 +247,9 @@ class CodeGeneratorX86 : public CodeGenerator { // Helper method to move a 64bits value between two locations. void Move64(Location destination, Location source); + // Generate a call to a static or direct method. + void GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invoke, Register temp); + // Emit a write barrier. void MarkGCCard(Register temp, Register card, Register object, Register value); @@ -261,6 +283,20 @@ class CodeGeneratorX86 : public CodeGenerator { DISALLOW_COPY_AND_ASSIGN(CodeGeneratorX86); }; +class SlowPathCodeX86 : public SlowPathCode { + public: + SlowPathCodeX86() : entry_label_(), exit_label_() {} + + Label* GetEntryLabel() { return &entry_label_; } + Label* GetExitLabel() { return &exit_label_; } + + private: + Label entry_label_; + Label exit_label_; + + DISALLOW_COPY_AND_ASSIGN(SlowPathCodeX86); +}; + } // namespace x86 } // namespace art diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index c1f601e6d4..ef60280016 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -39,28 +39,11 @@ static constexpr Register TMP = R11; static constexpr int kCurrentMethodStackOffset = 0; -static constexpr Register kRuntimeParameterCoreRegisters[] = { RDI, RSI, RDX }; -static constexpr size_t kRuntimeParameterCoreRegistersLength = - arraysize(kRuntimeParameterCoreRegisters); -static constexpr FloatRegister kRuntimeParameterFpuRegisters[] = { XMM0, XMM1 }; -static constexpr size_t kRuntimeParameterFpuRegistersLength = - arraysize(kRuntimeParameterFpuRegisters); static constexpr Register kCoreCalleeSaves[] = { RBX, RBP, R12, R13, R14, R15 }; static constexpr FloatRegister kFpuCalleeSaves[] = { XMM12, XMM13, XMM14, XMM15 }; static constexpr int kC2ConditionMask = 0x400; -class InvokeRuntimeCallingConvention : public CallingConvention<Register, FloatRegister> { - public: - InvokeRuntimeCallingConvention() - : CallingConvention(kRuntimeParameterCoreRegisters, - kRuntimeParameterCoreRegistersLength, - kRuntimeParameterFpuRegisters, - kRuntimeParameterFpuRegistersLength) {} - - private: - DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); -}; #define __ reinterpret_cast<X86_64Assembler*>(codegen->GetAssembler())-> diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 707c9992c0..a380b6a04c 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -37,6 +37,25 @@ static constexpr FloatRegister kParameterFloatRegisters[] = static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters); static constexpr size_t kParameterFloatRegistersLength = arraysize(kParameterFloatRegisters); +static constexpr Register kRuntimeParameterCoreRegisters[] = { RDI, RSI, RDX }; +static constexpr size_t kRuntimeParameterCoreRegistersLength = + arraysize(kRuntimeParameterCoreRegisters); +static constexpr FloatRegister kRuntimeParameterFpuRegisters[] = { XMM0, XMM1 }; +static constexpr size_t kRuntimeParameterFpuRegistersLength = + arraysize(kRuntimeParameterFpuRegisters); + +class InvokeRuntimeCallingConvention : public CallingConvention<Register, FloatRegister> { + public: + InvokeRuntimeCallingConvention() + : CallingConvention(kRuntimeParameterCoreRegisters, + kRuntimeParameterCoreRegistersLength, + kRuntimeParameterFpuRegisters, + kRuntimeParameterFpuRegistersLength) {} + + private: + DISALLOW_COPY_AND_ASSIGN(InvokeRuntimeCallingConvention); +}; + class InvokeDexCallingConvention : public CallingConvention<Register, FloatRegister> { public: InvokeDexCallingConvention() : CallingConvention( diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc index 40f0adc63d..6053ad51f4 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -474,10 +474,8 @@ TEST(CodegenTest, NonMaterializedCondition) { HBasicBlock* first_block = new (&allocator) HBasicBlock(graph); graph->AddBlock(first_block); entry->AddSuccessor(first_block); - HIntConstant* constant0 = new (&allocator) HIntConstant(0); - entry->AddInstruction(constant0); - HIntConstant* constant1 = new (&allocator) HIntConstant(1); - entry->AddInstruction(constant1); + HIntConstant* constant0 = graph->GetIntConstant(0); + HIntConstant* constant1 = graph->GetIntConstant(1); HEqual* equal = new (&allocator) HEqual(constant0, constant0); first_block->AddInstruction(equal); first_block->AddInstruction(new (&allocator) HIf(equal)); @@ -582,11 +580,9 @@ TEST(CodegenTest, MaterializedCondition1) { code_block->AddSuccessor(exit_block); graph->SetExitBlock(exit_block); - HIntConstant cst_lhs(lhs[i]); - code_block->AddInstruction(&cst_lhs); - HIntConstant cst_rhs(rhs[i]); - code_block->AddInstruction(&cst_rhs); - HLessThan cmp_lt(&cst_lhs, &cst_rhs); + HIntConstant* cst_lhs = graph->GetIntConstant(lhs[i]); + HIntConstant* cst_rhs = graph->GetIntConstant(rhs[i]); + HLessThan cmp_lt(cst_lhs, cst_rhs); code_block->AddInstruction(&cmp_lt); HReturn ret(&cmp_lt); code_block->AddInstruction(&ret); @@ -639,11 +635,9 @@ TEST(CodegenTest, MaterializedCondition2) { if_false_block->AddSuccessor(exit_block); graph->SetExitBlock(exit_block); - HIntConstant cst_lhs(lhs[i]); - if_block->AddInstruction(&cst_lhs); - HIntConstant cst_rhs(rhs[i]); - if_block->AddInstruction(&cst_rhs); - HLessThan cmp_lt(&cst_lhs, &cst_rhs); + HIntConstant* cst_lhs = graph->GetIntConstant(lhs[i]); + HIntConstant* cst_rhs = graph->GetIntConstant(rhs[i]); + HLessThan cmp_lt(cst_lhs, cst_rhs); if_block->AddInstruction(&cmp_lt); // We insert a temporary to separate the HIf from the HLessThan and force // the materialization of the condition. @@ -652,13 +646,11 @@ TEST(CodegenTest, MaterializedCondition2) { HIf if_lt(&cmp_lt); if_block->AddInstruction(&if_lt); - HIntConstant cst_lt(1); - if_true_block->AddInstruction(&cst_lt); - HReturn ret_lt(&cst_lt); + HIntConstant* cst_lt = graph->GetIntConstant(1); + HReturn ret_lt(cst_lt); if_true_block->AddInstruction(&ret_lt); - HIntConstant cst_ge(0); - if_false_block->AddInstruction(&cst_ge); - HReturn ret_ge(&cst_ge); + HIntConstant* cst_ge = graph->GetIntConstant(0); + HReturn ret_ge(cst_ge); if_false_block->AddInstruction(&ret_ge); auto hook_before_codegen = [](HGraph* graph_in) { diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc index ec0cc3e98b..b7a92b5ae5 100644 --- a/compiler/optimizing/constant_folding.cc +++ b/compiler/optimizing/constant_folding.cc @@ -55,20 +55,20 @@ void HConstantFolding::Run() { if (inst->IsBinaryOperation()) { // Constant folding: replace `op(a, b)' with a constant at // compile time if `a' and `b' are both constants. - HConstant* constant = - inst->AsBinaryOperation()->TryStaticEvaluation(); + HConstant* constant = inst->AsBinaryOperation()->TryStaticEvaluation(); if (constant != nullptr) { - inst->GetBlock()->ReplaceAndRemoveInstructionWith(inst, constant); + inst->ReplaceWith(constant); + inst->GetBlock()->RemoveInstruction(inst); } else { inst->Accept(&simplifier); } } else if (inst->IsUnaryOperation()) { // Constant folding: replace `op(a)' with a constant at compile // time if `a' is a constant. - HConstant* constant = - inst->AsUnaryOperation()->TryStaticEvaluation(); + HConstant* constant = inst->AsUnaryOperation()->TryStaticEvaluation(); if (constant != nullptr) { - inst->GetBlock()->ReplaceAndRemoveInstructionWith(inst, constant); + inst->ReplaceWith(constant); + inst->GetBlock()->RemoveInstruction(inst); } } else if (inst->IsDivZeroCheck()) { // We can safely remove the check if the input is a non-null constant. @@ -173,9 +173,8 @@ void InstructionWithAbsorbingInputSimplifier::VisitRem(HRem* instruction) { // REM dst, src, src // with // CONSTANT 0 - ArenaAllocator* allocator = GetGraph()->GetArena(); - block->ReplaceAndRemoveInstructionWith(instruction, - HConstant::NewConstant(allocator, type, 0)); + instruction->ReplaceWith(GetGraph()->GetConstant(type, 0)); + block->RemoveInstruction(instruction); } } @@ -195,7 +194,6 @@ void InstructionWithAbsorbingInputSimplifier::VisitSub(HSub* instruction) { } HBasicBlock* block = instruction->GetBlock(); - ArenaAllocator* allocator = GetGraph()->GetArena(); // We assume that GVN has run before, so we only perform a pointer // comparison. If for some reason the values are equal but the pointers are @@ -208,8 +206,8 @@ void InstructionWithAbsorbingInputSimplifier::VisitSub(HSub* instruction) { // CONSTANT 0 // Note that we cannot optimise `x - x` to `0` for floating-point. It does // not work when `x` is an infinity. - block->ReplaceAndRemoveInstructionWith(instruction, - HConstant::NewConstant(allocator, type, 0)); + instruction->ReplaceWith(GetGraph()->GetConstant(type, 0)); + block->RemoveInstruction(instruction); } } @@ -225,10 +223,8 @@ void InstructionWithAbsorbingInputSimplifier::VisitXor(HXor* instruction) { // CONSTANT 0 Primitive::Type type = instruction->GetType(); HBasicBlock* block = instruction->GetBlock(); - ArenaAllocator* allocator = GetGraph()->GetArena(); - - block->ReplaceAndRemoveInstructionWith(instruction, - HConstant::NewConstant(allocator, type, 0)); + instruction->ReplaceWith(GetGraph()->GetConstant(type, 0)); + block->RemoveInstruction(instruction); } } diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc index 6ceccfbf0e..6853d54c48 100644 --- a/compiler/optimizing/constant_folding_test.cc +++ b/compiler/optimizing/constant_folding_test.cc @@ -101,14 +101,16 @@ TEST(ConstantFolding, IntConstantFoldingNegation) { // Expected difference after constant folding. diff_t expected_cf_diff = { { " 2: IntConstant [5]\n", " 2: IntConstant\n" }, - { " 5: Neg(2) [8]\n", " 12: IntConstant [8]\n" }, + { " 10: SuspendCheck\n", " 10: SuspendCheck\n" + " 12: IntConstant [8]\n" }, + { " 5: Neg(2) [8]\n", removed }, { " 8: Return(5)\n", " 8: Return(12)\n" } }; std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the value of the computed constant. auto check_after_cf = [](HGraph* graph) { - HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction(); + HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction()->InputAt(0); ASSERT_TRUE(inst->IsIntConstant()); ASSERT_EQ(inst->AsIntConstant()->GetValue(), -1); }; @@ -160,14 +162,16 @@ TEST(ConstantFolding, IntConstantFoldingOnAddition1) { diff_t expected_cf_diff = { { " 3: IntConstant [9]\n", " 3: IntConstant\n" }, { " 5: IntConstant [9]\n", " 5: IntConstant\n" }, - { " 9: Add(3, 5) [12]\n", " 16: IntConstant [12]\n" }, + { " 14: SuspendCheck\n", " 14: SuspendCheck\n" + " 16: IntConstant [12]\n" }, + { " 9: Add(3, 5) [12]\n", removed }, { " 12: Return(9)\n", " 12: Return(16)\n" } }; std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the value of the computed constant. auto check_after_cf = [](HGraph* graph) { - HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction(); + HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction()->InputAt(0); ASSERT_TRUE(inst->IsIntConstant()); ASSERT_EQ(inst->AsIntConstant()->GetValue(), 3); }; @@ -195,8 +199,8 @@ TEST(ConstantFolding, IntConstantFoldingOnAddition1) { * v0 <- 1 0. const/4 v0, #+1 * v1 <- 2 1. const/4 v1, #+2 * v0 <- v0 + v1 2. add-int/2addr v0, v1 - * v1 <- 3 3. const/4 v1, #+3 - * v2 <- 4 4. const/4 v2, #+4 + * v1 <- 4 3. const/4 v1, #+4 + * v2 <- 5 4. const/4 v2, #+5 * v1 <- v1 + v2 5. add-int/2addr v1, v2 * v2 <- v0 + v1 6. add-int v2, v0, v1 * return v2 8. return v2 @@ -206,8 +210,8 @@ TEST(ConstantFolding, IntConstantFoldingOnAddition2) { Instruction::CONST_4 | 0 << 8 | 1 << 12, Instruction::CONST_4 | 1 << 8 | 2 << 12, Instruction::ADD_INT_2ADDR | 0 << 8 | 1 << 12, - Instruction::CONST_4 | 1 << 8 | 3 << 12, - Instruction::CONST_4 | 2 << 8 | 4 << 12, + Instruction::CONST_4 | 1 << 8 | 4 << 12, + Instruction::CONST_4 | 2 << 8 | 5 << 12, Instruction::ADD_INT_2ADDR | 1 << 8 | 2 << 12, Instruction::ADD_INT | 2 << 8, 0 | 1 << 8, Instruction::RETURN | 2 << 8); @@ -234,24 +238,28 @@ TEST(ConstantFolding, IntConstantFoldingOnAddition2) { { " 5: IntConstant [9]\n", " 5: IntConstant\n" }, { " 11: IntConstant [17]\n", " 11: IntConstant\n" }, { " 13: IntConstant [17]\n", " 13: IntConstant\n" }, - { " 9: Add(3, 5) [21]\n", " 28: IntConstant\n" }, - { " 17: Add(11, 13) [21]\n", " 29: IntConstant\n" }, - { " 21: Add(9, 17) [24]\n", " 30: IntConstant [24]\n" }, + { " 26: SuspendCheck\n", " 26: SuspendCheck\n" + " 28: IntConstant\n" + " 29: IntConstant\n" + " 30: IntConstant [24]\n" }, + { " 9: Add(3, 5) [21]\n", removed }, + { " 17: Add(11, 13) [21]\n", removed }, + { " 21: Add(9, 17) [24]\n", removed }, { " 24: Return(21)\n", " 24: Return(30)\n" } }; std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the values of the computed constants. auto check_after_cf = [](HGraph* graph) { - HInstruction* inst1 = graph->GetBlock(1)->GetFirstInstruction(); + HInstruction* inst1 = graph->GetBlock(1)->GetFirstInstruction()->InputAt(0); ASSERT_TRUE(inst1->IsIntConstant()); - ASSERT_EQ(inst1->AsIntConstant()->GetValue(), 3); - HInstruction* inst2 = inst1->GetNext(); + ASSERT_EQ(inst1->AsIntConstant()->GetValue(), 12); + HInstruction* inst2 = inst1->GetPrevious(); ASSERT_TRUE(inst2->IsIntConstant()); - ASSERT_EQ(inst2->AsIntConstant()->GetValue(), 7); - HInstruction* inst3 = inst2->GetNext(); + ASSERT_EQ(inst2->AsIntConstant()->GetValue(), 9); + HInstruction* inst3 = inst2->GetPrevious(); ASSERT_TRUE(inst3->IsIntConstant()); - ASSERT_EQ(inst3->AsIntConstant()->GetValue(), 10); + ASSERT_EQ(inst3->AsIntConstant()->GetValue(), 3); }; // Expected difference after dead code elimination. @@ -306,14 +314,16 @@ TEST(ConstantFolding, IntConstantFoldingOnSubtraction) { diff_t expected_cf_diff = { { " 3: IntConstant [9]\n", " 3: IntConstant\n" }, { " 5: IntConstant [9]\n", " 5: IntConstant\n" }, - { " 9: Sub(3, 5) [12]\n", " 16: IntConstant [12]\n" }, + { " 14: SuspendCheck\n", " 14: SuspendCheck\n" + " 16: IntConstant [12]\n" }, + { " 9: Sub(3, 5) [12]\n", removed }, { " 12: Return(9)\n", " 12: Return(16)\n" } }; std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the value of the computed constant. auto check_after_cf = [](HGraph* graph) { - HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction(); + HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction()->InputAt(0); ASSERT_TRUE(inst->IsIntConstant()); ASSERT_EQ(inst->AsIntConstant()->GetValue(), 1); }; @@ -368,14 +378,16 @@ TEST(ConstantFolding, LongConstantFoldingOnAddition) { diff_t expected_cf_diff = { { " 6: LongConstant [12]\n", " 6: LongConstant\n" }, { " 8: LongConstant [12]\n", " 8: LongConstant\n" }, - { " 12: Add(6, 8) [15]\n", " 19: LongConstant [15]\n" }, + { " 17: SuspendCheck\n", " 17: SuspendCheck\n" + " 19: LongConstant [15]\n" }, + { " 12: Add(6, 8) [15]\n", removed }, { " 15: Return(12)\n", " 15: Return(19)\n" } }; std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the value of the computed constant. auto check_after_cf = [](HGraph* graph) { - HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction(); + HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction()->InputAt(0); ASSERT_TRUE(inst->IsLongConstant()); ASSERT_EQ(inst->AsLongConstant()->GetValue(), 3); }; @@ -431,14 +443,16 @@ TEST(ConstantFolding, LongConstantFoldingOnSubtraction) { diff_t expected_cf_diff = { { " 6: LongConstant [12]\n", " 6: LongConstant\n" }, { " 8: LongConstant [12]\n", " 8: LongConstant\n" }, - { " 12: Sub(6, 8) [15]\n", " 19: LongConstant [15]\n" }, + { " 17: SuspendCheck\n", " 17: SuspendCheck\n" + " 19: LongConstant [15]\n" }, + { " 12: Sub(6, 8) [15]\n", removed }, { " 15: Return(12)\n", " 15: Return(19)\n" } }; std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the value of the computed constant. auto check_after_cf = [](HGraph* graph) { - HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction(); + HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction()->InputAt(0); ASSERT_TRUE(inst->IsLongConstant()); ASSERT_EQ(inst->AsLongConstant()->GetValue(), 1); }; @@ -469,51 +483,51 @@ TEST(ConstantFolding, LongConstantFoldingOnSubtraction) { * 16-bit * offset * ------ - * v0 <- 0 0. const/4 v0, #+0 - * v1 <- 1 1. const/4 v1, #+1 + * v0 <- 1 0. const/4 v0, #+1 + * v1 <- 2 1. const/4 v1, #+2 * v2 <- v0 + v1 2. add-int v2, v0, v1 * goto L2 4. goto +4 - * L1: v1 <- v0 + 3 5. add-int/lit16 v1, v0, #+3 + * L1: v1 <- v0 + 5 5. add-int/lit16 v1, v0, #+5 * goto L3 7. goto +4 - * L2: v0 <- v2 + 2 8. add-int/lit16 v0, v2, #+2 + * L2: v0 <- v2 + 4 8. add-int/lit16 v0, v2, #+4 * goto L1 10. goto +(-5) - * L3: v2 <- v1 + 4 11. add-int/lit16 v2, v1, #+4 + * L3: v2 <- v1 + 8 11. add-int/lit16 v2, v1, #+8 * return v2 13. return v2 */ TEST(ConstantFolding, IntConstantFoldingAndJumps) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( - Instruction::CONST_4 | 0 << 8 | 0 << 12, - Instruction::CONST_4 | 1 << 8 | 1 << 12, + Instruction::CONST_4 | 0 << 8 | 1 << 12, + Instruction::CONST_4 | 1 << 8 | 2 << 12, Instruction::ADD_INT | 2 << 8, 0 | 1 << 8, Instruction::GOTO | 4 << 8, - Instruction::ADD_INT_LIT16 | 1 << 8 | 0 << 12, 3, + Instruction::ADD_INT_LIT16 | 1 << 8 | 0 << 12, 5, Instruction::GOTO | 4 << 8, - Instruction::ADD_INT_LIT16 | 0 << 8 | 2 << 12, 2, + Instruction::ADD_INT_LIT16 | 0 << 8 | 2 << 12, 4, static_cast<uint16_t>(Instruction::GOTO | -5 << 8), - Instruction::ADD_INT_LIT16 | 2 << 8 | 1 << 12, 4, + Instruction::ADD_INT_LIT16 | 2 << 8 | 1 << 12, 8, Instruction::RETURN | 2 << 8); std::string expected_before = "BasicBlock 0, succ: 1\n" - " 3: IntConstant [9]\n" // v0 <- 0 - " 5: IntConstant [9]\n" // v1 <- 1 - " 13: IntConstant [14]\n" // const 3 - " 18: IntConstant [19]\n" // const 2 - " 24: IntConstant [25]\n" // const 4 + " 3: IntConstant [9]\n" // v0 <- 1 + " 5: IntConstant [9]\n" // v1 <- 2 + " 13: IntConstant [14]\n" // const 5 + " 18: IntConstant [19]\n" // const 4 + " 24: IntConstant [25]\n" // const 8 " 30: SuspendCheck\n" " 31: Goto 1\n" "BasicBlock 1, pred: 0, succ: 3\n" - " 9: Add(3, 5) [19]\n" // v2 <- v0 + v1 = 0 + 1 = 1 + " 9: Add(3, 5) [19]\n" // v2 <- v0 + v1 = 1 + 2 = 3 " 11: Goto 3\n" // goto L2 "BasicBlock 2, pred: 3, succ: 4\n" // L1: - " 14: Add(19, 13) [25]\n" // v1 <- v0 + 3 = 3 + 3 = 6 + " 14: Add(19, 13) [25]\n" // v1 <- v0 + 3 = 7 + 5 = 12 " 16: Goto 4\n" // goto L3 "BasicBlock 3, pred: 1, succ: 2\n" // L2: - " 19: Add(9, 18) [14]\n" // v0 <- v2 + 2 = 1 + 2 = 3 + " 19: Add(9, 18) [14]\n" // v0 <- v2 + 2 = 3 + 4 = 7 " 21: SuspendCheck\n" " 22: Goto 2\n" // goto L1 "BasicBlock 4, pred: 2, succ: 5\n" // L3: - " 25: Add(14, 24) [28]\n" // v2 <- v1 + 4 = 6 + 4 = 10 + " 25: Add(14, 24) [28]\n" // v2 <- v1 + 4 = 12 + 8 = 20 " 28: Return(25)\n" // return v2 "BasicBlock 5, pred: 4\n" " 29: Exit\n"; @@ -525,28 +539,33 @@ TEST(ConstantFolding, IntConstantFoldingAndJumps) { { " 13: IntConstant [14]\n", " 13: IntConstant\n" }, { " 18: IntConstant [19]\n", " 18: IntConstant\n" }, { " 24: IntConstant [25]\n", " 24: IntConstant\n" }, - { " 9: Add(3, 5) [19]\n", " 32: IntConstant []\n" }, - { " 14: Add(19, 13) [25]\n", " 34: IntConstant\n" }, - { " 19: Add(9, 18) [14]\n", " 33: IntConstant []\n" }, - { " 25: Add(14, 24) [28]\n", " 35: IntConstant [28]\n" }, + { " 30: SuspendCheck\n", " 30: SuspendCheck\n" + " 32: IntConstant []\n" + " 33: IntConstant []\n" + " 34: IntConstant\n" + " 35: IntConstant [28]\n" }, + { " 9: Add(3, 5) [19]\n", removed }, + { " 14: Add(19, 13) [25]\n", removed }, + { " 19: Add(9, 18) [14]\n", removed }, + { " 25: Add(14, 24) [28]\n", removed }, { " 28: Return(25)\n", " 28: Return(35)\n"} }; std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the values of the computed constants. auto check_after_cf = [](HGraph* graph) { - HInstruction* inst1 = graph->GetBlock(1)->GetFirstInstruction(); + HInstruction* inst1 = graph->GetBlock(4)->GetFirstInstruction()->InputAt(0); ASSERT_TRUE(inst1->IsIntConstant()); - ASSERT_EQ(inst1->AsIntConstant()->GetValue(), 1); - HInstruction* inst2 = graph->GetBlock(2)->GetFirstInstruction(); + ASSERT_EQ(inst1->AsIntConstant()->GetValue(), 20); + HInstruction* inst2 = inst1->GetPrevious(); ASSERT_TRUE(inst2->IsIntConstant()); - ASSERT_EQ(inst2->AsIntConstant()->GetValue(), 6); - HInstruction* inst3 = graph->GetBlock(3)->GetFirstInstruction(); + ASSERT_EQ(inst2->AsIntConstant()->GetValue(), 12); + HInstruction* inst3 = inst2->GetPrevious(); ASSERT_TRUE(inst3->IsIntConstant()); - ASSERT_EQ(inst3->AsIntConstant()->GetValue(), 3); - HInstruction* inst4 = graph->GetBlock(4)->GetFirstInstruction(); + ASSERT_EQ(inst3->AsIntConstant()->GetValue(), 7); + HInstruction* inst4 = inst3->GetPrevious(); ASSERT_TRUE(inst4->IsIntConstant()); - ASSERT_EQ(inst4->AsIntConstant()->GetValue(), 10); + ASSERT_EQ(inst4->AsIntConstant()->GetValue(), 3); }; // Expected difference after dead code elimination. @@ -611,25 +630,25 @@ TEST(ConstantFolding, ConstantCondition) { // Expected difference after constant folding. diff_t expected_cf_diff = { - { " 3: IntConstant [15, 22, 8]\n", " 3: IntConstant [15, 22]\n" }, + { " 3: IntConstant [15, 22, 8]\n", " 3: IntConstant [9, 15, 22]\n" }, { " 5: IntConstant [22, 8]\n", " 5: IntConstant [22]\n" }, - { " 8: GreaterThanOrEqual(3, 5) [9]\n", " 23: IntConstant [9]\n" }, - { " 9: If(8)\n", " 9: If(23)\n" } + { " 8: GreaterThanOrEqual(3, 5) [9]\n", removed }, + { " 9: If(8)\n", " 9: If(3)\n" } }; std::string expected_after_cf = Patch(expected_before, expected_cf_diff); // Check the values of the computed constants. auto check_after_cf = [](HGraph* graph) { - HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction(); + HInstruction* inst = graph->GetBlock(1)->GetFirstInstruction()->InputAt(0); ASSERT_TRUE(inst->IsIntConstant()); ASSERT_EQ(inst->AsIntConstant()->GetValue(), 1); }; // Expected difference after dead code elimination. diff_t expected_dce_diff = { - { " 3: IntConstant [15, 22]\n", " 3: IntConstant [22]\n" }, - { " 22: Phi(3, 5) [15]\n", " 22: Phi(3, 5)\n" }, - { " 15: Add(22, 3)\n", removed } + { " 3: IntConstant [9, 15, 22]\n", " 3: IntConstant [9, 22]\n" }, + { " 22: Phi(3, 5) [15]\n", " 22: Phi(3, 5)\n" }, + { " 15: Add(22, 3)\n", removed } }; std::string expected_after_dce = Patch(expected_after_cf, expected_dce_diff); diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index 09a3ae431f..7c3c2bf03d 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -80,8 +80,7 @@ void GraphChecker::VisitBasicBlock(HBasicBlock* block) { } // Ensure `block` ends with a branch instruction. - HInstruction* last_inst = block->GetLastInstruction(); - if (last_inst == nullptr || !last_inst->IsControlFlow()) { + if (!block->EndsWithControlFlowInstruction()) { AddError(StringPrintf("Block %d does not end with a branch instruction.", block->GetBlockId())); } @@ -476,4 +475,15 @@ void SSAChecker::VisitBinaryOperation(HBinaryOperation* op) { } } +void SSAChecker::VisitConstant(HConstant* instruction) { + HBasicBlock* block = instruction->GetBlock(); + if (!block->IsEntryBlock()) { + AddError(StringPrintf( + "%s %d should be in the entry block but is in block %d.", + instruction->DebugName(), + instruction->GetId(), + block->GetBlockId())); + } +} + } // namespace art diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h index 5ec3003ac8..89fea0a07f 100644 --- a/compiler/optimizing/graph_checker.h +++ b/compiler/optimizing/graph_checker.h @@ -107,6 +107,7 @@ class SSAChecker : public GraphChecker { void VisitBinaryOperation(HBinaryOperation* op) OVERRIDE; void VisitCondition(HCondition* op) OVERRIDE; void VisitIf(HIf* instruction) OVERRIDE; + void VisitConstant(HConstant* instruction) OVERRIDE; private: DISALLOW_COPY_AND_ASSIGN(SSAChecker); diff --git a/compiler/optimizing/graph_test.cc b/compiler/optimizing/graph_test.cc index 4742e4d073..50398b4790 100644 --- a/compiler/optimizing/graph_test.cc +++ b/compiler/optimizing/graph_test.cc @@ -28,8 +28,7 @@ namespace art { static HBasicBlock* createIfBlock(HGraph* graph, ArenaAllocator* allocator) { HBasicBlock* if_block = new (allocator) HBasicBlock(graph); graph->AddBlock(if_block); - HInstruction* instr = new (allocator) HIntConstant(4); - if_block->AddInstruction(instr); + HInstruction* instr = graph->GetIntConstant(4); HInstruction* equal = new (allocator) HEqual(instr, instr); if_block->AddInstruction(equal); instr = new (allocator) HIf(equal); @@ -45,6 +44,12 @@ static HBasicBlock* createGotoBlock(HGraph* graph, ArenaAllocator* allocator) { return block; } +static HBasicBlock* createEntryBlock(HGraph* graph, ArenaAllocator* allocator) { + HBasicBlock* block = createGotoBlock(graph, allocator); + graph->SetEntryBlock(block); + return block; +} + static HBasicBlock* createReturnBlock(HGraph* graph, ArenaAllocator* allocator) { HBasicBlock* block = new (allocator) HBasicBlock(graph); graph->AddBlock(block); @@ -69,7 +74,7 @@ TEST(GraphTest, IfSuccessorSimpleJoinBlock1) { ArenaAllocator allocator(&pool); HGraph* graph = new (&allocator) HGraph(&allocator); - HBasicBlock* entry_block = createGotoBlock(graph, &allocator); + HBasicBlock* entry_block = createEntryBlock(graph, &allocator); HBasicBlock* if_block = createIfBlock(graph, &allocator); HBasicBlock* if_true = createGotoBlock(graph, &allocator); HBasicBlock* return_block = createReturnBlock(graph, &allocator); @@ -104,7 +109,7 @@ TEST(GraphTest, IfSuccessorSimpleJoinBlock2) { ArenaAllocator allocator(&pool); HGraph* graph = new (&allocator) HGraph(&allocator); - HBasicBlock* entry_block = createGotoBlock(graph, &allocator); + HBasicBlock* entry_block = createEntryBlock(graph, &allocator); HBasicBlock* if_block = createIfBlock(graph, &allocator); HBasicBlock* if_false = createGotoBlock(graph, &allocator); HBasicBlock* return_block = createReturnBlock(graph, &allocator); @@ -139,12 +144,11 @@ TEST(GraphTest, IfSuccessorMultipleBackEdges1) { ArenaAllocator allocator(&pool); HGraph* graph = new (&allocator) HGraph(&allocator); - HBasicBlock* entry_block = createGotoBlock(graph, &allocator); + HBasicBlock* entry_block = createEntryBlock(graph, &allocator); HBasicBlock* if_block = createIfBlock(graph, &allocator); HBasicBlock* return_block = createReturnBlock(graph, &allocator); HBasicBlock* exit_block = createExitBlock(graph, &allocator); - graph->SetEntryBlock(entry_block); entry_block->AddSuccessor(if_block); if_block->AddSuccessor(if_block); if_block->AddSuccessor(return_block); @@ -175,12 +179,11 @@ TEST(GraphTest, IfSuccessorMultipleBackEdges2) { ArenaAllocator allocator(&pool); HGraph* graph = new (&allocator) HGraph(&allocator); - HBasicBlock* entry_block = createGotoBlock(graph, &allocator); + HBasicBlock* entry_block = createEntryBlock(graph, &allocator); HBasicBlock* if_block = createIfBlock(graph, &allocator); HBasicBlock* return_block = createReturnBlock(graph, &allocator); HBasicBlock* exit_block = createExitBlock(graph, &allocator); - graph->SetEntryBlock(entry_block); entry_block->AddSuccessor(if_block); if_block->AddSuccessor(return_block); if_block->AddSuccessor(if_block); @@ -211,13 +214,12 @@ TEST(GraphTest, IfSuccessorMultiplePreHeaders1) { ArenaAllocator allocator(&pool); HGraph* graph = new (&allocator) HGraph(&allocator); - HBasicBlock* entry_block = createGotoBlock(graph, &allocator); + HBasicBlock* entry_block = createEntryBlock(graph, &allocator); HBasicBlock* first_if_block = createIfBlock(graph, &allocator); HBasicBlock* if_block = createIfBlock(graph, &allocator); HBasicBlock* loop_block = createGotoBlock(graph, &allocator); HBasicBlock* return_block = createReturnBlock(graph, &allocator); - graph->SetEntryBlock(entry_block); entry_block->AddSuccessor(first_if_block); first_if_block->AddSuccessor(if_block); first_if_block->AddSuccessor(loop_block); @@ -251,13 +253,12 @@ TEST(GraphTest, IfSuccessorMultiplePreHeaders2) { ArenaAllocator allocator(&pool); HGraph* graph = new (&allocator) HGraph(&allocator); - HBasicBlock* entry_block = createGotoBlock(graph, &allocator); + HBasicBlock* entry_block = createEntryBlock(graph, &allocator); HBasicBlock* first_if_block = createIfBlock(graph, &allocator); HBasicBlock* if_block = createIfBlock(graph, &allocator); HBasicBlock* loop_block = createGotoBlock(graph, &allocator); HBasicBlock* return_block = createReturnBlock(graph, &allocator); - graph->SetEntryBlock(entry_block); entry_block->AddSuccessor(first_if_block); first_if_block->AddSuccessor(if_block); first_if_block->AddSuccessor(loop_block); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index cabfa488c0..49c0d3884f 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -149,6 +149,8 @@ class HGraphVisualizerPrinter : public HGraphVisitor { codegen_.DumpCoreRegister(output_, location.low()); output_ << " and "; codegen_.DumpCoreRegister(output_, location.high()); + } else if (location.IsUnallocated()) { + output_ << "<U>"; } else { DCHECK(location.IsDoubleStackSlot()); output_ << "2x" << location.GetStackIndex() << "(sp)"; diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 4b990f1ddd..2c17a67867 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -49,7 +49,8 @@ void HInliner::Run() { for (HInstruction* instruction = block->GetFirstInstruction(); instruction != nullptr;) { HInstruction* next = instruction->GetNext(); HInvokeStaticOrDirect* call = instruction->AsInvokeStaticOrDirect(); - if (call != nullptr) { + // As long as the call is not intrinsified, it is worth trying to inline. + if (call != nullptr && call->GetIntrinsic() == Intrinsics::kNone) { // We use the original invoke type to ensure the resolution of the called method // works properly. if (!TryInline(call, call->GetDexMethodIndex(), call->GetOriginalInvokeType())) { diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 2ef19b92a1..56ec8a7ed1 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -292,8 +292,7 @@ void InstructionSimplifierVisitor::VisitMul(HMul* instruction) { // MUL dst, src, pow_of_2 // with // SHL dst, src, log2(pow_of_2) - HIntConstant* shift = new (allocator) HIntConstant(WhichPowerOf2(factor)); - block->InsertInstructionBefore(shift, instruction); + HIntConstant* shift = GetGraph()->GetIntConstant(WhichPowerOf2(factor)); HShl* shl = new(allocator) HShl(type, input_other, shift); block->ReplaceAndRemoveInstructionWith(instruction, shl); } diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 36cf8568e5..628a844cc7 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -191,8 +191,10 @@ static Intrinsics GetIntrinsic(InlineMethod method) { case kIntrinsicCompareTo: return Intrinsics::kStringCompareTo; case kIntrinsicIsEmptyOrLength: - return ((method.d.data & kIntrinsicFlagIsEmpty) == 0) ? - Intrinsics::kStringLength : Intrinsics::kStringIsEmpty; + // The inliner can handle these two cases - and this is the preferred approach + // since after inlining the call is no longer visible (as opposed to waiting + // until codegen to handle intrinsic). + return Intrinsics::kNone; case kIntrinsicIndexOf: return ((method.d.data & kIntrinsicFlagBase0) == 0) ? Intrinsics::kStringIndexOfAfter : Intrinsics::kStringIndexOf; diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 0c9eb94172..33176f009c 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -847,6 +847,36 @@ void IntrinsicCodeGeneratorARM::VisitStringCharAt(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +void IntrinsicLocationsBuilderARM::VisitStringCompareTo(HInvoke* invoke) { + // The inputs plus one temp. + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetOut(Location::RegisterLocation(R0)); +} + +void IntrinsicCodeGeneratorARM::VisitStringCompareTo(HInvoke* invoke) { + ArmAssembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + // Note that the null check must have been done earlier. + DCHECK(!invoke->CanDoImplicitNullCheck()); + + Register argument = locations->InAt(1).AsRegister<Register>(); + __ cmp(argument, ShifterOperand(0)); + SlowPathCodeARM* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke); + codegen_->AddSlowPath(slow_path); + __ b(slow_path->GetEntryLabel(), EQ); + + __ LoadFromOffset( + kLoadWord, LR, TR, QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pStringCompareTo).Int32Value()); + __ blx(LR); + __ Bind(slow_path->GetExitLabel()); +} + // Unimplemented intrinsics. #define UNIMPLEMENTED_INTRINSIC(Name) \ @@ -873,9 +903,6 @@ UNIMPLEMENTED_INTRINSIC(MathRoundDouble) // Could be done by changing rounding UNIMPLEMENTED_INTRINSIC(MathRoundFloat) // Could be done by changing rounding mode, maybe? UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) // High register pressure. UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) -UNIMPLEMENTED_INTRINSIC(StringCompareTo) -UNIMPLEMENTED_INTRINSIC(StringIsEmpty) // Might not want to do these two anyways, inlining should -UNIMPLEMENTED_INTRINSIC(StringLength) // be good enough here. UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index 19b04ae094..72d303c870 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -40,6 +40,7 @@ namespace arm64 { using helpers::DRegisterFrom; using helpers::FPRegisterFrom; using helpers::HeapOperand; +using helpers::LocationFrom; using helpers::RegisterFrom; using helpers::SRegisterFrom; using helpers::WRegisterFrom; @@ -990,6 +991,36 @@ void IntrinsicCodeGeneratorARM64::VisitStringCharAt(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) { + // The inputs plus one temp. + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); + locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimInt)); +} + +void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) { + vixl::MacroAssembler* masm = GetVIXLAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + // Note that the null check must have been done earlier. + DCHECK(!invoke->CanDoImplicitNullCheck()); + + Register argument = WRegisterFrom(locations->InAt(1)); + __ Cmp(argument, 0); + SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke); + codegen_->AddSlowPath(slow_path); + __ B(eq, slow_path->GetEntryLabel()); + + __ Ldr( + lr, MemOperand(tr, QUICK_ENTRYPOINT_OFFSET(kArm64WordSize, pStringCompareTo).Int32Value())); + __ Blr(lr); + __ Bind(slow_path->GetExitLabel()); +} + // Unimplemented intrinsics. #define UNIMPLEMENTED_INTRINSIC(Name) \ @@ -999,9 +1030,6 @@ void IntrinsicCodeGeneratorARM64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED } UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) -UNIMPLEMENTED_INTRINSIC(StringCompareTo) -UNIMPLEMENTED_INTRINSIC(StringIsEmpty) // Might not want to do these two anyways, inlining should -UNIMPLEMENTED_INTRINSIC(StringLength) // be good enough here. UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h index 9cc77c6251..10f6e1d6c7 100644 --- a/compiler/optimizing/intrinsics_list.h +++ b/compiler/optimizing/intrinsics_list.h @@ -60,10 +60,8 @@ V(MemoryPokeShortNative, kStatic) \ V(StringCharAt, kDirect) \ V(StringCompareTo, kDirect) \ - V(StringIsEmpty, kDirect) \ V(StringIndexOf, kDirect) \ V(StringIndexOfAfter, kDirect) \ - V(StringLength, kDirect) \ V(UnsafeCASInt, kDirect) \ V(UnsafeCASLong, kDirect) \ V(UnsafeCASObject, kDirect) \ diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc new file mode 100644 index 0000000000..384737f55a --- /dev/null +++ b/compiler/optimizing/intrinsics_x86.cc @@ -0,0 +1,1208 @@ +/* + * Copyright (C) 2015 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 "intrinsics_x86.h" + +#include "code_generator_x86.h" +#include "entrypoints/quick/quick_entrypoints.h" +#include "intrinsics.h" +#include "mirror/array-inl.h" +#include "mirror/art_method.h" +#include "mirror/string.h" +#include "thread.h" +#include "utils/x86/assembler_x86.h" +#include "utils/x86/constants_x86.h" + +namespace art { + +namespace x86 { + +static constexpr int kDoubleNaNHigh = 0x7FF80000; +static constexpr int kDoubleNaNLow = 0x00000000; +static constexpr int kFloatNaN = 0x7FC00000; + +X86Assembler* IntrinsicCodeGeneratorX86::GetAssembler() { + return reinterpret_cast<X86Assembler*>(codegen_->GetAssembler()); +} + +ArenaAllocator* IntrinsicCodeGeneratorX86::GetAllocator() { + return codegen_->GetGraph()->GetArena(); +} + +bool IntrinsicLocationsBuilderX86::TryDispatch(HInvoke* invoke) { + Dispatch(invoke); + LocationSummary* res = invoke->GetLocations(); + return res != nullptr && res->Intrinsified(); +} + +#define __ reinterpret_cast<X86Assembler*>(codegen->GetAssembler())-> + +// TODO: target as memory. +static void MoveFromReturnRegister(Location target, + Primitive::Type type, + CodeGeneratorX86* codegen) { + if (!target.IsValid()) { + DCHECK(type == Primitive::kPrimVoid); + return; + } + + switch (type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimChar: + case Primitive::kPrimShort: + case Primitive::kPrimInt: + case Primitive::kPrimNot: { + Register target_reg = target.AsRegister<Register>(); + if (target_reg != EAX) { + __ movl(target_reg, EAX); + } + break; + } + case Primitive::kPrimLong: { + Register target_reg_lo = target.AsRegisterPairLow<Register>(); + Register target_reg_hi = target.AsRegisterPairHigh<Register>(); + if (target_reg_lo != EAX) { + __ movl(target_reg_lo, EAX); + } + if (target_reg_hi != EDX) { + __ movl(target_reg_hi, EDX); + } + break; + } + + case Primitive::kPrimVoid: + LOG(FATAL) << "Unexpected void type for valid location " << target; + UNREACHABLE(); + + case Primitive::kPrimDouble: { + XmmRegister target_reg = target.AsFpuRegister<XmmRegister>(); + if (target_reg != XMM0) { + __ movsd(target_reg, XMM0); + } + break; + } + case Primitive::kPrimFloat: { + XmmRegister target_reg = target.AsFpuRegister<XmmRegister>(); + if (target_reg != XMM0) { + __ movss(target_reg, XMM0); + } + break; + } + } +} + +static void MoveArguments(HInvoke* invoke, ArenaAllocator* arena, CodeGeneratorX86* codegen) { + if (invoke->InputCount() == 0) { + return; + } + + LocationSummary* locations = invoke->GetLocations(); + InvokeDexCallingConventionVisitor calling_convention_visitor; + + // We're moving potentially two or more locations to locations that could overlap, so we need + // a parallel move resolver. + HParallelMove parallel_move(arena); + + for (size_t i = 0; i < invoke->InputCount(); i++) { + HInstruction* input = invoke->InputAt(i); + Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType()); + Location actual_loc = locations->InAt(i); + + parallel_move.AddMove(actual_loc, cc_loc, nullptr); + } + + codegen->GetMoveResolver()->EmitNativeCode(¶llel_move); +} + +// Slow-path for fallback (calling the managed code to handle the intrinsic) in an intrinsified +// call. This will copy the arguments into the positions for a regular call. +// +// Note: The actual parameters are required to be in the locations given by the invoke's location +// summary. If an intrinsic modifies those locations before a slowpath call, they must be +// restored! +class IntrinsicSlowPathX86 : public SlowPathCodeX86 { + public: + explicit IntrinsicSlowPathX86(HInvoke* invoke, Register temp) + : invoke_(invoke) { + // The temporary register has to be EAX for x86 invokes. + DCHECK_EQ(temp, EAX); + } + + void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE { + CodeGeneratorX86* codegen = down_cast<CodeGeneratorX86*>(codegen_in); + __ Bind(GetEntryLabel()); + + SaveLiveRegisters(codegen, invoke_->GetLocations()); + + MoveArguments(invoke_, codegen->GetGraph()->GetArena(), codegen); + + if (invoke_->IsInvokeStaticOrDirect()) { + codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), EAX); + } else { + UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented"; + UNREACHABLE(); + } + + // Copy the result back to the expected output. + Location out = invoke_->GetLocations()->Out(); + if (out.IsValid()) { + DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory. + DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg())); + MoveFromReturnRegister(out, invoke_->GetType(), codegen); + } + + RestoreLiveRegisters(codegen, invoke_->GetLocations()); + __ jmp(GetExitLabel()); + } + + private: + // The instruction where this slow path is happening. + HInvoke* const invoke_; + + DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathX86); +}; + +#undef __ +#define __ assembler-> + +static void CreateFPToIntLocations(ArenaAllocator* arena, HInvoke* invoke, bool is64bit) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresRegister()); + if (is64bit) { + locations->AddTemp(Location::RequiresFpuRegister()); + } +} + +static void CreateIntToFPLocations(ArenaAllocator* arena, HInvoke* invoke, bool is64bit) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresFpuRegister()); + if (is64bit) { + locations->AddTemp(Location::RequiresFpuRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); + } +} + +static void MoveFPToInt(LocationSummary* locations, bool is64bit, X86Assembler* assembler) { + Location input = locations->InAt(0); + Location output = locations->Out(); + if (is64bit) { + // Need to use the temporary. + XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); + __ movsd(temp, input.AsFpuRegister<XmmRegister>()); + __ movd(output.AsRegisterPairLow<Register>(), temp); + __ psrlq(temp, Immediate(32)); + __ movd(output.AsRegisterPairHigh<Register>(), temp); + } else { + __ movd(output.AsRegister<Register>(), input.AsFpuRegister<XmmRegister>()); + } +} + +static void MoveIntToFP(LocationSummary* locations, bool is64bit, X86Assembler* assembler) { + Location input = locations->InAt(0); + Location output = locations->Out(); + if (is64bit) { + // Need to use the temporary. + XmmRegister temp1 = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); + XmmRegister temp2 = locations->GetTemp(1).AsFpuRegister<XmmRegister>(); + __ movd(temp1, input.AsRegisterPairLow<Register>()); + __ movd(temp2, input.AsRegisterPairHigh<Register>()); + __ punpckldq(temp1, temp2); + __ movsd(output.AsFpuRegister<XmmRegister>(), temp1); + } else { + __ movd(output.AsFpuRegister<XmmRegister>(), input.AsRegister<Register>()); + } +} + +void IntrinsicLocationsBuilderX86::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) { + CreateFPToIntLocations(arena_, invoke, true); +} +void IntrinsicLocationsBuilderX86::VisitDoubleLongBitsToDouble(HInvoke* invoke) { + CreateIntToFPLocations(arena_, invoke, true); +} + +void IntrinsicCodeGeneratorX86::VisitDoubleDoubleToRawLongBits(HInvoke* invoke) { + MoveFPToInt(invoke->GetLocations(), true, GetAssembler()); +} +void IntrinsicCodeGeneratorX86::VisitDoubleLongBitsToDouble(HInvoke* invoke) { + MoveIntToFP(invoke->GetLocations(), true, GetAssembler()); +} + +void IntrinsicLocationsBuilderX86::VisitFloatFloatToRawIntBits(HInvoke* invoke) { + CreateFPToIntLocations(arena_, invoke, false); +} +void IntrinsicLocationsBuilderX86::VisitFloatIntBitsToFloat(HInvoke* invoke) { + CreateIntToFPLocations(arena_, invoke, false); +} + +void IntrinsicCodeGeneratorX86::VisitFloatFloatToRawIntBits(HInvoke* invoke) { + MoveFPToInt(invoke->GetLocations(), false, GetAssembler()); +} +void IntrinsicCodeGeneratorX86::VisitFloatIntBitsToFloat(HInvoke* invoke) { + MoveIntToFP(invoke->GetLocations(), false, GetAssembler()); +} + +static void CreateIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); +} + +static void CreateLongToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); +} + +static void CreateLongToLongLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); +} + +static void GenReverseBytes(LocationSummary* locations, + Primitive::Type size, + X86Assembler* assembler) { + Register out = locations->Out().AsRegister<Register>(); + + switch (size) { + case Primitive::kPrimShort: + // TODO: Can be done with an xchg of 8b registers. This is straight from Quick. + __ bswapl(out); + __ sarl(out, Immediate(16)); + break; + case Primitive::kPrimInt: + __ bswapl(out); + break; + default: + LOG(FATAL) << "Unexpected size for reverse-bytes: " << size; + UNREACHABLE(); + } +} + +void IntrinsicLocationsBuilderX86::VisitIntegerReverseBytes(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitIntegerReverseBytes(HInvoke* invoke) { + GenReverseBytes(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler()); +} + +void IntrinsicLocationsBuilderX86::VisitShortReverseBytes(HInvoke* invoke) { + CreateIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitShortReverseBytes(HInvoke* invoke) { + GenReverseBytes(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler()); +} + + +// TODO: Consider Quick's way of doing Double abs through integer operations, as the immediate we +// need is 64b. + +static void CreateFloatToFloat(ArenaAllocator* arena, HInvoke* invoke) { + // TODO: Enable memory operations when the assembler supports them. + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresFpuRegister()); + // TODO: Allow x86 to work with memory. This requires assembler support, see below. + // locations->SetInAt(0, Location::Any()); // X86 can work on memory directly. + locations->SetOut(Location::SameAsFirstInput()); +} + +static void MathAbsFP(LocationSummary* locations, bool is64bit, X86Assembler* assembler) { + Location output = locations->Out(); + + if (output.IsFpuRegister()) { + // Create the right constant on an aligned stack. + if (is64bit) { + __ subl(ESP, Immediate(8)); + __ pushl(Immediate(0x7FFFFFFF)); + __ pushl(Immediate(0xFFFFFFFF)); + __ andpd(output.AsFpuRegister<XmmRegister>(), Address(ESP, 0)); + } else { + __ subl(ESP, Immediate(12)); + __ pushl(Immediate(0x7FFFFFFF)); + __ andps(output.AsFpuRegister<XmmRegister>(), Address(ESP, 0)); + } + __ addl(ESP, Immediate(16)); + } else { + // TODO: update when assember support is available. + UNIMPLEMENTED(FATAL) << "Needs assembler support."; +// Once assembler support is available, in-memory operations look like this: +// if (is64bit) { +// DCHECK(output.IsDoubleStackSlot()); +// __ andl(Address(Register(RSP), output.GetHighStackIndex(kX86WordSize)), +// Immediate(0x7FFFFFFF)); +// } else { +// DCHECK(output.IsStackSlot()); +// // Can use and with a literal directly. +// __ andl(Address(Register(RSP), output.GetStackIndex()), Immediate(0x7FFFFFFF)); +// } + } +} + +void IntrinsicLocationsBuilderX86::VisitMathAbsDouble(HInvoke* invoke) { + CreateFloatToFloat(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMathAbsDouble(HInvoke* invoke) { + MathAbsFP(invoke->GetLocations(), true, GetAssembler()); +} + +void IntrinsicLocationsBuilderX86::VisitMathAbsFloat(HInvoke* invoke) { + CreateFloatToFloat(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMathAbsFloat(HInvoke* invoke) { + MathAbsFP(invoke->GetLocations(), false, GetAssembler()); +} + +static void CreateAbsIntLocation(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RegisterLocation(EAX)); + locations->SetOut(Location::SameAsFirstInput()); + locations->AddTemp(Location::RegisterLocation(EDX)); +} + +static void GenAbsInteger(LocationSummary* locations, X86Assembler* assembler) { + Location output = locations->Out(); + Register out = output.AsRegister<Register>(); + DCHECK_EQ(out, EAX); + Register temp = locations->GetTemp(0).AsRegister<Register>(); + DCHECK_EQ(temp, EDX); + + // Sign extend EAX into EDX. + __ cdq(); + + // XOR EAX with sign. + __ xorl(EAX, EDX); + + // Subtract out sign to correct. + __ subl(EAX, EDX); + + // The result is in EAX. +} + +static void CreateAbsLongLocation(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); + locations->AddTemp(Location::RequiresRegister()); +} + +static void GenAbsLong(LocationSummary* locations, X86Assembler* assembler) { + Location input = locations->InAt(0); + Register input_lo = input.AsRegisterPairLow<Register>(); + Register input_hi = input.AsRegisterPairHigh<Register>(); + Location output = locations->Out(); + Register output_lo = output.AsRegisterPairLow<Register>(); + Register output_hi = output.AsRegisterPairHigh<Register>(); + Register temp = locations->GetTemp(0).AsRegister<Register>(); + + // Compute the sign into the temporary. + __ movl(temp, input_hi); + __ sarl(temp, Immediate(31)); + + // Store the sign into the output. + __ movl(output_lo, temp); + __ movl(output_hi, temp); + + // XOR the input to the output. + __ xorl(output_lo, input_lo); + __ xorl(output_hi, input_hi); + + // Subtract the sign. + __ subl(output_lo, temp); + __ sbbl(output_hi, temp); +} + +void IntrinsicLocationsBuilderX86::VisitMathAbsInt(HInvoke* invoke) { + CreateAbsIntLocation(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMathAbsInt(HInvoke* invoke) { + GenAbsInteger(invoke->GetLocations(), GetAssembler()); +} + +void IntrinsicLocationsBuilderX86::VisitMathAbsLong(HInvoke* invoke) { + CreateAbsLongLocation(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMathAbsLong(HInvoke* invoke) { + GenAbsLong(invoke->GetLocations(), GetAssembler()); +} + +static void GenMinMaxFP(LocationSummary* locations, bool is_min, bool is_double, + X86Assembler* assembler) { + Location op1_loc = locations->InAt(0); + Location op2_loc = locations->InAt(1); + Location out_loc = locations->Out(); + XmmRegister out = out_loc.AsFpuRegister<XmmRegister>(); + + // Shortcut for same input locations. + if (op1_loc.Equals(op2_loc)) { + DCHECK(out_loc.Equals(op1_loc)); + return; + } + + // (out := op1) + // out <=? op2 + // if Nan jmp Nan_label + // if out is min jmp done + // if op2 is min jmp op2_label + // handle -0/+0 + // jmp done + // Nan_label: + // out := NaN + // op2_label: + // out := op2 + // done: + // + // This removes one jmp, but needs to copy one input (op1) to out. + // + // TODO: This is straight from Quick (except literal pool). Make NaN an out-of-line slowpath? + + XmmRegister op2 = op2_loc.AsFpuRegister<XmmRegister>(); + + Label nan, done, op2_label; + if (is_double) { + __ ucomisd(out, op2); + } else { + __ ucomiss(out, op2); + } + + __ j(Condition::kParityEven, &nan); + + __ j(is_min ? Condition::kAbove : Condition::kBelow, &op2_label); + __ j(is_min ? Condition::kBelow : Condition::kAbove, &done); + + // Handle 0.0/-0.0. + if (is_min) { + if (is_double) { + __ orpd(out, op2); + } else { + __ orps(out, op2); + } + } else { + if (is_double) { + __ andpd(out, op2); + } else { + __ andps(out, op2); + } + } + __ jmp(&done); + + // NaN handling. + __ Bind(&nan); + if (is_double) { + __ pushl(Immediate(kDoubleNaNHigh)); + __ pushl(Immediate(kDoubleNaNLow)); + __ movsd(out, Address(ESP, 0)); + __ addl(ESP, Immediate(8)); + } else { + __ pushl(Immediate(kFloatNaN)); + __ movss(out, Address(ESP, 0)); + __ addl(ESP, Immediate(4)); + } + __ jmp(&done); + + // out := op2; + __ Bind(&op2_label); + if (is_double) { + __ movsd(out, op2); + } else { + __ movss(out, op2); + } + + // Done. + __ Bind(&done); +} + +static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + // The following is sub-optimal, but all we can do for now. It would be fine to also accept + // the second input to be the output (we can simply swap inputs). + locations->SetOut(Location::SameAsFirstInput()); +} + +void IntrinsicLocationsBuilderX86::VisitMathMinDoubleDouble(HInvoke* invoke) { + CreateFPFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMathMinDoubleDouble(HInvoke* invoke) { + GenMinMaxFP(invoke->GetLocations(), true, true, GetAssembler()); +} + +void IntrinsicLocationsBuilderX86::VisitMathMinFloatFloat(HInvoke* invoke) { + CreateFPFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMathMinFloatFloat(HInvoke* invoke) { + GenMinMaxFP(invoke->GetLocations(), true, false, GetAssembler()); +} + +void IntrinsicLocationsBuilderX86::VisitMathMaxDoubleDouble(HInvoke* invoke) { + CreateFPFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMathMaxDoubleDouble(HInvoke* invoke) { + GenMinMaxFP(invoke->GetLocations(), false, true, GetAssembler()); +} + +void IntrinsicLocationsBuilderX86::VisitMathMaxFloatFloat(HInvoke* invoke) { + CreateFPFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMathMaxFloatFloat(HInvoke* invoke) { + GenMinMaxFP(invoke->GetLocations(), false, false, GetAssembler()); +} + +static void GenMinMax(LocationSummary* locations, bool is_min, bool is_long, + X86Assembler* assembler) { + Location op1_loc = locations->InAt(0); + Location op2_loc = locations->InAt(1); + + // Shortcut for same input locations. + if (op1_loc.Equals(op2_loc)) { + // Can return immediately, as op1_loc == out_loc. + // Note: if we ever support separate registers, e.g., output into memory, we need to check for + // a copy here. + DCHECK(locations->Out().Equals(op1_loc)); + return; + } + + if (is_long) { + // Need to perform a subtract to get the sign right. + // op1 is already in the same location as the output. + Location output = locations->Out(); + Register output_lo = output.AsRegisterPairLow<Register>(); + Register output_hi = output.AsRegisterPairHigh<Register>(); + + Register op2_lo = op2_loc.AsRegisterPairLow<Register>(); + Register op2_hi = op2_loc.AsRegisterPairHigh<Register>(); + + // Spare register to compute the subtraction to set condition code. + Register temp = locations->GetTemp(0).AsRegister<Register>(); + + // Subtract off op2_low. + __ movl(temp, output_lo); + __ subl(temp, op2_lo); + + // Now use the same tempo and the borrow to finish the subtraction of op2_hi. + __ movl(temp, output_hi); + __ sbbl(temp, op2_hi); + + // Now the condition code is correct. + Condition cond = is_min ? Condition::kGreaterEqual : Condition::kLess; + __ cmovl(cond, output_lo, op2_lo); + __ cmovl(cond, output_hi, op2_hi); + } else { + Register out = locations->Out().AsRegister<Register>(); + Register op2 = op2_loc.AsRegister<Register>(); + + // (out := op1) + // out <=? op2 + // if out is min jmp done + // out := op2 + // done: + + __ cmpl(out, op2); + Condition cond = is_min ? Condition::kGreater : Condition::kLess; + __ cmovl(cond, out, op2); + } +} + +static void CreateIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); +} + +static void CreateLongLongToLongLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + // Register to use to perform a long subtract to set cc. + locations->AddTemp(Location::RequiresRegister()); +} + +void IntrinsicLocationsBuilderX86::VisitMathMinIntInt(HInvoke* invoke) { + CreateIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMathMinIntInt(HInvoke* invoke) { + GenMinMax(invoke->GetLocations(), true, false, GetAssembler()); +} + +void IntrinsicLocationsBuilderX86::VisitMathMinLongLong(HInvoke* invoke) { + CreateLongLongToLongLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMathMinLongLong(HInvoke* invoke) { + GenMinMax(invoke->GetLocations(), true, true, GetAssembler()); +} + +void IntrinsicLocationsBuilderX86::VisitMathMaxIntInt(HInvoke* invoke) { + CreateIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMathMaxIntInt(HInvoke* invoke) { + GenMinMax(invoke->GetLocations(), false, false, GetAssembler()); +} + +void IntrinsicLocationsBuilderX86::VisitMathMaxLongLong(HInvoke* invoke) { + CreateLongLongToLongLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMathMaxLongLong(HInvoke* invoke) { + GenMinMax(invoke->GetLocations(), false, true, GetAssembler()); +} + +static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister()); +} + +void IntrinsicLocationsBuilderX86::VisitMathSqrt(HInvoke* invoke) { + CreateFPToFPLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMathSqrt(HInvoke* invoke) { + LocationSummary* locations = invoke->GetLocations(); + XmmRegister in = locations->InAt(0).AsFpuRegister<XmmRegister>(); + XmmRegister out = locations->Out().AsFpuRegister<XmmRegister>(); + + GetAssembler()->sqrtsd(out, in); +} + +void IntrinsicLocationsBuilderX86::VisitStringCharAt(HInvoke* invoke) { + // The inputs plus one temp. + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCallOnSlowPath, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::SameAsFirstInput()); + // Needs to be EAX for the invoke. + locations->AddTemp(Location::RegisterLocation(EAX)); +} + +void IntrinsicCodeGeneratorX86::VisitStringCharAt(HInvoke* invoke) { + LocationSummary* locations = invoke->GetLocations(); + + // Location of reference to data array + const int32_t value_offset = mirror::String::ValueOffset().Int32Value(); + // Location of count + const int32_t count_offset = mirror::String::CountOffset().Int32Value(); + // Starting offset within data array + const int32_t offset_offset = mirror::String::OffsetOffset().Int32Value(); + // Start of char data with array_ + const int32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value(); + + Register obj = locations->InAt(0).AsRegister<Register>(); + Register idx = locations->InAt(1).AsRegister<Register>(); + Register out = locations->Out().AsRegister<Register>(); + Location temp_loc = locations->GetTemp(0); + Register temp = temp_loc.AsRegister<Register>(); + + // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth + // the cost. + // TODO: For simplicity, the index parameter is requested in a register, so different from Quick + // we will not optimize the code for constants (which would save a register). + + SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke, temp); + codegen_->AddSlowPath(slow_path); + + X86Assembler* assembler = GetAssembler(); + + __ cmpl(idx, Address(obj, count_offset)); + codegen_->MaybeRecordImplicitNullCheck(invoke); + __ j(kAboveEqual, slow_path->GetEntryLabel()); + + // Get the actual element. + __ movl(temp, idx); // temp := idx. + __ addl(temp, Address(obj, offset_offset)); // temp := offset + idx. + __ movl(out, Address(obj, value_offset)); // obj := obj.array. + // out = out[2*temp]. + __ movzxw(out, Address(out, temp, ScaleFactor::TIMES_2, data_offset)); + + __ Bind(slow_path->GetExitLabel()); +} + +void IntrinsicLocationsBuilderX86::VisitStringCompareTo(HInvoke* invoke) { + // The inputs plus one temp. + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetOut(Location::RegisterLocation(EAX)); + // Needs to be EAX for the invoke. + locations->AddTemp(Location::RegisterLocation(EAX)); +} + +void IntrinsicCodeGeneratorX86::VisitStringCompareTo(HInvoke* invoke) { + X86Assembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + // Note that the null check must have been done earlier. + DCHECK(!invoke->CanDoImplicitNullCheck()); + + Register argument = locations->InAt(1).AsRegister<Register>(); + __ testl(argument, argument); + SlowPathCodeX86* slow_path = new (GetAllocator()) IntrinsicSlowPathX86( + invoke, locations->GetTemp(0).AsRegister<Register>()); + codegen_->AddSlowPath(slow_path); + __ j(kEqual, slow_path->GetEntryLabel()); + + __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pStringCompareTo))); + __ Bind(slow_path->GetExitLabel()); +} + +static void GenPeek(LocationSummary* locations, Primitive::Type size, X86Assembler* assembler) { + Register address = locations->InAt(0).AsRegisterPairLow<Register>(); + Location out_loc = locations->Out(); + // x86 allows unaligned access. We do not have to check the input or use specific instructions + // to avoid a SIGBUS. + switch (size) { + case Primitive::kPrimByte: + __ movsxb(out_loc.AsRegister<Register>(), Address(address, 0)); + break; + case Primitive::kPrimShort: + __ movsxw(out_loc.AsRegister<Register>(), Address(address, 0)); + break; + case Primitive::kPrimInt: + __ movl(out_loc.AsRegister<Register>(), Address(address, 0)); + break; + case Primitive::kPrimLong: + __ movl(out_loc.AsRegisterPairLow<Register>(), Address(address, 0)); + __ movl(out_loc.AsRegisterPairHigh<Register>(), Address(address, 4)); + break; + default: + LOG(FATAL) << "Type not recognized for peek: " << size; + UNREACHABLE(); + } +} + +void IntrinsicLocationsBuilderX86::VisitMemoryPeekByte(HInvoke* invoke) { + CreateLongToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMemoryPeekByte(HInvoke* invoke) { + GenPeek(invoke->GetLocations(), Primitive::kPrimByte, GetAssembler()); +} + +void IntrinsicLocationsBuilderX86::VisitMemoryPeekIntNative(HInvoke* invoke) { + CreateLongToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMemoryPeekIntNative(HInvoke* invoke) { + GenPeek(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler()); +} + +void IntrinsicLocationsBuilderX86::VisitMemoryPeekLongNative(HInvoke* invoke) { + CreateLongToLongLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMemoryPeekLongNative(HInvoke* invoke) { + GenPeek(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler()); +} + +void IntrinsicLocationsBuilderX86::VisitMemoryPeekShortNative(HInvoke* invoke) { + CreateLongToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMemoryPeekShortNative(HInvoke* invoke) { + GenPeek(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler()); +} + +static void CreateLongIntToVoidLocations(ArenaAllocator* arena, Primitive::Type size, + HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::RequiresRegister()); + HInstruction *value = invoke->InputAt(1); + if (size == Primitive::kPrimByte) { + locations->SetInAt(1, Location::ByteRegisterOrConstant(EDX, value)); + } else { + locations->SetInAt(1, Location::RegisterOrConstant(value)); + } +} + +static void GenPoke(LocationSummary* locations, Primitive::Type size, X86Assembler* assembler) { + Register address = locations->InAt(0).AsRegisterPairLow<Register>(); + Location value_loc = locations->InAt(1); + // x86 allows unaligned access. We do not have to check the input or use specific instructions + // to avoid a SIGBUS. + switch (size) { + case Primitive::kPrimByte: + if (value_loc.IsConstant()) { + __ movb(Address(address, 0), + Immediate(value_loc.GetConstant()->AsIntConstant()->GetValue())); + } else { + __ movb(Address(address, 0), value_loc.AsRegister<ByteRegister>()); + } + break; + case Primitive::kPrimShort: + if (value_loc.IsConstant()) { + __ movw(Address(address, 0), + Immediate(value_loc.GetConstant()->AsIntConstant()->GetValue())); + } else { + __ movw(Address(address, 0), value_loc.AsRegister<Register>()); + } + break; + case Primitive::kPrimInt: + if (value_loc.IsConstant()) { + __ movl(Address(address, 0), + Immediate(value_loc.GetConstant()->AsIntConstant()->GetValue())); + } else { + __ movl(Address(address, 0), value_loc.AsRegister<Register>()); + } + break; + case Primitive::kPrimLong: + if (value_loc.IsConstant()) { + int64_t value = value_loc.GetConstant()->AsLongConstant()->GetValue(); + __ movl(Address(address, 0), Immediate(Low32Bits(value))); + __ movl(Address(address, 4), Immediate(High32Bits(value))); + } else { + __ movl(Address(address, 0), value_loc.AsRegisterPairLow<Register>()); + __ movl(Address(address, 4), value_loc.AsRegisterPairHigh<Register>()); + } + break; + default: + LOG(FATAL) << "Type not recognized for poke: " << size; + UNREACHABLE(); + } +} + +void IntrinsicLocationsBuilderX86::VisitMemoryPokeByte(HInvoke* invoke) { + CreateLongIntToVoidLocations(arena_, Primitive::kPrimByte, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMemoryPokeByte(HInvoke* invoke) { + GenPoke(invoke->GetLocations(), Primitive::kPrimByte, GetAssembler()); +} + +void IntrinsicLocationsBuilderX86::VisitMemoryPokeIntNative(HInvoke* invoke) { + CreateLongIntToVoidLocations(arena_, Primitive::kPrimInt, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMemoryPokeIntNative(HInvoke* invoke) { + GenPoke(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler()); +} + +void IntrinsicLocationsBuilderX86::VisitMemoryPokeLongNative(HInvoke* invoke) { + CreateLongIntToVoidLocations(arena_, Primitive::kPrimLong, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMemoryPokeLongNative(HInvoke* invoke) { + GenPoke(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler()); +} + +void IntrinsicLocationsBuilderX86::VisitMemoryPokeShortNative(HInvoke* invoke) { + CreateLongIntToVoidLocations(arena_, Primitive::kPrimShort, invoke); +} + +void IntrinsicCodeGeneratorX86::VisitMemoryPokeShortNative(HInvoke* invoke) { + GenPoke(invoke->GetLocations(), Primitive::kPrimShort, GetAssembler()); +} + +void IntrinsicLocationsBuilderX86::VisitThreadCurrentThread(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetOut(Location::RequiresRegister()); +} + +void IntrinsicCodeGeneratorX86::VisitThreadCurrentThread(HInvoke* invoke) { + Register out = invoke->GetLocations()->Out().AsRegister<Register>(); + GetAssembler()->fs()->movl(out, Address::Absolute(Thread::PeerOffset<kX86WordSize>())); +} + +static void GenUnsafeGet(LocationSummary* locations, Primitive::Type type, + bool is_volatile, X86Assembler* assembler) { + Register base = locations->InAt(1).AsRegister<Register>(); + Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); + Location output = locations->Out(); + + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimNot: + __ movl(output.AsRegister<Register>(), Address(base, offset, ScaleFactor::TIMES_1, 0)); + break; + + case Primitive::kPrimLong: { + Register output_lo = output.AsRegisterPairLow<Register>(); + Register output_hi = output.AsRegisterPairHigh<Register>(); + if (is_volatile) { + // Need to use a XMM to read atomically. + XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); + __ movsd(temp, Address(base, offset, ScaleFactor::TIMES_1, 0)); + __ movd(output_lo, temp); + __ psrlq(temp, Immediate(32)); + __ movd(output_hi, temp); + } else { + __ movl(output_lo, Address(base, offset, ScaleFactor::TIMES_1, 0)); + __ movl(output_hi, Address(base, offset, ScaleFactor::TIMES_1, 4)); + } + } + break; + + default: + LOG(FATAL) << "Unsupported op size " << type; + UNREACHABLE(); + } +} + +static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke, + bool is_long, bool is_volatile) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::NoLocation()); // Unused receiver. + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + if (is_long) { + if (is_volatile) { + // Need to use XMM to read volatile. + locations->AddTemp(Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresRegister()); + } else { + locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); + } + } else { + locations->SetOut(Location::RequiresRegister()); + } +} + +void IntrinsicLocationsBuilderX86::VisitUnsafeGet(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke, false, false); +} +void IntrinsicLocationsBuilderX86::VisitUnsafeGetVolatile(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke, false, true); +} +void IntrinsicLocationsBuilderX86::VisitUnsafeGetLong(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke, false, false); +} +void IntrinsicLocationsBuilderX86::VisitUnsafeGetLongVolatile(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke, true, true); +} +void IntrinsicLocationsBuilderX86::VisitUnsafeGetObject(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke, false, false); +} +void IntrinsicLocationsBuilderX86::VisitUnsafeGetObjectVolatile(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke, false, true); +} + + +void IntrinsicCodeGeneratorX86::VisitUnsafeGet(HInvoke* invoke) { + GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimInt, false, GetAssembler()); +} +void IntrinsicCodeGeneratorX86::VisitUnsafeGetVolatile(HInvoke* invoke) { + GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimInt, true, GetAssembler()); +} +void IntrinsicCodeGeneratorX86::VisitUnsafeGetLong(HInvoke* invoke) { + GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimLong, false, GetAssembler()); +} +void IntrinsicCodeGeneratorX86::VisitUnsafeGetLongVolatile(HInvoke* invoke) { + GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimLong, true, GetAssembler()); +} +void IntrinsicCodeGeneratorX86::VisitUnsafeGetObject(HInvoke* invoke) { + GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimNot, false, GetAssembler()); +} +void IntrinsicCodeGeneratorX86::VisitUnsafeGetObjectVolatile(HInvoke* invoke) { + GenUnsafeGet(invoke->GetLocations(), Primitive::kPrimNot, true, GetAssembler()); +} + + +static void CreateIntIntIntIntToVoidPlusTempsLocations(ArenaAllocator* arena, + Primitive::Type type, + HInvoke* invoke, + bool is_volatile) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::NoLocation()); // Unused receiver. + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetInAt(3, Location::RequiresRegister()); + if (type == Primitive::kPrimNot) { + // Need temp registers for card-marking. + locations->AddTemp(Location::RequiresRegister()); + // Ensure the value is in a byte register. + locations->AddTemp(Location::RegisterLocation(ECX)); + } else if (type == Primitive::kPrimLong && is_volatile) { + locations->AddTemp(Location::RequiresFpuRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); + } +} + +void IntrinsicLocationsBuilderX86::VisitUnsafePut(HInvoke* invoke) { + CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimInt, invoke, false); +} +void IntrinsicLocationsBuilderX86::VisitUnsafePutOrdered(HInvoke* invoke) { + CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimInt, invoke, false); +} +void IntrinsicLocationsBuilderX86::VisitUnsafePutVolatile(HInvoke* invoke) { + CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimInt, invoke, true); +} +void IntrinsicLocationsBuilderX86::VisitUnsafePutObject(HInvoke* invoke) { + CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimNot, invoke, false); +} +void IntrinsicLocationsBuilderX86::VisitUnsafePutObjectOrdered(HInvoke* invoke) { + CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimNot, invoke, false); +} +void IntrinsicLocationsBuilderX86::VisitUnsafePutObjectVolatile(HInvoke* invoke) { + CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimNot, invoke, true); +} +void IntrinsicLocationsBuilderX86::VisitUnsafePutLong(HInvoke* invoke) { + CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimLong, invoke, false); +} +void IntrinsicLocationsBuilderX86::VisitUnsafePutLongOrdered(HInvoke* invoke) { + CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimLong, invoke, false); +} +void IntrinsicLocationsBuilderX86::VisitUnsafePutLongVolatile(HInvoke* invoke) { + CreateIntIntIntIntToVoidPlusTempsLocations(arena_, Primitive::kPrimLong, invoke, true); +} + +// We don't care for ordered: it requires an AnyStore barrier, which is already given by the x86 +// memory model. +static void GenUnsafePut(LocationSummary* locations, + Primitive::Type type, + bool is_volatile, + CodeGeneratorX86* codegen) { + X86Assembler* assembler = reinterpret_cast<X86Assembler*>(codegen->GetAssembler()); + Register base = locations->InAt(1).AsRegister<Register>(); + Register offset = locations->InAt(2).AsRegisterPairLow<Register>(); + Location value_loc = locations->InAt(3); + + if (type == Primitive::kPrimLong) { + Register value_lo = value_loc.AsRegisterPairLow<Register>(); + Register value_hi = value_loc.AsRegisterPairHigh<Register>(); + if (is_volatile) { + XmmRegister temp1 = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); + XmmRegister temp2 = locations->GetTemp(1).AsFpuRegister<XmmRegister>(); + __ movd(temp1, value_lo); + __ movd(temp2, value_hi); + __ punpckldq(temp1, temp2); + __ movsd(Address(base, offset, ScaleFactor::TIMES_1, 0), temp1); + } else { + __ movl(Address(base, offset, ScaleFactor::TIMES_1, 0), value_lo); + __ movl(Address(base, offset, ScaleFactor::TIMES_1, 4), value_hi); + } + } else { + __ movl(Address(base, offset, ScaleFactor::TIMES_1, 0), value_loc.AsRegister<Register>()); + } + + if (is_volatile) { + __ mfence(); + } + + if (type == Primitive::kPrimNot) { + codegen->MarkGCCard(locations->GetTemp(0).AsRegister<Register>(), + locations->GetTemp(1).AsRegister<Register>(), + base, + value_loc.AsRegister<Register>()); + } +} + +void IntrinsicCodeGeneratorX86::VisitUnsafePut(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, codegen_); +} +void IntrinsicCodeGeneratorX86::VisitUnsafePutOrdered(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, false, codegen_); +} +void IntrinsicCodeGeneratorX86::VisitUnsafePutVolatile(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), Primitive::kPrimInt, true, codegen_); +} +void IntrinsicCodeGeneratorX86::VisitUnsafePutObject(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, codegen_); +} +void IntrinsicCodeGeneratorX86::VisitUnsafePutObjectOrdered(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, false, codegen_); +} +void IntrinsicCodeGeneratorX86::VisitUnsafePutObjectVolatile(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), Primitive::kPrimNot, true, codegen_); +} +void IntrinsicCodeGeneratorX86::VisitUnsafePutLong(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, codegen_); +} +void IntrinsicCodeGeneratorX86::VisitUnsafePutLongOrdered(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, false, codegen_); +} +void IntrinsicCodeGeneratorX86::VisitUnsafePutLongVolatile(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, codegen_); +} + +// Unimplemented intrinsics. + +#define UNIMPLEMENTED_INTRINSIC(Name) \ +void IntrinsicLocationsBuilderX86::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ +} \ +void IntrinsicCodeGeneratorX86::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ +} + +UNIMPLEMENTED_INTRINSIC(IntegerReverse) +UNIMPLEMENTED_INTRINSIC(LongReverse) +UNIMPLEMENTED_INTRINSIC(LongReverseBytes) +UNIMPLEMENTED_INTRINSIC(MathFloor) +UNIMPLEMENTED_INTRINSIC(MathCeil) +UNIMPLEMENTED_INTRINSIC(MathRint) +UNIMPLEMENTED_INTRINSIC(MathRoundDouble) +UNIMPLEMENTED_INTRINSIC(MathRoundFloat) +UNIMPLEMENTED_INTRINSIC(StringIndexOf) +UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) +UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) +UNIMPLEMENTED_INTRINSIC(UnsafeCASInt) +UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) +UNIMPLEMENTED_INTRINSIC(UnsafeCASObject) +UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) + +} // namespace x86 +} // namespace art diff --git a/compiler/optimizing/intrinsics_x86.h b/compiler/optimizing/intrinsics_x86.h new file mode 100644 index 0000000000..e1e8260a5f --- /dev/null +++ b/compiler/optimizing/intrinsics_x86.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2015 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_OPTIMIZING_INTRINSICS_X86_H_ +#define ART_COMPILER_OPTIMIZING_INTRINSICS_X86_H_ + +#include "intrinsics.h" + +namespace art { + +class ArenaAllocator; +class HInvokeStaticOrDirect; +class HInvokeVirtual; + +namespace x86 { + +class CodeGeneratorX86; +class X86Assembler; + +class IntrinsicLocationsBuilderX86 FINAL : public IntrinsicVisitor { + public: + explicit IntrinsicLocationsBuilderX86(ArenaAllocator* arena) : arena_(arena) {} + + // Define visitor methods. + +#define OPTIMIZING_INTRINSICS(Name, IsStatic) \ + void Visit ## Name(HInvoke* invoke) OVERRIDE; +#include "intrinsics_list.h" +INTRINSICS_LIST(OPTIMIZING_INTRINSICS) +#undef INTRINSICS_LIST +#undef OPTIMIZING_INTRINSICS + + // Check whether an invoke is an intrinsic, and if so, create a location summary. Returns whether + // a corresponding LocationSummary with the intrinsified_ flag set was generated and attached to + // the invoke. + bool TryDispatch(HInvoke* invoke); + + private: + ArenaAllocator* arena_; + + DISALLOW_COPY_AND_ASSIGN(IntrinsicLocationsBuilderX86); +}; + +class IntrinsicCodeGeneratorX86 FINAL : public IntrinsicVisitor { + public: + explicit IntrinsicCodeGeneratorX86(CodeGeneratorX86* codegen) : codegen_(codegen) {} + + // Define visitor methods. + +#define OPTIMIZING_INTRINSICS(Name, IsStatic) \ + void Visit ## Name(HInvoke* invoke) OVERRIDE; +#include "intrinsics_list.h" +INTRINSICS_LIST(OPTIMIZING_INTRINSICS) +#undef INTRINSICS_LIST +#undef OPTIMIZING_INTRINSICS + + private: + X86Assembler* GetAssembler(); + + ArenaAllocator* GetAllocator(); + + CodeGeneratorX86* codegen_; + + DISALLOW_COPY_AND_ASSIGN(IntrinsicCodeGeneratorX86); +}; + +} // namespace x86 +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_INTRINSICS_X86_H_ diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 2064b18138..736cea88cb 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -667,6 +667,34 @@ void IntrinsicCodeGeneratorX86_64::VisitStringCharAt(HInvoke* invoke) { __ Bind(slow_path->GetExitLabel()); } +void IntrinsicLocationsBuilderX86_64::VisitStringCompareTo(HInvoke* invoke) { + LocationSummary* locations = new (arena_) LocationSummary(invoke, + LocationSummary::kCall, + kIntrinsified); + InvokeRuntimeCallingConvention calling_convention; + locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1))); + locations->SetOut(Location::RegisterLocation(RAX)); +} + +void IntrinsicCodeGeneratorX86_64::VisitStringCompareTo(HInvoke* invoke) { + X86_64Assembler* assembler = GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); + + // Note that the null check must have been done earlier. + DCHECK(!invoke->CanDoImplicitNullCheck()); + + CpuRegister argument = locations->InAt(1).AsRegister<CpuRegister>(); + __ testl(argument, argument); + SlowPathCodeX86_64* slow_path = new (GetAllocator()) IntrinsicSlowPathX86_64(invoke); + codegen_->AddSlowPath(slow_path); + __ j(kEqual, slow_path->GetEntryLabel()); + + __ gs()->call(Address::Absolute( + QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pStringCompareTo), true)); + __ Bind(slow_path->GetExitLabel()); +} + static void GenPeek(LocationSummary* locations, Primitive::Type size, X86_64Assembler* assembler) { CpuRegister address = locations->InAt(0).AsRegister<CpuRegister>(); CpuRegister out = locations->Out().AsRegister<CpuRegister>(); // == address, here for clarity. @@ -986,9 +1014,6 @@ UNIMPLEMENTED_INTRINSIC(MathCeil) UNIMPLEMENTED_INTRINSIC(MathRint) UNIMPLEMENTED_INTRINSIC(MathRoundDouble) UNIMPLEMENTED_INTRINSIC(MathRoundFloat) -UNIMPLEMENTED_INTRINSIC(StringIsEmpty) // Might not want to do these two anyways, inlining should -UNIMPLEMENTED_INTRINSIC(StringLength) // be good enough here. -UNIMPLEMENTED_INTRINSIC(StringCompareTo) UNIMPLEMENTED_INTRINSIC(StringIndexOf) UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 4f6565d315..dca612e6b7 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -287,39 +287,62 @@ bool HGraph::AnalyzeNaturalLoops() const { return true; } -void HGraph::AddConstant(HConstant* instruction) { - HInstruction* last_instruction = entry_block_->GetLastInstruction(); - if (last_instruction == nullptr || !last_instruction->IsControlFlow()) { - // Called from the builder. Insert at the end of the block. - entry_block_->AddInstruction(instruction); +void HGraph::InsertConstant(HConstant* constant) { + // New constants are inserted before the final control-flow instruction + // of the graph, or at its end if called from the graph builder. + if (entry_block_->EndsWithControlFlowInstruction()) { + entry_block_->InsertInstructionBefore(constant, entry_block_->GetLastInstruction()); } else { - // Entry block ends with control-flow. Insert before the last instruction. - entry_block_->InsertInstructionBefore(instruction, last_instruction); + entry_block_->AddInstruction(constant); } } HNullConstant* HGraph::GetNullConstant() { if (cached_null_constant_ == nullptr) { cached_null_constant_ = new (arena_) HNullConstant(); - AddConstant(cached_null_constant_); + InsertConstant(cached_null_constant_); } return cached_null_constant_; } -HIntConstant* HGraph::GetIntConstant0() { - if (cached_int_constant0_ == nullptr) { - cached_int_constant0_ = new (arena_) HIntConstant(0); - AddConstant(cached_int_constant0_); +template <class InstructionType, typename ValueType> +InstructionType* HGraph::CreateConstant(ValueType value, + ArenaSafeMap<ValueType, InstructionType*>* cache) { + // Try to find an existing constant of the given value. + InstructionType* constant = nullptr; + auto cached_constant = cache->find(value); + if (cached_constant != cache->end()) { + constant = cached_constant->second; } - return cached_int_constant0_; + + // If not found or previously deleted, create and cache a new instruction. + if (constant == nullptr || constant->GetBlock() == nullptr) { + constant = new (arena_) InstructionType(value); + cache->Overwrite(value, constant); + InsertConstant(constant); + } + return constant; } -HIntConstant* HGraph::GetIntConstant1() { - if (cached_int_constant1_ == nullptr) { - cached_int_constant1_ = new (arena_) HIntConstant(1); - AddConstant(cached_int_constant1_); +HConstant* HGraph::GetConstant(Primitive::Type type, int64_t value) { + switch (type) { + case Primitive::Type::kPrimBoolean: + DCHECK(IsUint<1>(value)); + FALLTHROUGH_INTENDED; + case Primitive::Type::kPrimByte: + case Primitive::Type::kPrimChar: + case Primitive::Type::kPrimShort: + case Primitive::Type::kPrimInt: + DCHECK(IsInt(Primitive::ComponentSize(type) * kBitsPerByte, value)); + return GetIntConstant(static_cast<int32_t>(value)); + + case Primitive::Type::kPrimLong: + return GetLongConstant(value); + + default: + LOG(FATAL) << "Unsupported constant type"; + UNREACHABLE(); } - return cached_int_constant1_; } void HLoopInformation::Add(HBasicBlock* block) { @@ -676,7 +699,7 @@ void HGraphVisitor::VisitBasicBlock(HBasicBlock* block) { HConstant* HUnaryOperation::TryStaticEvaluation() const { if (GetInput()->IsIntConstant()) { int32_t value = Evaluate(GetInput()->AsIntConstant()->GetValue()); - return new(GetBlock()->GetGraph()->GetArena()) HIntConstant(value); + return GetBlock()->GetGraph()->GetIntConstant(value); } else if (GetInput()->IsLongConstant()) { // TODO: Implement static evaluation of long unary operations. // @@ -692,15 +715,15 @@ HConstant* HBinaryOperation::TryStaticEvaluation() const { if (GetLeft()->IsIntConstant() && GetRight()->IsIntConstant()) { int32_t value = Evaluate(GetLeft()->AsIntConstant()->GetValue(), GetRight()->AsIntConstant()->GetValue()); - return new(GetBlock()->GetGraph()->GetArena()) HIntConstant(value); + return GetBlock()->GetGraph()->GetIntConstant(value); } else if (GetLeft()->IsLongConstant() && GetRight()->IsLongConstant()) { int64_t value = Evaluate(GetLeft()->AsLongConstant()->GetValue(), GetRight()->AsLongConstant()->GetValue()); if (GetResultType() == Primitive::kPrimLong) { - return new(GetBlock()->GetGraph()->GetArena()) HLongConstant(value); + return GetBlock()->GetGraph()->GetLongConstant(value); } else { DCHECK_EQ(GetResultType(), Primitive::kPrimInt); - return new(GetBlock()->GetGraph()->GetArena()) HIntConstant(value); + return GetBlock()->GetGraph()->GetIntConstant(static_cast<int32_t>(value)); } } return nullptr; @@ -733,16 +756,6 @@ bool HCondition::IsBeforeWhenDisregardMoves(HIf* if_) const { return this == if_->GetPreviousDisregardingMoves(); } -HConstant* HConstant::NewConstant(ArenaAllocator* allocator, Primitive::Type type, int64_t val) { - if (type == Primitive::kPrimInt) { - DCHECK(IsInt<32>(val)); - return new (allocator) HIntConstant(val); - } else { - DCHECK_EQ(type, Primitive::kPrimLong); - return new (allocator) HLongConstant(val); - } -} - bool HInstruction::Equals(HInstruction* other) const { if (!InstructionTypeEquals(other)) return false; DCHECK_EQ(GetKind(), other->GetKind()); @@ -832,6 +845,10 @@ bool HBasicBlock::IsSingleGoto() const { && (loop_info == nullptr || !loop_info->IsBackEdge(*this)); } +bool HBasicBlock::EndsWithControlFlowInstruction() const { + return !GetInstructions().IsEmpty() && GetLastInstruction()->IsControlFlow(); +} + bool HBasicBlock::EndsWithIf() const { return !GetInstructions().IsEmpty() && GetLastInstruction()->IsIf(); } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 9c751fb9c5..21ed3504f1 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -17,6 +17,7 @@ #ifndef ART_COMPILER_OPTIMIZING_NODES_H_ #define ART_COMPILER_OPTIMIZING_NODES_H_ +#include "base/arena_containers.h" #include "base/arena_object.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "handle.h" @@ -33,16 +34,20 @@ namespace art { class GraphChecker; class HBasicBlock; +class HDoubleConstant; class HEnvironment; +class HFloatConstant; +class HGraphVisitor; class HInstruction; class HIntConstant; class HInvoke; -class HGraphVisitor; +class HLongConstant; class HNullConstant; class HPhi; class HSuspendCheck; class LiveInterval; class LocationSummary; +class SsaBuilder; static const int kDefaultNumberOfBlocks = 8; static const int kDefaultNumberOfSuccessors = 2; @@ -115,7 +120,10 @@ class HGraph : public ArenaObject<kArenaAllocMisc> { temporaries_vreg_slots_(0), has_array_accesses_(false), debuggable_(debuggable), - current_instruction_id_(start_instruction_id) {} + current_instruction_id_(start_instruction_id), + cached_null_constant_(nullptr), + cached_int_constants_(std::less<int32_t>(), arena->Adapter()), + cached_long_constants_(std::less<int64_t>(), arena->Adapter()) {} ArenaAllocator* GetArena() const { return arena_; } const GrowableArray<HBasicBlock*>& GetBlocks() const { return blocks_; } @@ -128,7 +136,6 @@ class HGraph : public ArenaObject<kArenaAllocMisc> { void SetExitBlock(HBasicBlock* block) { exit_block_ = block; } void AddBlock(HBasicBlock* block); - void AddConstant(HConstant* instruction); // Try building the SSA form of this graph, with dominance computation and loop // recognition. Returns whether it was successful in doing all these steps. @@ -219,9 +226,17 @@ class HGraph : public ArenaObject<kArenaAllocMisc> { bool IsDebuggable() const { return debuggable_; } + // Returns a constant of the given type and value. If it does not exist + // already, it is created and inserted into the graph. Only integral types + // are currently supported. + HConstant* GetConstant(Primitive::Type type, int64_t value); HNullConstant* GetNullConstant(); - HIntConstant* GetIntConstant0(); - HIntConstant* GetIntConstant1(); + HIntConstant* GetIntConstant(int32_t value) { + return CreateConstant(value, &cached_int_constants_); + } + HLongConstant* GetLongConstant(int64_t value) { + return CreateConstant(value, &cached_long_constants_); + } private: HBasicBlock* FindCommonDominator(HBasicBlock* first, HBasicBlock* second) const; @@ -235,6 +250,10 @@ class HGraph : public ArenaObject<kArenaAllocMisc> { void RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const; void RemoveDeadBlocks(const ArenaBitVector& visited) const; + template <class InstType, typename ValueType> + InstType* CreateConstant(ValueType value, ArenaSafeMap<ValueType, InstType*>* cache); + void InsertConstant(HConstant* instruction); + ArenaAllocator* const arena_; // List of blocks in insertion order. @@ -269,12 +288,10 @@ class HGraph : public ArenaObject<kArenaAllocMisc> { // The current id to assign to a newly added instruction. See HInstruction.id_. int32_t current_instruction_id_; - // Cached null constant that might be created when building SSA form. - HNullConstant* cached_null_constant_; - // Cached common constants often needed by optimization passes. - HIntConstant* cached_int_constant0_; - HIntConstant* cached_int_constant1_; + HNullConstant* cached_null_constant_; + ArenaSafeMap<int32_t, HIntConstant*> cached_int_constants_; + ArenaSafeMap<int64_t, HLongConstant*> cached_long_constants_; ART_FRIEND_TEST(GraphTest, IfSuccessorSimpleJoinBlock1); DISALLOW_COPY_AND_ASSIGN(HGraph); @@ -602,6 +619,7 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> { bool IsCatchBlock() const { return is_catch_block_; } void SetIsCatchBlock() { is_catch_block_ = true; } + bool EndsWithControlFlowInstruction() const; bool EndsWithIf() const; bool HasSinglePhi() const; @@ -1908,8 +1926,6 @@ class HConstant : public HExpression<0> { virtual bool IsZero() const { return false; } virtual bool IsOne() const { return false; } - static HConstant* NewConstant(ArenaAllocator* allocator, Primitive::Type type, int64_t val); - DECLARE_INSTRUCTION(Constant); private: @@ -1918,8 +1934,6 @@ class HConstant : public HExpression<0> { class HFloatConstant : public HConstant { public: - explicit HFloatConstant(float value) : HConstant(Primitive::kPrimFloat), value_(value) {} - float GetValue() const { return value_; } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { @@ -1944,15 +1958,19 @@ class HFloatConstant : public HConstant { DECLARE_INSTRUCTION(FloatConstant); private: + explicit HFloatConstant(float value) : HConstant(Primitive::kPrimFloat), value_(value) {} + const float value_; + // Only the SsaBuilder can currently create floating-point constants. If we + // ever need to create them later in the pipeline, we will have to handle them + // the same way as integral constants. + friend class SsaBuilder; DISALLOW_COPY_AND_ASSIGN(HFloatConstant); }; class HDoubleConstant : public HConstant { public: - explicit HDoubleConstant(double value) : HConstant(Primitive::kPrimDouble), value_(value) {} - double GetValue() const { return value_; } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { @@ -1977,15 +1995,19 @@ class HDoubleConstant : public HConstant { DECLARE_INSTRUCTION(DoubleConstant); private: + explicit HDoubleConstant(double value) : HConstant(Primitive::kPrimDouble), value_(value) {} + const double value_; + // Only the SsaBuilder can currently create floating-point constants. If we + // ever need to create them later in the pipeline, we will have to handle them + // the same way as integral constants. + friend class SsaBuilder; DISALLOW_COPY_AND_ASSIGN(HDoubleConstant); }; class HNullConstant : public HConstant { public: - HNullConstant() : HConstant(Primitive::kPrimNot) {} - bool InstructionDataEquals(HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { return true; } @@ -1997,6 +2019,9 @@ class HNullConstant : public HConstant { DECLARE_INSTRUCTION(NullConstant); private: + HNullConstant() : HConstant(Primitive::kPrimNot) {} + + friend class HGraph; DISALLOW_COPY_AND_ASSIGN(HNullConstant); }; @@ -2004,8 +2029,6 @@ class HNullConstant : public HConstant { // synthesized (for example with the if-eqz instruction). class HIntConstant : public HConstant { public: - explicit HIntConstant(int32_t value) : HConstant(Primitive::kPrimInt), value_(value) {} - int32_t GetValue() const { return value_; } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { @@ -2026,15 +2049,18 @@ class HIntConstant : public HConstant { DECLARE_INSTRUCTION(IntConstant); private: + explicit HIntConstant(int32_t value) : HConstant(Primitive::kPrimInt), value_(value) {} + const int32_t value_; + friend class HGraph; + ART_FRIEND_TEST(GraphTest, InsertInstructionBefore); + ART_FRIEND_TEST(ParallelMoveTest, ConstantLast); DISALLOW_COPY_AND_ASSIGN(HIntConstant); }; class HLongConstant : public HConstant { public: - explicit HLongConstant(int64_t value) : HConstant(Primitive::kPrimLong), value_(value) {} - int64_t GetValue() const { return value_; } bool InstructionDataEquals(HInstruction* other) const OVERRIDE { @@ -2050,8 +2076,11 @@ class HLongConstant : public HConstant { DECLARE_INSTRUCTION(LongConstant); private: + explicit HLongConstant(int64_t value) : HConstant(Primitive::kPrimLong), value_(value) {} + const int64_t value_; + friend class HGraph; DISALLOW_COPY_AND_ASSIGN(HLongConstant); }; @@ -3086,7 +3115,7 @@ class HLoadString : public HExpression<0> { class HClinitCheck : public HExpression<1> { public: explicit HClinitCheck(HLoadClass* constant, uint32_t dex_pc) - : HExpression(Primitive::kPrimNot, SideEffects::All()), + : HExpression(Primitive::kPrimNot, SideEffects::ChangesSomething()), dex_pc_(dex_pc) { SetRawInputAt(0, constant); } diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 5ce73baef2..b2f9c65153 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -583,8 +583,13 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, if (method != nullptr) { return method; } - return delegate_->Compile(code_item, access_flags, invoke_type, class_def_idx, method_idx, - class_loader, dex_file); + method = delegate_->Compile(code_item, access_flags, invoke_type, class_def_idx, method_idx, + class_loader, dex_file); + + if (method != nullptr) { + compilation_stats_.RecordStat(MethodCompilationStat::kCompiledQuick); + } + return method; } Compiler* CreateOptimizingCompiler(CompilerDriver* driver) { diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 22ec2a5167..b97a66719d 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -28,6 +28,7 @@ enum MethodCompilationStat { kAttemptCompilation = 0, kCompiledBaseline, kCompiledOptimized, + kCompiledQuick, kInlinedInvoke, kNotCompiledUnsupportedIsa, kNotCompiledPathological, @@ -65,16 +66,22 @@ class OptimizingCompilerStats { compile_stats_[kCompiledBaseline] * 100 / compile_stats_[kAttemptCompilation]; size_t optimized_percent = compile_stats_[kCompiledOptimized] * 100 / compile_stats_[kAttemptCompilation]; + size_t quick_percent = + compile_stats_[kCompiledQuick] * 100 / compile_stats_[kAttemptCompilation]; std::ostringstream oss; - oss << "Attempted compilation of " << compile_stats_[kAttemptCompilation] << " methods: " - << unoptimized_percent << "% (" << compile_stats_[kCompiledBaseline] << ") unoptimized, " - << optimized_percent << "% (" << compile_stats_[kCompiledOptimized] << ") optimized."; + oss << "Attempted compilation of " << compile_stats_[kAttemptCompilation] << " methods: "; + + oss << unoptimized_percent << "% (" << compile_stats_[kCompiledBaseline] << ") unoptimized, "; + oss << optimized_percent << "% (" << compile_stats_[kCompiledOptimized] << ") optimized, "; + oss << quick_percent << "% (" << compile_stats_[kCompiledQuick] << ") quick."; + + LOG(INFO) << oss.str(); + for (int i = 0; i < kLastStat; i++) { if (compile_stats_[i] != 0) { - oss << "\n" << PrintMethodCompilationStat(i) << ": " << compile_stats_[i]; + VLOG(compiler) << PrintMethodCompilationStat(i) << ": " << compile_stats_[i]; } } - LOG(INFO) << oss.str(); } } @@ -84,6 +91,7 @@ class OptimizingCompilerStats { case kAttemptCompilation : return "kAttemptCompilation"; case kCompiledBaseline : return "kCompiledBaseline"; case kCompiledOptimized : return "kCompiledOptimized"; + case kCompiledQuick : return "kCompiledQuick"; case kInlinedInvoke : return "kInlinedInvoke"; case kNotCompiledUnsupportedIsa : return "kNotCompiledUnsupportedIsa"; case kNotCompiledPathological : return "kNotCompiledPathological"; diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index cecc210cbf..cf38bd3f8c 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -213,7 +213,7 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { LiveInterval* interval = LiveInterval::MakeTempInterval(allocator_, Primitive::kPrimInt); temp_intervals_.Add(interval); - interval->AddRange(position, position + 1); + interval->AddTempUse(instruction, i); unhandled_core_intervals_.Add(interval); break; } @@ -222,7 +222,7 @@ void RegisterAllocator::ProcessInstruction(HInstruction* instruction) { LiveInterval* interval = LiveInterval::MakeTempInterval(allocator_, Primitive::kPrimDouble); temp_intervals_.Add(interval); - interval->AddRange(position, position + 1); + interval->AddTempUse(instruction, i); if (codegen_->NeedsTwoRegisters(Primitive::kPrimDouble)) { interval->AddHighInterval(true); LiveInterval* high = interval->GetHighInterval(); @@ -851,6 +851,23 @@ bool RegisterAllocator::TrySplitNonPairOrUnalignedPairIntervalAt(size_t position return false; } +bool RegisterAllocator::PotentiallyRemoveOtherHalf(LiveInterval* interval, + GrowableArray<LiveInterval*>* intervals, + size_t index) { + if (interval->IsLowInterval()) { + DCHECK_EQ(intervals->Get(index), interval->GetHighInterval()); + intervals->DeleteAt(index); + return true; + } else if (interval->IsHighInterval()) { + DCHECK_GT(index, 0u); + DCHECK_EQ(intervals->Get(index - 1), interval->GetLowInterval()); + intervals->DeleteAt(index - 1); + return true; + } else { + return false; + } +} + // Find the register that is used the last, and spill the interval // that holds it. If the first use of `current` is after that register // we spill `current` instead. @@ -974,33 +991,17 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) { if (active->GetRegister() == reg) { DCHECK(!active->IsFixed()); LiveInterval* split = Split(active, current->GetStart()); - active_.DeleteAt(i); if (split != active) { handled_.Add(active); } + active_.DeleteAt(i); + PotentiallyRemoveOtherHalf(active, &active_, i); AddSorted(unhandled_, split); - - if (active->IsLowInterval() || active->IsHighInterval()) { - LiveInterval* other_half = active->IsLowInterval() - ? active->GetHighInterval() - : active->GetLowInterval(); - // We also need to remove the other half from the list of actives. - bool found = false; - for (size_t j = 0; j < active_.Size(); ++j) { - if (active_.Get(j) == other_half) { - found = true; - active_.DeleteAt(j); - handled_.Add(other_half); - break; - } - } - DCHECK(found); - } break; } } - for (size_t i = 0, e = inactive_.Size(); i < e; ++i) { + for (size_t i = 0; i < inactive_.Size(); ++i) { LiveInterval* inactive = inactive_.Get(i); if (inactive->GetRegister() == reg) { if (!current->IsSplit() && !inactive->IsFixed()) { @@ -1024,29 +1025,14 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) { // If it's inactive, it must start before the current interval. DCHECK_NE(split, inactive); inactive_.DeleteAt(i); + if (PotentiallyRemoveOtherHalf(inactive, &inactive_, i) && inactive->IsHighInterval()) { + // We have removed an entry prior to `inactive`. So we need to decrement. + --i; + } + // Decrement because we have removed `inactive` from the list. --i; - --e; handled_.Add(inactive); AddSorted(unhandled_, split); - - if (inactive->IsLowInterval() || inactive->IsHighInterval()) { - LiveInterval* other_half = inactive->IsLowInterval() - ? inactive->GetHighInterval() - : inactive->GetLowInterval(); - - // We also need to remove the other half from the list of inactives. - bool found = false; - for (size_t j = 0; j < inactive_.Size(); ++j) { - if (inactive_.Get(j) == other_half) { - found = true; - inactive_.DeleteAt(j); - --e; - handled_.Add(other_half); - break; - } - } - DCHECK(found); - } } } } @@ -1695,8 +1681,6 @@ void RegisterAllocator::Resolve() { } // Assign temp locations. - HInstruction* current = nullptr; - size_t temp_index = 0; for (size_t i = 0; i < temp_intervals_.Size(); ++i) { LiveInterval* temp = temp_intervals_.Get(i); if (temp->IsHighInterval()) { @@ -1704,25 +1688,20 @@ void RegisterAllocator::Resolve() { continue; } HInstruction* at = liveness_.GetTempUser(temp); - if (at != current) { - temp_index = 0; - current = at; - } + size_t temp_index = liveness_.GetTempIndex(temp); LocationSummary* locations = at->GetLocations(); switch (temp->GetType()) { case Primitive::kPrimInt: - locations->SetTempAt( - temp_index++, Location::RegisterLocation(temp->GetRegister())); + locations->SetTempAt(temp_index, Location::RegisterLocation(temp->GetRegister())); break; case Primitive::kPrimDouble: if (codegen_->NeedsTwoRegisters(Primitive::kPrimDouble)) { Location location = Location::FpuRegisterPairLocation( temp->GetRegister(), temp->GetHighInterval()->GetRegister()); - locations->SetTempAt(temp_index++, location); + locations->SetTempAt(temp_index, location); } else { - locations->SetTempAt( - temp_index++, Location::FpuRegisterLocation(temp->GetRegister())); + locations->SetTempAt(temp_index, Location::FpuRegisterLocation(temp->GetRegister())); } break; diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h index fcc61128a6..717be75533 100644 --- a/compiler/optimizing/register_allocator.h +++ b/compiler/optimizing/register_allocator.h @@ -144,6 +144,13 @@ class RegisterAllocator { size_t first_register_use, size_t* next_use); + // If `interval` has another half, remove it from the list of `intervals`. + // `index` holds the index at which `interval` is in `intervals`. + // Returns whether there is another half. + bool PotentiallyRemoveOtherHalf(LiveInterval* interval, + GrowableArray<LiveInterval*>* intervals, + size_t index); + ArenaAllocator* const allocator_; CodeGenerator* const codegen_; const SsaLivenessAnalysis& liveness_; diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc index 7a2d84b056..7c3a0357d6 100644 --- a/compiler/optimizing/register_allocator_test.cc +++ b/compiler/optimizing/register_allocator_test.cc @@ -644,11 +644,10 @@ static HGraph* BuildTwoSubs(ArenaAllocator* allocator, graph->AddBlock(entry); graph->SetEntryBlock(entry); HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimInt); - HInstruction* constant1 = new (allocator) HIntConstant(0); - HInstruction* constant2 = new (allocator) HIntConstant(0); entry->AddInstruction(parameter); - entry->AddInstruction(constant1); - entry->AddInstruction(constant2); + + HInstruction* constant1 = graph->GetIntConstant(1); + HInstruction* constant2 = graph->GetIntConstant(2); HBasicBlock* block = new (allocator) HBasicBlock(graph); graph->AddBlock(block); diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index ae6bf16f77..fcc4e69b37 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -353,7 +353,7 @@ void SsaBuilder::VisitBasicBlock(HBasicBlock* block) { * is used for floating point operations. We create a floating-point equivalent * constant to make the operations correctly typed. */ -static HFloatConstant* GetFloatEquivalent(HIntConstant* constant) { +HFloatConstant* SsaBuilder::GetFloatEquivalent(HIntConstant* constant) { // We place the floating point constant next to this constant. HFloatConstant* result = constant->GetNext()->AsFloatConstant(); if (result == nullptr) { @@ -375,7 +375,7 @@ static HFloatConstant* GetFloatEquivalent(HIntConstant* constant) { * is used for floating point operations. We create a floating-point equivalent * constant to make the operations correctly typed. */ -static HDoubleConstant* GetDoubleEquivalent(HLongConstant* constant) { +HDoubleConstant* SsaBuilder::GetDoubleEquivalent(HLongConstant* constant) { // We place the floating point constant next to this constant. HDoubleConstant* result = constant->GetNext()->AsDoubleConstant(); if (result == nullptr) { @@ -398,7 +398,7 @@ static HDoubleConstant* GetDoubleEquivalent(HLongConstant* constant) { * floating point registers and core registers), we need to create a copy of the * phi with a floating point / reference type. */ -static HPhi* GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive::Type type) { +HPhi* SsaBuilder::GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive::Type type) { // We place the floating point /reference phi next to this phi. HInstruction* next = phi->GetNext(); if (next != nullptr diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h index 24dc449513..569b3e2223 100644 --- a/compiler/optimizing/ssa_builder.h +++ b/compiler/optimizing/ssa_builder.h @@ -85,6 +85,10 @@ class SsaBuilder : public HGraphVisitor { static constexpr const char* kSsaBuilderPassName = "ssa_builder"; private: + static HFloatConstant* GetFloatEquivalent(HIntConstant* constant); + static HDoubleConstant* GetDoubleEquivalent(HLongConstant* constant); + static HPhi* GetFloatDoubleOrReferenceEquivalentOfPhi(HPhi* phi, Primitive::Type type); + // Locals for the current block being visited. HEnvironment* current_locals_; diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index 56ccd717cf..0f3973e5fb 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -318,6 +318,8 @@ static int RegisterOrLowRegister(Location location) { int LiveInterval::FindFirstRegisterHint(size_t* free_until) const { DCHECK(!IsHighInterval()); + if (IsTemp()) return kNoRegister; + if (GetParent() == this && defined_by_ != nullptr) { // This is the first interval for the instruction. Try to find // a register based on its definition. diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index b57029d1a7..bc78dc2e76 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -180,6 +180,15 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { // This interval is the result of a split. bool IsSplit() const { return parent_ != this; } + void AddTempUse(HInstruction* instruction, size_t temp_index) { + DCHECK(IsTemp()); + DCHECK(first_use_ == nullptr) << "A temporary can only have one user"; + size_t position = instruction->GetLifetimePosition(); + first_use_ = new (allocator_) UsePosition( + instruction, temp_index, /* is_environment */ false, position, first_use_); + AddRange(position, position + 1); + } + void AddUse(HInstruction* instruction, size_t input_index, bool is_environment) { // Set the use within the instruction. size_t position = instruction->GetLifetimePosition() + 1; @@ -856,7 +865,15 @@ class SsaLivenessAnalysis : public ValueObject { HInstruction* GetTempUser(LiveInterval* temp) const { // A temporary shares the same lifetime start as the instruction that requires it. DCHECK(temp->IsTemp()); - return GetInstructionFromPosition(temp->GetStart() / 2); + HInstruction* user = GetInstructionFromPosition(temp->GetStart() / 2); + DCHECK_EQ(user, temp->GetFirstUse()->GetUser()); + return user; + } + + size_t GetTempIndex(LiveInterval* temp) const { + // We use the input index to store the index of the temporary in the user's temporary list. + DCHECK(temp->IsTemp()); + return temp->GetFirstUse()->GetInputIndex(); } size_t GetMaxLifetimePosition() const { diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 5818a37a46..b77e60471b 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -27,6 +27,32 @@ namespace art { +// Helper to build art::StackMapStream::LocationCatalogEntriesIndices. +class LocationCatalogEntriesIndicesEmptyFn { + public: + void MakeEmpty(std::pair<DexRegisterLocation, size_t>& item) const { + item.first = DexRegisterLocation::None(); + } + bool IsEmpty(const std::pair<DexRegisterLocation, size_t>& item) const { + return item.first == DexRegisterLocation::None(); + } +}; + +// Hash function for art::StackMapStream::LocationCatalogEntriesIndices. +// This hash function does not create collisions. +class DexRegisterLocationHashFn { + public: + size_t operator()(DexRegisterLocation key) const { + // Concatenate `key`s fields to create a 64-bit value to be hashed. + int64_t kind_and_value = + (static_cast<int64_t>(key.kind_) << 32) | static_cast<int64_t>(key.value_); + return inner_hash_fn_(kind_and_value); + } + private: + std::hash<int64_t> inner_hash_fn_; +}; + + /** * Collects and builds stack maps for a method. All the stack maps * for a method are placed in a CodeInfo object. @@ -36,6 +62,7 @@ class StackMapStream : public ValueObject { explicit StackMapStream(ArenaAllocator* allocator) : allocator_(allocator), stack_maps_(allocator, 10), + location_catalog_entries_(allocator, 4), dex_register_locations_(allocator, 10 * 4), inline_infos_(allocator, 2), stack_mask_max_(-1), @@ -111,6 +138,7 @@ class StackMapStream : public ValueObject { size_t ComputeNeededSize() { size_t size = CodeInfo::kFixedSize + + ComputeDexRegisterLocationCatalogSize() + ComputeStackMapsSize() + ComputeDexRegisterMapsSize() + ComputeInlineInfoSize(); @@ -131,21 +159,39 @@ class StackMapStream : public ValueObject { native_pc_offset_max_); } - // Compute the size of the Dex register map of `entry`. + // Compute the size of the Dex register location catalog of `entry`. + size_t ComputeDexRegisterLocationCatalogSize() const { + size_t size = DexRegisterLocationCatalog::kFixedSize; + for (size_t location_catalog_entry_index = 0; + location_catalog_entry_index < location_catalog_entries_.Size(); + ++location_catalog_entry_index) { + DexRegisterLocation dex_register_location = + location_catalog_entries_.Get(location_catalog_entry_index); + size += DexRegisterLocationCatalog::EntrySize(dex_register_location); + } + return size; + } + size_t ComputeDexRegisterMapSize(const StackMapEntry& entry) const { + // Size of the map in bytes. size_t size = DexRegisterMap::kFixedSize; - // Add the bit mask for the dex register liveness. - size += DexRegisterMap::LiveBitMaskSize(entry.num_dex_registers); - for (size_t dex_register_number = 0, index_in_dex_register_locations = 0; + // Add the live bit mask for the Dex register liveness. + size += DexRegisterMap::GetLiveBitMaskSize(entry.num_dex_registers); + // Compute the size of the set of live Dex register entries. + size_t number_of_live_dex_registers = 0; + for (size_t dex_register_number = 0; dex_register_number < entry.num_dex_registers; ++dex_register_number) { if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) { - DexRegisterLocation dex_register_location = dex_register_locations_.Get( - entry.dex_register_locations_start_index + index_in_dex_register_locations); - size += DexRegisterMap::EntrySize(dex_register_location); - index_in_dex_register_locations++; + ++number_of_live_dex_registers; } } + size_t map_entries_size_in_bits = + DexRegisterMap::SingleEntrySizeInBits(location_catalog_entries_.Size()) + * number_of_live_dex_registers; + size_t map_entries_size_in_bytes = + RoundUp(map_entries_size_in_bits, kBitsPerByte) / kBitsPerByte; + size += map_entries_size_in_bytes; return size; } @@ -168,8 +214,16 @@ class StackMapStream : public ValueObject { + (number_of_stack_maps_with_inline_info_ * InlineInfo::kFixedSize); } + size_t ComputeDexRegisterLocationCatalogStart() const { + return CodeInfo::kFixedSize; + } + + size_t ComputeStackMapsStart() const { + return ComputeDexRegisterLocationCatalogStart() + ComputeDexRegisterLocationCatalogSize(); + } + size_t ComputeDexRegisterMapsStart() { - return CodeInfo::kFixedSize + ComputeStackMapsSize(); + return ComputeStackMapsStart() + ComputeStackMapsSize(); } size_t ComputeInlineInfoStart() { @@ -198,7 +252,25 @@ class StackMapStream : public ValueObject { inline_info_size, dex_register_map_size, dex_pc_max_, native_pc_offset_max_); code_info.SetNumberOfStackMaps(stack_maps_.Size()); code_info.SetStackMaskSize(stack_mask_size); - DCHECK_EQ(code_info.StackMapsSize(), ComputeStackMapsSize()); + DCHECK_EQ(code_info.GetStackMapsSize(), ComputeStackMapsSize()); + + // Set the Dex register location catalog. + code_info.SetNumberOfDexRegisterLocationCatalogEntries( + location_catalog_entries_.Size()); + MemoryRegion dex_register_location_catalog_region = region.Subregion( + ComputeDexRegisterLocationCatalogStart(), + ComputeDexRegisterLocationCatalogSize()); + DexRegisterLocationCatalog dex_register_location_catalog(dex_register_location_catalog_region); + // Offset in `dex_register_location_catalog` where to store the next + // register location. + size_t location_catalog_offset = DexRegisterLocationCatalog::kFixedSize; + for (size_t i = 0, e = location_catalog_entries_.Size(); i < e; ++i) { + DexRegisterLocation dex_register_location = location_catalog_entries_.Get(i); + dex_register_location_catalog.SetRegisterInfo(location_catalog_offset, dex_register_location); + location_catalog_offset += DexRegisterLocationCatalog::EntrySize(dex_register_location); + } + // Ensure we reached the end of the Dex registers location_catalog. + DCHECK_EQ(location_catalog_offset, dex_register_location_catalog_region.size()); uintptr_t next_dex_register_map_offset = 0; uintptr_t next_inline_info_offset = 0; @@ -234,25 +306,25 @@ class StackMapStream : public ValueObject { stack_map.SetDexRegisterMapOffset( code_info, register_region.start() - dex_register_locations_region.start()); - // Offset in `dex_register_map` where to store the next register entry. - size_t offset = DexRegisterMap::kFixedSize; - dex_register_map.SetLiveBitMask(offset, - entry.num_dex_registers, - *entry.live_dex_registers_mask); - offset += DexRegisterMap::LiveBitMaskSize(entry.num_dex_registers); + // Set the live bit mask. + dex_register_map.SetLiveBitMask(entry.num_dex_registers, *entry.live_dex_registers_mask); + + // Set the dex register location mapping data. for (size_t dex_register_number = 0, index_in_dex_register_locations = 0; dex_register_number < entry.num_dex_registers; ++dex_register_number) { if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) { - DexRegisterLocation dex_register_location = dex_register_locations_.Get( - entry.dex_register_locations_start_index + index_in_dex_register_locations); - dex_register_map.SetRegisterInfo(offset, dex_register_location); - offset += DexRegisterMap::EntrySize(dex_register_location); + size_t location_catalog_entry_index = + dex_register_locations_.Get(entry.dex_register_locations_start_index + + index_in_dex_register_locations); + dex_register_map.SetLocationCatalogEntryIndex( + index_in_dex_register_locations, + location_catalog_entry_index, + entry.num_dex_registers, + location_catalog_entries_.Size()); ++index_in_dex_register_locations; } } - // Ensure we reached the end of the Dex registers region. - DCHECK_EQ(offset, register_region.size()); } } @@ -282,12 +354,31 @@ class StackMapStream : public ValueObject { } void AddDexRegisterEntry(uint16_t dex_register, DexRegisterLocation::Kind kind, int32_t value) { + StackMapEntry entry = stack_maps_.Get(stack_maps_.Size() - 1); + DCHECK_LT(dex_register, entry.num_dex_registers); + if (kind != DexRegisterLocation::Kind::kNone) { // Ensure we only use non-compressed location kind at this stage. DCHECK(DexRegisterLocation::IsShortLocationKind(kind)) << DexRegisterLocation::PrettyDescriptor(kind); - dex_register_locations_.Add(DexRegisterLocation(kind, value)); - StackMapEntry entry = stack_maps_.Get(stack_maps_.Size() - 1); + DexRegisterLocation location(kind, value); + + // Look for Dex register `location` in the location catalog (using the + // companion hash map of locations to indices). Use its index if it + // is already in the location catalog. If not, insert it (in the + // location catalog and the hash map) and use the newly created index. + auto it = location_catalog_entries_indices_.Find(location); + if (it != location_catalog_entries_indices_.end()) { + // Retrieve the index from the hash map. + dex_register_locations_.Add(it->second); + } else { + // Create a new entry in the location catalog and the hash map. + size_t index = location_catalog_entries_.Size(); + location_catalog_entries_.Add(location); + dex_register_locations_.Add(index); + location_catalog_entries_indices_.Insert(std::make_pair(location, index)); + } + entry.live_dex_registers_mask->SetBit(dex_register); entry.dex_register_map_hash += (1 << dex_register); entry.dex_register_map_hash += static_cast<uint32_t>(value); @@ -354,9 +445,9 @@ class StackMapStream : public ValueObject { return false; } if (a.live_dex_registers_mask->IsBitSet(i)) { - DexRegisterLocation a_loc = dex_register_locations_.Get( + size_t a_loc = dex_register_locations_.Get( a.dex_register_locations_start_index + index_in_dex_register_locations); - DexRegisterLocation b_loc = dex_register_locations_.Get( + size_t b_loc = dex_register_locations_.Get( b.dex_register_locations_start_index + index_in_dex_register_locations); if (a_loc != b_loc) { return false; @@ -369,7 +460,18 @@ class StackMapStream : public ValueObject { ArenaAllocator* allocator_; GrowableArray<StackMapEntry> stack_maps_; - GrowableArray<DexRegisterLocation> dex_register_locations_; + + // A catalog of unique [location_kind, register_value] pairs (per method). + GrowableArray<DexRegisterLocation> location_catalog_entries_; + // Map from Dex register location catalog entries to their indices in the + // location catalog. + typedef HashMap<DexRegisterLocation, size_t, LocationCatalogEntriesIndicesEmptyFn, + DexRegisterLocationHashFn> LocationCatalogEntriesIndices; + LocationCatalogEntriesIndices location_catalog_entries_indices_; + + // A set of concatenated maps of Dex register locations indices to + // `location_catalog_entries_`. + GrowableArray<size_t> dex_register_locations_; GrowableArray<InlineInfoEntry> inline_infos_; int stack_mask_max_; uint32_t dex_pc_max_; @@ -380,10 +482,6 @@ class StackMapStream : public ValueObject { static constexpr uint32_t kNoSameDexMapFound = -1; - ART_FRIEND_TEST(StackMapTest, Test1); - ART_FRIEND_TEST(StackMapTest, Test2); - ART_FRIEND_TEST(StackMapTest, TestNonLiveDexRegisters); - DISALLOW_COPY_AND_ASSIGN(StackMapStream); }; diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index e5a9790254..b9bf0165f3 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -31,6 +31,8 @@ static bool SameBits(MemoryRegion region, const BitVector& bit_vector) { return true; } +using Kind = DexRegisterLocation::Kind; + TEST(StackMapTest, Test1) { ArenaPool pool; ArenaAllocator arena(&pool); @@ -39,8 +41,8 @@ TEST(StackMapTest, Test1) { ArenaBitVector sp_mask(&arena, 0, false); size_t number_of_dex_registers = 2; stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); - stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInStack, 0); - stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2); + stream.AddDexRegisterEntry(0, Kind::kInStack, 0); // Short location. + stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Short location. size_t size = stream.ComputeNeededSize(); void* memory = arena.Alloc(size, kArenaAllocMisc); @@ -51,6 +53,16 @@ TEST(StackMapTest, Test1) { ASSERT_EQ(0u, code_info.GetStackMaskSize()); ASSERT_EQ(1u, code_info.GetNumberOfStackMaps()); + uint32_t number_of_location_catalog_entries = + code_info.GetNumberOfDexRegisterLocationCatalogEntries(); + ASSERT_EQ(2u, number_of_location_catalog_entries); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); + // The Dex register location catalog contains: + // - one 1-byte short Dex register location, and + // - one 5-byte large Dex register location. + size_t expected_location_catalog_size = 1u + 5u; + ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); + StackMap stack_map = code_info.GetStackMapAt(0); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); @@ -62,14 +74,40 @@ TEST(StackMapTest, Test1) { ASSERT_TRUE(SameBits(stack_mask, sp_mask)); ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info)); - DexRegisterMap dex_registers = code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); - ASSERT_EQ(7u, dex_registers.Size()); - DexRegisterLocation location0 = dex_registers.GetLocationKindAndValue(0, number_of_dex_registers); - DexRegisterLocation location1 = dex_registers.GetLocationKindAndValue(1, number_of_dex_registers); - ASSERT_EQ(DexRegisterLocation::Kind::kInStack, location0.GetKind()); - ASSERT_EQ(DexRegisterLocation::Kind::kConstant, location1.GetKind()); - ASSERT_EQ(DexRegisterLocation::Kind::kInStack, location0.GetInternalKind()); - ASSERT_EQ(DexRegisterLocation::Kind::kConstantLargeValue, location1.GetInternalKind()); + DexRegisterMap dex_register_map = + code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); + ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); + ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); + // The Dex register map contains: + // - one 1-byte live bit mask, and + // - one 1-byte set of location catalog entry indices composed of two 2-bit values. + size_t expected_dex_register_map_size = 1u + 1u; + ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); + + ASSERT_EQ(Kind::kInStack, + dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kConstant, + dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kInStack, + dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kConstantLargeValue, + dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info)); + ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info)); + ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info)); + + size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( + 0, number_of_dex_registers, number_of_location_catalog_entries); + size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( + 1, number_of_dex_registers, number_of_location_catalog_entries); + ASSERT_EQ(0u, index0); + ASSERT_EQ(1u, index1); + DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); + DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + ASSERT_EQ(Kind::kInStack, location0.GetKind()); + ASSERT_EQ(Kind::kConstant, location1.GetKind()); + ASSERT_EQ(Kind::kInStack, location0.GetInternalKind()); + ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind()); ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); @@ -86,8 +124,8 @@ TEST(StackMapTest, Test2) { sp_mask1.SetBit(4); size_t number_of_dex_registers = 2; stream.AddStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 2); - stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInStack, 0); - stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2); + stream.AddDexRegisterEntry(0, Kind::kInStack, 0); // Short location. + stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location. stream.AddInlineInfoEntry(42); stream.AddInlineInfoEntry(82); @@ -95,8 +133,8 @@ TEST(StackMapTest, Test2) { sp_mask2.SetBit(3); sp_mask1.SetBit(8); stream.AddStackMapEntry(1, 128, 0xFF, &sp_mask2, number_of_dex_registers, 0); - stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 18); - stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kInFpuRegister, 3); + stream.AddDexRegisterEntry(0, Kind::kInRegister, 18); // Short location. + stream.AddDexRegisterEntry(1, Kind::kInFpuRegister, 3); // Short location. size_t size = stream.ComputeNeededSize(); void* memory = arena.Alloc(size, kArenaAllocMisc); @@ -107,6 +145,16 @@ TEST(StackMapTest, Test2) { ASSERT_EQ(1u, code_info.GetStackMaskSize()); ASSERT_EQ(2u, code_info.GetNumberOfStackMaps()); + uint32_t number_of_location_catalog_entries = + code_info.GetNumberOfDexRegisterLocationCatalogEntries(); + ASSERT_EQ(4u, number_of_location_catalog_entries); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); + // The Dex register location catalog contains: + // - three 1-byte short Dex register locations, and + // - one 5-byte large Dex register location. + size_t expected_location_catalog_size = 3u * 1u + 5u; + ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); + // First stack map. { StackMap stack_map = code_info.GetStackMapAt(0); @@ -120,17 +168,40 @@ TEST(StackMapTest, Test2) { ASSERT_TRUE(SameBits(stack_mask, sp_mask1)); ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info)); - DexRegisterMap dex_registers = + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); - ASSERT_EQ(7u, dex_registers.Size()); - DexRegisterLocation location0 = - dex_registers.GetLocationKindAndValue(0, number_of_dex_registers); - DexRegisterLocation location1 = - dex_registers.GetLocationKindAndValue(1, number_of_dex_registers); - ASSERT_EQ(DexRegisterLocation::Kind::kInStack, location0.GetKind()); - ASSERT_EQ(DexRegisterLocation::Kind::kConstant, location1.GetKind()); - ASSERT_EQ(DexRegisterLocation::Kind::kInStack, location0.GetInternalKind()); - ASSERT_EQ(DexRegisterLocation::Kind::kConstantLargeValue, location1.GetInternalKind()); + ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); + ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); + ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); + // The Dex register map contains: + // - one 1-byte live bit mask, and + // - one 1-byte set of location catalog entry indices composed of two 2-bit values. + size_t expected_dex_register_map_size = 1u + 1u; + ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); + + ASSERT_EQ(Kind::kInStack, + dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kConstant, + dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kInStack, + dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kConstantLargeValue, + dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info)); + ASSERT_EQ(0, dex_register_map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info)); + ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info)); + + size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( + 0, number_of_dex_registers, number_of_location_catalog_entries); + size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( + 1, number_of_dex_registers, number_of_location_catalog_entries); + ASSERT_EQ(0u, index0); + ASSERT_EQ(1u, index1); + DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); + DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + ASSERT_EQ(Kind::kInStack, location0.GetKind()); + ASSERT_EQ(Kind::kConstant, location1.GetKind()); + ASSERT_EQ(Kind::kInStack, location0.GetInternalKind()); + ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind()); ASSERT_EQ(0, location0.GetValue()); ASSERT_EQ(-2, location1.GetValue()); @@ -154,17 +225,40 @@ TEST(StackMapTest, Test2) { ASSERT_TRUE(SameBits(stack_mask, sp_mask2)); ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info)); - DexRegisterMap dex_registers = + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); - ASSERT_EQ(3u, dex_registers.Size()); - DexRegisterLocation location0 = - dex_registers.GetLocationKindAndValue(0, number_of_dex_registers); - DexRegisterLocation location1 = - dex_registers.GetLocationKindAndValue(1, number_of_dex_registers); - ASSERT_EQ(DexRegisterLocation::Kind::kInRegister, location0.GetKind()); - ASSERT_EQ(DexRegisterLocation::Kind::kInFpuRegister, location1.GetKind()); - ASSERT_EQ(DexRegisterLocation::Kind::kInRegister, location0.GetInternalKind()); - ASSERT_EQ(DexRegisterLocation::Kind::kInFpuRegister, location1.GetInternalKind()); + ASSERT_TRUE(dex_register_map.IsDexRegisterLive(0)); + ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); + ASSERT_EQ(2u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); + // The Dex register map contains: + // - one 1-byte live bit mask, and + // - one 1-byte set of location catalog entry indices composed of two 2-bit values. + size_t expected_dex_register_map_size = 1u + 1u; + ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); + + ASSERT_EQ(Kind::kInRegister, + dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kInFpuRegister, + dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kInRegister, + dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kInFpuRegister, + dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info)); + ASSERT_EQ(18, dex_register_map.GetMachineRegister(0, number_of_dex_registers, code_info)); + ASSERT_EQ(3, dex_register_map.GetMachineRegister(1, number_of_dex_registers, code_info)); + + size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( + 0, number_of_dex_registers, number_of_location_catalog_entries); + size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( + 1, number_of_dex_registers, number_of_location_catalog_entries); + ASSERT_EQ(2u, index0); + ASSERT_EQ(3u, index1); + DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); + DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + ASSERT_EQ(Kind::kInRegister, location0.GetKind()); + ASSERT_EQ(Kind::kInFpuRegister, location1.GetKind()); + ASSERT_EQ(Kind::kInRegister, location0.GetInternalKind()); + ASSERT_EQ(Kind::kInFpuRegister, location1.GetInternalKind()); ASSERT_EQ(18, location0.GetValue()); ASSERT_EQ(3, location1.GetValue()); @@ -180,8 +274,8 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { ArenaBitVector sp_mask(&arena, 0, false); uint32_t number_of_dex_registers = 2; stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); - stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kNone, 0); - stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2); + stream.AddDexRegisterEntry(0, Kind::kNone, 0); // No location. + stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location. size_t size = stream.ComputeNeededSize(); void* memory = arena.Alloc(size, kArenaAllocMisc); @@ -189,14 +283,62 @@ TEST(StackMapTest, TestNonLiveDexRegisters) { stream.FillIn(region); CodeInfo code_info(region); + ASSERT_EQ(0u, code_info.GetStackMaskSize()); + ASSERT_EQ(1u, code_info.GetNumberOfStackMaps()); + + uint32_t number_of_location_catalog_entries = + code_info.GetNumberOfDexRegisterLocationCatalogEntries(); + ASSERT_EQ(1u, number_of_location_catalog_entries); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); + // The Dex register location catalog contains: + // - one 5-byte large Dex register location. + size_t expected_location_catalog_size = 5u; + ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); + StackMap stack_map = code_info.GetStackMapAt(0); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); + ASSERT_EQ(0u, stack_map.GetDexPc(code_info)); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(code_info)); + ASSERT_EQ(0x3u, stack_map.GetRegisterMask(code_info)); + ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info)); - DexRegisterMap dex_registers = code_info.GetDexRegisterMapOf(stack_map, 2); - ASSERT_EQ(DexRegisterLocation::Kind::kNone, - dex_registers.GetLocationKind(0, number_of_dex_registers)); - ASSERT_EQ(DexRegisterLocation::Kind::kConstant, - dex_registers.GetLocationKind(1, number_of_dex_registers)); - ASSERT_EQ(-2, dex_registers.GetConstant(1, number_of_dex_registers)); + DexRegisterMap dex_register_map = + code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); + ASSERT_FALSE(dex_register_map.IsDexRegisterLive(0)); + ASSERT_TRUE(dex_register_map.IsDexRegisterLive(1)); + ASSERT_EQ(1u, dex_register_map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); + // The Dex register map contains: + // - one 1-byte live bit mask. + // No space is allocated for the sole location catalog entry index, as it is useless. + size_t expected_dex_register_map_size = 1u + 0u; + ASSERT_EQ(expected_dex_register_map_size, dex_register_map.Size()); + + ASSERT_EQ(Kind::kNone, + dex_register_map.GetLocationKind(0, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kConstant, + dex_register_map.GetLocationKind(1, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kNone, + dex_register_map.GetLocationInternalKind(0, number_of_dex_registers, code_info)); + ASSERT_EQ(Kind::kConstantLargeValue, + dex_register_map.GetLocationInternalKind(1, number_of_dex_registers, code_info)); + ASSERT_EQ(-2, dex_register_map.GetConstant(1, number_of_dex_registers, code_info)); + + size_t index0 = dex_register_map.GetLocationCatalogEntryIndex( + 0, number_of_dex_registers, number_of_location_catalog_entries); + size_t index1 = dex_register_map.GetLocationCatalogEntryIndex( + 1, number_of_dex_registers, number_of_location_catalog_entries); + ASSERT_EQ(DexRegisterLocationCatalog::kNoLocationEntryIndex, index0); + ASSERT_EQ(0u, index1); + DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); + DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + ASSERT_EQ(Kind::kNone, location0.GetKind()); + ASSERT_EQ(Kind::kConstant, location1.GetKind()); + ASSERT_EQ(Kind::kNone, location0.GetInternalKind()); + ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind()); + ASSERT_EQ(0, location0.GetValue()); + ASSERT_EQ(-2, location1.GetValue()); + ASSERT_FALSE(stack_map.HasInlineInfo(code_info)); } @@ -209,14 +351,21 @@ TEST(StackMapTest, DexRegisterMapOffsetOverflow) { StackMapStream stream(&arena); ArenaBitVector sp_mask(&arena, 0, false); - uint32_t number_of_dex_registers = 0xEA; + uint32_t number_of_dex_registers = 1024; + // Create the first stack map (and its Dex register map). stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); - for (uint32_t i = 0; i < number_of_dex_registers - 9; ++i) { - stream.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kConstant, 0); + uint32_t number_of_dex_live_registers_in_dex_register_map_0 = number_of_dex_registers - 8; + for (uint32_t i = 0; i < number_of_dex_live_registers_in_dex_register_map_0; ++i) { + // Use two different Dex register locations to populate this map, + // as using a single value (in the whole CodeInfo object) would + // make this Dex register mapping data empty (see + // art::DexRegisterMap::SingleEntrySizeInBits). + stream.AddDexRegisterEntry(i, Kind::kConstant, i % 2); // Short location. } + // Create the second stack map (and its Dex register map). stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); for (uint32_t i = 0; i < number_of_dex_registers; ++i) { - stream.AddDexRegisterEntry(i, DexRegisterLocation::Kind::kConstant, 0); + stream.AddDexRegisterEntry(i, Kind::kConstant, 0); // Short location. } size_t size = stream.ComputeNeededSize(); @@ -225,10 +374,36 @@ TEST(StackMapTest, DexRegisterMapOffsetOverflow) { stream.FillIn(region); CodeInfo code_info(region); - StackMap stack_map = code_info.GetStackMapAt(1); - ASSERT_TRUE(stack_map.HasDexRegisterMap(code_info)); - ASSERT_NE(stack_map.GetDexRegisterMapOffset(code_info), StackMap::kNoDexRegisterMap); - ASSERT_EQ(stack_map.GetDexRegisterMapOffset(code_info), StackMap::kNoDexRegisterMapSmallEncoding); + // The location catalog contains two entries (DexRegisterLocation(kConstant, 0) + // and DexRegisterLocation(kConstant, 1)), therefore the location catalog index + // has a size of 1 bit. + uint32_t number_of_location_catalog_entries = + code_info.GetNumberOfDexRegisterLocationCatalogEntries(); + ASSERT_EQ(2u, number_of_location_catalog_entries); + ASSERT_EQ(1u, DexRegisterMap::SingleEntrySizeInBits(number_of_location_catalog_entries)); + + // The first Dex register map contains: + // - a live register bit mask for 1024 registers (that is, 128 bytes of + // data); and + // - Dex register mapping information for 1016 1-bit Dex (live) register + // locations (that is, 127 bytes of data). + // Hence it has a size of 255 bytes, and therefore... + ASSERT_EQ(128u, DexRegisterMap::GetLiveBitMaskSize(number_of_dex_registers)); + StackMap stack_map0 = code_info.GetStackMapAt(0); + DexRegisterMap dex_register_map0 = + code_info.GetDexRegisterMapOf(stack_map0, number_of_dex_registers); + ASSERT_EQ(127u, dex_register_map0.GetLocationMappingDataSize(number_of_dex_registers, + number_of_location_catalog_entries)); + ASSERT_EQ(255u, dex_register_map0.Size()); + + StackMap stack_map1 = code_info.GetStackMapAt(1); + ASSERT_TRUE(stack_map1.HasDexRegisterMap(code_info)); + // ...the offset of the second Dex register map (relative to the + // beginning of the Dex register maps region) is 255 (i.e., + // kNoDexRegisterMapSmallEncoding). + ASSERT_NE(StackMap::kNoDexRegisterMap, stack_map1.GetDexRegisterMapOffset(code_info)); + ASSERT_EQ(StackMap::kNoDexRegisterMapSmallEncoding, + stack_map1.GetDexRegisterMapOffset(code_info)); } TEST(StackMapTest, TestShareDexRegisterMap) { @@ -240,16 +415,16 @@ TEST(StackMapTest, TestShareDexRegisterMap) { uint32_t number_of_dex_registers = 2; // First stack map. stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); - stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 0); - stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2); + stream.AddDexRegisterEntry(0, Kind::kInRegister, 0); // Short location. + stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location. // Second stack map, which should share the same dex register map. stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); - stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 0); - stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2); + stream.AddDexRegisterEntry(0, Kind::kInRegister, 0); // Short location. + stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location. // Third stack map (doesn't share the dex register map). stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); - stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 2); - stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2); + stream.AddDexRegisterEntry(0, Kind::kInRegister, 2); // Short location. + stream.AddDexRegisterEntry(1, Kind::kConstant, -2); // Large location. size_t size = stream.ComputeNeededSize(); void* memory = arena.Alloc(size, kArenaAllocMisc); @@ -260,20 +435,20 @@ TEST(StackMapTest, TestShareDexRegisterMap) { // Verify first stack map. StackMap sm0 = ci.GetStackMapAt(0); DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, number_of_dex_registers); - ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers)); - ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers)); + ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers, ci)); + ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers, ci)); // Verify second stack map. StackMap sm1 = ci.GetStackMapAt(1); DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1, number_of_dex_registers); - ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers)); - ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers)); + ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers, ci)); + ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers, ci)); // Verify third stack map. StackMap sm2 = ci.GetStackMapAt(2); DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2, number_of_dex_registers); - ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers)); - ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers)); + ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers, ci)); + ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers, ci)); // Verify dex register map offsets. ASSERT_EQ(sm0.GetDexRegisterMapOffset(ci), sm1.GetDexRegisterMapOffset(ci)); @@ -281,4 +456,39 @@ TEST(StackMapTest, TestShareDexRegisterMap) { ASSERT_NE(sm1.GetDexRegisterMapOffset(ci), sm2.GetDexRegisterMapOffset(ci)); } +TEST(StackMapTest, TestNoDexRegisterMap) { + ArenaPool pool; + ArenaAllocator arena(&pool); + StackMapStream stream(&arena); + + ArenaBitVector sp_mask(&arena, 0, false); + uint32_t number_of_dex_registers = 0; + stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0); + + size_t size = stream.ComputeNeededSize(); + void* memory = arena.Alloc(size, kArenaAllocMisc); + MemoryRegion region(memory, size); + stream.FillIn(region); + + CodeInfo code_info(region); + ASSERT_EQ(0u, code_info.GetStackMaskSize()); + ASSERT_EQ(1u, code_info.GetNumberOfStackMaps()); + + uint32_t number_of_location_catalog_entries = + code_info.GetNumberOfDexRegisterLocationCatalogEntries(); + ASSERT_EQ(0u, number_of_location_catalog_entries); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(); + ASSERT_EQ(0u, location_catalog.Size()); + + StackMap stack_map = code_info.GetStackMapAt(0); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64))); + ASSERT_EQ(0u, stack_map.GetDexPc(code_info)); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(code_info)); + ASSERT_EQ(0x3u, stack_map.GetRegisterMask(code_info)); + + ASSERT_FALSE(stack_map.HasDexRegisterMap(code_info)); + ASSERT_FALSE(stack_map.HasInlineInfo(code_info)); +} + } // namespace art diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index 90170ceed5..5773459ff5 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -146,6 +146,12 @@ void X86Assembler::movl(const Address& dst, Label* lbl) { EmitLabel(lbl, dst.length_ + 5); } +void X86Assembler::bswapl(Register dst) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0xC8 + dst); +} + void X86Assembler::movzxb(Register dst, ByteRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x0F); @@ -725,6 +731,32 @@ void X86Assembler::xorpd(XmmRegister dst, XmmRegister src) { } +void X86Assembler::andps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x54); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::andpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x54); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::orpd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x56); + EmitXmmRegisterOperand(dst, src); +} + + void X86Assembler::xorps(XmmRegister dst, const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x0F); @@ -733,6 +765,14 @@ void X86Assembler::xorps(XmmRegister dst, const Address& src) { } +void X86Assembler::orps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x56); + EmitXmmRegisterOperand(dst, src); +} + + void X86Assembler::xorps(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x0F); @@ -741,6 +781,14 @@ void X86Assembler::xorps(XmmRegister dst, XmmRegister src) { } +void X86Assembler::andps(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x54); + EmitOperand(dst, src); +} + + void X86Assembler::andpd(XmmRegister dst, const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); @@ -1090,6 +1138,13 @@ void X86Assembler::subl(Register reg, const Address& address) { } +void X86Assembler::subl(const Address& address, Register reg) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x29); + EmitOperand(reg, address); +} + + void X86Assembler::cdq() { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x99); @@ -1175,6 +1230,13 @@ void X86Assembler::sbbl(Register dst, const Address& address) { } +void X86Assembler::sbbl(const Address& address, Register src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x19); + EmitOperand(src, address); +} + + void X86Assembler::incl(Register reg) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x40 + reg); diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index 4d20db03a9..6ccf2e365d 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -231,6 +231,8 @@ class X86Assembler FINAL : public Assembler { void movl(const Address& dst, const Immediate& imm); void movl(const Address& dst, Label* lbl); + void bswapl(Register dst); + void movzxb(Register dst, ByteRegister src); void movzxb(Register dst, const Address& src); void movsxb(Register dst, ByteRegister src); @@ -318,7 +320,13 @@ class X86Assembler FINAL : public Assembler { void xorps(XmmRegister dst, const Address& src); void xorps(XmmRegister dst, XmmRegister src); + void andpd(XmmRegister dst, XmmRegister src); void andpd(XmmRegister dst, const Address& src); + void andps(XmmRegister dst, XmmRegister src); + void andps(XmmRegister dst, const Address& src); + + void orpd(XmmRegister dst, XmmRegister src); + void orps(XmmRegister dst, XmmRegister src); void flds(const Address& src); void fstps(const Address& dst); @@ -389,6 +397,7 @@ class X86Assembler FINAL : public Assembler { void subl(Register dst, Register src); void subl(Register reg, const Immediate& imm); void subl(Register reg, const Address& address); + void subl(const Address& address, Register src); void cdq(); @@ -407,6 +416,7 @@ class X86Assembler FINAL : public Assembler { void sbbl(Register dst, Register src); void sbbl(Register reg, const Immediate& imm); void sbbl(Register reg, const Address& address); + void sbbl(const Address& address, Register src); void incl(Register reg); void incl(const Address& address); diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index dfea7837e2..2e1b7aef04 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1053,7 +1053,12 @@ class Dex2Oat FINAL { } verification_results_.reset(new VerificationResults(compiler_options_.get())); - callbacks_.reset(new QuickCompilerCallbacks(verification_results_.get(), &method_inliner_map_)); + callbacks_.reset(new QuickCompilerCallbacks( + verification_results_.get(), + &method_inliner_map_, + image_ ? + CompilerCallbacks::CallbackMode::kCompileBootImage : + CompilerCallbacks::CallbackMode::kCompileApp)); runtime_options.push_back(std::make_pair("compilercallbacks", callbacks_.get())); runtime_options.push_back( std::make_pair("imageinstructionset", GetInstructionSetString(instruction_set_))); @@ -1224,19 +1229,16 @@ class Dex2Oat FINAL { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); OpenClassPathFiles(runtime_->GetClassPathString(), dex_files_, &class_path_files_); ScopedObjectAccess soa(self); - std::vector<const DexFile*> class_path_files(dex_files_); + + // Classpath: first the class-path given. + std::vector<const DexFile*> class_path_files; for (auto& class_path_file : class_path_files_) { class_path_files.push_back(class_path_file.get()); } + // Then the dex files we'll compile. Thus we'll resolve the class-path first. + class_path_files.insert(class_path_files.end(), dex_files_.begin(), dex_files_.end()); - for (size_t i = 0; i < class_path_files.size(); i++) { - class_linker->RegisterDexFile(*class_path_files[i]); - } - soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader); - ScopedLocalRef<jobject> class_loader_local(soa.Env(), - soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader)); - class_loader = soa.Env()->NewGlobalRef(class_loader_local.get()); - Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path_files); + class_loader = class_linker->CreatePathClassLoader(self, class_path_files); } driver_.reset(new CompilerDriver(compiler_options_.get(), diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index daca971de0..322d3aa8f1 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1597,7 +1597,7 @@ class ImageDumper { os << StringPrintf("null %s\n", PrettyDescriptor(field->GetTypeDescriptor()).c_str()); } else { // Grab the field type without causing resolution. - mirror::Class* field_type = field->GetType(false); + mirror::Class* field_type = field->GetType<false>(); if (field_type != nullptr) { PrettyObjectValue(os, field_type, value); } else { @@ -2158,16 +2158,12 @@ static int DumpOatWithRuntime(Runtime* runtime, OatFile* oat_file, OatDumperOpti } // Need a class loader. - soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader); - ScopedLocalRef<jobject> class_loader_local(soa.Env(), - soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader)); - jobject class_loader = soa.Env()->NewGlobalRef(class_loader_local.get()); // Fake that we're a compiler. std::vector<const DexFile*> class_path; for (auto& dex_file : dex_files) { class_path.push_back(dex_file.get()); } - runtime->SetCompileTimeClassPath(class_loader, class_path); + jobject class_loader = class_linker->CreatePathClassLoader(self, class_path); // Use the class loader while dumping. StackHandleScope<1> scope(self); diff --git a/runtime/Android.mk b/runtime/Android.mk index 5548d949d9..dde5407e12 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -103,6 +103,7 @@ LIBART_COMMON_SRC_FILES := \ mirror/array.cc \ mirror/class.cc \ mirror/dex_cache.cc \ + mirror/field.cc \ mirror/object.cc \ mirror/reference.cc \ mirror/stack_trace_element.cc \ diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 16f0e70999..0c2250eab8 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1094,7 +1094,7 @@ ENTRY art_quick_resolution_trampoline lw $a0, ARG_SLOT_SIZE($sp) # load resolved method to $a0 RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME move $t9, $v0 # code pointer must be in $t9 to generate the global pointer - jalr $zero, $v0 # tail call to method + jalr $zero, $t9 # tail call to method nop 1: RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME @@ -1203,29 +1203,28 @@ art_quick_instrumentation_exit: .cpload $t9 move $ra, $zero # link register is to here, so clobber with 0 for later checks + SETUP_REFS_ONLY_CALLEE_SAVE_FRAME addiu $sp, $sp, -16 # allocate temp storage on the stack .cfi_adjust_cfa_offset 16 - sw $v0, 12($sp) - .cfi_rel_offset 2, 32 - sw $v1, 8($sp) - .cfi_rel_offset 3, 36 - s.d $f0, 0($sp) - SETUP_REFS_ONLY_CALLEE_SAVE_FRAME + sw $v0, ARG_SLOT_SIZE+12($sp) + .cfi_rel_offset 2, ARG_SLOT_SIZE+12 + sw $v1, ARG_SLOT_SIZE+8($sp) + .cfi_rel_offset 3, ARG_SLOT_SIZE+8 + s.d $f0, ARG_SLOT_SIZE($sp) s.d $f0, 16($sp) # pass fpr result move $a2, $v0 # pass gpr result move $a3, $v1 - addiu $a1, $sp, ARG_SLOT_SIZE # pass $sp (remove arg slots) + addiu $a1, $sp, ARG_SLOT_SIZE+16 # pass $sp (remove arg slots and temp storage) jal artInstrumentationMethodExitFromCode # (Thread*, SP, gpr_res, fpr_res) move $a0, rSELF # pass Thread::Current - move $t0, $v0 # set aside returned link register + move $t9, $v0 # set aside returned link register move $ra, $v1 # set link register for deoptimization - addiu $sp, $sp, ARG_SLOT_SIZE+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE # args slot + refs_only callee save frame - lw $v0, 12($sp) # restore return values - lw $v1, 8($sp) - l.d $f0, 0($sp) - jalr $zero, $t0 # return - addiu $sp, $sp, 16 # remove temp storage from stack - .cfi_adjust_cfa_offset -16 + lw $v0, ARG_SLOT_SIZE+12($sp) # restore return values + lw $v1, ARG_SLOT_SIZE+8($sp) + l.d $f0, ARG_SLOT_SIZE($sp) + jalr $zero, $t9 # return + addiu $sp, $sp, ARG_SLOT_SIZE+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE+16 # restore stack + .cfi_adjust_cfa_offset -(ARG_SLOT_SIZE+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE+16) END art_quick_instrumentation_exit /* diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 8cb95f1ab6..697bf003ea 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1504,11 +1504,11 @@ art_quick_instrumentation_exit: move $a1, $t0 # pass $sp jal artInstrumentationMethodExitFromCode # (Thread*, SP, gpr_res, fpr_res) move $a0, rSELF # pass Thread::Current - move $t0, $v0 # set aside returned link register + move $t9, $v0 # set aside returned link register move $ra, $v1 # set link register for deoptimization ld $v0, 0($sp) # restore return values l.d $f0, 8($sp) - jalr $zero, $t0 # return + jalr $zero, $t9 # return daddiu $sp, $sp, 16+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE # 16 bytes of saved values + ref_only callee save frame .cfi_adjust_cfa_offset -(16+FRAME_SIZE_REFS_ONLY_CALLEE_SAVE) END art_quick_instrumentation_exit diff --git a/runtime/base/arena_containers.h b/runtime/base/arena_containers.h index ceff6e85a3..e6fe6c0ad4 100644 --- a/runtime/base/arena_containers.h +++ b/runtime/base/arena_containers.h @@ -85,8 +85,7 @@ class ArenaAllocatorAdapterKindImpl { typedef ArenaAllocatorAdapterKindImpl<kArenaAllocatorCountAllocations> ArenaAllocatorAdapterKind; template <> -class ArenaAllocatorAdapter<void> - : private DebugStackReference, private ArenaAllocatorAdapterKind { +class ArenaAllocatorAdapter<void> : private ArenaAllocatorAdapterKind { public: typedef void value_type; typedef void* pointer; @@ -99,14 +98,12 @@ class ArenaAllocatorAdapter<void> explicit ArenaAllocatorAdapter(ArenaAllocator* arena_allocator, ArenaAllocKind kind = kArenaAllocSTL) - : DebugStackReference(arena_allocator), - ArenaAllocatorAdapterKind(kind), + : ArenaAllocatorAdapterKind(kind), arena_allocator_(arena_allocator) { } template <typename U> ArenaAllocatorAdapter(const ArenaAllocatorAdapter<U>& other) - : DebugStackReference(other), - ArenaAllocatorAdapterKind(other), + : ArenaAllocatorAdapterKind(other), arena_allocator_(other.arena_allocator_) { } ArenaAllocatorAdapter(const ArenaAllocatorAdapter&) = default; @@ -121,7 +118,7 @@ class ArenaAllocatorAdapter<void> }; template <typename T> -class ArenaAllocatorAdapter : private DebugStackReference, private ArenaAllocatorAdapterKind { +class ArenaAllocatorAdapter : private ArenaAllocatorAdapterKind { public: typedef T value_type; typedef T* pointer; @@ -137,14 +134,12 @@ class ArenaAllocatorAdapter : private DebugStackReference, private ArenaAllocato }; explicit ArenaAllocatorAdapter(ArenaAllocator* arena_allocator, ArenaAllocKind kind) - : DebugStackReference(arena_allocator), - ArenaAllocatorAdapterKind(kind), + : ArenaAllocatorAdapterKind(kind), arena_allocator_(arena_allocator) { } template <typename U> ArenaAllocatorAdapter(const ArenaAllocatorAdapter<U>& other) - : DebugStackReference(other), - ArenaAllocatorAdapterKind(other), + : ArenaAllocatorAdapterKind(other), arena_allocator_(other.arena_allocator_) { } ArenaAllocatorAdapter(const ArenaAllocatorAdapter&) = default; diff --git a/runtime/base/hash_map.h b/runtime/base/hash_map.h index c0f903f51f..eab80ff19f 100644 --- a/runtime/base/hash_map.h +++ b/runtime/base/hash_map.h @@ -48,7 +48,7 @@ class HashMapWrapper { Fn fn_; }; -template <class Key, class Value, class EmptyFn = DefaultEmptyFn<Key>, +template <class Key, class Value, class EmptyFn, class HashFn = std::hash<Key>, class Pred = std::equal_to<Key>, class Alloc = std::allocator<std::pair<Key, Value>>> class HashMap : public HashSet<std::pair<Key, Value>, EmptyFn, HashMapWrapper<HashFn>, diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h index 204546d55c..5d9cd35c83 100644 --- a/runtime/check_reference_map_visitor.h +++ b/runtime/check_reference_map_visitor.h @@ -75,7 +75,7 @@ class CheckReferenceMapVisitor : public StackVisitor { int reg = registers[i]; CHECK(reg < m->GetCodeItem()->registers_size_); DexRegisterLocation location = - dex_register_map.GetLocationKindAndValue(reg, number_of_dex_registers); + dex_register_map.GetDexRegisterLocation(reg, number_of_dex_registers, code_info); switch (location.GetKind()) { case DexRegisterLocation::Kind::kNone: // Not set, should not be a reference. diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 67872d76ae..a89196d830 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -56,6 +56,7 @@ #include "mirror/class-inl.h" #include "mirror/class_loader.h" #include "mirror/dex_cache-inl.h" +#include "mirror/field.h" #include "mirror/iftable-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" @@ -313,7 +314,7 @@ void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b java_lang_String->SetObjectSize(mirror::String::InstanceSize()); mirror::Class::SetStatus(java_lang_String, mirror::Class::kStatusResolved, self); - // Setup Reference. + // Setup java.lang.ref.Reference. Handle<mirror::Class> java_lang_ref_Reference(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::Reference::ClassSize()))); mirror::Reference::SetClass(java_lang_ref_Reference.Get()); @@ -321,7 +322,7 @@ void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b mirror::Class::SetStatus(java_lang_ref_Reference, mirror::Class::kStatusResolved, self); // Create storage for root classes, save away our work so far (requires descriptors). - class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class> >( + class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>( mirror::ObjectArray<mirror::Class>::Alloc(self, object_array_class.Get(), kClassRootsMax)); CHECK(!class_roots_.IsNull()); @@ -531,6 +532,19 @@ void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b mirror::Class* java_lang_reflect_Proxy = FindSystemClass(self, "Ljava/lang/reflect/Proxy;"); SetClassRoot(kJavaLangReflectProxy, java_lang_reflect_Proxy); + // Create java.lang.reflect.Field.class root. + mirror::Class* java_lang_reflect_Field = FindSystemClass(self, "Ljava/lang/reflect/Field;"); + CHECK(java_lang_reflect_Field != nullptr); + SetClassRoot(kJavaLangReflectField, java_lang_reflect_Field); + mirror::Field::SetClass(java_lang_reflect_Field); + + // Create java.lang.reflect.Field array root. + mirror::Class* java_lang_reflect_Field_array = + FindSystemClass(self, "[Ljava/lang/reflect/Field;"); + CHECK(java_lang_reflect_Field_array != nullptr); + SetClassRoot(kJavaLangReflectFieldArrayClass, java_lang_reflect_Field_array); + mirror::Field::SetArrayClass(java_lang_reflect_Field_array); + // java.lang.ref classes need to be specially flagged, but otherwise are normal classes // finish initializing Reference class mirror::Class::SetStatus(java_lang_ref_Reference, mirror::Class::kStatusNotReady, self); @@ -818,9 +832,10 @@ void ClassLinker::InitFromImage() { VLOG(startup) << "ClassLinker::InitFromImage entering"; CHECK(!init_done_); - Thread* self = Thread::Current(); - gc::Heap* heap = Runtime::Current()->GetHeap(); - gc::space::ImageSpace* space = heap->GetImageSpace(); + Runtime* const runtime = Runtime::Current(); + Thread* const self = Thread::Current(); + gc::Heap* const heap = runtime->GetHeap(); + gc::space::ImageSpace* const space = heap->GetImageSpace(); dex_cache_image_class_lookup_required_ = true; CHECK(space != nullptr); OatFile& oat_file = GetImageOatFile(space); @@ -875,7 +890,7 @@ void ClassLinker::InitFromImage() { // bitmap walk. mirror::ArtMethod::SetClass(GetClassRoot(kJavaLangReflectArtMethod)); size_t art_method_object_size = mirror::ArtMethod::GetJavaLangReflectArtMethod()->GetObjectSize(); - if (!Runtime::Current()->IsAotCompiler()) { + if (!runtime->IsAotCompiler()) { // Aot compiler supports having an image with a different pointer size than the runtime. This // happens on the host for compile 32 bit tests since we use a 64 bit libart compiler. We may // also use 32 bit dex2oat on a system with 64 bit apps. @@ -890,7 +905,6 @@ void ClassLinker::InitFromImage() { } // Set entry point to interpreter if in InterpretOnly mode. - Runtime* runtime = Runtime::Current(); if (!runtime->IsAotCompiler() && runtime->GetInstrumentation()->InterpretOnly()) { heap->VisitObjects(InitFromImageInterpretOnlyCallback, this); } @@ -903,6 +917,8 @@ void ClassLinker::InitFromImage() { array_iftable_ = GcRoot<mirror::IfTable>(GetClassRoot(kObjectArrayClass)->GetIfTable()); DCHECK_EQ(array_iftable_.Read(), GetClassRoot(kBooleanArrayClass)->GetIfTable()); // String class root was set above + mirror::Field::SetClass(GetClassRoot(kJavaLangReflectField)); + mirror::Field::SetArrayClass(GetClassRoot(kJavaLangReflectFieldArrayClass)); mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference)); mirror::ArtField::SetClass(GetClassRoot(kJavaLangReflectArtField)); mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass)); @@ -1088,6 +1104,8 @@ ClassLinker::~ClassLinker() { mirror::Reference::ResetClass(); mirror::ArtField::ResetClass(); mirror::ArtMethod::ResetClass(); + mirror::Field::ResetClass(); + mirror::Field::ResetArrayClass(); mirror::BooleanArray::ResetArrayClass(); mirror::ByteArray::ResetArrayClass(); mirror::CharArray::ResetArrayClass(); @@ -1372,38 +1390,6 @@ mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, self->SetException(pre_allocated); return nullptr; } - } else if (Runtime::Current()->UseCompileTimeClassPath()) { - // First try with the bootstrap class loader. - if (class_loader.Get() != nullptr) { - klass = LookupClass(self, descriptor, hash, nullptr); - if (klass != nullptr) { - return EnsureResolved(self, descriptor, klass); - } - } - // If the lookup failed search the boot class path. We don't perform a recursive call to avoid - // a NoClassDefFoundError being allocated. - ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_); - if (pair.second != nullptr) { - return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first, - *pair.second); - } - // Next try the compile time class path. - const std::vector<const DexFile*>* class_path; - { - ScopedObjectAccessUnchecked soa(self); - ScopedLocalRef<jobject> jclass_loader(soa.Env(), - soa.AddLocalReference<jobject>(class_loader.Get())); - class_path = &Runtime::Current()->GetCompileTimeClassPath(jclass_loader.get()); - } - pair = FindInClassPath(descriptor, hash, *class_path); - if (pair.second != nullptr) { - return DefineClass(self, descriptor, hash, class_loader, *pair.first, *pair.second); - } else { - // Use the pre-allocated NCDFE at compile time to avoid wasting time constructing exceptions. - mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); - self->SetException(pre_allocated); - return nullptr; - } } else { ScopedObjectAccessUnchecked soa(self); mirror::Class* cp_klass = FindClassInPathClassLoader(soa, self, descriptor, hash, @@ -1411,6 +1397,14 @@ mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, if (cp_klass != nullptr) { return cp_klass; } + + if (Runtime::Current()->IsAotCompiler()) { + // Oops, compile-time, can't run actual class-loader code. + mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError(); + self->SetException(pre_allocated); + return nullptr; + } + ScopedLocalRef<jobject> class_loader_object(soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get())); std::string class_name_string(DescriptorToDot(descriptor)); @@ -1767,7 +1761,7 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { return; // No direct methods => no static methods. } Runtime* runtime = Runtime::Current(); - if (!runtime->IsStarted() || runtime->UseCompileTimeClassPath()) { + if (!runtime->IsStarted()) { if (runtime->IsAotCompiler() || runtime->GetHeap()->HasImageSpace()) { return; // OAT file unavailable. } @@ -1907,7 +1901,7 @@ void ClassLinker::LoadClass(Thread* self, const DexFile& dex_file, bool has_oat_class = false; - if (Runtime::Current()->IsStarted() && !Runtime::Current()->UseCompileTimeClassPath()) { + if (Runtime::Current()->IsStarted() && !Runtime::Current()->IsAotCompiler()) { OatFile::OatClass oat_class = FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class); if (has_oat_class) { @@ -2808,7 +2802,7 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, mirror::Class // classes. if (Runtime::Current()->IsAotCompiler()) { // Are we compiling the bootclasspath? - if (!Runtime::Current()->UseCompileTimeClassPath()) { + if (Runtime::Current()->GetCompilerCallbacks()->IsBootImage()) { return false; } // We are compiling an app (not the image). @@ -5213,10 +5207,12 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) { "Ljava/lang/ref/Reference;", "Ljava/lang/reflect/ArtField;", "Ljava/lang/reflect/ArtMethod;", + "Ljava/lang/reflect/Field;", "Ljava/lang/reflect/Proxy;", "[Ljava/lang/String;", "[Ljava/lang/reflect/ArtField;", "[Ljava/lang/reflect/ArtMethod;", + "[Ljava/lang/reflect/Field;", "Ljava/lang/ClassLoader;", "Ljava/lang/Throwable;", "Ljava/lang/ClassNotFoundException;", @@ -5286,6 +5282,10 @@ std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(const char* descr } bool ClassLinker::MayBeCalledWithDirectCodePointer(mirror::ArtMethod* m) { + if (Runtime::Current()->UseJit()) { + // JIT can have direct code pointers from any method to any other method. + return true; + } // Non-image methods don't use direct code pointer. if (!m->GetDeclaringClass()->IsBootStrapClassLoaded()) { return false; @@ -5315,4 +5315,95 @@ bool ClassLinker::MayBeCalledWithDirectCodePointer(mirror::ArtMethod* m) { } } +jobject ClassLinker::CreatePathClassLoader(Thread* self, std::vector<const DexFile*>& dex_files) { + // SOAAlreadyRunnable is protected, and we need something to add a global reference. + // We could move the jobject to the callers, but all call-sites do this... + ScopedObjectAccessUnchecked soa(self); + + // Register the dex files. + for (const DexFile* dex_file : dex_files) { + RegisterDexFile(*dex_file); + } + + // For now, create a libcore-level DexFile for each ART DexFile. This "explodes" multidex. + StackHandleScope<11> hs(self); + + Handle<mirror::ArtField> h_dex_elements_field = + hs.NewHandle(soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)); + + mirror::Class* dex_elements_class = h_dex_elements_field->GetType<true>(); + DCHECK(dex_elements_class != nullptr); + DCHECK(dex_elements_class->IsArrayClass()); + Handle<mirror::ObjectArray<mirror::Object>> h_dex_elements(hs.NewHandle( + mirror::ObjectArray<mirror::Object>::Alloc(self, dex_elements_class, dex_files.size()))); + Handle<mirror::Class> h_dex_element_class = + hs.NewHandle(dex_elements_class->GetComponentType()); + + Handle<mirror::ArtField> h_element_file_field = + hs.NewHandle( + soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile)); + DCHECK_EQ(h_dex_element_class.Get(), h_element_file_field->GetDeclaringClass()); + + Handle<mirror::ArtField> h_cookie_field = + hs.NewHandle(soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie)); + DCHECK_EQ(h_cookie_field->GetDeclaringClass(), h_element_file_field->GetType<false>()); + + // Fill the elements array. + int32_t index = 0; + for (const DexFile* dex_file : dex_files) { + StackHandleScope<3> hs2(self); + + Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc(self, 1)); + DCHECK(h_long_array.Get() != nullptr); + h_long_array->Set(0, reinterpret_cast<intptr_t>(dex_file)); + + Handle<mirror::Object> h_dex_file = hs2.NewHandle( + h_cookie_field->GetDeclaringClass()->AllocObject(self)); + DCHECK(h_dex_file.Get() != nullptr); + h_cookie_field->SetObject<false>(h_dex_file.Get(), h_long_array.Get()); + + Handle<mirror::Object> h_element = hs2.NewHandle(h_dex_element_class->AllocObject(self)); + DCHECK(h_element.Get() != nullptr); + h_element_file_field->SetObject<false>(h_element.Get(), h_dex_file.Get()); + + h_dex_elements->Set(index, h_element.Get()); + index++; + } + DCHECK_EQ(index, h_dex_elements->GetLength()); + + // Create DexPathList. + Handle<mirror::Object> h_dex_path_list = hs.NewHandle( + h_dex_elements_field->GetDeclaringClass()->AllocObject(self)); + DCHECK(h_dex_path_list.Get() != nullptr); + // Set elements. + h_dex_elements_field->SetObject<false>(h_dex_path_list.Get(), h_dex_elements.Get()); + + // Create PathClassLoader. + Handle<mirror::Class> h_path_class_class = hs.NewHandle( + soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)); + Handle<mirror::Object> h_path_class_loader = hs.NewHandle( + h_path_class_class->AllocObject(self)); + DCHECK(h_path_class_loader.Get() != nullptr); + // Set DexPathList. + Handle<mirror::ArtField> h_path_list_field = hs.NewHandle( + soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)); + DCHECK(h_path_list_field.Get() != nullptr); + h_path_list_field->SetObject<false>(h_path_class_loader.Get(), h_dex_path_list.Get()); + + // Make a pretend boot-classpath. + // TODO: Should we scan the image? + Handle<mirror::ArtField> h_parent_field = hs.NewHandle( + mirror::Class::FindField(self, hs.NewHandle(h_path_class_loader->GetClass()), "parent", + "Ljava/lang/ClassLoader;")); + DCHECK(h_parent_field.Get() != nullptr); + mirror::Object* boot_cl = + soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self); + h_parent_field->SetObject<false>(h_path_class_loader.Get(), boot_cl); + + // Make it a global ref and return. + ScopedLocalRef<jobject> local_ref( + soa.Env(), soa.Env()->AddLocalReference<jobject>(h_path_class_loader.Get())); + return soa.Env()->NewGlobalRef(local_ref.get()); +} + } // namespace art diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 4ebce3e962..ec984cb703 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -72,10 +72,12 @@ class ClassLinker { kJavaLangRefReference, kJavaLangReflectArtField, kJavaLangReflectArtMethod, + kJavaLangReflectField, kJavaLangReflectProxy, kJavaLangStringArrayClass, kJavaLangReflectArtFieldArrayClass, kJavaLangReflectArtMethodArrayClass, + kJavaLangReflectFieldArrayClass, kJavaLangClassLoader, kJavaLangThrowable, kJavaLangClassNotFoundException, @@ -454,6 +456,11 @@ class ClassLinker { bool MayBeCalledWithDirectCodePointer(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Creates a GlobalRef PathClassLoader that can be used to load classes from the given dex files. + // Note: the objects are not completely set up. Do not use this outside of tests and the compiler. + jobject CreatePathClassLoader(Thread* self, std::vector<const DexFile*>& dex_files) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + private: static void InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 1789ab12d2..3e727e7408 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -24,11 +24,13 @@ #include "dex_file.h" #include "entrypoints/entrypoint_utils-inl.h" #include "gc/heap.h" +#include "mirror/accessible_object.h" #include "mirror/art_field-inl.h" #include "mirror/art_method.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/dex_cache.h" +#include "mirror/field.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/proxy.h" @@ -177,7 +179,7 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_TRUE(field->GetClass() != nullptr); EXPECT_EQ(klass, field->GetDeclaringClass()); EXPECT_TRUE(field->GetName() != nullptr); - EXPECT_TRUE(field->GetType(true) != nullptr); + EXPECT_TRUE(field->GetType<true>() != nullptr); } void AssertClass(const std::string& descriptor, Handle<mirror::Class> klass) @@ -283,7 +285,7 @@ class ClassLinkerTest : public CommonRuntimeTest { for (size_t i = 0; i < klass->NumInstanceFields(); i++) { mirror::ArtField* field = klass->GetInstanceField(i); fhandle.Assign(field); - mirror::Class* field_type = fhandle->GetType(true); + mirror::Class* field_type = fhandle->GetType<true>(); ASSERT_TRUE(field_type != nullptr); if (!field->IsPrimitiveType()) { ASSERT_TRUE(!field_type->IsPrimitive()); @@ -394,7 +396,12 @@ struct CheckOffsets { // Art method have a different size due to the padding field. if (!klass->IsArtMethodClass() && !klass->IsClassClass() && !is_static) { - size_t expected_size = is_static ? klass->GetClassSize(): klass->GetObjectSize(); + // Currently only required for AccessibleObject since of the padding fields. The class linker + // says AccessibleObject is 9 bytes but sizeof(AccessibleObject) is 12 bytes due to padding. + // The RoundUp is to get around this case. + static constexpr size_t kPackAlignment = 4; + size_t expected_size = RoundUp(is_static ? klass->GetClassSize(): klass->GetObjectSize(), + kPackAlignment); if (sizeof(T) != expected_size) { LOG(ERROR) << "Class size mismatch:" << " class=" << class_descriptor @@ -596,6 +603,22 @@ struct FinalizerReferenceOffsets : public CheckOffsets<mirror::FinalizerReferenc }; }; +struct AccessibleObjectOffsets : public CheckOffsets<mirror::AccessibleObject> { + AccessibleObjectOffsets() : CheckOffsets<mirror::AccessibleObject>(false, "Ljava/lang/reflect/AccessibleObject;") { + offsets.push_back(CheckOffset(mirror::AccessibleObject::FlagOffset().Uint32Value(), "flag")); + }; +}; + +struct FieldOffsets : public CheckOffsets<mirror::Field> { + FieldOffsets() : CheckOffsets<mirror::Field>(false, "Ljava/lang/reflect/Field;") { + offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Field, access_flags_), "accessFlags")); + offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Field, declaring_class_), "declaringClass")); + offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Field, dex_field_index_), "dexFieldIndex")); + offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Field, offset_), "offset")); + offsets.push_back(CheckOffset(OFFSETOF_MEMBER(mirror::Field, type_), "type")); + }; +}; + // C++ fields must exactly match the fields in the Java classes. If this fails, // reorder the fields in the C++ class. Managed class fields are ordered by // ClassLinker::LinkFields. @@ -613,6 +636,8 @@ TEST_F(ClassLinkerTest, ValidateFieldOrderOfJavaCppUnionClasses) { EXPECT_TRUE(DexCacheOffsets().Check()); EXPECT_TRUE(ReferenceOffsets().Check()); EXPECT_TRUE(FinalizerReferenceOffsets().Check()); + EXPECT_TRUE(AccessibleObjectOffsets().Check()); + EXPECT_TRUE(FieldOffsets().Check()); } TEST_F(ClassLinkerTest, FindClassNonexistent) { @@ -621,6 +646,20 @@ TEST_F(ClassLinkerTest, FindClassNonexistent) { AssertNonExistentClass("LNoSuchClass;"); } +TEST_F(ClassLinkerTest, GetDexFiles) { + ScopedObjectAccess soa(Thread::Current()); + + jobject jclass_loader = LoadDex("Nested"); + std::vector<const DexFile*> dex_files(GetDexFiles(jclass_loader)); + ASSERT_EQ(dex_files.size(), 1U); + EXPECT_TRUE(EndsWith(dex_files[0]->GetLocation(), "Nested.jar")); + + jobject jclass_loader2 = LoadDex("MultiDex"); + std::vector<const DexFile*> dex_files2(GetDexFiles(jclass_loader2)); + ASSERT_EQ(dex_files2.size(), 2U); + EXPECT_TRUE(EndsWith(dex_files2[0]->GetLocation(), "MultiDex.jar")); +} + TEST_F(ClassLinkerTest, FindClassNested) { ScopedObjectAccess soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); @@ -985,11 +1024,10 @@ TEST_F(ClassLinkerTest, ResolveVerifyAndClinit) { ScopedObjectAccess soa(Thread::Current()); jobject jclass_loader = LoadDex("StaticsFromCode"); + const DexFile* dex_file = GetFirstDexFile(jclass_loader); StackHandleScope<1> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader))); - const DexFile* dex_file = Runtime::Current()->GetCompileTimeClassPath(jclass_loader)[0]; - CHECK(dex_file != nullptr); mirror::Class* klass = class_linker_->FindClass(soa.Self(), "LStaticsFromCode;", class_loader); mirror::ArtMethod* clinit = klass->FindClassInitializer(); mirror::ArtMethod* getS0 = klass->FindDirectMethod("getS0", "()Ljava/lang/Object;"); diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index e0d62d7012..d400010e4b 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -34,6 +34,7 @@ #include "gc_root-inl.h" #include "gc/heap.h" #include "gtest/gtest.h" +#include "handle_scope-inl.h" #include "interpreter/unstarted_runtime.h" #include "jni_internal.h" #include "mirror/class_loader.h" @@ -219,7 +220,6 @@ void CommonRuntimeTest::SetUp() { std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB)); std::string max_heap_string(StringPrintf("-Xmx%zdm", gc::Heap::kDefaultMaximumSize / MB)); - callbacks_.reset(new NoopCompilerCallbacks()); RuntimeOptions options; std::string boot_class_path_string = "-Xbootclasspath:" + GetLibCoreDexFileName(); @@ -227,9 +227,16 @@ void CommonRuntimeTest::SetUp() { options.push_back(std::make_pair("-Xcheck:jni", nullptr)); options.push_back(std::make_pair(min_heap_string, nullptr)); options.push_back(std::make_pair(max_heap_string, nullptr)); - options.push_back(std::make_pair("compilercallbacks", callbacks_.get())); + + callbacks_.reset(new NoopCompilerCallbacks()); + SetUpRuntimeOptions(&options); + // Install compiler-callbacks if SetupRuntimeOptions hasn't deleted them. + if (callbacks_.get() != nullptr) { + options.push_back(std::make_pair("compilercallbacks", callbacks_.get())); + } + PreRuntimeCreate(); if (!Runtime::Create(options, false)) { LOG(FATAL) << "Failed to create runtime"; @@ -386,22 +393,89 @@ std::unique_ptr<const DexFile> CommonRuntimeTest::OpenTestDexFile(const char* na return std::move(vector[0]); } +std::vector<const DexFile*> CommonRuntimeTest::GetDexFiles(jobject jclass_loader) { + std::vector<const DexFile*> ret; + + ScopedObjectAccess soa(Thread::Current()); + + StackHandleScope<4> hs(Thread::Current()); + Handle<mirror::ClassLoader> class_loader = hs.NewHandle( + soa.Decode<mirror::ClassLoader*>(jclass_loader)); + + DCHECK_EQ(class_loader->GetClass(), + soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)); + DCHECK_EQ(class_loader->GetParent()->GetClass(), + soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)); + + // The class loader is a PathClassLoader which inherits from BaseDexClassLoader. + // We need to get the DexPathList and loop through it. + Handle<mirror::ArtField> cookie_field = + hs.NewHandle(soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie)); + Handle<mirror::ArtField> dex_file_field = + hs.NewHandle( + soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile)); + mirror::Object* dex_path_list = + soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)-> + GetObject(class_loader.Get()); + if (dex_path_list != nullptr && dex_file_field.Get() != nullptr && + cookie_field.Get() != nullptr) { + // DexPathList has an array dexElements of Elements[] which each contain a dex file. + mirror::Object* dex_elements_obj = + soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)-> + GetObject(dex_path_list); + // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look + // at the mCookie which is a DexFile vector. + if (dex_elements_obj != nullptr) { + Handle<mirror::ObjectArray<mirror::Object>> dex_elements = + hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>()); + for (int32_t i = 0; i < dex_elements->GetLength(); ++i) { + mirror::Object* element = dex_elements->GetWithoutChecks(i); + if (element == nullptr) { + // Should never happen, fall back to java code to throw a NPE. + break; + } + mirror::Object* dex_file = dex_file_field->GetObject(element); + if (dex_file != nullptr) { + mirror::LongArray* long_array = cookie_field->GetObject(dex_file)->AsLongArray(); + DCHECK(long_array != nullptr); + int32_t long_array_size = long_array->GetLength(); + for (int32_t j = 0; j < long_array_size; ++j) { + const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>( + long_array->GetWithoutChecks(j))); + if (cp_dex_file == nullptr) { + LOG(WARNING) << "Null DexFile"; + continue; + } + ret.push_back(cp_dex_file); + } + } + } + } + } + + return ret; +} + +const DexFile* CommonRuntimeTest::GetFirstDexFile(jobject jclass_loader) { + std::vector<const DexFile*> tmp(GetDexFiles(jclass_loader)); + DCHECK(!tmp.empty()); + const DexFile* ret = tmp[0]; + DCHECK(ret != nullptr); + return ret; +} + jobject CommonRuntimeTest::LoadDex(const char* dex_name) { std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name); std::vector<const DexFile*> class_path; CHECK_NE(0U, dex_files.size()); for (auto& dex_file : dex_files) { class_path.push_back(dex_file.get()); - class_linker_->RegisterDexFile(*dex_file); loaded_dex_files_.push_back(std::move(dex_file)); } + Thread* self = Thread::Current(); - JNIEnvExt* env = self->GetJniEnv(); - ScopedLocalRef<jobject> class_loader_local(env, - env->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader)); - jobject class_loader = env->NewGlobalRef(class_loader_local.get()); - self->SetClassLoaderOverride(class_loader_local.get()); - Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path); + jobject class_loader = Runtime::Current()->GetClassLinker()->CreatePathClassLoader(self, class_path); + self->SetClassLoaderOverride(class_loader); return class_loader; } diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index cce8485a42..5fbc2ee680 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -133,10 +133,18 @@ class CommonRuntimeTest : public testing::Test { const DexFile* java_lang_dex_file_; std::vector<const DexFile*> boot_class_path_; + // Get the dex files from a PathClassLoader. This in order of the dex elements and their dex + // arrays. + std::vector<const DexFile*> GetDexFiles(jobject jclass_loader); + + // Get the first dex file from a PathClassLoader. Will abort if it is null. + const DexFile* GetFirstDexFile(jobject jclass_loader); + + std::unique_ptr<CompilerCallbacks> callbacks_; + private: static std::string GetCoreFileLocation(const char* suffix); - std::unique_ptr<CompilerCallbacks> callbacks_; std::vector<std::unique_ptr<const DexFile>> loaded_dex_files_; }; diff --git a/runtime/compiler_callbacks.h b/runtime/compiler_callbacks.h index d1a68615b1..b296e39c5e 100644 --- a/runtime/compiler_callbacks.h +++ b/runtime/compiler_callbacks.h @@ -29,19 +29,32 @@ class MethodVerifier; } // namespace verifier class CompilerCallbacks { - public: - virtual ~CompilerCallbacks() { } + public: + enum class CallbackMode { // private + kCompileBootImage, + kCompileApp + }; - virtual bool MethodVerified(verifier::MethodVerifier* verifier) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; - virtual void ClassRejected(ClassReference ref) = 0; + virtual ~CompilerCallbacks() { } - // Return true if we should attempt to relocate to a random base address if we have not already - // done so. Return false if relocating in this way would be problematic. - virtual bool IsRelocationPossible() = 0; + virtual bool MethodVerified(verifier::MethodVerifier* verifier) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; + virtual void ClassRejected(ClassReference ref) = 0; - protected: - CompilerCallbacks() { } + // Return true if we should attempt to relocate to a random base address if we have not already + // done so. Return false if relocating in this way would be problematic. + virtual bool IsRelocationPossible() = 0; + + bool IsBootImage() { + return mode_ == CallbackMode::kCompileBootImage; + } + + protected: + explicit CompilerCallbacks(CallbackMode mode) : mode_(mode) { } + + private: + // Whether the compiler is creating a boot image. + const CallbackMode mode_; }; } // namespace art diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 7144577acb..a767cf086f 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1451,22 +1451,31 @@ static uint16_t MangleSlot(uint16_t slot, mirror::ArtMethod* m) * Circularly shifts registers so that arguments come last. Reverts * slots to dex style argument placement. */ -static uint16_t DemangleSlot(uint16_t slot, mirror::ArtMethod* m) +static uint16_t DemangleSlot(uint16_t slot, mirror::ArtMethod* m, JDWP::JdwpError* error) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const DexFile::CodeItem* code_item = m->GetCodeItem(); if (code_item == nullptr) { // We should not get here for a method without code (native, proxy or abstract). Log it and // return the slot as is since all registers are arguments. LOG(WARNING) << "Trying to demangle slot for method without code " << PrettyMethod(m); - return slot; - } - uint16_t ins_size = code_item->ins_size_; - uint16_t locals_size = code_item->registers_size_ - ins_size; - if (slot < ins_size) { - return slot + locals_size; + uint16_t vreg_count = mirror::ArtMethod::NumArgRegisters(m->GetShorty()); + if (slot < vreg_count) { + *error = JDWP::ERR_NONE; + return slot; + } } else { - return slot - ins_size; + if (slot < code_item->registers_size_) { + uint16_t ins_size = code_item->ins_size_; + uint16_t locals_size = code_item->registers_size_ - ins_size; + *error = JDWP::ERR_NONE; + return (slot < ins_size) ? slot + locals_size : slot - ins_size; + } } + + // Slot is invalid in the method. + LOG(ERROR) << "Invalid local slot " << slot << " for method " << PrettyMethod(m); + *error = JDWP::ERR_INVALID_SLOT; + return DexFile::kDexNoIndex16; } JDWP::JdwpError Dbg::OutputDeclaredFields(JDWP::RefTypeId class_id, bool with_generic, JDWP::ExpandBuf* pReply) { @@ -1798,7 +1807,7 @@ static JDWP::JdwpError SetFieldValueImpl(JDWP::ObjectId object_id, JDWP::FieldId HandleWrapper<mirror::Object> h_v(hs.NewHandleWrapper(&v)); HandleWrapper<mirror::ArtField> h_f(hs.NewHandleWrapper(&f)); HandleWrapper<mirror::Object> h_o(hs.NewHandleWrapper(&o)); - field_type = h_f->GetType(true); + field_type = h_f->GetType<true>(); } if (!field_type->IsAssignableFrom(v->GetClass())) { return JDWP::ERR_INVALID_OBJECT; @@ -2427,6 +2436,9 @@ JDWP::JdwpError Dbg::GetLocalValues(JDWP::Request* request, JDWP::ExpandBuf* pRe if (error != JDWP::ERR_NONE) { return error; } + if (!IsSuspendedForDebugger(soa, thread)) { + return JDWP::ERR_THREAD_NOT_SUSPENDED; + } } // Find the frame with the given frame_id. std::unique_ptr<Context> context(Context::Create()); @@ -2455,73 +2467,81 @@ JDWP::JdwpError Dbg::GetLocalValues(JDWP::Request* request, JDWP::ExpandBuf* pRe return JDWP::ERR_NONE; } +constexpr JDWP::JdwpError kStackFrameLocalAccessError = JDWP::ERR_ABSENT_INFORMATION; + +static std::string GetStackContextAsString(const StackVisitor& visitor) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return StringPrintf(" at DEX pc 0x%08x in method %s", visitor.GetDexPc(false), + PrettyMethod(visitor.GetMethod()).c_str()); +} + +static JDWP::JdwpError FailGetLocalValue(const StackVisitor& visitor, uint16_t vreg, + JDWP::JdwpTag tag) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + LOG(ERROR) << "Failed to read " << tag << " local from register v" << vreg + << GetStackContextAsString(visitor); + return kStackFrameLocalAccessError; +} + JDWP::JdwpError Dbg::GetLocalValue(const StackVisitor& visitor, ScopedObjectAccessUnchecked& soa, int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t width) { mirror::ArtMethod* m = visitor.GetMethod(); - uint16_t reg = DemangleSlot(slot, m); + JDWP::JdwpError error = JDWP::ERR_NONE; + uint16_t vreg = DemangleSlot(slot, m, &error); + if (error != JDWP::ERR_NONE) { + return error; + } // TODO: check that the tag is compatible with the actual type of the slot! - // TODO: check slot is valid for this method or return INVALID_SLOT error. - constexpr JDWP::JdwpError kFailureErrorCode = JDWP::ERR_ABSENT_INFORMATION; switch (tag) { case JDWP::JT_BOOLEAN: { CHECK_EQ(width, 1U); uint32_t intVal; - if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) { - VLOG(jdwp) << "get boolean local " << reg << " = " << intVal; - JDWP::Set1(buf + 1, intVal != 0); - } else { - VLOG(jdwp) << "failed to get boolean local " << reg; - return kFailureErrorCode; + if (!visitor.GetVReg(m, vreg, kIntVReg, &intVal)) { + return FailGetLocalValue(visitor, vreg, tag); } + VLOG(jdwp) << "get boolean local " << vreg << " = " << intVal; + JDWP::Set1(buf + 1, intVal != 0); break; } case JDWP::JT_BYTE: { CHECK_EQ(width, 1U); uint32_t intVal; - if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) { - VLOG(jdwp) << "get byte local " << reg << " = " << intVal; - JDWP::Set1(buf + 1, intVal); - } else { - VLOG(jdwp) << "failed to get byte local " << reg; - return kFailureErrorCode; + if (!visitor.GetVReg(m, vreg, kIntVReg, &intVal)) { + return FailGetLocalValue(visitor, vreg, tag); } + VLOG(jdwp) << "get byte local " << vreg << " = " << intVal; + JDWP::Set1(buf + 1, intVal); break; } case JDWP::JT_SHORT: case JDWP::JT_CHAR: { CHECK_EQ(width, 2U); uint32_t intVal; - if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) { - VLOG(jdwp) << "get short/char local " << reg << " = " << intVal; - JDWP::Set2BE(buf + 1, intVal); - } else { - VLOG(jdwp) << "failed to get short/char local " << reg; - return kFailureErrorCode; + if (!visitor.GetVReg(m, vreg, kIntVReg, &intVal)) { + return FailGetLocalValue(visitor, vreg, tag); } + VLOG(jdwp) << "get short/char local " << vreg << " = " << intVal; + JDWP::Set2BE(buf + 1, intVal); break; } case JDWP::JT_INT: { CHECK_EQ(width, 4U); uint32_t intVal; - if (visitor.GetVReg(m, reg, kIntVReg, &intVal)) { - VLOG(jdwp) << "get int local " << reg << " = " << intVal; - JDWP::Set4BE(buf + 1, intVal); - } else { - VLOG(jdwp) << "failed to get int local " << reg; - return kFailureErrorCode; + if (!visitor.GetVReg(m, vreg, kIntVReg, &intVal)) { + return FailGetLocalValue(visitor, vreg, tag); } + VLOG(jdwp) << "get int local " << vreg << " = " << intVal; + JDWP::Set4BE(buf + 1, intVal); break; } case JDWP::JT_FLOAT: { CHECK_EQ(width, 4U); uint32_t intVal; - if (visitor.GetVReg(m, reg, kFloatVReg, &intVal)) { - VLOG(jdwp) << "get float local " << reg << " = " << intVal; - JDWP::Set4BE(buf + 1, intVal); - } else { - VLOG(jdwp) << "failed to get float local " << reg; - return kFailureErrorCode; + if (!visitor.GetVReg(m, vreg, kFloatVReg, &intVal)) { + return FailGetLocalValue(visitor, vreg, tag); } + VLOG(jdwp) << "get float local " << vreg << " = " << intVal; + JDWP::Set4BE(buf + 1, intVal); break; } case JDWP::JT_ARRAY: @@ -2533,47 +2553,44 @@ JDWP::JdwpError Dbg::GetLocalValue(const StackVisitor& visitor, ScopedObjectAcce case JDWP::JT_THREAD_GROUP: { CHECK_EQ(width, sizeof(JDWP::ObjectId)); uint32_t intVal; - if (visitor.GetVReg(m, reg, kReferenceVReg, &intVal)) { - mirror::Object* o = reinterpret_cast<mirror::Object*>(intVal); - VLOG(jdwp) << "get " << tag << " object local " << reg << " = " << o; - if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) { - LOG(FATAL) << "Register " << reg << " expected to hold " << tag << " object: " << o; - } - tag = TagFromObject(soa, o); - JDWP::SetObjectId(buf + 1, gRegistry->Add(o)); - } else { - VLOG(jdwp) << "failed to get " << tag << " object local " << reg; - return kFailureErrorCode; + if (!visitor.GetVReg(m, vreg, kReferenceVReg, &intVal)) { + return FailGetLocalValue(visitor, vreg, tag); } + mirror::Object* o = reinterpret_cast<mirror::Object*>(intVal); + VLOG(jdwp) << "get " << tag << " object local " << vreg << " = " << o; + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) { + LOG(FATAL) << StringPrintf("Found invalid object %#" PRIxPTR " in register v%u", + reinterpret_cast<uintptr_t>(o), vreg) + << GetStackContextAsString(visitor); + UNREACHABLE(); + } + tag = TagFromObject(soa, o); + JDWP::SetObjectId(buf + 1, gRegistry->Add(o)); break; } case JDWP::JT_DOUBLE: { CHECK_EQ(width, 8U); uint64_t longVal; - if (visitor.GetVRegPair(m, reg, kDoubleLoVReg, kDoubleHiVReg, &longVal)) { - VLOG(jdwp) << "get double local " << reg << " = " << longVal; - JDWP::Set8BE(buf + 1, longVal); - } else { - VLOG(jdwp) << "failed to get double local " << reg; - return kFailureErrorCode; + if (!visitor.GetVRegPair(m, vreg, kDoubleLoVReg, kDoubleHiVReg, &longVal)) { + return FailGetLocalValue(visitor, vreg, tag); } + VLOG(jdwp) << "get double local " << vreg << " = " << longVal; + JDWP::Set8BE(buf + 1, longVal); break; } case JDWP::JT_LONG: { CHECK_EQ(width, 8U); uint64_t longVal; - if (visitor.GetVRegPair(m, reg, kLongLoVReg, kLongHiVReg, &longVal)) { - VLOG(jdwp) << "get long local " << reg << " = " << longVal; - JDWP::Set8BE(buf + 1, longVal); - } else { - VLOG(jdwp) << "failed to get long local " << reg; - return kFailureErrorCode; + if (!visitor.GetVRegPair(m, vreg, kLongLoVReg, kLongHiVReg, &longVal)) { + return FailGetLocalValue(visitor, vreg, tag); } + VLOG(jdwp) << "get long local " << vreg << " = " << longVal; + JDWP::Set8BE(buf + 1, longVal); break; } default: LOG(FATAL) << "Unknown tag " << tag; - break; + UNREACHABLE(); } // Prepend tag, which may have been updated. @@ -2594,6 +2611,9 @@ JDWP::JdwpError Dbg::SetLocalValues(JDWP::Request* request) { if (error != JDWP::ERR_NONE) { return error; } + if (!IsSuspendedForDebugger(soa, thread)) { + return JDWP::ERR_THREAD_NOT_SUSPENDED; + } } // Find the frame with the given frame_id. std::unique_ptr<Context> context(Context::Create()); @@ -2620,46 +2640,50 @@ JDWP::JdwpError Dbg::SetLocalValues(JDWP::Request* request) { return JDWP::ERR_NONE; } +template<typename T> +static JDWP::JdwpError FailSetLocalValue(const StackVisitor& visitor, uint16_t vreg, + JDWP::JdwpTag tag, T value) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + LOG(ERROR) << "Failed to write " << tag << " local " << value + << " (0x" << std::hex << value << ") into register v" << vreg + << GetStackContextAsString(visitor); + return kStackFrameLocalAccessError; +} + JDWP::JdwpError Dbg::SetLocalValue(StackVisitor& visitor, int slot, JDWP::JdwpTag tag, uint64_t value, size_t width) { mirror::ArtMethod* m = visitor.GetMethod(); - uint16_t reg = DemangleSlot(slot, m); + JDWP::JdwpError error = JDWP::ERR_NONE; + uint16_t vreg = DemangleSlot(slot, m, &error); + if (error != JDWP::ERR_NONE) { + return error; + } // TODO: check that the tag is compatible with the actual type of the slot! - // TODO: check slot is valid for this method or return INVALID_SLOT error. - constexpr JDWP::JdwpError kFailureErrorCode = JDWP::ERR_ABSENT_INFORMATION; switch (tag) { case JDWP::JT_BOOLEAN: case JDWP::JT_BYTE: CHECK_EQ(width, 1U); - if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kIntVReg)) { - VLOG(jdwp) << "failed to set boolean/byte local " << reg << " = " - << static_cast<uint32_t>(value); - return kFailureErrorCode; + if (!visitor.SetVReg(m, vreg, static_cast<uint32_t>(value), kIntVReg)) { + return FailSetLocalValue(visitor, vreg, tag, static_cast<uint32_t>(value)); } break; case JDWP::JT_SHORT: case JDWP::JT_CHAR: CHECK_EQ(width, 2U); - if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kIntVReg)) { - VLOG(jdwp) << "failed to set short/char local " << reg << " = " - << static_cast<uint32_t>(value); - return kFailureErrorCode; + if (!visitor.SetVReg(m, vreg, static_cast<uint32_t>(value), kIntVReg)) { + return FailSetLocalValue(visitor, vreg, tag, static_cast<uint32_t>(value)); } break; case JDWP::JT_INT: CHECK_EQ(width, 4U); - if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kIntVReg)) { - VLOG(jdwp) << "failed to set int local " << reg << " = " - << static_cast<uint32_t>(value); - return kFailureErrorCode; + if (!visitor.SetVReg(m, vreg, static_cast<uint32_t>(value), kIntVReg)) { + return FailSetLocalValue(visitor, vreg, tag, static_cast<uint32_t>(value)); } break; case JDWP::JT_FLOAT: CHECK_EQ(width, 4U); - if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(value), kFloatVReg)) { - VLOG(jdwp) << "failed to set float local " << reg << " = " - << static_cast<uint32_t>(value); - return kFailureErrorCode; + if (!visitor.SetVReg(m, vreg, static_cast<uint32_t>(value), kFloatVReg)) { + return FailSetLocalValue(visitor, vreg, tag, static_cast<uint32_t>(value)); } break; case JDWP::JT_ARRAY: @@ -2670,38 +2694,35 @@ JDWP::JdwpError Dbg::SetLocalValue(StackVisitor& visitor, int slot, JDWP::JdwpTa case JDWP::JT_THREAD: case JDWP::JT_THREAD_GROUP: { CHECK_EQ(width, sizeof(JDWP::ObjectId)); - JDWP::JdwpError error; mirror::Object* o = gRegistry->Get<mirror::Object*>(static_cast<JDWP::ObjectId>(value), &error); if (error != JDWP::ERR_NONE) { VLOG(jdwp) << tag << " object " << o << " is an invalid object"; return JDWP::ERR_INVALID_OBJECT; - } else if (!visitor.SetVReg(m, reg, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(o)), - kReferenceVReg)) { - VLOG(jdwp) << "failed to set " << tag << " object local " << reg << " = " << o; - return kFailureErrorCode; + } + if (!visitor.SetVReg(m, vreg, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(o)), + kReferenceVReg)) { + return FailSetLocalValue(visitor, vreg, tag, reinterpret_cast<uintptr_t>(o)); } break; } case JDWP::JT_DOUBLE: { CHECK_EQ(width, 8U); - if (!visitor.SetVRegPair(m, reg, value, kDoubleLoVReg, kDoubleHiVReg)) { - VLOG(jdwp) << "failed to set double local " << reg << " = " << value; - return kFailureErrorCode; + if (!visitor.SetVRegPair(m, vreg, value, kDoubleLoVReg, kDoubleHiVReg)) { + return FailSetLocalValue(visitor, vreg, tag, value); } break; } case JDWP::JT_LONG: { CHECK_EQ(width, 8U); - if (!visitor.SetVRegPair(m, reg, value, kLongLoVReg, kLongHiVReg)) { - VLOG(jdwp) << "failed to set double local " << reg << " = " << value; - return kFailureErrorCode; + if (!visitor.SetVRegPair(m, vreg, value, kLongLoVReg, kLongHiVReg)) { + return FailSetLocalValue(visitor, vreg, tag, value); } break; } default: LOG(FATAL) << "Unknown tag " << tag; - break; + UNREACHABLE(); } return JDWP::ERR_NONE; } @@ -3153,6 +3174,11 @@ static const Breakpoint* FindFirstBreakpointForMethod(mirror::ArtMethod* m) return nullptr; } +bool Dbg::MethodHasAnyBreakpoints(mirror::ArtMethod* method) { + ReaderMutexLock mu(Thread::Current(), *Locks::breakpoint_lock_); + return FindFirstBreakpointForMethod(method) != nullptr; +} + // Sanity checks all existing breakpoints on the same method. static void SanityCheckExistingBreakpoints(mirror::ArtMethod* m, DeoptimizationRequest::Kind deoptimization_kind) diff --git a/runtime/debugger.h b/runtime/debugger.h index d015294eac..4f4a781c23 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -253,6 +253,10 @@ class Dbg { // Returns true if we had -Xrunjdwp or -agentlib:jdwp= on the command line. static bool IsJdwpConfigured(); + // Returns true if a method has any breakpoints. + static bool MethodHasAnyBreakpoints(mirror::ArtMethod* method) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::breakpoint_lock_); + static bool IsDisposed(); /* diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index a3ab026ba8..a3104525b6 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -285,7 +285,7 @@ bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, const Instruction HandleWrapper<mirror::ArtField> h_f(hs.NewHandleWrapper(&f)); HandleWrapper<mirror::Object> h_reg(hs.NewHandleWrapper(®)); HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper(&obj)); - field_class = h_f->GetType(true); + field_class = h_f->GetType<true>(); } if (!reg->VerifierInstanceOf(field_class)) { // This should never happen. diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 98dfdbd2ea..1b08e80495 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -29,6 +29,7 @@ #include "mirror/array-inl.h" #include "mirror/art_method-inl.h" #include "mirror/class.h" +#include "mirror/field-inl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" @@ -219,19 +220,11 @@ static void UnstartedClassGetDeclaredField( PrettyDescriptor(klass).c_str()); return; } - // TODO: getDeclaredField calls GetType once the field is found to ensure a - // NoClassDefFoundError is thrown if the field's type cannot be resolved. - mirror::Class* jlr_Field = self->DecodeJObject( - WellKnownClasses::java_lang_reflect_Field)->AsClass(); - StackHandleScope<1> hs(self); - Handle<mirror::Object> field(hs.NewHandle(jlr_Field->AllocNonMovableObject(self))); - CHECK(field.Get() != nullptr); - mirror::ArtMethod* c = jlr_Field->FindDeclaredDirectMethod("<init>", - "(Ljava/lang/reflect/ArtField;)V"); - uint32_t args[1]; - args[0] = StackReference<mirror::Object>::FromMirrorPtr(found).AsVRegValue(); - EnterInterpreterFromInvoke(self, c, field.Get(), args, nullptr); - result->SetL(field.Get()); + if (Runtime::Current()->IsActiveTransaction()) { + result->SetL(mirror::Field::CreateFromArtField<true>(self, found, true)); + } else { + result->SetL(mirror::Field::CreateFromArtField<false>(self, found, true)); + } } static void UnstartedVmClassLoaderFindLoadedClass( diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 13c1f813bd..5dc739edb2 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -128,6 +128,10 @@ bool Jit::LoadCompiler(std::string* error_msg) { bool Jit::CompileMethod(mirror::ArtMethod* method, Thread* self) { DCHECK(!method->IsRuntimeMethod()); + if (Dbg::IsDebuggerActive() && Dbg::MethodHasAnyBreakpoints(method)) { + VLOG(jit) << "JIT not compiling " << PrettyMethod(method) << " due to breakpoint"; + return false; + } const bool result = jit_compile_method_(jit_compiler_handle_, method, self); if (result) { method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 6063e1e8e8..5e38470584 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -41,6 +41,7 @@ #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" +#include "mirror/field.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" @@ -164,8 +165,10 @@ static mirror::ClassLoader* GetClassLoader(const ScopedObjectAccess& soa) // See if the override ClassLoader is set for gtests. class_loader = soa.Decode<mirror::ClassLoader*>(soa.Self()->GetClassLoaderOverride()); if (class_loader != nullptr) { - // If so, CommonCompilerTest should have set UseCompileTimeClassPath. - CHECK(Runtime::Current()->UseCompileTimeClassPath()); + // If so, CommonCompilerTest should have marked the runtime as a compiler not compiling an + // image. + CHECK(Runtime::Current()->IsAotCompiler()); + CHECK(!Runtime::Current()->IsCompilingBootImage()); return class_loader; } // Use the BOOTCLASSPATH. @@ -344,7 +347,13 @@ class JNI { static jfieldID FromReflectedField(JNIEnv* env, jobject jlr_field) { CHECK_NON_NULL_ARGUMENT(jlr_field); ScopedObjectAccess soa(env); - return soa.EncodeField(mirror::ArtField::FromReflectedField(soa, jlr_field)); + mirror::Object* obj_field = soa.Decode<mirror::Object*>(jlr_field); + if (obj_field->GetClass() != mirror::Field::StaticClass()) { + // Not even a java.lang.reflect.Field, return nullptr. + return nullptr; + } + auto* field = static_cast<mirror::Field*>(obj_field); + return soa.EncodeField(field->GetArtField()); } static jobject ToReflectedMethod(JNIEnv* env, jclass, jmethodID mid, jboolean) { @@ -371,14 +380,7 @@ class JNI { CHECK_NON_NULL_ARGUMENT(fid); ScopedObjectAccess soa(env); mirror::ArtField* f = soa.DecodeField(fid); - ScopedLocalRef<jobject> art_field(env, soa.AddLocalReference<jobject>(f)); - jobject reflect_field = env->AllocObject(WellKnownClasses::java_lang_reflect_Field); - if (env->ExceptionCheck()) { - return nullptr; - } - SetObjectField(env, reflect_field, - WellKnownClasses::java_lang_reflect_Field_artField, art_field.get()); - return reflect_field; + return soa.AddLocalReference<jobject>(mirror::Field::CreateFromArtField(soa.Self(), f, true)); } static jclass GetObjectClass(JNIEnv* env, jobject java_object) { diff --git a/runtime/memory_region.h b/runtime/memory_region.h index f867f6a75a..6a784eb061 100644 --- a/runtime/memory_region.h +++ b/runtime/memory_region.h @@ -48,22 +48,28 @@ class MemoryRegion FINAL : public ValueObject { uint8_t* end() const { return start() + size_; } // Load value of type `T` at `offset`. The memory address corresponding - // to `offset` should be word-aligned. - template<typename T> T Load(uintptr_t offset) const { - // TODO: DCHECK that the address is word-aligned. - return *ComputeInternalPointer<T>(offset); + // to `offset` should be word-aligned (on ARM, this is a requirement). + template<typename T> + ALWAYS_INLINE T Load(uintptr_t offset) const { + T* address = ComputeInternalPointer<T>(offset); + DCHECK(IsWordAligned(address)); + return *address; } // Store `value` (of type `T`) at `offset`. The memory address - // corresponding to `offset` should be word-aligned. - template<typename T> void Store(uintptr_t offset, T value) const { - // TODO: DCHECK that the address is word-aligned. - *ComputeInternalPointer<T>(offset) = value; + // corresponding to `offset` should be word-aligned (on ARM, this is + // a requirement). + template<typename T> + ALWAYS_INLINE void Store(uintptr_t offset, T value) const { + T* address = ComputeInternalPointer<T>(offset); + DCHECK(IsWordAligned(address)); + *address = value; } // Load value of type `T` at `offset`. The memory address corresponding // to `offset` does not need to be word-aligned. - template<typename T> T LoadUnaligned(uintptr_t offset) const { + template<typename T> + ALWAYS_INLINE T LoadUnaligned(uintptr_t offset) const { // Equivalent unsigned integer type corresponding to T. typedef typename UnsignedIntegerType<sizeof(T)>::type U; U equivalent_unsigned_integer_value = 0; @@ -77,7 +83,8 @@ class MemoryRegion FINAL : public ValueObject { // Store `value` (of type `T`) at `offset`. The memory address // corresponding to `offset` does not need to be word-aligned. - template<typename T> void StoreUnaligned(uintptr_t offset, T value) const { + template<typename T> + ALWAYS_INLINE void StoreUnaligned(uintptr_t offset, T value) const { // Equivalent unsigned integer type corresponding to T. typedef typename UnsignedIntegerType<sizeof(T)>::type U; U equivalent_unsigned_integer_value = bit_cast<U, T>(value); @@ -88,19 +95,20 @@ class MemoryRegion FINAL : public ValueObject { } } - template<typename T> T* PointerTo(uintptr_t offset) const { + template<typename T> + ALWAYS_INLINE T* PointerTo(uintptr_t offset) const { return ComputeInternalPointer<T>(offset); } // Load a single bit in the region. The bit at offset 0 is the least // significant bit in the first byte. - bool LoadBit(uintptr_t bit_offset) const { + ALWAYS_INLINE bool LoadBit(uintptr_t bit_offset) const { uint8_t bit_mask; uint8_t byte = *ComputeBitPointer(bit_offset, &bit_mask); return byte & bit_mask; } - void StoreBit(uintptr_t bit_offset, bool value) const { + ALWAYS_INLINE void StoreBit(uintptr_t bit_offset, bool value) const { uint8_t bit_mask; uint8_t* byte = ComputeBitPointer(bit_offset, &bit_mask); if (value) { @@ -110,6 +118,31 @@ class MemoryRegion FINAL : public ValueObject { } } + // Load `length` bits from the region starting at bit offset `bit_offset`. + // The bit at the smallest offset is the least significant bit in the + // loaded value. `length` must not be larger than the number of bits + // contained in the return value (32). + uint32_t LoadBits(uintptr_t bit_offset, size_t length) const { + CHECK_LE(length, sizeof(uint32_t) * kBitsPerByte); + uint32_t value = 0u; + for (size_t i = 0; i < length; ++i) { + value |= LoadBit(bit_offset + i) << i; + } + return value; + } + + // Store `value` on `length` bits in the region starting at bit offset + // `bit_offset`. The bit at the smallest offset is the least significant + // bit of the stored `value`. `value` must not be larger than `length` + // bits. + void StoreBits(uintptr_t bit_offset, uint32_t value, size_t length) { + CHECK_LT(value, 2u << length); + for (size_t i = 0; i < length; ++i) { + bool ith_bit = value & (1 << i); + StoreBit(bit_offset + i, ith_bit); + } + } + void CopyFrom(size_t offset, const MemoryRegion& from) const; // Compute a sub memory region based on an existing one. @@ -126,7 +159,8 @@ class MemoryRegion FINAL : public ValueObject { } private: - template<typename T> T* ComputeInternalPointer(size_t offset) const { + template<typename T> + ALWAYS_INLINE T* ComputeInternalPointer(size_t offset) const { CHECK_GE(size(), sizeof(T)); CHECK_LE(offset, size() - sizeof(T)); return reinterpret_cast<T*>(start() + offset); @@ -134,13 +168,20 @@ class MemoryRegion FINAL : public ValueObject { // Locate the bit with the given offset. Returns a pointer to the byte // containing the bit, and sets bit_mask to the bit within that byte. - uint8_t* ComputeBitPointer(uintptr_t bit_offset, uint8_t* bit_mask) const { + ALWAYS_INLINE uint8_t* ComputeBitPointer(uintptr_t bit_offset, uint8_t* bit_mask) const { uintptr_t bit_remainder = (bit_offset & (kBitsPerByte - 1)); *bit_mask = (1U << bit_remainder); uintptr_t byte_offset = (bit_offset >> kBitsPerByteLog2); return ComputeInternalPointer<uint8_t>(byte_offset); } + // Is `address` aligned on a machine word? + template<typename T> static bool IsWordAligned(const T* address) { + // Word alignment in bytes. + size_t kWordAlignment = GetInstructionSetPointerSize(kRuntimeISA); + return IsAlignedParam(address, kWordAlignment); + } + void* pointer_; size_t size_; }; diff --git a/runtime/mirror/accessible_object.h b/runtime/mirror/accessible_object.h new file mode 100644 index 0000000000..6d4c0f6fb3 --- /dev/null +++ b/runtime/mirror/accessible_object.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_MIRROR_ACCESSIBLE_OBJECT_H_ +#define ART_RUNTIME_MIRROR_ACCESSIBLE_OBJECT_H_ + +#include "class.h" +#include "gc_root.h" +#include "object.h" +#include "object_callbacks.h" +#include "read_barrier_option.h" +#include "thread.h" + +namespace art { + +namespace mirror { + +// C++ mirror of java.lang.reflect.AccessibleObject +class MANAGED AccessibleObject : public Object { + public: + static MemberOffset FlagOffset() { + return OFFSET_OF_OBJECT_MEMBER(AccessibleObject, flag_); + } + + template<bool kTransactionActive> + void SetAccessible(bool value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + UNUSED(padding_); + return SetFieldBoolean<kTransactionActive>(FlagOffset(), value ? 1u : 0u); + } + + bool IsAccessible() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetFieldBoolean(FlagOffset()); + } + + private: + uint8_t flag_; + // Padding required for now since "packed" will cause reflect.Field fields to not be aligned + // otherwise. + uint8_t padding_[3]; + + DISALLOW_IMPLICIT_CONSTRUCTORS(AccessibleObject); +}; + +} // namespace mirror +} // namespace art + +#endif // ART_RUNTIME_MIRROR_ACCESSIBLE_OBJECT_H_ diff --git a/runtime/mirror/art_field-inl.h b/runtime/mirror/art_field-inl.h index 2b406bdd91..986852f200 100644 --- a/runtime/mirror/art_field-inl.h +++ b/runtime/mirror/art_field-inl.h @@ -34,7 +34,7 @@ namespace art { namespace mirror { inline uint32_t ArtField::ClassSize() { - uint32_t vtable_entries = Object::kVTableLength + 6; + uint32_t vtable_entries = Object::kVTableLength; return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0); } @@ -290,16 +290,19 @@ inline bool ArtField::IsPrimitiveType() SHARED_LOCKS_REQUIRED(Locks::mutator_loc return GetTypeAsPrimitiveType() != Primitive::kPrimNot; } -inline Class* ArtField::GetType(bool resolve) { - uint32_t field_index = GetDexFieldIndex(); - if (UNLIKELY(GetDeclaringClass()->IsProxyClass())) { +template <bool kResolve> +inline Class* ArtField::GetType() { + const uint32_t field_index = GetDexFieldIndex(); + auto* declaring_class = GetDeclaringClass(); + if (UNLIKELY(declaring_class->IsProxyClass())) { return Runtime::Current()->GetClassLinker()->FindSystemClass(Thread::Current(), GetTypeDescriptor()); } - const DexFile* dex_file = GetDexFile(); + auto* dex_cache = declaring_class->GetDexCache(); + const DexFile* const dex_file = dex_cache->GetDexFile(); const DexFile::FieldId& field_id = dex_file->GetFieldId(field_index); - mirror::Class* type = GetDexCache()->GetResolvedType(field_id.type_idx_); - if (resolve && (type == nullptr)) { + mirror::Class* type = dex_cache->GetResolvedType(field_id.type_idx_); + if (kResolve && UNLIKELY(type == nullptr)) { type = Runtime::Current()->GetClassLinker()->ResolveType(field_id.type_idx_, this); CHECK(type != nullptr || Thread::Current()->IsExceptionPending()); } @@ -318,12 +321,19 @@ inline const DexFile* ArtField::GetDexFile() SHARED_LOCKS_REQUIRED(Locks::mutato return GetDexCache()->GetDexFile(); } -inline ArtField* ArtField::FromReflectedField(const ScopedObjectAccessAlreadyRunnable& soa, - jobject jlr_field) { - mirror::ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_reflect_Field_artField); - mirror::ArtField* field = f->GetObject(soa.Decode<mirror::Object*>(jlr_field))->AsArtField(); - DCHECK(field != nullptr); - return field; +inline String* ArtField::GetStringName(Thread* self, bool resolve) { + auto dex_field_index = GetDexFieldIndex(); + CHECK_NE(dex_field_index, DexFile::kDexNoIndex); + auto* dex_cache = GetDexCache(); + const auto* dex_file = dex_cache->GetDexFile(); + const auto& field_id = dex_file->GetFieldId(dex_field_index); + auto* name = dex_cache->GetResolvedString(field_id.name_idx_); + if (resolve && name == nullptr) { + StackHandleScope<1> hs(self); + name = Runtime::Current()->GetClassLinker()->ResolveString( + *dex_file, field_id.name_idx_, hs.NewHandle(dex_cache)); + } + return name; } } // namespace mirror diff --git a/runtime/mirror/art_field.cc b/runtime/mirror/art_field.cc index 3cea4a1b9b..4c36753724 100644 --- a/runtime/mirror/art_field.cc +++ b/runtime/mirror/art_field.cc @@ -45,7 +45,7 @@ void ArtField::ResetClass() { void ArtField::SetOffset(MemberOffset num_bytes) { DCHECK(GetDeclaringClass()->IsLoaded() || GetDeclaringClass()->IsErroneous()); if (kIsDebugBuild && Runtime::Current()->IsAotCompiler() && - !Runtime::Current()->UseCompileTimeClassPath()) { + Runtime::Current()->IsCompilingBootImage()) { Primitive::Type type = GetTypeAsPrimitiveType(); if (type == Primitive::kPrimDouble || type == Primitive::kPrimLong) { DCHECK_ALIGNED(num_bytes.Uint32Value(), 8); diff --git a/runtime/mirror/art_field.h b/runtime/mirror/art_field.h index a1d8844664..d640165aef 100644 --- a/runtime/mirror/art_field.h +++ b/runtime/mirror/art_field.h @@ -47,10 +47,6 @@ class MANAGED ArtField FINAL : public Object { return sizeof(ArtField); } - ALWAYS_INLINE static ArtField* FromReflectedField(const ScopedObjectAccessAlreadyRunnable& soa, - jobject jlr_field) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - Class* GetDeclaringClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void SetDeclaringClass(Class *new_declaring_class) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -155,13 +151,17 @@ class MANAGED ArtField FINAL : public Object { const char* GetName() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Resolves / returns the name from the dex cache. + String* GetStringName(Thread* self, bool resolve) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + const char* GetTypeDescriptor() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); Primitive::Type GetTypeAsPrimitiveType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsPrimitiveType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - Class* GetType(bool resolve) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template <bool kResolve> + Class* GetType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); size_t FieldSize() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 9fa6073698..29851a9d4f 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -228,8 +228,12 @@ void Class::DumpClass(std::ostream& os, int flags) { os << " interfaces (" << num_direct_interfaces << "):\n"; for (size_t i = 0; i < num_direct_interfaces; ++i) { Class* interface = GetDirectInterface(self, h_this, i); - const ClassLoader* cl = interface->GetClassLoader(); - os << StringPrintf(" %2zd: %s (cl=%p)\n", i, PrettyClass(interface).c_str(), cl); + if (interface == nullptr) { + os << StringPrintf(" %2zd: nullptr!\n", i); + } else { + const ClassLoader* cl = interface->GetClassLoader(); + os << StringPrintf(" %2zd: %s (cl=%p)\n", i, PrettyClass(interface).c_str(), cl); + } } } if (!IsLoaded()) { diff --git a/runtime/mirror/field-inl.h b/runtime/mirror/field-inl.h new file mode 100644 index 0000000000..24ebc481a7 --- /dev/null +++ b/runtime/mirror/field-inl.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_MIRROR_FIELD_INL_H_ +#define ART_RUNTIME_MIRROR_FIELD_INL_H_ + +#include "field.h" + +#include "art_field-inl.h" +#include "runtime-inl.h" + +namespace art { + +namespace mirror { + +template <bool kTransactionActive> +inline mirror::Field* Field::CreateFromArtField(Thread* self, mirror::ArtField* field, + bool force_resolve) { + CHECK(!kMovingFields); + // Try to resolve type before allocating since this is a thread suspension point. + mirror::Class* type = field->GetType<true>(); + + if (type == nullptr) { + if (force_resolve) { + if (kIsDebugBuild) { + self->AssertPendingException(); + } + return nullptr; + } else { + // Can't resolve, clear the exception if it isn't OOME and continue with a null type. + mirror::Throwable* exception = self->GetException(); + if (exception->GetClass()->DescriptorEquals("Ljava/lang/OutOfMemoryError;")) { + return nullptr; + } + self->ClearException(); + } + } + StackHandleScope<1> hs(self); + auto ret = hs.NewHandle(static_cast<Field*>(StaticClass()->AllocObject(self))); + if (ret.Get() == nullptr) { + if (kIsDebugBuild) { + self->AssertPendingException(); + } + return nullptr; + } + auto dex_field_index = field->GetDexFieldIndex(); + auto* resolved_field = field->GetDexCache()->GetResolvedField(dex_field_index); + if (resolved_field != nullptr) { + DCHECK_EQ(resolved_field, field); + } else { + // We rely on the field being resolved so that we can back to the ArtField + // (i.e. FromReflectedMethod). + field->GetDexCache()->SetResolvedField(dex_field_index, field); + } + ret->SetType<kTransactionActive>(type); + ret->SetDeclaringClass<kTransactionActive>(field->GetDeclaringClass()); + ret->SetAccessFlags<kTransactionActive>(field->GetAccessFlags()); + ret->SetDexFieldIndex<kTransactionActive>(dex_field_index); + ret->SetOffset<kTransactionActive>(field->GetOffset().Int32Value()); + return ret.Get(); +} + +} // namespace mirror +} // namespace art + +#endif // ART_RUNTIME_MIRROR_FIELD_INL_H_ diff --git a/runtime/mirror/field.cc b/runtime/mirror/field.cc new file mode 100644 index 0000000000..1724682f79 --- /dev/null +++ b/runtime/mirror/field.cc @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2015 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 "field-inl.h" + +#include "dex_cache-inl.h" +#include "object_array-inl.h" +#include "object-inl.h" + +namespace art { +namespace mirror { + +GcRoot<Class> Field::static_class_; +GcRoot<Class> Field::array_class_; + +void Field::SetClass(Class* klass) { + CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; + CHECK(klass != nullptr); + static_class_ = GcRoot<Class>(klass); +} + +void Field::ResetClass() { + CHECK(!static_class_.IsNull()); + static_class_ = GcRoot<Class>(nullptr); +} + +void Field::SetArrayClass(Class* klass) { + CHECK(array_class_.IsNull()) << array_class_.Read() << " " << klass; + CHECK(klass != nullptr); + array_class_ = GcRoot<Class>(klass); +} + +void Field::ResetArrayClass() { + CHECK(!array_class_.IsNull()); + array_class_ = GcRoot<Class>(nullptr); +} + +void Field::VisitRoots(RootCallback* callback, void* arg) { + static_class_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass)); + array_class_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass)); +} + +ArtField* Field::GetArtField() { + mirror::DexCache* const dex_cache = GetDeclaringClass()->GetDexCache(); + mirror::ArtField* const art_field = dex_cache->GetResolvedField(GetDexFieldIndex()); + CHECK(art_field != nullptr); + return art_field; +} + +} // namespace mirror +} // namespace art diff --git a/runtime/mirror/field.h b/runtime/mirror/field.h new file mode 100644 index 0000000000..f54340a6a4 --- /dev/null +++ b/runtime/mirror/field.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_MIRROR_FIELD_H_ +#define ART_RUNTIME_MIRROR_FIELD_H_ + +#include "accessible_object.h" +#include "gc_root.h" +#include "object.h" +#include "object_callbacks.h" +#include "read_barrier_option.h" + +namespace art { + +struct FieldOffsets; + +namespace mirror { + +class ArtField; +class Class; +class String; + +// C++ mirror of java.lang.reflect.Field. +class MANAGED Field : public AccessibleObject { + public: + static mirror::Class* StaticClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return static_class_.Read(); + } + + static mirror::Class* ArrayClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return array_class_.Read(); + } + + ALWAYS_INLINE uint32_t GetDexFieldIndex() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetField32(OFFSET_OF_OBJECT_MEMBER(Field, dex_field_index_)); + } + + mirror::Class* GetDeclaringClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetFieldObject<Class>(OFFSET_OF_OBJECT_MEMBER(Field, declaring_class_)); + } + + uint32_t GetAccessFlags() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetField32(OFFSET_OF_OBJECT_MEMBER(Field, access_flags_)); + } + + bool IsStatic() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return (GetAccessFlags() & kAccStatic) != 0; + } + + bool IsFinal() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return (GetAccessFlags() & kAccFinal) != 0; + } + + bool IsVolatile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return (GetAccessFlags() & kAccVolatile) != 0; + } + + ALWAYS_INLINE Primitive::Type GetTypeAsPrimitiveType() + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetType()->GetPrimitiveType(); + } + + mirror::Class* GetType() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetFieldObject<mirror::Class>(OFFSET_OF_OBJECT_MEMBER(Field, type_)); + } + + int32_t GetOffset() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetField32(OFFSET_OF_OBJECT_MEMBER(Field, offset_)); + } + + static void SetClass(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static void SetArrayClass(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static void ResetClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static void ResetArrayClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + static void VisitRoots(RootCallback* callback, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Slow, try to use only for PrettyField and such. + mirror::ArtField* GetArtField() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + template <bool kTransactionActive = false> + static mirror::Field* CreateFromArtField(Thread* self, mirror::ArtField* field, + bool force_resolve) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + private: + HeapReference<mirror::Class> declaring_class_; + HeapReference<mirror::Class> type_; + int32_t access_flags_; + int32_t dex_field_index_; + int32_t offset_; + + template<bool kTransactionActive> + void SetDeclaringClass(mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(Field, declaring_class_), c); + } + + template<bool kTransactionActive> + void SetType(mirror::Class* type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(Field, type_), type); + } + + template<bool kTransactionActive> + void SetAccessFlags(uint32_t flags) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(Field, access_flags_), flags); + } + + template<bool kTransactionActive> + void SetDexFieldIndex(uint32_t idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(Field, dex_field_index_), idx); + } + + template<bool kTransactionActive> + void SetOffset(uint32_t offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(Field, offset_), offset); + } + + static GcRoot<Class> static_class_; // java.lang.reflect.Field.class. + static GcRoot<Class> array_class_; // array of java.lang.reflect.Field. + + friend struct art::FieldOffsets; // for verifying offset information + DISALLOW_IMPLICIT_CONSTRUCTORS(Field); +}; + +} // namespace mirror +} // namespace art + +#endif // ART_RUNTIME_MIRROR_FIELD_H_ diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index bbbdf98be8..57ac46fec6 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -214,7 +214,7 @@ void Object::CheckFieldAssignmentImpl(MemberOffset field_offset, Object* new_val if (field->GetOffset().Int32Value() == field_offset.Int32Value()) { CHECK_NE(field->GetTypeAsPrimitiveType(), Primitive::kPrimNot); // TODO: resolve the field type for moving GC. - mirror::Class* field_type = field->GetType(!kMovingCollector); + mirror::Class* field_type = field->GetType<!kMovingCollector>(); if (field_type != nullptr) { CHECK(field_type->IsAssignableFrom(new_value->GetClass())); } @@ -236,7 +236,7 @@ void Object::CheckFieldAssignmentImpl(MemberOffset field_offset, Object* new_val if (field->GetOffset().Int32Value() == field_offset.Int32Value()) { CHECK_NE(field->GetTypeAsPrimitiveType(), Primitive::kPrimNot); // TODO: resolve the field type for moving GC. - mirror::Class* field_type = field->GetType(!kMovingCollector); + mirror::Class* field_type = field->GetType<!kMovingCollector>(); if (field_type != nullptr) { CHECK(field_type->IsAssignableFrom(new_value->GetClass())); } diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index 780c5aef42..b730670796 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -100,7 +100,7 @@ class MANAGED LOCKABLE Object { template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> bool VerifierInstanceOf(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> - bool InstanceOf(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + ALWAYS_INLINE bool InstanceOf(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, ReadBarrierOption kReadBarrierOption = kWithReadBarrier> diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index 21972a1ccf..1ce298d848 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -360,8 +360,7 @@ TEST_F(ObjectTest, StaticFieldFromCode) { // pretend we are trying to access 'Static.s0' from StaticsFromCode.<clinit> ScopedObjectAccess soa(Thread::Current()); jobject class_loader = LoadDex("StaticsFromCode"); - const DexFile* dex_file = Runtime::Current()->GetCompileTimeClassPath(class_loader)[0]; - CHECK(dex_file != NULL); + const DexFile* dex_file = GetFirstDexFile(class_loader); StackHandleScope<2> hs(soa.Self()); Handle<mirror::ClassLoader> loader(hs.NewHandle(soa.Decode<ClassLoader*>(class_loader))); diff --git a/runtime/mirror/reference.h b/runtime/mirror/reference.h index 7345448ed7..69ef69c662 100644 --- a/runtime/mirror/reference.h +++ b/runtime/mirror/reference.h @@ -99,7 +99,7 @@ class MANAGED Reference : public Object { return java_lang_ref_Reference_.Read<kReadBarrierOption>(); } static void SetClass(Class* klass); - static void ResetClass(void); + static void ResetClass(); static void VisitRoots(RootCallback* callback, void* arg); private: diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index 57ca2b1303..2724d91f6b 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -79,7 +79,9 @@ static void VMDebug_resetAllocCount(JNIEnv*, jclass, jint kinds) { static void VMDebug_startMethodTracingDdmsImpl(JNIEnv*, jclass, jint bufferSize, jint flags, jboolean samplingEnabled, jint intervalUs) { - Trace::Start("[DDMS]", -1, bufferSize, flags, true, samplingEnabled, intervalUs); + Trace::Start("[DDMS]", -1, bufferSize, flags, Trace::TraceOutputMode::kDDMS, + samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, + intervalUs); } static void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceFilename, @@ -102,7 +104,9 @@ static void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceF if (traceFilename.c_str() == NULL) { return; } - Trace::Start(traceFilename.c_str(), fd, bufferSize, flags, false, samplingEnabled, intervalUs); + Trace::Start(traceFilename.c_str(), fd, bufferSize, flags, Trace::TraceOutputMode::kFile, + samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, + intervalUs); } static void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring javaTraceFilename, @@ -112,7 +116,9 @@ static void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring java if (traceFilename.c_str() == NULL) { return; } - Trace::Start(traceFilename.c_str(), -1, bufferSize, flags, false, samplingEnabled, intervalUs); + Trace::Start(traceFilename.c_str(), -1, bufferSize, flags, Trace::TraceOutputMode::kFile, + samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, + intervalUs); } static jint VMDebug_getMethodTracingMode(JNIEnv*, jclass) { diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 60d14e9c7a..0ca9d24824 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -17,21 +17,28 @@ #include "java_lang_Class.h" #include "class_linker.h" +#include "common_throws.h" #include "dex_file-inl.h" #include "jni_internal.h" #include "nth_caller_visitor.h" +#include "mirror/art_field-inl.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" +#include "mirror/field.h" #include "mirror/object-inl.h" +#include "mirror/object_array-inl.h" +#include "mirror/string-inl.h" #include "scoped_thread_state_change.h" #include "scoped_fast_native_object_access.h" #include "ScopedLocalRef.h" #include "ScopedUtfChars.h" +#include "utf.h" #include "well_known_classes.h" namespace art { -static mirror::Class* DecodeClass(const ScopedFastNativeObjectAccess& soa, jobject java_class) +ALWAYS_INLINE static inline mirror::Class* DecodeClass( + const ScopedFastNativeObjectAccess& soa, jobject java_class) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::Class* c = soa.Decode<mirror::Class*>(java_class); DCHECK(c != NULL); @@ -97,10 +104,173 @@ static jobjectArray Class_getProxyInterfaces(JNIEnv* env, jobject javaThis) { return soa.AddLocalReference<jobjectArray>(c->GetInterfaces()->Clone(soa.Self())); } +static mirror::ObjectArray<mirror::Field>* GetDeclaredFields( + Thread* self, mirror::Class* klass, bool public_only, bool force_resolve) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + StackHandleScope<3> hs(self); + auto h_ifields = hs.NewHandle(klass->GetIFields()); + auto h_sfields = hs.NewHandle(klass->GetSFields()); + const int32_t num_ifields = h_ifields.Get() != nullptr ? h_ifields->GetLength() : 0; + const int32_t num_sfields = h_sfields.Get() != nullptr ? h_sfields->GetLength() : 0; + int32_t array_size = num_ifields + num_sfields; + if (public_only) { + // Lets go subtract all the non public fields. + for (int32_t i = 0; i < num_ifields; ++i) { + if (!h_ifields->GetWithoutChecks(i)->IsPublic()) { + --array_size; + } + } + for (int32_t i = 0; i < num_sfields; ++i) { + if (!h_sfields->GetWithoutChecks(i)->IsPublic()) { + --array_size; + } + } + } + int32_t array_idx = 0; + auto object_array = hs.NewHandle(mirror::ObjectArray<mirror::Field>::Alloc( + self, mirror::Field::ArrayClass(), array_size)); + if (object_array.Get() == nullptr) { + return nullptr; + } + for (int32_t i = 0; i < num_ifields; ++i) { + auto* art_field = h_ifields->GetWithoutChecks(i); + if (!public_only || art_field->IsPublic()) { + auto* field = mirror::Field::CreateFromArtField(self, art_field, force_resolve); + if (field == nullptr) { + if (kIsDebugBuild) { + self->AssertPendingException(); + } + // Maybe null due to OOME or type resolving exception. + return nullptr; + } + object_array->SetWithoutChecks<false>(array_idx++, field); + } + } + for (int32_t i = 0; i < num_sfields; ++i) { + auto* art_field = h_sfields->GetWithoutChecks(i); + if (!public_only || art_field->IsPublic()) { + auto* field = mirror::Field::CreateFromArtField(self, art_field, force_resolve); + if (field == nullptr) { + if (kIsDebugBuild) { + self->AssertPendingException(); + } + return nullptr; + } + object_array->SetWithoutChecks<false>(array_idx++, field); + } + } + CHECK_EQ(array_idx, array_size); + return object_array.Get(); +} + +static jobjectArray Class_getDeclaredFieldsUnchecked(JNIEnv* env, jobject javaThis, + jboolean publicOnly) { + ScopedFastNativeObjectAccess soa(env); + return soa.AddLocalReference<jobjectArray>( + GetDeclaredFields(soa.Self(), DecodeClass(soa, javaThis), publicOnly != JNI_FALSE, false)); +} + +static jobjectArray Class_getDeclaredFields(JNIEnv* env, jobject javaThis) { + ScopedFastNativeObjectAccess soa(env); + return soa.AddLocalReference<jobjectArray>( + GetDeclaredFields(soa.Self(), DecodeClass(soa, javaThis), false, true)); +} + +static jobjectArray Class_getPublicDeclaredFields(JNIEnv* env, jobject javaThis) { + ScopedFastNativeObjectAccess soa(env); + return soa.AddLocalReference<jobjectArray>( + GetDeclaredFields(soa.Self(), DecodeClass(soa, javaThis), true, true)); +} + +// Performs a binary search through an array of fields, TODO: Is this fast enough if we don't use +// the dex cache for lookups? I think CompareModifiedUtf8ToUtf16AsCodePointValues should be fairly +// fast. +ALWAYS_INLINE static inline mirror::ArtField* FindFieldByName( + Thread* self ATTRIBUTE_UNUSED, mirror::String* name, + mirror::ObjectArray<mirror::ArtField>* fields) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + uint32_t low = 0; + uint32_t high = fields->GetLength(); + const uint16_t* const data = name->GetCharArray()->GetData() + name->GetOffset(); + const size_t length = name->GetLength(); + while (low < high) { + auto mid = (low + high) / 2; + mirror::ArtField* const field = fields->GetWithoutChecks(mid); + int result = CompareModifiedUtf8ToUtf16AsCodePointValues(field->GetName(), data, length); + // Alternate approach, only a few % faster at the cost of more allocations. + // int result = field->GetStringName(self, true)->CompareTo(name); + if (result < 0) { + low = mid + 1; + } else if (result > 0) { + high = mid; + } else { + return field; + } + } + if (kIsDebugBuild) { + for (int32_t i = 0; i < fields->GetLength(); ++i) { + CHECK_NE(fields->GetWithoutChecks(i)->GetName(), name->ToModifiedUtf8()); + } + } + return nullptr; +} + +ALWAYS_INLINE static inline mirror::Field* GetDeclaredField( + Thread* self, mirror::Class* c, mirror::String* name) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + auto* instance_fields = c->GetIFields(); + if (instance_fields != nullptr) { + auto* art_field = FindFieldByName(self, name, instance_fields); + if (art_field != nullptr) { + return mirror::Field::CreateFromArtField(self, art_field, true); + } + } + auto* static_fields = c->GetSFields(); + if (static_fields != nullptr) { + auto* art_field = FindFieldByName(self, name, static_fields); + if (art_field != nullptr) { + return mirror::Field::CreateFromArtField(self, art_field, true); + } + } + return nullptr; +} + +static jobject Class_getDeclaredFieldInternal(JNIEnv* env, jobject javaThis, jstring name) { + ScopedFastNativeObjectAccess soa(env); + auto* name_string = soa.Decode<mirror::String*>(name); + return soa.AddLocalReference<jobject>( + GetDeclaredField(soa.Self(), DecodeClass(soa, javaThis), name_string)); +} + +static jobject Class_getDeclaredField(JNIEnv* env, jobject javaThis, jstring name) { + ScopedFastNativeObjectAccess soa(env); + auto* name_string = soa.Decode<mirror::String*>(name); + if (name == nullptr) { + ThrowNullPointerException("name == null"); + return nullptr; + } + auto* klass = DecodeClass(soa, javaThis); + mirror::Field* result = GetDeclaredField(soa.Self(), klass, name_string); + if (result == nullptr) { + std::string name_str = name_string->ToModifiedUtf8(); + // We may have a pending exception if we failed to resolve. + if (!soa.Self()->IsExceptionPending()) { + soa.Self()->ThrowNewException("Ljava/lang/NoSuchFieldException;", name_str.c_str()); + } + return nullptr; + } + return soa.AddLocalReference<jobject>(result); +} + static JNINativeMethod gMethods[] = { NATIVE_METHOD(Class, classForName, "!(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"), NATIVE_METHOD(Class, getNameNative, "!()Ljava/lang/String;"), NATIVE_METHOD(Class, getProxyInterfaces, "!()[Ljava/lang/Class;"), + NATIVE_METHOD(Class, getDeclaredFields, "!()[Ljava/lang/reflect/Field;"), + NATIVE_METHOD(Class, getPublicDeclaredFields, "!()[Ljava/lang/reflect/Field;"), + NATIVE_METHOD(Class, getDeclaredFieldsUnchecked, "!(Z)[Ljava/lang/reflect/Field;"), + NATIVE_METHOD(Class, getDeclaredFieldInternal, "!(Ljava/lang/String;)Ljava/lang/reflect/Field;"), + NATIVE_METHOD(Class, getDeclaredField, "!(Ljava/lang/String;)Ljava/lang/reflect/Field;"), }; void register_java_lang_Class(JNIEnv* env) { diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc index 9c5bde9c1e..721b7a3b76 100644 --- a/runtime/native/java_lang_reflect_Field.cc +++ b/runtime/native/java_lang_reflect_Field.cc @@ -21,35 +21,35 @@ #include "common_throws.h" #include "dex_file-inl.h" #include "jni_internal.h" -#include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" +#include "mirror/field.h" #include "reflection-inl.h" #include "scoped_fast_native_object_access.h" namespace art { template<bool kIsSet> -ALWAYS_INLINE inline static bool VerifyFieldAccess(Thread* self, mirror::ArtField* field, +ALWAYS_INLINE inline static bool VerifyFieldAccess(Thread* self, mirror::Field* field, mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (kIsSet && field->IsFinal()) { ThrowIllegalAccessException( StringPrintf("Cannot set %s field %s of class %s", PrettyJavaAccessFlags(field->GetAccessFlags()).c_str(), - PrettyField(field).c_str(), + PrettyField(field->GetArtField()).c_str(), field->GetDeclaringClass() == nullptr ? "null" : PrettyClass(field->GetDeclaringClass()).c_str()).c_str()); return false; } mirror::Class* calling_class = nullptr; if (!VerifyAccess(self, obj, field->GetDeclaringClass(), field->GetAccessFlags(), - &calling_class)) { + &calling_class, 1)) { ThrowIllegalAccessException( StringPrintf("Class %s cannot access %s field %s of class %s", calling_class == nullptr ? "null" : PrettyClass(calling_class).c_str(), PrettyJavaAccessFlags(field->GetAccessFlags()).c_str(), - PrettyField(field).c_str(), + PrettyField(field->GetArtField()).c_str(), field->GetDeclaringClass() == nullptr ? "null" : PrettyClass(field->GetDeclaringClass()).c_str()).c_str()); return false; @@ -58,38 +58,37 @@ ALWAYS_INLINE inline static bool VerifyFieldAccess(Thread* self, mirror::ArtFiel } template<bool kAllowReferences> -ALWAYS_INLINE inline static bool GetFieldValue(mirror::Object* o, mirror::ArtField* f, +ALWAYS_INLINE inline static bool GetFieldValue(mirror::Object* o, mirror::Field* f, Primitive::Type field_type, JValue* value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK_EQ(value->GetJ(), INT64_C(0)); + MemberOffset offset(f->GetOffset()); + const bool is_volatile = f->IsVolatile(); switch (field_type) { case Primitive::kPrimBoolean: - value->SetZ(f->GetBoolean(o)); + value->SetZ(is_volatile ? o->GetFieldBooleanVolatile(offset) : o->GetFieldBoolean(offset)); return true; case Primitive::kPrimByte: - value->SetB(f->GetByte(o)); + value->SetB(is_volatile ? o->GetFieldByteVolatile(offset) : o->GetFieldByte(offset)); return true; case Primitive::kPrimChar: - value->SetC(f->GetChar(o)); - return true; - case Primitive::kPrimDouble: - value->SetD(f->GetDouble(o)); - return true; - case Primitive::kPrimFloat: - value->SetF(f->GetFloat(o)); + value->SetC(is_volatile ? o->GetFieldCharVolatile(offset) : o->GetFieldChar(offset)); return true; case Primitive::kPrimInt: - value->SetI(f->GetInt(o)); + case Primitive::kPrimFloat: + value->SetI(is_volatile ? o->GetField32Volatile(offset) : o->GetField32(offset)); return true; case Primitive::kPrimLong: - value->SetJ(f->GetLong(o)); + case Primitive::kPrimDouble: + value->SetJ(is_volatile ? o->GetField64Volatile(offset) : o->GetField64(offset)); return true; case Primitive::kPrimShort: - value->SetS(f->GetShort(o)); + value->SetS(is_volatile ? o->GetFieldShortVolatile(offset) : o->GetFieldShort(offset)); return true; case Primitive::kPrimNot: if (kAllowReferences) { - value->SetL(f->GetObject(o)); + value->SetL(is_volatile ? o->GetFieldObjectVolatile<mirror::Object>(offset) : + o->GetFieldObject<mirror::Object>(offset)); return true; } // Else break to report an error. @@ -98,23 +97,23 @@ ALWAYS_INLINE inline static bool GetFieldValue(mirror::Object* o, mirror::ArtFie // Never okay. break; } - ThrowIllegalArgumentException(StringPrintf("Not a primitive field: %s", - PrettyField(f).c_str()).c_str()); + ThrowIllegalArgumentException( + StringPrintf("Not a primitive field: %s", PrettyField(f->GetArtField()).c_str()).c_str()); return false; } ALWAYS_INLINE inline static bool CheckReceiver(const ScopedFastNativeObjectAccess& soa, - jobject j_rcvr, mirror::ArtField** f, + jobject j_rcvr, mirror::Field** f, mirror::Object** class_or_rcvr) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { soa.Self()->AssertThreadSuspensionIsAllowable(); mirror::Class* declaringClass = (*f)->GetDeclaringClass(); if ((*f)->IsStatic()) { if (UNLIKELY(!declaringClass->IsInitialized())) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); StackHandleScope<2> hs(soa.Self()); - HandleWrapper<mirror::ArtField> h_f(hs.NewHandleWrapper(f)); + HandleWrapper<mirror::Field> h_f(hs.NewHandleWrapper(f)); HandleWrapper<mirror::Class> h_klass(hs.NewHandleWrapper(&declaringClass)); + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); if (UNLIKELY(!class_linker->EnsureInitialized(soa.Self(), h_klass, true, true))) { DCHECK(soa.Self()->IsExceptionPending()); return false; @@ -131,16 +130,16 @@ ALWAYS_INLINE inline static bool CheckReceiver(const ScopedFastNativeObjectAcces return true; } -static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) { +static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj) { ScopedFastNativeObjectAccess soa(env); - mirror::ArtField* f = mirror::ArtField::FromReflectedField(soa, javaField); + mirror::Field* f = soa.Decode<mirror::Field*>(javaField); mirror::Object* o = nullptr; if (!CheckReceiver(soa, javaObj, &f, &o)) { DCHECK(soa.Self()->IsExceptionPending()); return nullptr; } // If field is not set to be accessible, verify it can be accessed by the caller. - if ((accessible == JNI_FALSE) && !VerifyFieldAccess<false>(soa.Self(), f, o)) { + if (!f->IsAccessible() && !VerifyFieldAccess<false>(soa.Self(), f, o)) { DCHECK(soa.Self()->IsExceptionPending()); return nullptr; } @@ -157,9 +156,9 @@ static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj, jboole template<Primitive::Type kPrimitiveType> ALWAYS_INLINE inline static JValue GetPrimitiveField(JNIEnv* env, jobject javaField, - jobject javaObj, jboolean accessible) { + jobject javaObj) { ScopedFastNativeObjectAccess soa(env); - mirror::ArtField* f = mirror::ArtField::FromReflectedField(soa, javaField); + mirror::Field* f = soa.Decode<mirror::Field*>(javaField); mirror::Object* o = nullptr; if (!CheckReceiver(soa, javaObj, &f, &o)) { DCHECK(soa.Self()->IsExceptionPending()); @@ -167,7 +166,7 @@ ALWAYS_INLINE inline static JValue GetPrimitiveField(JNIEnv* env, jobject javaFi } // If field is not set to be accessible, verify it can be accessed by the caller. - if (accessible == JNI_FALSE && !VerifyFieldAccess<false>(soa.Self(), f, o)) { + if (!f->IsAccessible() && !VerifyFieldAccess<false>(soa.Self(), f, o)) { DCHECK(soa.Self()->IsExceptionPending()); return JValue(); } @@ -198,72 +197,97 @@ ALWAYS_INLINE inline static JValue GetPrimitiveField(JNIEnv* env, jobject javaFi return wide_value; } -static jboolean Field_getBoolean(JNIEnv* env, jobject javaField, jobject javaObj, - jboolean accessible) { - return GetPrimitiveField<Primitive::kPrimBoolean>(env, javaField, javaObj, accessible).GetZ(); +static jboolean Field_getBoolean(JNIEnv* env, jobject javaField, jobject javaObj) { + return GetPrimitiveField<Primitive::kPrimBoolean>(env, javaField, javaObj).GetZ(); } -static jbyte Field_getByte(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) { - return GetPrimitiveField<Primitive::kPrimByte>(env, javaField, javaObj, accessible).GetB(); +static jbyte Field_getByte(JNIEnv* env, jobject javaField, jobject javaObj) { + return GetPrimitiveField<Primitive::kPrimByte>(env, javaField, javaObj).GetB(); } -static jchar Field_getChar(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) { - return GetPrimitiveField<Primitive::kPrimChar>(env, javaField, javaObj, accessible).GetC(); +static jchar Field_getChar(JNIEnv* env, jobject javaField, jobject javaObj) { + return GetPrimitiveField<Primitive::kPrimChar>(env, javaField, javaObj).GetC(); } -static jdouble Field_getDouble(JNIEnv* env, jobject javaField, jobject javaObj, - jboolean accessible) { - return GetPrimitiveField<Primitive::kPrimDouble>(env, javaField, javaObj, accessible).GetD(); +static jdouble Field_getDouble(JNIEnv* env, jobject javaField, jobject javaObj) { + return GetPrimitiveField<Primitive::kPrimDouble>(env, javaField, javaObj).GetD(); } -static jfloat Field_getFloat(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) { - return GetPrimitiveField<Primitive::kPrimFloat>(env, javaField, javaObj, accessible).GetF(); +static jfloat Field_getFloat(JNIEnv* env, jobject javaField, jobject javaObj) { + return GetPrimitiveField<Primitive::kPrimFloat>(env, javaField, javaObj).GetF(); } -static jint Field_getInt(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) { - return GetPrimitiveField<Primitive::kPrimInt>(env, javaField, javaObj, accessible).GetI(); +static jint Field_getInt(JNIEnv* env, jobject javaField, jobject javaObj) { + return GetPrimitiveField<Primitive::kPrimInt>(env, javaField, javaObj).GetI(); } -static jlong Field_getLong(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) { - return GetPrimitiveField<Primitive::kPrimLong>(env, javaField, javaObj, accessible).GetJ(); +static jlong Field_getLong(JNIEnv* env, jobject javaField, jobject javaObj) { + return GetPrimitiveField<Primitive::kPrimLong>(env, javaField, javaObj).GetJ(); } -static jshort Field_getShort(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) { - return GetPrimitiveField<Primitive::kPrimShort>(env, javaField, javaObj, accessible).GetS(); +static jshort Field_getShort(JNIEnv* env, jobject javaField, jobject javaObj) { + return GetPrimitiveField<Primitive::kPrimShort>(env, javaField, javaObj).GetS(); } -static void SetFieldValue(mirror::Object* o, mirror::ArtField* f, Primitive::Type field_type, - bool allow_references, const JValue& new_value) +ALWAYS_INLINE inline static void SetFieldValue(mirror::Object* o, mirror::Field* f, + Primitive::Type field_type, bool allow_references, + const JValue& new_value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(f->GetDeclaringClass()->IsInitialized()); + MemberOffset offset(f->GetOffset()); + const bool is_volatile = f->IsVolatile(); switch (field_type) { case Primitive::kPrimBoolean: - f->SetBoolean<false>(o, new_value.GetZ()); + if (is_volatile) { + o->SetFieldBooleanVolatile<false>(offset, new_value.GetZ()); + } else { + o->SetFieldBoolean<false>(offset, new_value.GetZ()); + } break; case Primitive::kPrimByte: - f->SetByte<false>(o, new_value.GetB()); + if (is_volatile) { + o->SetFieldBooleanVolatile<false>(offset, new_value.GetB()); + } else { + o->SetFieldBoolean<false>(offset, new_value.GetB()); + } break; case Primitive::kPrimChar: - f->SetChar<false>(o, new_value.GetC()); - break; - case Primitive::kPrimDouble: - f->SetDouble<false>(o, new_value.GetD()); - break; - case Primitive::kPrimFloat: - f->SetFloat<false>(o, new_value.GetF()); + if (is_volatile) { + o->SetFieldBooleanVolatile<false>(offset, new_value.GetC()); + } else { + o->SetFieldBoolean<false>(offset, new_value.GetC()); + } break; case Primitive::kPrimInt: - f->SetInt<false>(o, new_value.GetI()); + case Primitive::kPrimFloat: + if (is_volatile) { + o->SetField32Volatile<false>(offset, new_value.GetI()); + } else { + o->SetField32<false>(offset, new_value.GetI()); + } break; case Primitive::kPrimLong: - f->SetLong<false>(o, new_value.GetJ()); + case Primitive::kPrimDouble: + if (is_volatile) { + o->SetField64Volatile<false>(offset, new_value.GetJ()); + } else { + o->SetField64<false>(offset, new_value.GetJ()); + } break; case Primitive::kPrimShort: - f->SetShort<false>(o, new_value.GetS()); + if (is_volatile) { + o->SetFieldShortVolatile<false>(offset, new_value.GetS()); + } else { + o->SetFieldShort<false>(offset, new_value.GetS()); + } break; case Primitive::kPrimNot: if (allow_references) { - f->SetObject<false>(o, new_value.GetL()); + if (is_volatile) { + o->SetFieldObjectVolatile<false>(offset, new_value.GetL()); + } else { + o->SetFieldObject<false>(offset, new_value.GetL()); + } break; } // Else fall through to report an error. @@ -271,15 +295,14 @@ static void SetFieldValue(mirror::Object* o, mirror::ArtField* f, Primitive::Typ case Primitive::kPrimVoid: // Never okay. ThrowIllegalArgumentException(StringPrintf("Not a primitive field: %s", - PrettyField(f).c_str()).c_str()); + PrettyField(f->GetArtField()).c_str()).c_str()); return; } } -static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject javaValue, - jboolean accessible) { +static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject javaValue) { ScopedFastNativeObjectAccess soa(env); - mirror::ArtField* f = mirror::ArtField::FromReflectedField(soa, javaField); + mirror::Field* f = soa.Decode<mirror::Field*>(javaField); // Check that the receiver is non-null and an instance of the field's declaring class. mirror::Object* o = nullptr; if (!CheckReceiver(soa, javaObj, &f, &o)) { @@ -287,18 +310,11 @@ static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject j return; } mirror::Class* field_type; - const char* field_type_desciptor = f->GetTypeDescriptor(); + const char* field_type_desciptor = f->GetArtField()->GetTypeDescriptor(); Primitive::Type field_prim_type = Primitive::GetType(field_type_desciptor[0]); if (field_prim_type == Primitive::kPrimNot) { - StackHandleScope<2> hs(soa.Self()); - HandleWrapper<mirror::Object> h_o(hs.NewHandleWrapper(&o)); - HandleWrapper<mirror::ArtField> h_f(hs.NewHandleWrapper(&f)); - // May cause resolution. - field_type = h_f->GetType(true); - if (field_type == nullptr) { - DCHECK(soa.Self()->IsExceptionPending()); - return; - } + field_type = f->GetType(); + DCHECK(field_type != nullptr); } else { field_type = Runtime::Current()->GetClassLinker()->FindPrimitiveClass(field_type_desciptor[0]); } @@ -306,12 +322,12 @@ static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject j // Unbox the value, if necessary. mirror::Object* boxed_value = soa.Decode<mirror::Object*>(javaValue); JValue unboxed_value; - if (!UnboxPrimitiveForField(boxed_value, field_type, f, &unboxed_value)) { + if (!UnboxPrimitiveForField(boxed_value, field_type, f->GetArtField(), &unboxed_value)) { DCHECK(soa.Self()->IsExceptionPending()); return; } // If field is not set to be accessible, verify it can be accessed by the caller. - if ((accessible == JNI_FALSE) && !VerifyFieldAccess<true>(soa.Self(), f, o)) { + if (!f->IsAccessible() && !VerifyFieldAccess<true>(soa.Self(), f, o)) { DCHECK(soa.Self()->IsExceptionPending()); return; } @@ -320,9 +336,9 @@ static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject j template<Primitive::Type kPrimitiveType> static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, - const JValue& new_value, jboolean accessible) { + const JValue& new_value) { ScopedFastNativeObjectAccess soa(env); - mirror::ArtField* f = mirror::ArtField::FromReflectedField(soa, javaField); + mirror::Field* f = soa.Decode<mirror::Field*>(javaField); mirror::Object* o = nullptr; if (!CheckReceiver(soa, javaObj, &f, &o)) { return; @@ -330,7 +346,7 @@ static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, Primitive::Type field_type = f->GetTypeAsPrimitiveType(); if (UNLIKELY(field_type == Primitive::kPrimNot)) { ThrowIllegalArgumentException(StringPrintf("Not a primitive field: %s", - PrettyField(f).c_str()).c_str()); + PrettyField(f->GetArtField()).c_str()).c_str()); return; } @@ -342,7 +358,7 @@ static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, } // If field is not set to be accessible, verify it can be accessed by the caller. - if ((accessible == JNI_FALSE) && !VerifyFieldAccess<true>(soa.Self(), f, o)) { + if (!f->IsAccessible() && !VerifyFieldAccess<true>(soa.Self(), f, o)) { DCHECK(soa.Self()->IsExceptionPending()); return; } @@ -351,81 +367,73 @@ static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, SetFieldValue(o, f, field_type, false, wide_value); } -static void Field_setBoolean(JNIEnv* env, jobject javaField, jobject javaObj, jboolean z, - jboolean accessible) { +static void Field_setBoolean(JNIEnv* env, jobject javaField, jobject javaObj, jboolean z) { JValue value; value.SetZ(z); - SetPrimitiveField<Primitive::kPrimBoolean>(env, javaField, javaObj, value, accessible); + SetPrimitiveField<Primitive::kPrimBoolean>(env, javaField, javaObj, value); } -static void Field_setByte(JNIEnv* env, jobject javaField, jobject javaObj, jbyte b, - jboolean accessible) { +static void Field_setByte(JNIEnv* env, jobject javaField, jobject javaObj, jbyte b) { JValue value; value.SetB(b); - SetPrimitiveField<Primitive::kPrimByte>(env, javaField, javaObj, value, accessible); + SetPrimitiveField<Primitive::kPrimByte>(env, javaField, javaObj, value); } -static void Field_setChar(JNIEnv* env, jobject javaField, jobject javaObj, jchar c, - jboolean accessible) { +static void Field_setChar(JNIEnv* env, jobject javaField, jobject javaObj, jchar c) { JValue value; value.SetC(c); - SetPrimitiveField<Primitive::kPrimChar>(env, javaField, javaObj, value, accessible); + SetPrimitiveField<Primitive::kPrimChar>(env, javaField, javaObj, value); } -static void Field_setDouble(JNIEnv* env, jobject javaField, jobject javaObj, jdouble d, - jboolean accessible) { +static void Field_setDouble(JNIEnv* env, jobject javaField, jobject javaObj, jdouble d) { JValue value; value.SetD(d); - SetPrimitiveField<Primitive::kPrimDouble>(env, javaField, javaObj, value, accessible); + SetPrimitiveField<Primitive::kPrimDouble>(env, javaField, javaObj, value); } -static void Field_setFloat(JNIEnv* env, jobject javaField, jobject javaObj, jfloat f, - jboolean accessible) { +static void Field_setFloat(JNIEnv* env, jobject javaField, jobject javaObj, jfloat f) { JValue value; value.SetF(f); - SetPrimitiveField<Primitive::kPrimFloat>(env, javaField, javaObj, value, accessible); + SetPrimitiveField<Primitive::kPrimFloat>(env, javaField, javaObj, value); } -static void Field_setInt(JNIEnv* env, jobject javaField, jobject javaObj, jint i, - jboolean accessible) { +static void Field_setInt(JNIEnv* env, jobject javaField, jobject javaObj, jint i) { JValue value; value.SetI(i); - SetPrimitiveField<Primitive::kPrimInt>(env, javaField, javaObj, value, accessible); + SetPrimitiveField<Primitive::kPrimInt>(env, javaField, javaObj, value); } -static void Field_setLong(JNIEnv* env, jobject javaField, jobject javaObj, jlong j, - jboolean accessible) { +static void Field_setLong(JNIEnv* env, jobject javaField, jobject javaObj, jlong j) { JValue value; value.SetJ(j); - SetPrimitiveField<Primitive::kPrimLong>(env, javaField, javaObj, value, accessible); + SetPrimitiveField<Primitive::kPrimLong>(env, javaField, javaObj, value); } -static void Field_setShort(JNIEnv* env, jobject javaField, jobject javaObj, jshort s, - jboolean accessible) { +static void Field_setShort(JNIEnv* env, jobject javaField, jobject javaObj, jshort s) { JValue value; value.SetS(s); - SetPrimitiveField<Primitive::kPrimShort>(env, javaField, javaObj, value, accessible); + SetPrimitiveField<Primitive::kPrimShort>(env, javaField, javaObj, value); } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(Field, get, "!(Ljava/lang/Object;Z)Ljava/lang/Object;"), - NATIVE_METHOD(Field, getBoolean, "!(Ljava/lang/Object;Z)Z"), - NATIVE_METHOD(Field, getByte, "!(Ljava/lang/Object;Z)B"), - NATIVE_METHOD(Field, getChar, "!(Ljava/lang/Object;Z)C"), - NATIVE_METHOD(Field, getDouble, "!(Ljava/lang/Object;Z)D"), - NATIVE_METHOD(Field, getFloat, "!(Ljava/lang/Object;Z)F"), - NATIVE_METHOD(Field, getInt, "!(Ljava/lang/Object;Z)I"), - NATIVE_METHOD(Field, getLong, "!(Ljava/lang/Object;Z)J"), - NATIVE_METHOD(Field, getShort, "!(Ljava/lang/Object;Z)S"), - NATIVE_METHOD(Field, set, "!(Ljava/lang/Object;Ljava/lang/Object;Z)V"), - NATIVE_METHOD(Field, setBoolean, "!(Ljava/lang/Object;ZZ)V"), - NATIVE_METHOD(Field, setByte, "!(Ljava/lang/Object;BZ)V"), - NATIVE_METHOD(Field, setChar, "!(Ljava/lang/Object;CZ)V"), - NATIVE_METHOD(Field, setDouble, "!(Ljava/lang/Object;DZ)V"), - NATIVE_METHOD(Field, setFloat, "!(Ljava/lang/Object;FZ)V"), - NATIVE_METHOD(Field, setInt, "!(Ljava/lang/Object;IZ)V"), - NATIVE_METHOD(Field, setLong, "!(Ljava/lang/Object;JZ)V"), - NATIVE_METHOD(Field, setShort, "!(Ljava/lang/Object;SZ)V"), + NATIVE_METHOD(Field, get, "!(Ljava/lang/Object;)Ljava/lang/Object;"), + NATIVE_METHOD(Field, getBoolean, "!(Ljava/lang/Object;)Z"), + NATIVE_METHOD(Field, getByte, "!(Ljava/lang/Object;)B"), + NATIVE_METHOD(Field, getChar, "!(Ljava/lang/Object;)C"), + NATIVE_METHOD(Field, getDouble, "!(Ljava/lang/Object;)D"), + NATIVE_METHOD(Field, getFloat, "!(Ljava/lang/Object;)F"), + NATIVE_METHOD(Field, getInt, "!(Ljava/lang/Object;)I"), + NATIVE_METHOD(Field, getLong, "!(Ljava/lang/Object;)J"), + NATIVE_METHOD(Field, getShort, "!(Ljava/lang/Object;)S"), + NATIVE_METHOD(Field, set, "!(Ljava/lang/Object;Ljava/lang/Object;)V"), + NATIVE_METHOD(Field, setBoolean, "!(Ljava/lang/Object;Z)V"), + NATIVE_METHOD(Field, setByte, "!(Ljava/lang/Object;B)V"), + NATIVE_METHOD(Field, setChar, "!(Ljava/lang/Object;C)V"), + NATIVE_METHOD(Field, setDouble, "!(Ljava/lang/Object;D)V"), + NATIVE_METHOD(Field, setFloat, "!(Ljava/lang/Object;F)V"), + NATIVE_METHOD(Field, setInt, "!(Ljava/lang/Object;I)V"), + NATIVE_METHOD(Field, setLong, "!(Ljava/lang/Object;J)V"), + NATIVE_METHOD(Field, setShort, "!(Ljava/lang/Object;S)V"), }; void register_java_lang_reflect_Field(JNIEnv* env) { diff --git a/runtime/noop_compiler_callbacks.h b/runtime/noop_compiler_callbacks.h index 300abc9f7a..1cbf2bbda4 100644 --- a/runtime/noop_compiler_callbacks.h +++ b/runtime/noop_compiler_callbacks.h @@ -23,7 +23,7 @@ namespace art { class NoopCompilerCallbacks FINAL : public CompilerCallbacks { public: - NoopCompilerCallbacks() {} + NoopCompilerCallbacks() : CompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileApp) {} ~NoopCompilerCallbacks() {} bool MethodVerified(verifier::MethodVerifier* verifier ATTRIBUTE_UNUSED) OVERRIDE { diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 9a17b01c02..d92f59bde6 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -737,18 +737,19 @@ bool OatFileAssistant::DexFilenameToOdexFilename(const std::string& location, CHECK(error_msg != nullptr); // The odex file name is formed by replacing the dex_location extension with - // .odex and inserting an isa directory. For example: + // .odex and inserting an oat/<isa> directory. For example: // location = /foo/bar/baz.jar - // odex_location = /foo/bar/<isa>/baz.odex + // odex_location = /foo/bar/oat/<isa>/baz.odex - // Find the directory portion of the dex location and add the isa directory. + // Find the directory portion of the dex location and add the oat/<isa> + // directory. size_t pos = location.rfind('/'); if (pos == std::string::npos) { *error_msg = "Dex location " + location + " has no directory."; return false; } std::string dir = location.substr(0, pos+1); - dir += std::string(GetInstructionSetString(isa)); + dir += "oat/" + std::string(GetInstructionSetString(isa)); // Find the file portion of the dex location. std::string file; diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 958b44048d..f2abcf99d3 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -151,11 +151,12 @@ class OatFileAssistant { static std::vector<std::unique_ptr<const DexFile>> LoadDexFiles( const OatFile& oat_file, const char* dex_location); - // If the dex file has been pre-compiled on the host, the compiled oat file - // will have the extension .odex, and is referred to as the odex file. - // It is called odex for legacy reasons; the file is really an oat file. The - // odex file will typically have a patch delta of 0 and need to be relocated - // before use for the purposes of ASLR. + // If the dex file has been installed with a compiled oat file alongside + // it, the compiled oat file will have the extension .odex, and is referred + // to as the odex file. It is called odex for legacy reasons; the file is + // really an oat file. The odex file will often, but not always, have a + // patch delta of 0 and need to be relocated before use for the purposes of + // ASLR. The odex file is treated as if it were read-only. // These methods return the location and status of the odex file for the dex // location. // Notes: diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 41dc2d7206..0422fcda1a 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -27,6 +27,7 @@ #include "class_linker.h" #include "common_runtime_test.h" +#include "compiler_callbacks.h" #include "mem_map.h" #include "os.h" #include "thread-inl.h" @@ -44,10 +45,13 @@ class OatFileAssistantTest : public CommonRuntimeTest { scratch_dir_ = android_data_ + "/OatFileAssistantTest"; ASSERT_EQ(0, mkdir(scratch_dir_.c_str(), 0700)); - // Create a subdirectory in scratch for the current isa. - // This is the location that will be used for odex files in the tests. - isa_dir_ = scratch_dir_ + "/" + GetInstructionSetString(kRuntimeISA); - ASSERT_EQ(0, mkdir(isa_dir_.c_str(), 0700)); + // Create a subdirectory in scratch for odex files. + odex_oat_dir_ = scratch_dir_ + "/oat"; + ASSERT_EQ(0, mkdir(odex_oat_dir_.c_str(), 0700)); + + odex_dir_ = odex_oat_dir_ + "/" + std::string(GetInstructionSetString(kRuntimeISA)); + ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700)); + // Verify the environment is as we expect uint32_t checksum; @@ -74,11 +78,7 @@ class OatFileAssistantTest : public CommonRuntimeTest { nullptr)); // Make sure compilercallbacks are not set so that relocation will be // enabled. - for (std::pair<std::string, const void*>& pair : *options) { - if (pair.first == "compilercallbacks") { - pair.second = nullptr; - } - } + callbacks_.reset(); } virtual void PreRuntimeCreate() { @@ -90,8 +90,11 @@ class OatFileAssistantTest : public CommonRuntimeTest { } virtual void TearDown() { - ClearDirectory(isa_dir_.c_str()); - ASSERT_EQ(0, rmdir(isa_dir_.c_str())); + ClearDirectory(odex_dir_.c_str()); + ASSERT_EQ(0, rmdir(odex_dir_.c_str())); + + ClearDirectory(odex_oat_dir_.c_str()); + ASSERT_EQ(0, rmdir(odex_oat_dir_.c_str())); ClearDirectory(scratch_dir_.c_str()); ASSERT_EQ(0, rmdir(scratch_dir_.c_str())); @@ -153,10 +156,10 @@ class OatFileAssistantTest : public CommonRuntimeTest { return scratch_dir_; } - // ISA directory is the subdirectory in the scratch directory where odex + // Odex directory is the subdirectory in the scratch directory where odex // files should be located. - std::string GetISADir() { - return isa_dir_; + std::string GetOdexDir() { + return odex_dir_; } // Generate an odex file for the purposes of test. @@ -241,7 +244,8 @@ class OatFileAssistantTest : public CommonRuntimeTest { } std::string scratch_dir_; - std::string isa_dir_; + std::string odex_oat_dir_; + std::string odex_dir_; std::vector<std::unique_ptr<MemMap>> image_reservation_; }; @@ -340,7 +344,7 @@ TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) { // Expect: The oat file status is kUpToDate. TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) { std::string dex_location = GetScratchDir() + "/RelativeEncodedDexLocation.jar"; - std::string oat_location = GetISADir() + "/RelativeEncodedDexLocation.oat"; + std::string oat_location = GetOdexDir() + "/RelativeEncodedDexLocation.oat"; // Create the dex file Copy(GetMultiDexSrc1(), dex_location); @@ -393,7 +397,7 @@ TEST_F(OatFileAssistantTest, OatOutOfDate) { // Expect: The oat file status is kNeedsRelocation. TEST_F(OatFileAssistantTest, DexOdexNoOat) { std::string dex_location = GetScratchDir() + "/DexOdexNoOat.jar"; - std::string odex_location = GetISADir() + "/DexOdexNoOat.odex"; + std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex"; // Create the dex and odex files Copy(GetDexSrc1(), dex_location); @@ -419,7 +423,7 @@ TEST_F(OatFileAssistantTest, DexOdexNoOat) { // Expect: The oat file status is kNeedsRelocation. TEST_F(OatFileAssistantTest, StrippedDexOdexNoOat) { std::string dex_location = GetScratchDir() + "/StrippedDexOdexNoOat.jar"; - std::string odex_location = GetISADir() + "/StrippedDexOdexNoOat.odex"; + std::string odex_location = GetOdexDir() + "/StrippedDexOdexNoOat.odex"; // Create the dex and odex files Copy(GetDexSrc1(), dex_location); @@ -468,7 +472,7 @@ TEST_F(OatFileAssistantTest, StrippedDexOdexNoOat) { // Expect: The oat file status is kNeedsRelocation. TEST_F(OatFileAssistantTest, StrippedDexOdexOat) { std::string dex_location = GetScratchDir() + "/StrippedDexOdexOat.jar"; - std::string odex_location = GetISADir() + "/StrippedDexOdexOat.odex"; + std::string odex_location = GetOdexDir() + "/StrippedDexOdexOat.odex"; // Create the oat file from a different dex file so it looks out of date. Copy(GetDexSrc2(), dex_location); @@ -525,8 +529,8 @@ TEST_F(OatFileAssistantTest, StrippedDexOdexOat) { // Expect: It shouldn't crash. TEST_F(OatFileAssistantTest, OdexOatOverlap) { std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar"; - std::string odex_location = GetISADir() + "/OdexOatOverlap.odex"; - std::string oat_location = GetISADir() + "/OdexOatOverlap.oat"; + std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex"; + std::string oat_location = GetOdexDir() + "/OdexOatOverlap.oat"; // Create the dex and odex files Copy(GetDexSrc1(), dex_location); @@ -563,7 +567,7 @@ TEST_F(OatFileAssistantTest, OdexOatOverlap) { // Expect: The oat file status is kUpToDate, because PIC needs no relocation. TEST_F(OatFileAssistantTest, DexPicOdexNoOat) { std::string dex_location = GetScratchDir() + "/DexPicOdexNoOat.jar"; - std::string odex_location = GetISADir() + "/DexPicOdexNoOat.odex"; + std::string odex_location = GetOdexDir() + "/DexPicOdexNoOat.odex"; // Create the dex and odex files Copy(GetDexSrc1(), dex_location); @@ -803,7 +807,7 @@ class RaceGenerateTask : public Task { // avoid using up the virtual memory address space. TEST_F(OatFileAssistantTest, RaceToGenerate) { std::string dex_location = GetScratchDir() + "/RaceToGenerate.jar"; - std::string oat_location = GetISADir() + "/RaceToGenerate.oat"; + std::string oat_location = GetOdexDir() + "/RaceToGenerate.oat"; // We use the lib core dex file, because it's large, and hopefully should // take a while to generate. @@ -833,7 +837,7 @@ TEST_F(OatFileAssistantTest, RaceToGenerate) { // Expect: We should load the odex file non-executable. TEST_F(OatFileAssistantNoDex2OatTest, LoadDexOdexNoOat) { std::string dex_location = GetScratchDir() + "/LoadDexOdexNoOat.jar"; - std::string odex_location = GetISADir() + "/LoadDexOdexNoOat.odex"; + std::string odex_location = GetOdexDir() + "/LoadDexOdexNoOat.odex"; // Create the dex and odex files Copy(GetDexSrc1(), dex_location); @@ -855,7 +859,7 @@ TEST_F(OatFileAssistantNoDex2OatTest, LoadDexOdexNoOat) { // Expect: We should load the odex file non-executable. TEST_F(OatFileAssistantNoDex2OatTest, LoadMultiDexOdexNoOat) { std::string dex_location = GetScratchDir() + "/LoadMultiDexOdexNoOat.jar"; - std::string odex_location = GetISADir() + "/LoadMultiDexOdexNoOat.odex"; + std::string odex_location = GetOdexDir() + "/LoadMultiDexOdexNoOat.odex"; // Create the dex and odex files Copy(GetMultiDexSrc1(), dex_location); @@ -878,11 +882,11 @@ TEST(OatFileAssistantUtilsTest, DexFilenameToOdexFilename) { EXPECT_TRUE(OatFileAssistant::DexFilenameToOdexFilename( "/foo/bar/baz.jar", kArm, &odex_file, &error_msg)) << error_msg; - EXPECT_EQ("/foo/bar/arm/baz.odex", odex_file); + EXPECT_EQ("/foo/bar/oat/arm/baz.odex", odex_file); EXPECT_TRUE(OatFileAssistant::DexFilenameToOdexFilename( "/foo/bar/baz.funnyext", kArm, &odex_file, &error_msg)) << error_msg; - EXPECT_EQ("/foo/bar/arm/baz.odex", odex_file); + EXPECT_EQ("/foo/bar/oat/arm/baz.odex", odex_file); EXPECT_FALSE(OatFileAssistant::DexFilenameToOdexFilename( "nopath.jar", kArm, &odex_file, &error_msg)); diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc index 3260992fba..cb97049c5b 100644 --- a/runtime/proxy_test.cc +++ b/runtime/proxy_test.cc @@ -185,7 +185,7 @@ TEST_F(ProxyTest, ProxyFieldHelper) { MutableHandle<mirror::ArtField> fhandle = hs.NewHandle(static_fields->Get(0)); EXPECT_EQ("interfaces", std::string(fhandle->GetName())); EXPECT_EQ("[Ljava/lang/Class;", std::string(fhandle->GetTypeDescriptor())); - EXPECT_EQ(interfacesFieldClass.Get(), fhandle->GetType(true)); + EXPECT_EQ(interfacesFieldClass.Get(), fhandle->GetType<true>()); std::string temp; EXPECT_EQ("L$Proxy1234;", std::string(fhandle->GetDeclaringClass()->GetDescriptor(&temp))); EXPECT_FALSE(fhandle->IsPrimitiveType()); @@ -194,7 +194,7 @@ TEST_F(ProxyTest, ProxyFieldHelper) { fhandle.Assign(static_fields->Get(1)); EXPECT_EQ("throws", std::string(fhandle->GetName())); EXPECT_EQ("[[Ljava/lang/Class;", std::string(fhandle->GetTypeDescriptor())); - EXPECT_EQ(throwsFieldClass.Get(), fhandle->GetType(true)); + EXPECT_EQ(throwsFieldClass.Get(), fhandle->GetType<true>()); EXPECT_EQ("L$Proxy1234;", std::string(fhandle->GetDeclaringClass()->GetDescriptor(&temp))); EXPECT_FALSE(fhandle->IsPrimitiveType()); } diff --git a/runtime/reflection-inl.h b/runtime/reflection-inl.h index f21c1a0eb4..f54d4ca123 100644 --- a/runtime/reflection-inl.h +++ b/runtime/reflection-inl.h @@ -22,6 +22,7 @@ #include "base/stringprintf.h" #include "common_throws.h" #include "jvalue.h" +#include "mirror/object-inl.h" #include "primitive.h" #include "utils.h" @@ -99,6 +100,17 @@ inline bool ConvertPrimitiveValue(bool unbox_for_result, return false; } +inline bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c) { + if (UNLIKELY(o == nullptr)) { + ThrowNullPointerException("null receiver"); + return false; + } else if (UNLIKELY(!o->InstanceOf(c))) { + InvalidReceiverError(o, c); + return false; + } + return true; +} + } // namespace art #endif // ART_RUNTIME_REFLECTION_INL_H_ diff --git a/runtime/reflection.cc b/runtime/reflection.cc index a54a39d5d6..4e94de4139 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -24,7 +24,6 @@ #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" -#include "mirror/class.h" #include "mirror/object_array-inl.h" #include "mirror/object_array.h" #include "nth_caller_visitor.h" @@ -588,7 +587,7 @@ jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaM // If method is not set to be accessible, verify it can be accessed by the caller. mirror::Class* calling_class = nullptr; if (!accessible && !VerifyAccess(soa.Self(), receiver, declaring_class, m->GetAccessFlags(), - &calling_class)) { + &calling_class, 2)) { ThrowIllegalAccessException( StringPrintf("Class %s cannot access %s method %s of class %s", calling_class == nullptr ? "null" : PrettyClass(calling_class).c_str(), @@ -628,21 +627,6 @@ jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaM return soa.AddLocalReference<jobject>(BoxPrimitive(Primitive::GetType(shorty[0]), result)); } -bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c) { - if (o == nullptr) { - ThrowNullPointerException("null receiver"); - return false; - } else if (!o->InstanceOf(c)) { - std::string expected_class_name(PrettyDescriptor(c)); - std::string actual_class_name(PrettyTypeOf(o)); - ThrowIllegalArgumentException(StringPrintf("Expected receiver of type %s, but got %s", - expected_class_name.c_str(), - actual_class_name.c_str()).c_str()); - return false; - } - return true; -} - mirror::Object* BoxPrimitive(Primitive::Type src_class, const JValue& value) { if (src_class == Primitive::kPrimNot) { return value.GetL(); @@ -810,11 +794,11 @@ bool UnboxPrimitiveForResult(mirror::Object* o, } bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class, - uint32_t access_flags, mirror::Class** calling_class) { + uint32_t access_flags, mirror::Class** calling_class, size_t num_frames) { if ((access_flags & kAccPublic) != 0) { return true; } - NthCallerVisitor visitor(self, 2); + NthCallerVisitor visitor(self, num_frames); visitor.WalkStack(); if (UNLIKELY(visitor.caller == nullptr)) { // The caller is an attached native thread. @@ -840,4 +824,12 @@ bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_cl return declaring_class->IsInSamePackage(caller_class); } +void InvalidReceiverError(mirror::Object* o, mirror::Class* c) { + std::string expected_class_name(PrettyDescriptor(c)); + std::string actual_class_name(PrettyTypeOf(o)); + ThrowIllegalArgumentException(StringPrintf("Expected receiver of type %s, but got %s", + expected_class_name.c_str(), + actual_class_name.c_str()).c_str()); +} + } // namespace art diff --git a/runtime/reflection.h b/runtime/reflection.h index 857d63b07c..ff970e5507 100644 --- a/runtime/reflection.h +++ b/runtime/reflection.h @@ -69,11 +69,14 @@ jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject metho jobject args, bool accessible) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); -bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c) +ALWAYS_INLINE bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class, - uint32_t access_flags, mirror::Class** calling_class) + uint32_t access_flags, mirror::Class** calling_class, size_t num_frames) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + +void InvalidReceiverError(mirror::Object* o, mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); } // namespace art diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 9ca00b1195..23a7db614c 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -50,9 +50,11 @@ #include "arch/x86_64/registers_x86_64.h" #include "asm_support.h" #include "atomic.h" +#include "base/arena_allocator.h" #include "base/dumpable.h" #include "base/unix_file/fd_file.h" #include "class_linker.h" +#include "compiler_callbacks.h" #include "debugger.h" #include "elf_file.h" #include "entrypoints/runtime_asm_entrypoints.h" @@ -73,6 +75,7 @@ #include "mirror/art_method-inl.h" #include "mirror/class-inl.h" #include "mirror/class_loader.h" +#include "mirror/field.h" #include "mirror/stack_trace_element.h" #include "mirror/throwable.h" #include "monitor.h" @@ -163,7 +166,6 @@ Runtime::Runtime() method_trace_(false), method_trace_file_size_(0), instrumentation_(), - use_compile_time_class_path_(false), main_thread_group_(nullptr), system_thread_group_(nullptr), system_class_loader_(nullptr), @@ -405,9 +407,9 @@ bool Runtime::Create(const RuntimeOptions& options, bool ignore_unrecognized) { return true; } -static jobject CreateSystemClassLoader() { - if (Runtime::Current()->UseCompileTimeClassPath()) { - return NULL; +static jobject CreateSystemClassLoader(Runtime* runtime) { + if (runtime->IsAotCompiler() && !runtime->GetCompilerCallbacks()->IsBootImage()) { + return nullptr; } ScopedObjectAccess soa(Thread::Current()); @@ -505,7 +507,7 @@ bool Runtime::Start() { Thread::FinishStartup(); - system_class_loader_ = CreateSystemClassLoader(); + system_class_loader_ = CreateSystemClassLoader(this); if (is_zygote_) { if (!InitZygote()) { @@ -1011,8 +1013,8 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) -1, static_cast<int>(method_trace_file_size_), 0, - false, - false, + Trace::TraceOutputMode::kFile, + Trace::TraceMode::kMethodTracing, 0); } @@ -1290,6 +1292,7 @@ void Runtime::VisitConstantRoots(RootCallback* callback, void* arg) { mirror::StackTraceElement::VisitRoots(callback, arg); mirror::String::VisitRoots(callback, arg); mirror::Throwable::VisitRoots(callback, arg); + mirror::Field::VisitRoots(callback, arg); // Visit all the primitive array types classes. mirror::PrimitiveArray<uint8_t>::VisitRoots(callback, arg); // BooleanArray mirror::PrimitiveArray<int8_t>::VisitRoots(callback, arg); // ByteArray @@ -1483,23 +1486,6 @@ void Runtime::SetCalleeSaveMethod(mirror::ArtMethod* method, CalleeSaveType type callee_save_methods_[type] = GcRoot<mirror::ArtMethod>(method); } -const std::vector<const DexFile*>& Runtime::GetCompileTimeClassPath(jobject class_loader) { - if (class_loader == NULL) { - return GetClassLinker()->GetBootClassPath(); - } - CHECK(UseCompileTimeClassPath()); - CompileTimeClassPaths::const_iterator it = compile_time_class_paths_.find(class_loader); - CHECK(it != compile_time_class_paths_.end()); - return it->second; -} - -void Runtime::SetCompileTimeClassPath(jobject class_loader, - std::vector<const DexFile*>& class_path) { - CHECK(!IsStarted()); - use_compile_time_class_path_ = true; - compile_time_class_paths_.Put(class_loader, class_path); -} - void Runtime::StartProfiler(const char* profile_output_filename) { profile_output_filename_ = profile_output_filename; profiler_started_ = @@ -1671,4 +1657,12 @@ void Runtime::CreateJit() { } } +bool Runtime::CanRelocate() const { + return !IsAotCompiler() || compiler_callbacks_->IsRelocationPossible(); +} + +bool Runtime::IsCompilingBootImage() const { + return IsCompiler() && compiler_callbacks_->IsBootImage(); +} + } // namespace art diff --git a/runtime/runtime.h b/runtime/runtime.h index 9a04835242..085335fd3c 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -27,10 +27,7 @@ #include <vector> #include "arch/instruction_set.h" -#include "base/allocator.h" -#include "base/arena_allocator.h" #include "base/macros.h" -#include "compiler_callbacks.h" #include "gc_root.h" #include "instrumentation.h" #include "jobject_comparator.h" @@ -43,6 +40,9 @@ namespace art { +class ArenaPool; +class CompilerCallbacks; + namespace gc { class Heap; namespace collector { @@ -112,9 +112,10 @@ class Runtime { return compiler_callbacks_ != nullptr; } - bool CanRelocate() const { - return !IsAotCompiler() || compiler_callbacks_->IsRelocationPossible(); - } + // If a compiler, are we compiling a boot image? + bool IsCompilingBootImage() const; + + bool CanRelocate() const; bool ShouldRelocate() const { return must_relocate_ && CanRelocate(); @@ -452,16 +453,6 @@ class Runtime { return &instrumentation_; } - bool UseCompileTimeClassPath() const { - return use_compile_time_class_path_; - } - - const std::vector<const DexFile*>& GetCompileTimeClassPath(jobject class_loader); - - // The caller is responsible for ensuring the class_path DexFiles remain - // valid as long as the Runtime object remains valid. - void SetCompileTimeClassPath(jobject class_loader, std::vector<const DexFile*>& class_path); - void StartProfiler(const char* profile_output_filename); void UpdateProfilerState(int state); @@ -685,12 +676,6 @@ class Runtime { size_t method_trace_file_size_; instrumentation::Instrumentation instrumentation_; - typedef AllocationTrackingSafeMap<jobject, std::vector<const DexFile*>, - kAllocatorTagCompileTimeClassPath, JobjectComparator> - CompileTimeClassPaths; - CompileTimeClassPaths compile_time_class_paths_; - bool use_compile_time_class_path_; - jobject main_thread_group_; jobject system_thread_group_; diff --git a/runtime/stack.cc b/runtime/stack.cc index 2d688ee438..4ae49ddd7c 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -206,21 +206,22 @@ bool StackVisitor::GetVRegFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg, DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); DexRegisterLocation::Kind location_kind = - dex_register_map.GetLocationKind(vreg, number_of_dex_registers); + dex_register_map.GetLocationKind(vreg, number_of_dex_registers, code_info); switch (location_kind) { case DexRegisterLocation::Kind::kInStack: { - const int32_t offset = dex_register_map.GetStackOffsetInBytes(vreg, number_of_dex_registers); + const int32_t offset = + dex_register_map.GetStackOffsetInBytes(vreg, number_of_dex_registers, code_info); const uint8_t* addr = reinterpret_cast<const uint8_t*>(cur_quick_frame_) + offset; *val = *reinterpret_cast<const uint32_t*>(addr); return true; } case DexRegisterLocation::Kind::kInRegister: case DexRegisterLocation::Kind::kInFpuRegister: { - uint32_t reg = dex_register_map.GetMachineRegister(vreg, number_of_dex_registers); + uint32_t reg = dex_register_map.GetMachineRegister(vreg, number_of_dex_registers, code_info); return GetRegisterIfAccessible(reg, kind, val); } case DexRegisterLocation::Kind::kConstant: - *val = dex_register_map.GetConstant(vreg, number_of_dex_registers); + *val = dex_register_map.GetConstant(vreg, number_of_dex_registers, code_info); return true; case DexRegisterLocation::Kind::kNone: return false; @@ -228,7 +229,7 @@ bool StackVisitor::GetVRegFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg, LOG(FATAL) << "Unexpected location kind" << DexRegisterLocation::PrettyDescriptor( - dex_register_map.GetLocationInternalKind(vreg, number_of_dex_registers)); + dex_register_map.GetLocationInternalKind(vreg, number_of_dex_registers, code_info)); UNREACHABLE(); } } @@ -396,18 +397,19 @@ bool StackVisitor::SetVRegFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg, DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map, number_of_dex_registers); DexRegisterLocation::Kind location_kind = - dex_register_map.GetLocationKind(vreg, number_of_dex_registers); + dex_register_map.GetLocationKind(vreg, number_of_dex_registers, code_info); uint32_t dex_pc = m->ToDexPc(cur_quick_frame_pc_, false); switch (location_kind) { case DexRegisterLocation::Kind::kInStack: { - const int32_t offset = dex_register_map.GetStackOffsetInBytes(vreg, number_of_dex_registers); + const int32_t offset = + dex_register_map.GetStackOffsetInBytes(vreg, number_of_dex_registers, code_info); uint8_t* addr = reinterpret_cast<uint8_t*>(cur_quick_frame_) + offset; *reinterpret_cast<uint32_t*>(addr) = new_value; return true; } case DexRegisterLocation::Kind::kInRegister: case DexRegisterLocation::Kind::kInFpuRegister: { - uint32_t reg = dex_register_map.GetMachineRegister(vreg, number_of_dex_registers); + uint32_t reg = dex_register_map.GetMachineRegister(vreg, number_of_dex_registers, code_info); return SetRegisterIfAccessible(reg, new_value, kind); } case DexRegisterLocation::Kind::kConstant: diff --git a/runtime/stack_map.cc b/runtime/stack_map.cc index 020a6e68ac..28f42c1fbd 100644 --- a/runtime/stack_map.cc +++ b/runtime/stack_map.cc @@ -18,11 +18,37 @@ namespace art { +constexpr size_t DexRegisterLocationCatalog::kNoLocationEntryIndex; + constexpr uint32_t StackMap::kNoDexRegisterMapSmallEncoding; constexpr uint32_t StackMap::kNoInlineInfoSmallEncoding; constexpr uint32_t StackMap::kNoDexRegisterMap; constexpr uint32_t StackMap::kNoInlineInfo; +DexRegisterLocation::Kind DexRegisterMap::GetLocationInternalKind(uint16_t dex_register_number, + uint16_t number_of_dex_registers, + const CodeInfo& code_info) const { + DexRegisterLocationCatalog dex_register_location_catalog = + code_info.GetDexRegisterLocationCatalog(); + size_t location_catalog_entry_index = GetLocationCatalogEntryIndex( + dex_register_number, + number_of_dex_registers, + code_info.GetNumberOfDexRegisterLocationCatalogEntries()); + return dex_register_location_catalog.GetLocationInternalKind(location_catalog_entry_index); +} + +DexRegisterLocation DexRegisterMap::GetDexRegisterLocation(uint16_t dex_register_number, + uint16_t number_of_dex_registers, + const CodeInfo& code_info) const { + DexRegisterLocationCatalog dex_register_location_catalog = + code_info.GetDexRegisterLocationCatalog(); + size_t location_catalog_entry_index = GetLocationCatalogEntryIndex( + dex_register_number, + number_of_dex_registers, + code_info.GetNumberOfDexRegisterLocationCatalogEntries()); + return dex_register_location_catalog.GetDexRegisterLocation(location_catalog_entry_index); +} + uint32_t StackMap::GetDexPc(const CodeInfo& info) const { return info.HasSmallDexPc() ? region_.LoadUnaligned<kSmallEncoding>(info.ComputeStackMapDexPcOffset()) @@ -143,6 +169,16 @@ MemoryRegion StackMap::GetStackMask(const CodeInfo& info) const { return region_.Subregion(info.ComputeStackMapStackMaskOffset(), info.GetStackMaskSize()); } +static void DumpRegisterMapping(std::ostream& os, + size_t dex_register_num, + DexRegisterLocation location, + const std::string& prefix = "v", + const std::string& suffix = "") { + os << " " << prefix << dex_register_num << ": " + << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind()) + << " (" << location.GetValue() << ")" << suffix << '\n'; +} + void CodeInfo::DumpStackMapHeader(std::ostream& os, size_t stack_map_num) const { StackMap stack_map = GetStackMapAt(stack_map_num); os << " StackMap " << stack_map_num @@ -174,7 +210,18 @@ void CodeInfo::Dump(std::ostream& os, uint16_t number_of_dex_registers) const { << ", has_small_native_pc=" << HasSmallNativePc() << ")\n"; - // Display stack maps along with Dex register maps. + // Display the Dex register location catalog. + size_t number_of_location_catalog_entries = GetNumberOfDexRegisterLocationCatalogEntries(); + size_t location_catalog_size_in_bytes = GetDexRegisterLocationCatalogSize(); + os << " DexRegisterLocationCatalog (number_of_entries=" << number_of_location_catalog_entries + << ", size_in_bytes=" << location_catalog_size_in_bytes << ")\n"; + DexRegisterLocationCatalog dex_register_location_catalog = GetDexRegisterLocationCatalog(); + for (size_t i = 0; i < number_of_location_catalog_entries; ++i) { + DexRegisterLocation location = dex_register_location_catalog.GetDexRegisterLocation(i); + DumpRegisterMapping(os, i, location, "entry "); + } + + // Display stack maps along with (live) Dex register maps. for (size_t i = 0; i < number_of_stack_maps; ++i) { StackMap stack_map = GetStackMapAt(i); DumpStackMapHeader(os, i); @@ -183,11 +230,13 @@ void CodeInfo::Dump(std::ostream& os, uint16_t number_of_dex_registers) const { // TODO: Display the bit mask of live Dex registers. for (size_t j = 0; j < number_of_dex_registers; ++j) { if (dex_register_map.IsDexRegisterLive(j)) { + size_t location_catalog_entry_index = dex_register_map.GetLocationCatalogEntryIndex( + j, number_of_dex_registers, number_of_location_catalog_entries); DexRegisterLocation location = - dex_register_map.GetLocationKindAndValue(j, number_of_dex_registers); - os << " " << "v" << j << ": " - << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind()) - << " (" << location.GetValue() << ")" << '\n'; + dex_register_map.GetDexRegisterLocation(j, number_of_dex_registers, *this); + DumpRegisterMapping( + os, j, location, "v", + "\t[entry " + std::to_string(static_cast<int>(location_catalog_entry_index)) + "]"); } } } diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 6ec7cc8525..ab7f926d39 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -26,13 +26,10 @@ namespace art { // Size of a frame slot, in bytes. This constant is a signed value, // to please the compiler in arithmetic operations involving int32_t // (signed) values. -static ssize_t constexpr kFrameSlotSize = 4; - -// Word alignment required on ARM, in bytes. -static constexpr size_t kWordAlignment = 4; +static constexpr ssize_t kFrameSlotSize = 4; // Size of Dex virtual registers. -static size_t constexpr kVRegSize = 4; +static constexpr size_t kVRegSize = 4; class CodeInfo; @@ -97,9 +94,9 @@ class DexRegisterLocation { * * In addition, DexRegisterMap also uses these values: * - kInStackLargeOffset: value holds a "large" stack offset (greater than - * 128 bytes); - * - kConstantLargeValue: value holds a "large" constant (lower than or - * equal to -16, or greater than 16). + * or equal to 128 bytes); + * - kConstantLargeValue: value holds a "large" constant (lower than 0, or + * or greater than or equal to 32). */ enum class Kind : uint8_t { // Short location kinds, for entries fitting on one byte (3 bits @@ -120,8 +117,7 @@ class DexRegisterLocation { kInStackLargeOffset = 5, // 0b101 // Large constant, that cannot fit on a 5-bit signed integer (i.e., - // lower than -2^(5-1) = -16, or greater than or equal to - // 2^(5-1) - 1 = 15). + // lower than 0, or greater than or equal to 2^5 = 32). kConstantLargeValue = 6, // 0b110 kLastLocationKind = kConstantLargeValue @@ -193,8 +189,10 @@ class DexRegisterLocation { } } - DexRegisterLocation(Kind kind, int32_t value) - : kind_(kind), value_(value) {} + // Required by art::StackMapStream::LocationCatalogEntriesIndices. + DexRegisterLocation() : kind_(Kind::kNone), value_(0) {} + + DexRegisterLocation(Kind kind, int32_t value) : kind_(kind), value_(value) {} static DexRegisterLocation None() { return DexRegisterLocation(Kind::kNone, 0); @@ -223,33 +221,23 @@ class DexRegisterLocation { private: Kind kind_; int32_t value_; + + friend class DexRegisterLocationHashFn; }; /** - * Information on dex register values for a specific PC. The information is - * of the form: - * [live_bit_mask, DexRegisterLocation+]. + * Store information on unique Dex register locations used in a method. + * The information is of the form: + * [DexRegisterLocation+]. * DexRegisterLocations are either 1- or 5-byte wide (see art::DexRegisterLocation::Kind). */ -class DexRegisterMap { +class DexRegisterLocationCatalog { public: - explicit DexRegisterMap(MemoryRegion region) : region_(region) {} + explicit DexRegisterLocationCatalog(MemoryRegion region) : region_(region) {} // Short (compressed) location, fitting on one byte. typedef uint8_t ShortLocation; - static size_t LiveBitMaskSize(uint16_t number_of_dex_registers) { - return RoundUp(number_of_dex_registers, kBitsPerByte) / kBitsPerByte; - } - - void SetLiveBitMask(size_t offset, - uint16_t number_of_dex_registers, - const BitVector& live_dex_registers_mask) { - for (uint16_t i = 0; i < number_of_dex_registers; i++) { - region_.StoreBit(offset + i, live_dex_registers_mask.IsBitSet(i)); - } - } - void SetRegisterInfo(size_t offset, const DexRegisterLocation& dex_register_location) { DexRegisterLocation::Kind kind = ComputeCompressedKind(dex_register_location); int32_t value = dex_register_location.GetValue(); @@ -265,12 +253,12 @@ class DexRegisterMap { DCHECK_EQ(value % kFrameSlotSize, 0); value /= kFrameSlotSize; } - DCHECK(IsUint<kValueBits>(value)) << value; + DCHECK(IsShortValue(value)) << value; region_.StoreUnaligned<ShortLocation>(offset, MakeShortLocation(kind, value)); } else { // Large location. Write the location on one byte and the value // on 4 bytes. - DCHECK(!IsUint<kValueBits>(value)) << value; + DCHECK(!IsShortValue(value)) << value; if (kind == DexRegisterLocation::Kind::kInStackLargeOffset) { // Also divide large stack offsets by 4 for the sake of consistency. DCHECK_EQ(value % kFrameSlotSize, 0); @@ -285,63 +273,39 @@ class DexRegisterMap { } } - bool IsDexRegisterLive(uint16_t dex_register_index) const { + // Find the offset of the location catalog entry number `location_catalog_entry_index`. + size_t FindLocationOffset(size_t location_catalog_entry_index) const { size_t offset = kFixedSize; - return region_.LoadBit(offset + dex_register_index); - } - - static constexpr size_t kNoDexRegisterLocationOffset = -1; - - static size_t GetDexRegisterMapLocationsOffset(uint16_t number_of_dex_registers) { - return kLiveBitMaskOffset + LiveBitMaskSize(number_of_dex_registers); - } - - // Find the offset of the Dex register location number `dex_register_index`. - size_t FindLocationOffset(uint16_t dex_register_index, uint16_t number_of_dex_registers) const { - if (!IsDexRegisterLive(dex_register_index)) return kNoDexRegisterLocationOffset; - size_t offset = GetDexRegisterMapLocationsOffset(number_of_dex_registers); - // Skip the first `dex_register_index - 1` entries. - for (uint16_t i = 0; i < dex_register_index; ++i) { - if (IsDexRegisterLive(i)) { - // Read the first next byte and inspect its first 3 bits to decide - // whether it is a short or a large location. - DexRegisterLocation::Kind kind = ExtractKindAtOffset(offset); - if (DexRegisterLocation::IsShortLocationKind(kind)) { - // Short location. Skip the current byte. - offset += SingleShortEntrySize(); - } else { - // Large location. Skip the 5 next bytes. - offset += SingleLargeEntrySize(); - } + // Skip the first `location_catalog_entry_index - 1` entries. + for (uint16_t i = 0; i < location_catalog_entry_index; ++i) { + // Read the first next byte and inspect its first 3 bits to decide + // whether it is a short or a large location. + DexRegisterLocation::Kind kind = ExtractKindAtOffset(offset); + if (DexRegisterLocation::IsShortLocationKind(kind)) { + // Short location. Skip the current byte. + offset += SingleShortEntrySize(); + } else { + // Large location. Skip the 5 next bytes. + offset += SingleLargeEntrySize(); } } return offset; } - // Get the surface kind. - DexRegisterLocation::Kind GetLocationKind(uint16_t dex_register_index, - uint16_t number_of_dex_registers) const { - return IsDexRegisterLive(dex_register_index) - ? DexRegisterLocation::ConvertToSurfaceKind( - GetLocationInternalKind(dex_register_index, number_of_dex_registers)) - : DexRegisterLocation::Kind::kNone; - } - - // Get the internal kind. - DexRegisterLocation::Kind GetLocationInternalKind(uint16_t dex_register_index, - uint16_t number_of_dex_registers) const { - return IsDexRegisterLive(dex_register_index) - ? ExtractKindAtOffset(FindLocationOffset(dex_register_index, number_of_dex_registers)) - : DexRegisterLocation::Kind::kNone; + // Get the internal kind of entry at `location_catalog_entry_index`. + DexRegisterLocation::Kind GetLocationInternalKind(size_t location_catalog_entry_index) const { + if (location_catalog_entry_index == kNoLocationEntryIndex) { + return DexRegisterLocation::Kind::kNone; + } + return ExtractKindAtOffset(FindLocationOffset(location_catalog_entry_index)); } - // TODO: Rename as GetDexRegisterLocation? - DexRegisterLocation GetLocationKindAndValue(uint16_t dex_register_index, - uint16_t number_of_dex_registers) const { - if (!IsDexRegisterLive(dex_register_index)) { + // Get the (surface) kind and value of entry at `location_catalog_entry_index`. + DexRegisterLocation GetDexRegisterLocation(size_t location_catalog_entry_index) const { + if (location_catalog_entry_index == kNoLocationEntryIndex) { return DexRegisterLocation::None(); } - size_t offset = FindLocationOffset(dex_register_index, number_of_dex_registers); + size_t offset = FindLocationOffset(location_catalog_entry_index); // Read the first byte and inspect its first 3 bits to get the location. ShortLocation first_byte = region_.LoadUnaligned<ShortLocation>(offset); DexRegisterLocation::Kind kind = ExtractKindFromShortLocation(first_byte); @@ -364,31 +328,6 @@ class DexRegisterMap { } } - int32_t GetStackOffsetInBytes(uint16_t dex_register_index, - uint16_t number_of_dex_registers) const { - DexRegisterLocation location = - GetLocationKindAndValue(dex_register_index, number_of_dex_registers); - DCHECK(location.GetKind() == DexRegisterLocation::Kind::kInStack); - // GetLocationKindAndValue returns the offset in bytes. - return location.GetValue(); - } - - int32_t GetConstant(uint16_t dex_register_index, uint16_t number_of_dex_registers) const { - DexRegisterLocation location = - GetLocationKindAndValue(dex_register_index, number_of_dex_registers); - DCHECK(location.GetKind() == DexRegisterLocation::Kind::kConstant); - return location.GetValue(); - } - - int32_t GetMachineRegister(uint16_t dex_register_index, uint16_t number_of_dex_registers) const { - DexRegisterLocation location = - GetLocationKindAndValue(dex_register_index, number_of_dex_registers); - DCHECK(location.GetInternalKind() == DexRegisterLocation::Kind::kInRegister - || location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegister) - << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind()); - return location.GetValue(); - } - // Compute the compressed kind of `location`. static DexRegisterLocation::Kind ComputeCompressedKind(const DexRegisterLocation& location) { switch (location.GetInternalKind()) { @@ -398,22 +337,21 @@ class DexRegisterMap { case DexRegisterLocation::Kind::kInRegister: DCHECK_GE(location.GetValue(), 0); - DCHECK_LT(location.GetValue(), 1 << DexRegisterMap::kValueBits); + DCHECK_LT(location.GetValue(), 1 << kValueBits); return DexRegisterLocation::Kind::kInRegister; case DexRegisterLocation::Kind::kInFpuRegister: DCHECK_GE(location.GetValue(), 0); - DCHECK_LT(location.GetValue(), 1 << DexRegisterMap::kValueBits); + DCHECK_LT(location.GetValue(), 1 << kValueBits); return DexRegisterLocation::Kind::kInFpuRegister; case DexRegisterLocation::Kind::kInStack: - DCHECK_EQ(location.GetValue() % kFrameSlotSize, 0); - return IsUint<DexRegisterMap::kValueBits>(location.GetValue() / kFrameSlotSize) + return IsShortStackOffsetValue(location.GetValue()) ? DexRegisterLocation::Kind::kInStack : DexRegisterLocation::Kind::kInStackLargeOffset; case DexRegisterLocation::Kind::kConstant: - return IsUint<DexRegisterMap::kValueBits>(location.GetValue()) + return IsShortConstantValue(location.GetValue()) ? DexRegisterLocation::Kind::kConstant : DexRegisterLocation::Kind::kConstantLargeValue; @@ -433,11 +371,10 @@ class DexRegisterMap { return true; case DexRegisterLocation::Kind::kInStack: - DCHECK_EQ(location.GetValue() % kFrameSlotSize, 0); - return IsUint<kValueBits>(location.GetValue() / kFrameSlotSize); + return IsShortStackOffsetValue(location.GetValue()); case DexRegisterLocation::Kind::kConstant: - return IsUint<kValueBits>(location.GetValue()); + return IsShortConstantValue(location.GetValue()); default: UNREACHABLE(); @@ -445,9 +382,7 @@ class DexRegisterMap { } static size_t EntrySize(const DexRegisterLocation& location) { - return CanBeEncodedAsShortLocation(location) - ? DexRegisterMap::SingleShortEntrySize() - : DexRegisterMap::SingleLargeEntrySize(); + return CanBeEncodedAsShortLocation(location) ? SingleShortEntrySize() : SingleLargeEntrySize(); } static size_t SingleShortEntrySize() { @@ -462,10 +397,14 @@ class DexRegisterMap { return region_.size(); } - static constexpr int kLiveBitMaskOffset = 0; - static constexpr int kFixedSize = kLiveBitMaskOffset; + // Special (invalid) Dex register location catalog entry index meaning + // that there is no location for a given Dex register (i.e., it is + // mapped to a DexRegisterLocation::Kind::kNone location). + static constexpr size_t kNoLocationEntryIndex = -1; private: + static constexpr int kFixedSize = 0; + // Width of the kind "field" in a short location, in bits. static constexpr size_t kKindBits = 3; // Width of the value "field" in a short location, in bits. @@ -476,10 +415,24 @@ class DexRegisterMap { static constexpr size_t kKindOffset = 0; static constexpr size_t kValueOffset = kKindBits; + static bool IsShortStackOffsetValue(int32_t value) { + DCHECK_EQ(value % kFrameSlotSize, 0); + return IsShortValue(value / kFrameSlotSize); + } + + static bool IsShortConstantValue(int32_t value) { + return IsShortValue(value); + } + + static bool IsShortValue(int32_t value) { + return IsUint<kValueBits>(value); + } + static ShortLocation MakeShortLocation(DexRegisterLocation::Kind kind, int32_t value) { - DCHECK(IsUint<kKindBits>(static_cast<uint8_t>(kind))) << static_cast<uint8_t>(kind); - DCHECK(IsUint<kValueBits>(value)) << value; - return (static_cast<uint8_t>(kind) & kKindMask) << kKindOffset + uint8_t kind_integer_value = static_cast<uint8_t>(kind); + DCHECK(IsUint<kKindBits>(kind_integer_value)) << kind_integer_value; + DCHECK(IsShortValue(value)) << value; + return (kind_integer_value & kKindMask) << kKindOffset | (value & kValueMask) << kValueOffset; } @@ -507,6 +460,210 @@ class DexRegisterMap { friend class StackMapStream; }; +/* Information on Dex register locations for a specific PC, mapping a + * stack map's Dex register to a location entry in a DexRegisterLocationCatalog. + * The information is of the form: + * [live_bit_mask, entries*] + * where entries are concatenated unsigned integer values encoded on a number + * of bits (fixed per DexRegisterMap instances of a CodeInfo object) depending + * on the number of entries in the Dex register location catalog + * (see DexRegisterMap::SingleEntrySizeInBits). The map is 1-byte aligned. + */ +class DexRegisterMap { + public: + explicit DexRegisterMap(MemoryRegion region) : region_(region) {} + + // Get the surface kind of Dex register `dex_register_number`. + DexRegisterLocation::Kind GetLocationKind(uint16_t dex_register_number, + uint16_t number_of_dex_registers, + const CodeInfo& code_info) const { + return DexRegisterLocation::ConvertToSurfaceKind( + GetLocationInternalKind(dex_register_number, number_of_dex_registers, code_info)); + } + + // Get the internal kind of Dex register `dex_register_number`. + DexRegisterLocation::Kind GetLocationInternalKind(uint16_t dex_register_number, + uint16_t number_of_dex_registers, + const CodeInfo& code_info) const; + + // Get the Dex register location `dex_register_number`. + DexRegisterLocation GetDexRegisterLocation(uint16_t dex_register_number, + uint16_t number_of_dex_registers, + const CodeInfo& code_info) const; + + int32_t GetStackOffsetInBytes(uint16_t dex_register_number, + uint16_t number_of_dex_registers, + const CodeInfo& code_info) const { + DexRegisterLocation location = + GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info); + DCHECK(location.GetKind() == DexRegisterLocation::Kind::kInStack); + // GetDexRegisterLocation returns the offset in bytes. + return location.GetValue(); + } + + int32_t GetConstant(uint16_t dex_register_number, + uint16_t number_of_dex_registers, + const CodeInfo& code_info) const { + DexRegisterLocation location = + GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info); + DCHECK(location.GetKind() == DexRegisterLocation::Kind::kConstant); + return location.GetValue(); + } + + int32_t GetMachineRegister(uint16_t dex_register_number, + uint16_t number_of_dex_registers, + const CodeInfo& code_info) const { + DexRegisterLocation location = + GetDexRegisterLocation(dex_register_number, number_of_dex_registers, code_info); + DCHECK(location.GetInternalKind() == DexRegisterLocation::Kind::kInRegister + || location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegister) + << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind()); + return location.GetValue(); + } + + // Get the index of the entry in the Dex register location catalog + // corresponding to `dex_register_number`. + size_t GetLocationCatalogEntryIndex(uint16_t dex_register_number, + uint16_t number_of_dex_registers, + size_t number_of_location_catalog_entries) const { + if (!IsDexRegisterLive(dex_register_number)) { + return DexRegisterLocationCatalog::kNoLocationEntryIndex; + } + + if (number_of_location_catalog_entries == 1) { + // We do not allocate space for location maps in the case of a + // single-entry location catalog, as it is useless. The only valid + // entry index is 0; + return 0; + } + + // The bit offset of the beginning of the map locations. + size_t map_locations_offset_in_bits = + GetLocationMappingDataOffset(number_of_dex_registers) * kBitsPerByte; + size_t index_in_dex_register_map = GetIndexInDexRegisterMap(dex_register_number); + DCHECK_LT(index_in_dex_register_map, GetNumberOfLiveDexRegisters(number_of_dex_registers)); + // The bit size of an entry. + size_t map_entry_size_in_bits = SingleEntrySizeInBits(number_of_location_catalog_entries); + // The bit offset where `index_in_dex_register_map` is located. + size_t entry_offset_in_bits = + map_locations_offset_in_bits + index_in_dex_register_map * map_entry_size_in_bits; + size_t location_catalog_entry_index = + region_.LoadBits(entry_offset_in_bits, map_entry_size_in_bits); + DCHECK_LT(location_catalog_entry_index, number_of_location_catalog_entries); + return location_catalog_entry_index; + } + + // Map entry at `index_in_dex_register_map` to `location_catalog_entry_index`. + void SetLocationCatalogEntryIndex(size_t index_in_dex_register_map, + size_t location_catalog_entry_index, + uint16_t number_of_dex_registers, + size_t number_of_location_catalog_entries) { + DCHECK_LT(index_in_dex_register_map, GetNumberOfLiveDexRegisters(number_of_dex_registers)); + DCHECK_LT(location_catalog_entry_index, number_of_location_catalog_entries); + + if (number_of_location_catalog_entries == 1) { + // We do not allocate space for location maps in the case of a + // single-entry location catalog, as it is useless. + return; + } + + // The bit offset of the beginning of the map locations. + size_t map_locations_offset_in_bits = + GetLocationMappingDataOffset(number_of_dex_registers) * kBitsPerByte; + // The bit size of an entry. + size_t map_entry_size_in_bits = SingleEntrySizeInBits(number_of_location_catalog_entries); + // The bit offset where `index_in_dex_register_map` is located. + size_t entry_offset_in_bits = + map_locations_offset_in_bits + index_in_dex_register_map * map_entry_size_in_bits; + region_.StoreBits(entry_offset_in_bits, location_catalog_entry_index, map_entry_size_in_bits); + } + + void SetLiveBitMask(uint16_t number_of_dex_registers, + const BitVector& live_dex_registers_mask) { + size_t live_bit_mask_offset_in_bits = GetLiveBitMaskOffset() * kBitsPerByte; + for (uint16_t i = 0; i < number_of_dex_registers; ++i) { + region_.StoreBit(live_bit_mask_offset_in_bits + i, live_dex_registers_mask.IsBitSet(i)); + } + } + + bool IsDexRegisterLive(uint16_t dex_register_number) const { + size_t live_bit_mask_offset_in_bits = GetLiveBitMaskOffset() * kBitsPerByte; + return region_.LoadBit(live_bit_mask_offset_in_bits + dex_register_number); + } + + size_t GetNumberOfLiveDexRegisters(uint16_t number_of_dex_registers) const { + size_t number_of_live_dex_registers = 0; + for (size_t i = 0; i < number_of_dex_registers; ++i) { + if (IsDexRegisterLive(i)) { + ++number_of_live_dex_registers; + } + } + return number_of_live_dex_registers; + } + + static size_t GetLiveBitMaskOffset() { + return kFixedSize; + } + + // Compute the size of the live register bit mask (in bytes), for a + // method having `number_of_dex_registers` Dex registers. + static size_t GetLiveBitMaskSize(uint16_t number_of_dex_registers) { + return RoundUp(number_of_dex_registers, kBitsPerByte) / kBitsPerByte; + } + + static size_t GetLocationMappingDataOffset(uint16_t number_of_dex_registers) { + return GetLiveBitMaskOffset() + GetLiveBitMaskSize(number_of_dex_registers); + } + + size_t GetLocationMappingDataSize(uint16_t number_of_dex_registers, + size_t number_of_location_catalog_entries) const { + size_t location_mapping_data_size_in_bits = + GetNumberOfLiveDexRegisters(number_of_dex_registers) + * SingleEntrySizeInBits(number_of_location_catalog_entries); + return RoundUp(location_mapping_data_size_in_bits, kBitsPerByte) / kBitsPerByte; + } + + // Return the size of a map entry in bits. Note that if + // `number_of_location_catalog_entries` equals 1, this function returns 0, + // which is fine, as there is no need to allocate a map for a + // single-entry location catalog; the only valid location catalog entry index + // for a live register in this case is 0 and there is no need to + // store it. + static size_t SingleEntrySizeInBits(size_t number_of_location_catalog_entries) { + // Handle the case of 0, as we cannot pass 0 to art::WhichPowerOf2. + return number_of_location_catalog_entries == 0 + ? 0u + : WhichPowerOf2(RoundUpToPowerOfTwo(number_of_location_catalog_entries)); + } + + // Return the size of the DexRegisterMap object, in bytes. + size_t Size() const { + return region_.size(); + } + + private: + // Return the index in the Dex register map corresponding to the Dex + // register number `dex_register_number`. + size_t GetIndexInDexRegisterMap(uint16_t dex_register_number) const { + if (!IsDexRegisterLive(dex_register_number)) { + return kInvalidIndexInDexRegisterMap; + } + return GetNumberOfLiveDexRegisters(dex_register_number); + } + + // Special (invalid) Dex register map entry index meaning that there + // is no index in the map for a given Dex register (i.e., it must + // have been mapped to a DexRegisterLocation::Kind::kNone location). + static constexpr size_t kInvalidIndexInDexRegisterMap = -1; + + static constexpr int kFixedSize = 0; + + MemoryRegion region_; + + friend class CodeInfo; + friend class StackMapStream; +}; + /** * A Stack Map holds compilation information for a specific PC necessary for: * - Mapping it to a dex PC, @@ -516,7 +673,8 @@ class DexRegisterMap { * - Knowing the values of dex registers. * * The information is of the form: - * [dex_pc, native_pc_offset, dex_register_map_offset, inlining_info_offset, register_mask, stack_mask]. + * [dex_pc, native_pc_offset, dex_register_map_offset, inlining_info_offset, register_mask, + * stack_mask]. * * Note that register_mask is fixed size, but stack_mask is variable size, depending on the * stack size of a method. @@ -622,7 +780,8 @@ class StackMap { /** * Wrapper around all compiler information collected for a method. * The information is of the form: - * [overall_size, number_of_stack_maps, stack_mask_size, StackMap+, DexRegisterInfo+, InlineInfo*]. + * [overall_size, number_of_location_catalog_entries, number_of_stack_maps, stack_mask_size, + * DexRegisterLocationCatalog+, StackMap+, DexRegisterMap+, InlineInfo*]. */ class CodeInfo { public: @@ -707,6 +866,16 @@ class CodeInfo { + (HasSmallDexRegisterMap() ? sizeof(uint8_t) : sizeof(uint32_t)); } + uint32_t GetDexRegisterLocationCatalogOffset() const { + return kFixedSize; + } + + DexRegisterLocationCatalog GetDexRegisterLocationCatalog() const { + return DexRegisterLocationCatalog(region_.Subregion( + GetDexRegisterLocationCatalogOffset(), + GetDexRegisterLocationCatalogSize())); + } + StackMap GetStackMapAt(size_t i) const { size_t size = StackMapSize(); return StackMap(GetStackMaps().Subregion(i * size, size)); @@ -720,6 +889,19 @@ class CodeInfo { region_.StoreUnaligned<uint32_t>(kOverallSizeOffset, size); } + uint32_t GetNumberOfDexRegisterLocationCatalogEntries() const { + return region_.LoadUnaligned<uint32_t>(kNumberOfDexRegisterLocationCatalogEntriesOffset); + } + + void SetNumberOfDexRegisterLocationCatalogEntries(uint32_t num_entries) { + region_.StoreUnaligned<uint32_t>(kNumberOfDexRegisterLocationCatalogEntriesOffset, num_entries); + } + + uint32_t GetDexRegisterLocationCatalogSize() const { + return ComputeDexRegisterLocationCatalogSize(GetDexRegisterLocationCatalogOffset(), + GetNumberOfDexRegisterLocationCatalogEntries()); + } + uint32_t GetStackMaskSize() const { return region_.LoadUnaligned<uint32_t>(kStackMaskSizeOffset); } @@ -748,22 +930,22 @@ class CodeInfo { } // Get the size all the stack maps of this CodeInfo object, in bytes. - size_t StackMapsSize() const { + size_t GetStackMapsSize() const { return StackMapSize() * GetNumberOfStackMaps(); } size_t GetDexRegisterMapsOffset() const { - return CodeInfo::kFixedSize + StackMapsSize(); + return GetStackMapsOffset() + GetStackMapsSize(); } uint32_t GetStackMapsOffset() const { - return kFixedSize; + return GetDexRegisterLocationCatalogOffset() + GetDexRegisterLocationCatalogSize(); } DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, uint32_t number_of_dex_registers) const { DCHECK(stack_map.HasDexRegisterMap(*this)); - uint32_t offset = stack_map.GetDexRegisterMapOffset(*this) + GetDexRegisterMapsOffset(); - size_t size = ComputeDexRegisterMapSize(offset, number_of_dex_registers); + uint32_t offset = GetDexRegisterMapsOffset() + stack_map.GetDexRegisterMapOffset(*this); + size_t size = ComputeDexRegisterMapSizeOf(offset, number_of_dex_registers); return DexRegisterMap(region_.Subregion(offset, size)); } @@ -806,7 +988,10 @@ class CodeInfo { // typedefs (and document the memory layout of CodeInfo). static constexpr int kOverallSizeOffset = 0; static constexpr int kEncodingInfoOffset = kOverallSizeOffset + sizeof(uint32_t); - static constexpr int kNumberOfStackMapsOffset = kEncodingInfoOffset + sizeof(uint8_t); + static constexpr int kNumberOfDexRegisterLocationCatalogEntriesOffset = + kEncodingInfoOffset + sizeof(uint8_t); + static constexpr int kNumberOfStackMapsOffset = + kNumberOfDexRegisterLocationCatalogEntriesOffset + sizeof(uint32_t); static constexpr int kStackMaskSizeOffset = kNumberOfStackMapsOffset + sizeof(uint32_t); static constexpr int kFixedSize = kStackMaskSizeOffset + sizeof(uint32_t); @@ -819,37 +1004,56 @@ class CodeInfo { MemoryRegion GetStackMaps() const { return region_.size() == 0 ? MemoryRegion() - : region_.Subregion(kFixedSize, StackMapsSize()); - } - - // Compute the size of a Dex register map starting at offset `origin` in - // `region_` and containing `number_of_dex_registers` locations. - size_t ComputeDexRegisterMapSize(uint32_t origin, uint32_t number_of_dex_registers) const { - // TODO: Ideally, we would like to use art::DexRegisterMap::Size or - // art::DexRegisterMap::FindLocationOffset, but the DexRegisterMap is not - // yet built. Try to factor common code. - size_t offset = - origin + DexRegisterMap::GetDexRegisterMapLocationsOffset(number_of_dex_registers); - - // Create a temporary DexRegisterMap to be able to call DexRegisterMap.IsDexRegisterLive. - DexRegisterMap only_live_mask(MemoryRegion(region_.Subregion(origin, offset - origin))); - - // Skip the first `number_of_dex_registers - 1` entries. - for (uint16_t i = 0; i < number_of_dex_registers; ++i) { - if (only_live_mask.IsDexRegisterLive(i)) { - // Read the first next byte and inspect its first 3 bits to decide - // whether it is a short or a large location. - DexRegisterMap::ShortLocation first_byte = - region_.LoadUnaligned<DexRegisterMap::ShortLocation>(offset); - DexRegisterLocation::Kind kind = - DexRegisterMap::ExtractKindFromShortLocation(first_byte); - if (DexRegisterLocation::IsShortLocationKind(kind)) { - // Short location. Skip the current byte. - offset += DexRegisterMap::SingleShortEntrySize(); - } else { - // Large location. Skip the 5 next bytes. - offset += DexRegisterMap::SingleLargeEntrySize(); - } + : region_.Subregion(GetStackMapsOffset(), GetStackMapsSize()); + } + + // Compute the size of the Dex register map associated to the stack map at + // `dex_register_map_offset_in_code_info`. + size_t ComputeDexRegisterMapSizeOf(uint32_t dex_register_map_offset_in_code_info, + uint16_t number_of_dex_registers) const { + // Offset where the actual mapping data starts within art::DexRegisterMap. + size_t location_mapping_data_offset_in_dex_register_map = + DexRegisterMap::GetLocationMappingDataOffset(number_of_dex_registers); + // Create a temporary art::DexRegisterMap to be able to call + // art::DexRegisterMap::GetNumberOfLiveDexRegisters and + DexRegisterMap dex_register_map_without_locations( + MemoryRegion(region_.Subregion(dex_register_map_offset_in_code_info, + location_mapping_data_offset_in_dex_register_map))); + size_t number_of_live_dex_registers = + dex_register_map_without_locations.GetNumberOfLiveDexRegisters(number_of_dex_registers); + size_t location_mapping_data_size_in_bits = + DexRegisterMap::SingleEntrySizeInBits(GetNumberOfDexRegisterLocationCatalogEntries()) + * number_of_live_dex_registers; + size_t location_mapping_data_size_in_bytes = + RoundUp(location_mapping_data_size_in_bits, kBitsPerByte) / kBitsPerByte; + size_t dex_register_map_size = + location_mapping_data_offset_in_dex_register_map + location_mapping_data_size_in_bytes; + return dex_register_map_size; + } + + // Compute the size of a Dex register location catalog starting at offset `origin` + // in `region_` and containing `number_of_dex_locations` entries. + size_t ComputeDexRegisterLocationCatalogSize(uint32_t origin, + uint32_t number_of_dex_locations) const { + // TODO: Ideally, we would like to use art::DexRegisterLocationCatalog::Size or + // art::DexRegisterLocationCatalog::FindLocationOffset, but the + // DexRegisterLocationCatalog is not yet built. Try to factor common code. + size_t offset = origin + DexRegisterLocationCatalog::kFixedSize; + + // Skip the first `number_of_dex_locations - 1` entries. + for (uint16_t i = 0; i < number_of_dex_locations; ++i) { + // Read the first next byte and inspect its first 3 bits to decide + // whether it is a short or a large location. + DexRegisterLocationCatalog::ShortLocation first_byte = + region_.LoadUnaligned<DexRegisterLocationCatalog::ShortLocation>(offset); + DexRegisterLocation::Kind kind = + DexRegisterLocationCatalog::ExtractKindFromShortLocation(first_byte); + if (DexRegisterLocation::IsShortLocationKind(kind)) { + // Short location. Skip the current byte. + offset += DexRegisterLocationCatalog::SingleShortEntrySize(); + } else { + // Large location. Skip the 5 next bytes. + offset += DexRegisterLocationCatalog::SingleLargeEntrySize(); } } size_t size = offset - origin; diff --git a/runtime/trace.cc b/runtime/trace.cc index 88be23fa0c..ea0a642855 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -17,6 +17,7 @@ #include "trace.h" #include <sys/uio.h> +#include <unistd.h> #define ATRACE_TAG ATRACE_TAG_DALVIK #include "cutils/trace.h" @@ -327,7 +328,7 @@ void* Trace::RunSamplingThread(void* arg) { } void Trace::Start(const char* trace_filename, int trace_fd, int buffer_size, int flags, - bool direct_to_ddms, bool sampling_enabled, int interval_us) { + TraceOutputMode output_mode, TraceMode trace_mode, int interval_us) { Thread* self = Thread::Current(); { MutexLock mu(self, *Locks::trace_lock_); @@ -338,7 +339,7 @@ void Trace::Start(const char* trace_filename, int trace_fd, int buffer_size, int } // Check interval if sampling is enabled - if (sampling_enabled && interval_us <= 0) { + if (trace_mode == TraceMode::kSampling && interval_us <= 0) { LOG(ERROR) << "Invalid sampling interval: " << interval_us; ScopedObjectAccess soa(self); ThrowRuntimeException("Invalid sampling interval: %d", interval_us); @@ -347,7 +348,7 @@ void Trace::Start(const char* trace_filename, int trace_fd, int buffer_size, int // Open trace file if not going directly to ddms. std::unique_ptr<File> trace_file; - if (!direct_to_ddms) { + if (output_mode != TraceOutputMode::kDDMS) { if (trace_fd < 0) { trace_file.reset(OS::CreateEmptyFile(trace_filename)); } else { @@ -376,8 +377,8 @@ void Trace::Start(const char* trace_filename, int trace_fd, int buffer_size, int LOG(ERROR) << "Trace already in progress, ignoring this request"; } else { enable_stats = (flags && kTraceCountAllocs) != 0; - the_trace_ = new Trace(trace_file.release(), buffer_size, flags, sampling_enabled); - if (sampling_enabled) { + the_trace_ = new Trace(trace_file.release(), buffer_size, flags, trace_mode); + if (trace_mode == TraceMode::kSampling) { CHECK_PTHREAD_CALL(pthread_create, (&sampling_pthread_, NULL, &RunSamplingThread, reinterpret_cast<void*>(interval_us)), "Sampling profiler thread"); @@ -426,7 +427,7 @@ void Trace::Stop() { stop_alloc_counting = (the_trace->flags_ & kTraceCountAllocs) != 0; the_trace->FinishTracing(); - if (the_trace->sampling_enabled_) { + if (the_trace->trace_mode_ == TraceMode::kSampling) { MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr); } else { @@ -464,16 +465,21 @@ TracingMode Trace::GetMethodTracingMode() { MutexLock mu(Thread::Current(), *Locks::trace_lock_); if (the_trace_ == NULL) { return kTracingInactive; - } else if (the_trace_->sampling_enabled_) { - return kSampleProfilingActive; } else { - return kMethodTracingActive; + switch (the_trace_->trace_mode_) { + case TraceMode::kSampling: + return kSampleProfilingActive; + case TraceMode::kMethodTracing: + return kMethodTracingActive; + } + LOG(FATAL) << "Unreachable"; + UNREACHABLE(); } } -Trace::Trace(File* trace_file, int buffer_size, int flags, bool sampling_enabled) +Trace::Trace(File* trace_file, int buffer_size, int flags, TraceMode trace_mode) : trace_file_(trace_file), buf_(new uint8_t[buffer_size]()), flags_(flags), - sampling_enabled_(sampling_enabled), clock_source_(default_clock_source_), + trace_mode_(trace_mode), clock_source_(default_clock_source_), buffer_size_(buffer_size), start_time_(MicroTime()), clock_overhead_ns_(GetClockOverheadNanoSeconds()), cur_offset_(0), overflow_(false) { // Set up the beginning of the trace. @@ -534,6 +540,7 @@ void Trace::FinishTracing() { os << StringPrintf("num-method-calls=%zd\n", num_records); os << StringPrintf("clock-call-overhead-nsec=%d\n", clock_overhead_ns_); os << StringPrintf("vm=art\n"); + os << StringPrintf("pid=%d\n", getpid()); if ((flags_ & kTraceCountAllocs) != 0) { os << StringPrintf("alloc-count=%d\n", Runtime::Current()->GetStat(KIND_ALLOCATED_OBJECTS)); os << StringPrintf("alloc-size=%d\n", Runtime::Current()->GetStat(KIND_ALLOCATED_BYTES)); diff --git a/runtime/trace.h b/runtime/trace.h index dd8186a2cb..80f926f31c 100644 --- a/runtime/trace.h +++ b/runtime/trace.h @@ -51,10 +51,20 @@ class Trace FINAL : public instrumentation::InstrumentationListener { kTraceCountAllocs = 1, }; + enum class TraceOutputMode { + kFile, + kDDMS + }; + + enum class TraceMode { + kMethodTracing, + kSampling + }; + static void SetDefaultClockSource(TraceClockSource clock_source); static void Start(const char* trace_filename, int trace_fd, int buffer_size, int flags, - bool direct_to_ddms, bool sampling_enabled, int interval_us) + TraceOutputMode output_mode, TraceMode trace_mode, int interval_us) LOCKS_EXCLUDED(Locks::mutator_lock_, Locks::thread_list_lock_, Locks::thread_suspend_count_lock_, @@ -107,7 +117,7 @@ class Trace FINAL : public instrumentation::InstrumentationListener { static void StoreExitingThreadInfo(Thread* thread); private: - explicit Trace(File* trace_file, int buffer_size, int flags, bool sampling_enabled); + explicit Trace(File* trace_file, int buffer_size, int flags, TraceMode trace_mode); // The sampling interval in microseconds is passed as an argument. static void* RunSamplingThread(void* arg) LOCKS_EXCLUDED(Locks::trace_lock_); @@ -148,7 +158,7 @@ class Trace FINAL : public instrumentation::InstrumentationListener { const int flags_; // True if traceview should sample instead of instrumenting method entry/exit. - const bool sampling_enabled_; + const TraceMode trace_mode_; const TraceClockSource clock_source_; diff --git a/runtime/utils.h b/runtime/utils.h index e20412e1ab..e6a6b1dfad 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -109,11 +109,17 @@ static inline bool IsAlignedParam(T x, int n) { DCHECK(::art::IsAlignedParam(value, alignment)) << reinterpret_cast<const void*>(value) // Check whether an N-bit two's-complement representation can hold value. -static inline bool IsInt(int N, intptr_t value) { - CHECK_LT(0, N); - CHECK_LT(N, kBitsPerIntPtrT); - intptr_t limit = static_cast<intptr_t>(1) << (N - 1); - return (-limit <= value) && (value < limit); +template <typename T> +static inline bool IsInt(int N, T value) { + int bitsPerT = sizeof(T) * kBitsPerByte; + if (N == bitsPerT) { + return true; + } else { + CHECK_LT(0, N); + CHECK_LT(N, bitsPerT); + T limit = static_cast<T>(1) << (N - 1); + return (-limit <= value) && (value < limit); + } } template <typename T> diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc index 6b36c192e8..833427eb28 100644 --- a/runtime/utils_test.cc +++ b/runtime/utils_test.cc @@ -432,4 +432,79 @@ TEST_F(UtilsTest, MinimumBitsToStore) { EXPECT_EQ(32u, MinimumBitsToStore(~static_cast<uint32_t>(0))); } +static constexpr int64_t INT_MIN_minus1 = static_cast<int64_t>(INT_MIN) - 1; +static constexpr int64_t INT_MAX_plus1 = static_cast<int64_t>(INT_MAX) + 1; +static constexpr int64_t UINT_MAX_plus1 = static_cast<int64_t>(UINT_MAX) + 1; + +TEST_F(UtilsTest, IsInt) { + EXPECT_FALSE(IsInt(1, -2)); + EXPECT_TRUE(IsInt(1, -1)); + EXPECT_TRUE(IsInt(1, 0)); + EXPECT_FALSE(IsInt(1, 1)); + + EXPECT_FALSE(IsInt(4, -9)); + EXPECT_TRUE(IsInt(4, -8)); + EXPECT_TRUE(IsInt(4, 7)); + EXPECT_FALSE(IsInt(4, 8)); + + EXPECT_FALSE(IsInt(32, INT_MIN_minus1)); + EXPECT_TRUE(IsInt(32, INT_MIN)); + EXPECT_TRUE(IsInt(32, INT_MAX)); + EXPECT_FALSE(IsInt(32, INT_MAX_plus1)); +} + +TEST_F(UtilsTest, IsInt_Static) { + EXPECT_FALSE(IsInt<1>(-2)); + EXPECT_TRUE(IsInt<1>(-1)); + EXPECT_TRUE(IsInt<1>(0)); + EXPECT_FALSE(IsInt<1>(1)); + + EXPECT_FALSE(IsInt<4>(-9)); + EXPECT_TRUE(IsInt<4>(-8)); + EXPECT_TRUE(IsInt<4>(7)); + EXPECT_FALSE(IsInt<4>(8)); + + EXPECT_FALSE(IsInt<32>(INT_MIN_minus1)); + EXPECT_TRUE(IsInt<32>(INT_MIN)); + EXPECT_TRUE(IsInt<32>(INT_MAX)); + EXPECT_FALSE(IsInt<32>(INT_MAX_plus1)); +} + +TEST_F(UtilsTest, IsUint) { + EXPECT_FALSE(IsUint<1>(-1)); + EXPECT_TRUE(IsUint<1>(0)); + EXPECT_TRUE(IsUint<1>(1)); + EXPECT_FALSE(IsUint<1>(2)); + + EXPECT_FALSE(IsUint<4>(-1)); + EXPECT_TRUE(IsUint<4>(0)); + EXPECT_TRUE(IsUint<4>(15)); + EXPECT_FALSE(IsUint<4>(16)); + + EXPECT_FALSE(IsUint<32>(-1)); + EXPECT_TRUE(IsUint<32>(0)); + EXPECT_TRUE(IsUint<32>(UINT_MAX)); + EXPECT_FALSE(IsUint<32>(UINT_MAX_plus1)); +} + +TEST_F(UtilsTest, IsAbsoluteUint) { + EXPECT_FALSE(IsAbsoluteUint<1>(-2)); + EXPECT_TRUE(IsAbsoluteUint<1>(-1)); + EXPECT_TRUE(IsAbsoluteUint<32>(0)); + EXPECT_TRUE(IsAbsoluteUint<1>(1)); + EXPECT_FALSE(IsAbsoluteUint<1>(2)); + + EXPECT_FALSE(IsAbsoluteUint<4>(-16)); + EXPECT_TRUE(IsAbsoluteUint<4>(-15)); + EXPECT_TRUE(IsAbsoluteUint<32>(0)); + EXPECT_TRUE(IsAbsoluteUint<4>(15)); + EXPECT_FALSE(IsAbsoluteUint<4>(16)); + + EXPECT_FALSE(IsAbsoluteUint<32>(-UINT_MAX_plus1)); + EXPECT_TRUE(IsAbsoluteUint<32>(-UINT_MAX)); + EXPECT_TRUE(IsAbsoluteUint<32>(0)); + EXPECT_TRUE(IsAbsoluteUint<32>(UINT_MAX)); + EXPECT_FALSE(IsAbsoluteUint<32>(UINT_MAX_plus1)); +} + } // namespace art diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 47e9bf537b..1d04192528 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -1752,8 +1752,21 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { Fail(VERIFY_ERROR_NO_CLASS) << " can't resolve returned type '" << return_type << "' or '" << reg_type << "'"; } else { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "returning '" << reg_type - << "', but expected from declaration '" << return_type << "'"; + bool soft_error = false; + // Check whether arrays are involved. They will show a valid class status, even + // if their components are erroneous. + if (reg_type.IsArrayTypes() && return_type.IsArrayTypes()) { + return_type.CanAssignArray(reg_type, reg_types_, class_loader_, &soft_error); + if (soft_error) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "array with erroneous component type: " + << reg_type << " vs " << return_type; + } + } + + if (!soft_error) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "returning '" << reg_type + << "', but expected from declaration '" << return_type << "'"; + } } } } @@ -3906,7 +3919,7 @@ void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, const RegType& { StackHandleScope<1> hs(self_); HandleWrapper<mirror::ArtField> h_field(hs.NewHandleWrapper(&field)); - field_type_class = h_field->GetType(can_load_classes_); + field_type_class = can_load_classes_ ? h_field->GetType<true>() : h_field->GetType<false>(); } if (field_type_class != nullptr) { field_type = ®_types_.FromClass(field->GetTypeDescriptor(), field_type_class, @@ -4022,7 +4035,7 @@ void MethodVerifier::VerifyQuickFieldAccess(const Instruction* inst, const RegTy { StackHandleScope<1> hs(Thread::Current()); HandleWrapper<mirror::ArtField> h_field(hs.NewHandleWrapper(&field)); - field_type_class = h_field->GetType(can_load_classes_); + field_type_class = can_load_classes_ ? h_field->GetType<true>() : h_field->GetType<false>(); } if (field_type_class != nullptr) { diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index 201169f794..97d0cbe17f 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -822,5 +822,42 @@ std::ostream& operator<<(std::ostream& os, const RegType& rhs) { return os; } +bool RegType::CanAssignArray(const RegType& src, RegTypeCache& reg_types, + Handle<mirror::ClassLoader> class_loader, bool* soft_error) const { + if (!IsArrayTypes() || !src.IsArrayTypes()) { + *soft_error = false; + return false; + } + + const RegType& cmp1 = reg_types.GetComponentType(*this, class_loader.Get()); + const RegType& cmp2 = reg_types.GetComponentType(src, class_loader.Get()); + + if (cmp1.IsAssignableFrom(cmp2)) { + return true; + } + if (cmp1.IsUnresolvedTypes()) { + if (cmp2.IsIntegralTypes() || cmp2.IsFloatTypes() || cmp2.IsArrayTypes()) { + *soft_error = false; + return false; + } + *soft_error = true; + return false; + } + if (cmp2.IsUnresolvedTypes()) { + if (cmp1.IsIntegralTypes() || cmp1.IsFloatTypes() || cmp1.IsArrayTypes()) { + *soft_error = false; + return false; + } + *soft_error = true; + return false; + } + if (!cmp1.IsArrayTypes() || !cmp2.IsArrayTypes()) { + *soft_error = false; + return false; + } + return cmp1.CanAssignArray(cmp2, reg_types, class_loader, soft_error); +} + + } // namespace verifier } // namespace art diff --git a/runtime/verifier/reg_type.h b/runtime/verifier/reg_type.h index 73e131eff5..d260650d5b 100644 --- a/runtime/verifier/reg_type.h +++ b/runtime/verifier/reg_type.h @@ -25,6 +25,7 @@ #include "base/macros.h" #include "base/mutex.h" #include "gc_root.h" +#include "handle_scope.h" #include "object_callbacks.h" #include "primitive.h" @@ -205,6 +206,17 @@ class RegType { bool IsAssignableFrom(const RegType& src) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Can this array type potentially be assigned by src. + // This function is necessary as array types are valid even if their components types are not, + // e.g., when they component type could not be resolved. The function will return true iff the + // types are assignable. It will return false otherwise. In case of return=false, soft_error + // will be set to true iff the assignment test failure should be treated as a soft-error, i.e., + // when both array types have the same 'depth' and the 'final' component types may be assignable + // (both are reference types). + bool CanAssignArray(const RegType& src, RegTypeCache& reg_types, + Handle<mirror::ClassLoader> class_loader, bool* soft_error) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Can this type be assigned by src? Variant of IsAssignableFrom that doesn't // allow assignment to // an interface from an Object. diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 00b4cefb76..d389244f47 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -109,7 +109,6 @@ jfieldID WellKnownClasses::java_lang_Throwable_stackTrace; jfieldID WellKnownClasses::java_lang_Throwable_stackState; jfieldID WellKnownClasses::java_lang_Throwable_suppressedExceptions; jfieldID WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod; -jfieldID WellKnownClasses::java_lang_reflect_Field_artField; jfieldID WellKnownClasses::java_lang_reflect_Proxy_h; jfieldID WellKnownClasses::java_nio_DirectByteBuffer_capacity; jfieldID WellKnownClasses::java_nio_DirectByteBuffer_effectiveDirectAddress; @@ -244,7 +243,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Throwable_stackState = CacheField(env, java_lang_Throwable, false, "stackState", "Ljava/lang/Object;"); java_lang_Throwable_suppressedExceptions = CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;"); java_lang_reflect_AbstractMethod_artMethod = CacheField(env, java_lang_reflect_AbstractMethod, false, "artMethod", "Ljava/lang/reflect/ArtMethod;"); - java_lang_reflect_Field_artField = CacheField(env, java_lang_reflect_Field, false, "artField", "Ljava/lang/reflect/ArtField;"); java_lang_reflect_Proxy_h = CacheField(env, java_lang_reflect_Proxy, false, "h", "Ljava/lang/reflect/InvocationHandler;"); java_nio_DirectByteBuffer_capacity = CacheField(env, java_nio_DirectByteBuffer, false, "capacity", "I"); java_nio_DirectByteBuffer_effectiveDirectAddress = CacheField(env, java_nio_DirectByteBuffer, false, "effectiveDirectAddress", "J"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index 1a4f0f8b85..2df1c0e6b0 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -103,7 +103,6 @@ struct WellKnownClasses { static jfieldID dalvik_system_DexPathList__Element_dexFile; static jfieldID dalvik_system_PathClassLoader_pathList; static jfieldID java_lang_reflect_AbstractMethod_artMethod; - static jfieldID java_lang_reflect_Field_artField; static jfieldID java_lang_reflect_Proxy_h; static jfieldID java_lang_Thread_daemon; static jfieldID java_lang_Thread_group; diff --git a/test/046-reflect/src/Main.java b/test/046-reflect/src/Main.java index 3fe3881a8d..59f7001a58 100644 --- a/test/046-reflect/src/Main.java +++ b/test/046-reflect/src/Main.java @@ -696,27 +696,34 @@ public class Main { private static void checkGetDeclaredConstructor() { try { Method.class.getDeclaredConstructor().setAccessible(true); - System.out.print("Didn't get an exception from method getDeclaredConstructor"); + System.out.print("Didn't get an exception from Method.class.getDeclaredConstructor().setAccessible"); + } catch (SecurityException e) { } catch (NoSuchMethodException e) { } catch (Exception e) { System.out.print(e); } try { Field.class.getDeclaredConstructor().setAccessible(true); - System.out.print("Didn't get an exception from field getDeclaredConstructor"); + System.out.print("Didn't get an exception from Field.class.getDeclaredConstructor().setAccessible"); + } catch (SecurityException e) { } catch (NoSuchMethodException e) { } catch (Exception e) { System.out.print(e); } try { Class.class.getDeclaredConstructor().setAccessible(true); - System.out.print("Didn't get an exception from class getDeclaredConstructor()"); + System.out.print("Didn't get an exception from Class.class.getDeclaredConstructor().setAccessible"); } catch (SecurityException e) { + } catch (NoSuchMethodException e) { } catch (Exception e) { System.out.print(e); } } + static void checkPrivateFieldAccess() { + (new OtherClass()).test(); + } + public static void main(String[] args) throws Exception { Main test = new Main(); test.run(); @@ -730,6 +737,7 @@ public class Main { checkUnique(); checkParametrizedTypeEqualsAndHashCode(); checkGenericArrayTypeEqualsAndHashCode(); + checkPrivateFieldAccess(); } } @@ -801,41 +809,41 @@ class Target extends SuperTarget { } class FieldNoisyInit { - static { - System.out.println("FieldNoisyInit is initializing"); - //Throwable th = new Throwable(); - //th.printStackTrace(); - } + static { + System.out.println("FieldNoisyInit is initializing"); + //Throwable th = new Throwable(); + //th.printStackTrace(); + } } class FieldNoisyInitUser { - static { - System.out.println("FieldNoisyInitUser is initializing"); - } - public static int staticField; - public static FieldNoisyInit noisy; + static { + System.out.println("FieldNoisyInitUser is initializing"); + } + public static int staticField; + public static FieldNoisyInit noisy; } class MethodNoisyInit { - static { - System.out.println("MethodNoisyInit is initializing"); - //Throwable th = new Throwable(); - //th.printStackTrace(); - } + static { + System.out.println("MethodNoisyInit is initializing"); + //Throwable th = new Throwable(); + //th.printStackTrace(); + } } class MethodNoisyInitUser { - static { - System.out.println("MethodNoisyInitUser is initializing"); - } - public static void staticMethod() {} - public void createMethodNoisyInit(MethodNoisyInit ni) {} + static { + System.out.println("MethodNoisyInitUser is initializing"); + } + public static void staticMethod() {} + public void createMethodNoisyInit(MethodNoisyInit ni) {} } class Thrower { - public Thrower() throws UnsupportedOperationException { - throw new UnsupportedOperationException(); - } + public Thrower() throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } } class ParametrizedTypeTest { @@ -847,3 +855,17 @@ class GenericArrayTypeTest<T> { public void aMethod(T[] names) {} public void aMethodIdentical(T[] names) {} } + +class OtherClass { + private static final long LONG = 1234; + public void test() { + try { + Field field = getClass().getDeclaredField("LONG"); + if (1234 != field.getLong(null)) { + System.out.println("ERROR: values don't match"); + } + } catch (Exception e) { + System.out.println(e); + } + } +}
\ No newline at end of file diff --git a/test/465-checker-clinit-gvn/expected.txt b/test/465-checker-clinit-gvn/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/465-checker-clinit-gvn/expected.txt diff --git a/test/465-checker-clinit-gvn/info.txt b/test/465-checker-clinit-gvn/info.txt new file mode 100644 index 0000000000..ac28a8ff45 --- /dev/null +++ b/test/465-checker-clinit-gvn/info.txt @@ -0,0 +1 @@ +Check that we GVN HClinitCheck instructions. diff --git a/test/465-checker-clinit-gvn/src/Main.java b/test/465-checker-clinit-gvn/src/Main.java new file mode 100644 index 0000000000..dcaef6fcfe --- /dev/null +++ b/test/465-checker-clinit-gvn/src/Main.java @@ -0,0 +1,78 @@ +/* +* Copyright (C) 2015 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. +*/ + +class OtherClass { + static { + a = 42; + b = 54; + } + + static int a; + static int b; +} + +public final class Main { + + // CHECK-START: int Main.accessTwoStatics() GVN (before) + // CHECK-DAG: [[Class1:l\d+]] LoadClass + // CHECK-DAG: ClinitCheck [ [[Class1]] ] + // CHECK-DAG: [[Class2:l\d+]] LoadClass + // CHECK-DAG: ClinitCheck [ [[Class2]] ] + + // CHECK-START: int Main.accessTwoStatics() GVN (after) + // CHECK-DAG: [[Class:l\d+]] LoadClass + // CHECK-DAG: ClinitCheck [ [[Class]] ] + // CHECK-NOT: ClinitCheck + + public static int accessTwoStatics() { + return OtherClass.b - OtherClass.a; + } + + // CHECK-START: int Main.accessTwoStaticsCallInBetween() GVN (before) + // CHECK-DAG: [[Class1:l\d+]] LoadClass + // CHECK-DAG: ClinitCheck [ [[Class1]] ] + // CHECK-DAG: [[Class2:l\d+]] LoadClass + // CHECK-DAG: ClinitCheck [ [[Class2]] ] + + // CHECK-START: int Main.accessTwoStaticsCallInBetween() GVN (after) + // CHECK-DAG: [[Class:l\d+]] LoadClass + // CHECK-DAG: ClinitCheck [ [[Class]] ] + // CHECK-NOT: ClinitCheck + + public static int accessTwoStaticsCallInBetween() { + int b = OtherClass.b; + foo(); + return b - OtherClass.a; + } + + public static void foo() { + try { + Thread.sleep(0); + } catch (Exception e) { + throw new Error(e); + } + } + + public static void main(String[] args) { + if (accessTwoStatics() != 12) { + throw new Error("Expected 12"); + } + + if (accessTwoStaticsCallInBetween() != 12) { + throw new Error("Expected 12"); + } + } +} diff --git a/test/467-regalloc-pair/expected.txt b/test/467-regalloc-pair/expected.txt new file mode 100644 index 0000000000..da39d9da2b --- /dev/null +++ b/test/467-regalloc-pair/expected.txt @@ -0,0 +1 @@ +In interface diff --git a/test/467-regalloc-pair/info.txt b/test/467-regalloc-pair/info.txt new file mode 100644 index 0000000000..882a29c029 --- /dev/null +++ b/test/467-regalloc-pair/info.txt @@ -0,0 +1,2 @@ +Regression test for optimizing's register allocator +that used to trip when compiling TestCase.testCase on x86. diff --git a/test/467-regalloc-pair/smali/TestCase.smali b/test/467-regalloc-pair/smali/TestCase.smali new file mode 100644 index 0000000000..a3101feb12 --- /dev/null +++ b/test/467-regalloc-pair/smali/TestCase.smali @@ -0,0 +1,59 @@ +# Copyright (C) 2015 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. + +.class public LTestCase; + +.super Ljava/lang/Object; + +.method public static testCase([BLMain;)V + .registers 12 + const/4 v2, 0 + array-length v0, v10 + div-int/lit8 v0, v0, 7 + invoke-static {v2, v0}, Ljava/lang/Math;->max(II)I + move-result v7 + move v6, v2 + move v3, v2 + :label5 + if-ge v6, v7, :label1 + const-wide/16 v0, 0 + move-wide v4, v0 + move v1, v2 + move v0, v3 + :label4 + const/4 v3, 6 + if-ge v1, v3, :label2 + const/16 v3, 8 + shl-long/2addr v4, v3 + add-int/lit8 v3, v0, 1 + aget-byte v0, v10, v0 + if-gez v0, :label3 + add-int/lit16 v0, v0, 256 + :label3 + int-to-long v8, v0 + or-long/2addr v4, v8 + add-int/lit8 v0, v1, 1 + move v1, v0 + move v0, v3 + goto :label4 + :label2 + add-int/lit8 v3, v0, 1 + aget-byte v0, v10, v0 + invoke-interface {v11, v4, v5, v0}, LItf;->invokeInterface(JI)V + add-int/lit8 v0, v6, 1 + move v6, v0 + goto :label5 + :label1 + return-void +.end method diff --git a/compiler/dex/quick/mips64/backend_mips64.h b/test/467-regalloc-pair/src/Main.java index cc30ae06d8..aac07fdc22 100644 --- a/compiler/dex/quick/mips64/backend_mips64.h +++ b/test/467-regalloc-pair/src/Main.java @@ -14,19 +14,24 @@ * limitations under the License. */ -#ifndef ART_COMPILER_DEX_QUICK_MIPS64_BACKEND_MIPS64_H_ -#define ART_COMPILER_DEX_QUICK_MIPS64_BACKEND_MIPS64_H_ +import java.lang.reflect.Method; -namespace art { +interface Itf { + public void invokeInterface(long l, int i); +} -struct CompilationUnit; -class Mir2Lir; -class MIRGraph; -class ArenaAllocator; +public class Main implements Itf { -Mir2Lir* Mips64CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph, - ArenaAllocator* const arena); + // Workaround for b/18051191. + class InnerClass {} -} // namespace art + public static void main(String[] args) throws Exception { + Class<?> c = Class.forName("TestCase"); + Method m = c.getMethod("testCase", byte[].class, Main.class); + m.invoke(null, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }, new Main()); + } -#endif // ART_COMPILER_DEX_QUICK_MIPS64_BACKEND_MIPS64_H_ + public void invokeInterface(long l, int i) { + System.out.println("In interface"); + } +} diff --git a/test/468-bool-simplifier-regression/expected.txt b/test/468-bool-simplifier-regression/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/468-bool-simplifier-regression/expected.txt diff --git a/test/468-bool-simplifier-regression/info.txt b/test/468-bool-simplifier-regression/info.txt new file mode 100644 index 0000000000..0a465846b1 --- /dev/null +++ b/test/468-bool-simplifier-regression/info.txt @@ -0,0 +1,2 @@ +Regression test for optimizing's boolean simplifier +that used to trip when a boolean value was the input of an If. diff --git a/test/468-bool-simplifier-regression/smali/TestCase.smali b/test/468-bool-simplifier-regression/smali/TestCase.smali new file mode 100644 index 0000000000..f36304d333 --- /dev/null +++ b/test/468-bool-simplifier-regression/smali/TestCase.smali @@ -0,0 +1,32 @@ +# Copyright (C) 2015 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. + +.class public LTestCase; + +.super Ljava/lang/Object; + +.field public static value:Z + +.method public static testCase()Z + .registers 2 + sget-boolean v0, LTestCase;->value:Z + const/4 v1, 1 + if-eq v0, v1, :label1 + const/4 v1, 1 + goto :label2 + :label1 + const/4 v1, 0 + :label2 + return v1 +.end method diff --git a/test/468-bool-simplifier-regression/src/Main.java b/test/468-bool-simplifier-regression/src/Main.java new file mode 100644 index 0000000000..1dd27c9287 --- /dev/null +++ b/test/468-bool-simplifier-regression/src/Main.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 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. + */ + +import java.lang.reflect.*; + +public class Main { + public static boolean runTest(boolean input) throws Exception { + Class<?> c = Class.forName("TestCase"); + Method m = c.getMethod("testCase"); + Field f = c.getField("value"); + f.set(null, (Boolean) input); + return (Boolean) m.invoke(null); + } + + public static void main(String[] args) throws Exception { + if (runTest(true) != false) { + throw new Error("Expected false, got true"); + } + + if (runTest(false) != true) { + throw new Error("Expected true, got false"); + } + } +} diff --git a/test/run-all-tests b/test/run-all-tests index d0b3cf9abe..13490c46e4 100755 --- a/test/run-all-tests +++ b/test/run-all-tests @@ -112,6 +112,7 @@ while true; do shift; elif [ "x$1" = "x--always-clean" ]; then run_args="${run_args} --always-clean" + shift elif expr "x$1" : "x--" >/dev/null 2>&1; then echo "unknown $0 option: $1" 1>&2 usage="yes" diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt index 30d99bb695..2040b57f4f 100644 --- a/tools/libcore_failures.txt +++ b/tools/libcore_failures.txt @@ -63,21 +63,18 @@ "org.apache.harmony.tests.java.text.SimpleDateFormatTest#test_parseLjava_lang_StringLjava_text_ParsePosition"] }, { - description: "Failing due to missing localhost on volantis.", - result: EXEC_FAILED, - modes: [device], - names: ["org.apache.harmony.luni.tests.internal.net.www.protocol.http.HttpURLConnectionTest", - "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest", - "org.apache.harmony.luni.tests.java.net.URLConnectionTest"] -}, -{ description: "Failing due to missing localhost on hammerhead and volantis.", result: EXEC_FAILED, modes: [device], names: ["libcore.javax.crypto.CipherTest#testCipherInitWithCertificate", "libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithFtpURLConnection", "libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithJarFtpURLConnection", - "libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithLoggingSocketHandler" + "libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithLoggingSocketHandler", + "libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithHttpURLConnection", + "libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithJarHttpURLConnection", + "org.apache.harmony.luni.tests.internal.net.www.protocol.http.HttpURLConnectionTest", + "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest", + "org.apache.harmony.luni.tests.java.net.URLConnectionTest" ] }, { diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh new file mode 100755 index 0000000000..90c01f544c --- /dev/null +++ b/tools/run-jdwp-tests.sh @@ -0,0 +1,89 @@ +#!/bin/bash +# +# Copyright (C) 2015 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. + +if [ ! -d libcore ]; then + echo "Script needs to be run at the root of the android tree" + exit 1 +fi + +if [[ $ANDROID_SERIAL == 03a79ae90ae5889b ]] || [[ $ANDROID_SERIAL == HT4CTJT03670 ]] || [[ $ANDROID_SERIAL == HT49CJT00070 ]]; then + echo "Not run because of localhost failures. Investigating." + exit 0 +fi + +# Jar containing all the tests. +test_jar=out/host/linux-x86/framework/apache-harmony-jdwp-tests-hostdex.jar +junit_jar=out/host/linux-x86/framework/junit.jar + +if [ ! -f $test_jar -o ! -f $junit_jar ]; then + echo "Before running, you must build jdwp tests and vogar:" \ + "make junit apache-harmony-jdwp-tests-hostdex vogar vogar.jar" + exit 1 +fi + +art="/data/local/tmp/system/bin/art" +# We use Quick's image on target because optimizing's image is not compiled debuggable. +image="-Ximage:/data/art-test/core.art" +args=$@ +debuggee_args="-Xcompiler-option --compiler-backend=Optimizing -Xcompiler-option --debuggable" +device_dir="--device-dir=/data/local/tmp" +# We use the art script on target to ensure the runner and the debuggee share the same +# image. +vm_command="--vm-command=$art" +image_compiler_option="" + +while true; do + if [[ "$1" == "--mode=host" ]]; then + # Specify bash explicitly since the art script cannot, since it has to run on the device + # with mksh. + art="bash out/host/linux-x86/bin/art" + # We force generation of a new image to avoid build-time and run-time classpath differences. + image="-Ximage:/system/non/existent" + # We do not need a device directory on host. + device_dir="" + # Vogar knows which VM to use on host. + vm_command="" + # We only compile the image on the host. Note that not providing this option + # puts us below the adb command limit for vogar. + image_compiler_option="--vm-arg -Ximage-compiler-option --vm-arg --debuggable" + shift + elif [[ $1 == -Ximage:* ]]; then + image="$1" + shift + elif [[ "$1" == "" ]]; then + break + else + shift + fi +done + +# Run the tests using vogar. +vogar $vm_command \ + --vm-arg $image \ + --verbose \ + $args \ + $device_dir \ + $image_compiler_option \ + --timeout 600 \ + --vm-arg -Djpda.settings.verbose=true \ + --vm-arg -Djpda.settings.syncPort=34016 \ + --vm-arg -Djpda.settings.transportAddress=127.0.0.1:55107 \ + --vm-arg -Djpda.settings.debuggeeJavaPath="$art $image $debuggee_args" \ + --classpath $test_jar \ + --classpath $junit_jar \ + --vm-arg -Xcompiler-option --vm-arg --compiler-backend=Optimizing \ + --vm-arg -Xcompiler-option --vm-arg --debuggable \ + org.apache.harmony.jpda.tests.share.AllTests |