diff options
| -rw-r--r-- | compiler/dex/compiler_enums.h | 3 | ||||
| -rw-r--r-- | compiler/dex/quick/arm/call_arm.cc | 29 | ||||
| -rw-r--r-- | compiler/dex/quick/x86/assemble_x86.cc | 165 | ||||
| -rw-r--r-- | compiler/dex/quick/x86/codegen_x86.h | 15 | ||||
| -rw-r--r-- | compiler/dex/quick/x86/x86_lir.h | 8 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator.cc | 6 | ||||
| -rw-r--r-- | runtime/arch/arm/fault_handler_arm.cc | 135 | ||||
| -rw-r--r-- | runtime/arch/arm/quick_entrypoints_arm.S | 25 | ||||
| -rw-r--r-- | runtime/base/logging.h | 1 | ||||
| -rw-r--r-- | runtime/fault_handler.cc | 26 | ||||
| -rw-r--r-- | runtime/parsed_options.cc | 3 | ||||
| -rw-r--r-- | runtime/stack.cc | 58 | ||||
| -rw-r--r-- | runtime/stack.h | 8 | ||||
| -rw-r--r-- | runtime/thread.cc | 15 | ||||
| -rw-r--r-- | runtime/utils.cc | 6 | ||||
| -rw-r--r-- | runtime/verifier/method_verifier.cc | 5 | ||||
| -rwxr-xr-x | test/etc/push-and-run-test-jar | 2 |
17 files changed, 293 insertions, 217 deletions
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index cbb2c300f2..05ab8ca3fb 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -129,7 +129,7 @@ enum ExtendedMIROpcode { kMirOpLast, }; -enum MIROptimizationFlagPositons { +enum MIROptimizationFlagPositions { kMIRIgnoreNullCheck = 0, kMIRNullCheckOnly, kMIRIgnoreRangeCheck, @@ -141,6 +141,7 @@ enum MIROptimizationFlagPositons { kMIRIgnoreSuspendCheck, kMIRDup, kMIRMark, // Temporary node mark. + kMIRLastMIRFlag, }; // For successor_block_list. diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index 163c0fed4a..d3477c9af3 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -360,6 +360,22 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { if (Runtime::Current()->ExplicitStackOverflowChecks()) { /* Load stack limit */ Load32Disp(rs_rARM_SELF, Thread::StackEndOffset<4>().Int32Value(), rs_r12); + } else { + // Implicit stack overflow check. + // Generate a load from [sp, #-overflowsize]. If this is in the stack + // redzone we will get a segmentation fault. + // + // Caveat coder: if someone changes the kStackOverflowReservedBytes value + // we need to make sure that it's loadable in an immediate field of + // a sub instruction. Otherwise we will get a temp allocation and the + // code size will increase. + // + // This is done before the callee save instructions to avoid any possibility + // of these overflowing. This uses r12 and that's never saved in a callee + // save. + OpRegRegImm(kOpSub, rs_r12, rs_rARM_SP, Thread::kStackOverflowReservedBytes); + Load32Disp(rs_r12, 0, rs_r12); + MarkPossibleStackOverflowException(); } } /* Spill core callee saves */ @@ -418,17 +434,8 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { AddSlowPath(new(arena_)StackOverflowSlowPath(this, branch, false, frame_size_)); } } else { - // Implicit stack overflow check. - // Generate a load from [sp, #-overflowsize]. If this is in the stack - // redzone we will get a segmentation fault. - // - // Caveat coder: if someone changes the kStackOverflowReservedBytes value - // we need to make sure that it's loadable in an immediate field of - // a sub instruction. Otherwise we will get a temp allocation and the - // code size will increase. - OpRegRegImm(kOpSub, rs_r12, rs_rARM_SP, Thread::kStackOverflowReservedBytes); - Load32Disp(rs_r12, 0, rs_r12); - MarkPossibleStackOverflowException(); + // Implicit stack overflow check has already been done. Just make room on the + // stack for the frame now. OpRegImm(kOpSub, rs_rARM_SP, frame_size_without_spills); } } else { diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc index 0ce081b975..7436e3960a 100644 --- a/compiler/dex/quick/x86/assemble_x86.cc +++ b/compiler/dex/quick/x86/assemble_x86.cc @@ -25,7 +25,7 @@ namespace art { const X86EncodingMap X86Mir2Lir::EncodingMap[kX86Last] = { { kX8632BitData, kData, IS_UNARY_OP, { 0, 0, 0x00, 0, 0, 0, 0, 4 }, "data", "0x!0d" }, { kX86Bkpt, kNullary, NO_OPERAND | IS_BRANCH, { 0, 0, 0xCC, 0, 0, 0, 0, 0 }, "int 3", "" }, - { kX86Nop, kNop, IS_UNARY_OP, { 0, 0, 0x90, 0, 0, 0, 0, 0 }, "nop", "" }, + { kX86Nop, kNop, NO_OPERAND, { 0, 0, 0x90, 0, 0, 0, 0, 0 }, "nop", "" }, #define ENCODING_MAP(opname, mem_use, reg_def, uses_ccodes, \ rm8_r8, rm32_r32, \ @@ -175,12 +175,14 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { kX86Mov32AI, kArrayImm, IS_STORE | IS_QUIN_OP | REG_USE01, { 0, 0, 0xC7, 0, 0, 0, 0, 4 }, "Mov32AI", "[!0r+!1r<<!2d+!3d],!4d" }, { kX86Mov32TI, kThreadImm, IS_STORE | IS_BINARY_OP, { THREAD_PREFIX, 0, 0xC7, 0, 0, 0, 0, 4 }, "Mov32TI", "fs:[!0d],!1d" }, - { kX86Lea32RM, kRegMem, IS_TERTIARY_OP | IS_LOAD | REG_DEF0_USE1, { 0, 0, 0x8D, 0, 0, 0, 0, 0 }, "Lea32RM", "!0r,[!1r+!2d]" }, + { kX86Lea32RM, kRegMem, IS_TERTIARY_OP | IS_LOAD | REG_DEF0_USE1, { 0, 0, 0x8D, 0, 0, 0, 0, 0 }, "Lea32RM", "!0r,[!1r+!2d]" }, { kX86Lea32RA, kRegArray, IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8D, 0, 0, 0, 0, 0 }, "Lea32RA", "!0r,[!1r+!2r<<!3d+!4d]" }, { kX86Cmov32RRC, kRegRegCond, IS_TERTIARY_OP | REG_DEF0_USE01 | USES_CCODES, {0, 0, 0x0F, 0x40, 0, 0, 0, 0}, "Cmovcc32RR", "!2c !0r,!1r" }, + { kX86Cmov32RMC, kRegMemCond, IS_QUAD_OP | IS_LOAD | REG_DEF0_USE01 | USES_CCODES, {0, 0, 0x0F, 0x40, 0, 0, 0, 0}, "Cmovcc32RM", "!3c !0r,[!1r+!2d]" }, + #define SHIFT_ENCODING_MAP(opname, modrm_opcode) \ { kX86 ## opname ## 8RI, kShiftRegImm, IS_BINARY_OP | REG_DEF0_USE0 | SETS_CCODES, { 0, 0, 0xC0, 0, 0, modrm_opcode, 0xD1, 1 }, #opname "8RI", "!0r,!1d" }, \ { kX86 ## opname ## 8MI, kShiftMemImm, IS_LOAD | IS_STORE | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xC0, 0, 0, modrm_opcode, 0xD1, 1 }, #opname "8MI", "[!0r+!1d],!2d" }, \ @@ -213,8 +215,10 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, #undef SHIFT_ENCODING_MAP { kX86Cmc, kNullary, NO_OPERAND, { 0, 0, 0xF5, 0, 0, 0, 0, 0}, "Cmc", "" }, - { kX86Shld32RRI, kRegRegImmRev, IS_TERTIARY_OP | REG_DEF0_USE01 | SETS_CCODES, { 0, 0, 0x0F, 0xA4, 0, 0, 0, 1}, "Shld32", "!0r,!1r,!2d" }, - { kX86Shrd32RRI, kRegRegImmRev, IS_TERTIARY_OP | REG_DEF0_USE01 | SETS_CCODES, { 0, 0, 0x0F, 0xAC, 0, 0, 0, 1}, "Shrd32", "!0r,!1r,!2d" }, + { kX86Shld32RRI, kRegRegImmRev, IS_TERTIARY_OP | REG_DEF0_USE01 | SETS_CCODES, { 0, 0, 0x0F, 0xA4, 0, 0, 0, 1}, "Shld32RRI", "!0r,!1r,!2d" }, + { kX86Shld32MRI, kMemRegImm, IS_QUAD_OP | REG_USE02 | IS_LOAD | IS_STORE | SETS_CCODES, { 0, 0, 0x0F, 0xA4, 0, 0, 0, 1}, "Shld32MRI", "[!0r+!1d],!2r,!3d" }, + { kX86Shrd32RRI, kRegRegImmRev, IS_TERTIARY_OP | REG_DEF0_USE01 | SETS_CCODES, { 0, 0, 0x0F, 0xAC, 0, 0, 0, 1}, "Shrd32RRI", "!0r,!1r,!2d" }, + { kX86Shrd32MRI, kMemRegImm, IS_QUAD_OP | REG_USE02 | IS_LOAD | IS_STORE | SETS_CCODES, { 0, 0, 0x0F, 0xAC, 0, 0, 0, 1}, "Shrd32MRI", "[!0r+!1d],!2r,!3d" }, { kX86Test8RI, kRegImm, IS_BINARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1}, "Test8RI", "!0r,!1d" }, { kX86Test8MI, kMemImm, IS_LOAD | IS_TERTIARY_OP | REG_USE0 | SETS_CCODES, { 0, 0, 0xF6, 0, 0, 0, 0, 1}, "Test8MI", "[!0r+!1d],!2d" }, @@ -233,15 +237,15 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, arr, arr_kind, arr_flags, imm, \ b_flags, hw_flags, w_flags, \ b_format, hw_format, w_format) \ -{ kX86 ## opname ## 8 ## reg, reg_kind, reg_flags | b_flags | sets_ccodes, { 0, 0, 0xF6, 0, 0, modrm, 0, imm << 0}, #opname "8" #reg, #b_format "!0r" }, \ -{ kX86 ## opname ## 8 ## mem, mem_kind, IS_LOAD | is_store | mem_flags | b_flags | sets_ccodes, { 0, 0, 0xF6, 0, 0, modrm, 0, imm << 0}, #opname "8" #mem, #b_format "[!0r+!1d]" }, \ -{ kX86 ## opname ## 8 ## arr, arr_kind, IS_LOAD | is_store | arr_flags | b_flags | sets_ccodes, { 0, 0, 0xF6, 0, 0, modrm, 0, imm << 0}, #opname "8" #arr, #b_format "[!0r+!1r<<!2d+!3d]" }, \ -{ kX86 ## opname ## 16 ## reg, reg_kind, reg_flags | hw_flags | sets_ccodes, { 0x66, 0, 0xF7, 0, 0, modrm, 0, imm << 1}, #opname "16" #reg, #hw_format "!0r" }, \ -{ kX86 ## opname ## 16 ## mem, mem_kind, IS_LOAD | is_store | mem_flags | hw_flags | sets_ccodes, { 0x66, 0, 0xF7, 0, 0, modrm, 0, imm << 1}, #opname "16" #mem, #hw_format "[!0r+!1d]" }, \ -{ kX86 ## opname ## 16 ## arr, arr_kind, IS_LOAD | is_store | arr_flags | hw_flags | sets_ccodes, { 0x66, 0, 0xF7, 0, 0, modrm, 0, imm << 1}, #opname "16" #arr, #hw_format "[!0r+!1r<<!2d+!3d]" }, \ -{ kX86 ## opname ## 32 ## reg, reg_kind, reg_flags | w_flags | sets_ccodes, { 0, 0, 0xF7, 0, 0, modrm, 0, imm << 2}, #opname "32" #reg, #w_format "!0r" }, \ -{ kX86 ## opname ## 32 ## mem, mem_kind, IS_LOAD | is_store | mem_flags | w_flags | sets_ccodes, { 0, 0, 0xF7, 0, 0, modrm, 0, imm << 2}, #opname "32" #mem, #w_format "[!0r+!1d]" }, \ -{ kX86 ## opname ## 32 ## arr, arr_kind, IS_LOAD | is_store | arr_flags | w_flags | sets_ccodes, { 0, 0, 0xF7, 0, 0, modrm, 0, imm << 2}, #opname "32" #arr, #w_format "[!0r+!1r<<!2d+!3d]" } +{ kX86 ## opname ## 8 ## reg, reg_kind, reg_flags | b_flags | sets_ccodes, { 0, 0, 0xF6, 0, 0, modrm, 0, imm << 0}, #opname "8" #reg, b_format "!0r" }, \ +{ kX86 ## opname ## 8 ## mem, mem_kind, IS_LOAD | is_store | mem_flags | b_flags | sets_ccodes, { 0, 0, 0xF6, 0, 0, modrm, 0, imm << 0}, #opname "8" #mem, b_format "[!0r+!1d]" }, \ +{ kX86 ## opname ## 8 ## arr, arr_kind, IS_LOAD | is_store | arr_flags | b_flags | sets_ccodes, { 0, 0, 0xF6, 0, 0, modrm, 0, imm << 0}, #opname "8" #arr, b_format "[!0r+!1r<<!2d+!3d]" }, \ +{ kX86 ## opname ## 16 ## reg, reg_kind, reg_flags | hw_flags | sets_ccodes, { 0x66, 0, 0xF7, 0, 0, modrm, 0, imm << 1}, #opname "16" #reg, hw_format "!0r" }, \ +{ kX86 ## opname ## 16 ## mem, mem_kind, IS_LOAD | is_store | mem_flags | hw_flags | sets_ccodes, { 0x66, 0, 0xF7, 0, 0, modrm, 0, imm << 1}, #opname "16" #mem, hw_format "[!0r+!1d]" }, \ +{ kX86 ## opname ## 16 ## arr, arr_kind, IS_LOAD | is_store | arr_flags | hw_flags | sets_ccodes, { 0x66, 0, 0xF7, 0, 0, modrm, 0, imm << 1}, #opname "16" #arr, hw_format "[!0r+!1r<<!2d+!3d]" }, \ +{ kX86 ## opname ## 32 ## reg, reg_kind, reg_flags | w_flags | sets_ccodes, { 0, 0, 0xF7, 0, 0, modrm, 0, imm << 2}, #opname "32" #reg, w_format "!0r" }, \ +{ kX86 ## opname ## 32 ## mem, mem_kind, IS_LOAD | is_store | mem_flags | w_flags | sets_ccodes, { 0, 0, 0xF7, 0, 0, modrm, 0, imm << 2}, #opname "32" #mem, w_format "[!0r+!1d]" }, \ +{ kX86 ## opname ## 32 ## arr, arr_kind, IS_LOAD | is_store | arr_flags | w_flags | sets_ccodes, { 0, 0, 0xF7, 0, 0, modrm, 0, imm << 2}, #opname "32" #arr, w_format "[!0r+!1r<<!2d+!3d]" } UNARY_ENCODING_MAP(Not, 0x2, IS_STORE, 0, R, kReg, IS_UNARY_OP | REG_DEF0_USE0, M, kMem, IS_BINARY_OP | REG_USE0, A, kArray, IS_QUAD_OP | REG_USE01, 0, 0, 0, 0, "", "", ""), UNARY_ENCODING_MAP(Neg, 0x3, IS_STORE, SETS_CCODES, R, kReg, IS_UNARY_OP | REG_DEF0_USE0, M, kMem, IS_BINARY_OP | REG_USE0, A, kArray, IS_QUAD_OP | REG_USE01, 0, 0, 0, 0, "", "", ""), @@ -258,9 +262,9 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { kX86Pop32R, kRegOpcode, IS_UNARY_OP | REG_DEF0 | REG_USE_SP | REG_DEF_SP | IS_LOAD, { 0, 0, 0x58, 0, 0, 0, 0, 0 }, "Pop32R", "!0r" }, #define EXT_0F_ENCODING_MAP(opname, prefix, opcode, reg_def) \ -{ kX86 ## opname ## RR, kRegReg, IS_BINARY_OP | reg_def | REG_USE01, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0 }, #opname "RR", "!0r,!1r" }, \ -{ kX86 ## opname ## RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | reg_def | REG_USE01, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0 }, #opname "RM", "!0r,[!1r+!2d]" }, \ -{ kX86 ## opname ## RA, kRegArray, IS_LOAD | IS_QUIN_OP | reg_def | REG_USE012, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0 }, #opname "RA", "!0r,[!1r+!2r<<!3d+!4d]" } +{ kX86 ## opname ## RR, kRegReg, IS_BINARY_OP | reg_def | REG_USE1, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0 }, #opname "RR", "!0r,!1r" }, \ +{ kX86 ## opname ## RM, kRegMem, IS_LOAD | IS_TERTIARY_OP | reg_def | REG_USE1, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0 }, #opname "RM", "!0r,[!1r+!2d]" }, \ +{ kX86 ## opname ## RA, kRegArray, IS_LOAD | IS_QUIN_OP | reg_def | REG_USE12, { prefix, 0, 0x0F, opcode, 0, 0, 0, 0 }, #opname "RA", "!0r,[!1r+!2r<<!3d+!4d]" } EXT_0F_ENCODING_MAP(Movsd, 0xF2, 0x10, REG_DEF0), { kX86MovsdMR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02, { 0xF2, 0, 0x0F, 0x11, 0, 0, 0, 0 }, "MovsdMR", "[!0r+!1d],!2r" }, @@ -276,23 +280,23 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, EXT_0F_ENCODING_MAP(Cvttss2si, 0xF3, 0x2C, REG_DEF0), EXT_0F_ENCODING_MAP(Cvtsd2si, 0xF2, 0x2D, REG_DEF0), EXT_0F_ENCODING_MAP(Cvtss2si, 0xF3, 0x2D, REG_DEF0), - EXT_0F_ENCODING_MAP(Ucomisd, 0x66, 0x2E, SETS_CCODES), - EXT_0F_ENCODING_MAP(Ucomiss, 0x00, 0x2E, SETS_CCODES), - EXT_0F_ENCODING_MAP(Comisd, 0x66, 0x2F, SETS_CCODES), - EXT_0F_ENCODING_MAP(Comiss, 0x00, 0x2F, SETS_CCODES), - EXT_0F_ENCODING_MAP(Orps, 0x00, 0x56, REG_DEF0), - EXT_0F_ENCODING_MAP(Xorps, 0x00, 0x57, REG_DEF0), - EXT_0F_ENCODING_MAP(Addsd, 0xF2, 0x58, REG_DEF0), - EXT_0F_ENCODING_MAP(Addss, 0xF3, 0x58, REG_DEF0), - EXT_0F_ENCODING_MAP(Mulsd, 0xF2, 0x59, REG_DEF0), - EXT_0F_ENCODING_MAP(Mulss, 0xF3, 0x59, REG_DEF0), + EXT_0F_ENCODING_MAP(Ucomisd, 0x66, 0x2E, SETS_CCODES|REG_USE0), + EXT_0F_ENCODING_MAP(Ucomiss, 0x00, 0x2E, SETS_CCODES|REG_USE0), + EXT_0F_ENCODING_MAP(Comisd, 0x66, 0x2F, SETS_CCODES|REG_USE0), + EXT_0F_ENCODING_MAP(Comiss, 0x00, 0x2F, SETS_CCODES|REG_USE0), + EXT_0F_ENCODING_MAP(Orps, 0x00, 0x56, REG_DEF0_USE0), + EXT_0F_ENCODING_MAP(Xorps, 0x00, 0x57, REG_DEF0_USE0), + EXT_0F_ENCODING_MAP(Addsd, 0xF2, 0x58, REG_DEF0_USE0), + EXT_0F_ENCODING_MAP(Addss, 0xF3, 0x58, REG_DEF0_USE0), + EXT_0F_ENCODING_MAP(Mulsd, 0xF2, 0x59, REG_DEF0_USE0), + EXT_0F_ENCODING_MAP(Mulss, 0xF3, 0x59, REG_DEF0_USE0), EXT_0F_ENCODING_MAP(Cvtsd2ss, 0xF2, 0x5A, REG_DEF0), EXT_0F_ENCODING_MAP(Cvtss2sd, 0xF3, 0x5A, REG_DEF0), - EXT_0F_ENCODING_MAP(Subsd, 0xF2, 0x5C, REG_DEF0), - EXT_0F_ENCODING_MAP(Subss, 0xF3, 0x5C, REG_DEF0), - EXT_0F_ENCODING_MAP(Divsd, 0xF2, 0x5E, REG_DEF0), - EXT_0F_ENCODING_MAP(Divss, 0xF3, 0x5E, REG_DEF0), - EXT_0F_ENCODING_MAP(Punpckldq, 0x66, 0x62, REG_DEF0), + EXT_0F_ENCODING_MAP(Subsd, 0xF2, 0x5C, REG_DEF0_USE0), + EXT_0F_ENCODING_MAP(Subss, 0xF3, 0x5C, REG_DEF0_USE0), + EXT_0F_ENCODING_MAP(Divsd, 0xF2, 0x5E, REG_DEF0_USE0), + EXT_0F_ENCODING_MAP(Divss, 0xF3, 0x5E, REG_DEF0_USE0), + EXT_0F_ENCODING_MAP(Punpckldq, 0x66, 0x62, REG_DEF0_USE0), { kX86PsrlqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 2, 0, 1 }, "PsrlqRI", "!0r,!1d" }, { kX86PsllqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 6, 0, 1 }, "PsllqRI", "!0r,!1d" }, @@ -322,7 +326,7 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { kX86MovhpsAR, kArrayReg, IS_STORE | IS_QUIN_OP | REG_USE014, { 0x0, 0, 0x0F, 0x17, 0, 0, 0, 0 }, "MovhpsAR", "[!0r+!1r<<!2d+!3d],!4r" }, EXT_0F_ENCODING_MAP(Movdxr, 0x66, 0x6E, REG_DEF0), - { kX86MovdrxRR, kRegRegStore, IS_BINARY_OP | REG_DEF0 | REG_USE01, { 0x66, 0, 0x0F, 0x7E, 0, 0, 0, 0 }, "MovdrxRR", "!0r,!1r" }, + { kX86MovdrxRR, kRegRegStore, IS_BINARY_OP | REG_DEF0 | REG_USE1, { 0x66, 0, 0x0F, 0x7E, 0, 0, 0, 0 }, "MovdrxRR", "!0r,!1r" }, { kX86MovdrxMR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02, { 0x66, 0, 0x0F, 0x7E, 0, 0, 0, 0 }, "MovdrxMR", "[!0r+!1d],!2r" }, { kX86MovdrxAR, kArrayReg, IS_STORE | IS_QUIN_OP | REG_USE014, { 0x66, 0, 0x0F, 0x7E, 0, 0, 0, 0 }, "MovdrxAR", "[!0r+!1r<<!2d+!3d],!4r" }, @@ -334,8 +338,8 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, // Encode the modrm opcode as an extra opcode byte to avoid computation during assembly. { kX86Mfence, kReg, NO_OPERAND, { 0, 0, 0x0F, 0xAE, 0, 6, 0, 0 }, "Mfence", "" }, - EXT_0F_ENCODING_MAP(Imul16, 0x66, 0xAF, REG_DEF0 | SETS_CCODES), - EXT_0F_ENCODING_MAP(Imul32, 0x00, 0xAF, REG_DEF0 | SETS_CCODES), + EXT_0F_ENCODING_MAP(Imul16, 0x66, 0xAF, REG_USE0 | REG_DEF0 | SETS_CCODES), + EXT_0F_ENCODING_MAP(Imul32, 0x00, 0xAF, REG_USE0 | REG_DEF0 | SETS_CCODES), { kX86CmpxchgRR, kRegRegStore, IS_BINARY_OP | REG_DEF0 | REG_USE01 | REG_DEFA_USEA | SETS_CCODES, { 0, 0, 0x0F, 0xB1, 0, 0, 0, 0 }, "Cmpxchg", "!0r,!1r" }, { kX86CmpxchgMR, kMemReg, IS_STORE | IS_TERTIARY_OP | REG_USE02 | REG_DEFA_USEA | SETS_CCODES, { 0, 0, 0x0F, 0xB1, 0, 0, 0, 0 }, "Cmpxchg", "[!0r+!1d],!2r" }, @@ -369,7 +373,7 @@ ENCODING_MAP(Cmp, IS_LOAD, 0, 0, { kX86StartOfMethod, kMacro, IS_UNARY_OP | SETS_CCODES, { 0, 0, 0, 0, 0, 0, 0, 0 }, "StartOfMethod", "!0r" }, { kX86PcRelLoadRA, kPcRel, IS_LOAD | IS_QUIN_OP | REG_DEF0_USE12, { 0, 0, 0x8B, 0, 0, 0, 0, 0 }, "PcRelLoadRA", "!0r,[!1r+!2r<<!3d+!4p]" }, { kX86PcRelAdr, kPcRel, IS_LOAD | IS_BINARY_OP | REG_DEF0, { 0, 0, 0xB8, 0, 0, 0, 0, 4 }, "PcRelAdr", "!0r,!1d" }, - { kX86RepneScasw, kPrefix2Nullary, NO_OPERAND | SETS_CCODES, { 0x66, 0xF2, 0xAF, 0, 0, 0, 0, 0 }, "RepNE ScasW", "" }, + { kX86RepneScasw, kPrefix2Nullary, NO_OPERAND | REG_USEA | REG_USEC | SETS_CCODES, { 0x66, 0xF2, 0xAF, 0, 0, 0, 0, 0 }, "RepNE ScasW", "" }, }; static size_t ComputeSize(const X86EncodingMap* entry, int base, int displacement, bool has_sib) { @@ -425,6 +429,8 @@ int X86Mir2Lir::GetInsnSize(LIR* lir) { return ComputeSize(entry, lir->operands[0], lir->operands[3], true); case kMemReg: // lir operands - 0: base, 1: disp, 2: reg return ComputeSize(entry, lir->operands[0], lir->operands[1], false); + case kMemRegImm: // lir operands - 0: base, 1: disp, 2: reg 3: immediate + return ComputeSize(entry, lir->operands[0], lir->operands[1], false); case kArrayReg: // lir operands - 0: base, 1: index, 2: scale, 3: disp, 4: reg return ComputeSize(entry, lir->operands[0], lir->operands[3], true); case kThreadReg: // lir operands - 0: disp, 1: reg @@ -489,6 +495,8 @@ int X86Mir2Lir::GetInsnSize(LIR* lir) { return ComputeSize(entry, lir->operands[0], lir->operands[3], true); case kRegRegCond: // lir operands - 0: reg, 1: reg, 2: cond return ComputeSize(entry, 0, 0, false); + case kRegMemCond: // lir operands - 0: reg, 1: reg, 2: disp, 3:cond + return ComputeSize(entry, lir->operands[1], lir->operands[2], false); case kJcc: if (lir->opcode == kX86Jcc8) { return 2; // opcode + rel8 @@ -729,6 +737,14 @@ void X86Mir2Lir::EmitArrayReg(const X86EncodingMap* entry, uint8_t base, uint8_t EmitRegArray(entry, reg, base, index, scale, disp); } +void X86Mir2Lir::EmitArrayImm(const X86EncodingMap* entry, uint8_t base, uint8_t index, int scale, + int disp, int32_t imm) { + EmitPrefixAndOpcode(entry); + EmitModrmSibDisp(entry->skeleton.modrm_opcode, base, index, scale, disp); + DCHECK_EQ(0, entry->skeleton.ax_opcode); + EmitImm(entry, static_cast<int16_t>(imm)); +} + void X86Mir2Lir::EmitRegThread(const X86EncodingMap* entry, uint8_t reg, int disp) { DCHECK_NE(entry->skeleton.prefix1, 0); EmitPrefixAndOpcode(entry); @@ -788,6 +804,11 @@ void X86Mir2Lir::EmitRegMemImm(const X86EncodingMap* entry, EmitImm(entry, imm); } +void X86Mir2Lir::EmitMemRegImm(const X86EncodingMap* entry, + uint8_t base, int disp, uint8_t reg, int32_t imm) { + EmitRegMemImm(entry, reg, base, disp, imm); +} + void X86Mir2Lir::EmitRegImm(const X86EncodingMap* entry, uint8_t reg, int imm) { if (entry->skeleton.prefix1 != 0) { code_buffer_.push_back(entry->skeleton.prefix1); @@ -889,6 +910,26 @@ void X86Mir2Lir::EmitShiftMemCl(const X86EncodingMap* entry, uint8_t base, DCHECK_EQ(0, entry->skeleton.immediate_bytes); } +void X86Mir2Lir::EmitShiftMemImm(const X86EncodingMap* entry, uint8_t base, + int displacement, int imm) { + EmitPrefix(entry); + if (imm != 1) { + code_buffer_.push_back(entry->skeleton.opcode); + } else { + // Shorter encoding for 1 bit shift + code_buffer_.push_back(entry->skeleton.ax_opcode); + } + DCHECK_NE(0x0F, entry->skeleton.opcode); + DCHECK_EQ(0, entry->skeleton.extra_opcode1); + DCHECK_EQ(0, entry->skeleton.extra_opcode2); + EmitModrmDisp(entry->skeleton.modrm_opcode, base, displacement); + if (imm != 1) { + DCHECK_EQ(entry->skeleton.immediate_bytes, 1); + DCHECK(IS_SIMM8(imm)); + code_buffer_.push_back(imm & 0xFF); + } +} + void X86Mir2Lir::EmitRegCond(const X86EncodingMap* entry, uint8_t reg, uint8_t condition) { if (entry->skeleton.prefix1 != 0) { code_buffer_.push_back(entry->skeleton.prefix1); @@ -910,6 +951,25 @@ void X86Mir2Lir::EmitRegCond(const X86EncodingMap* entry, uint8_t reg, uint8_t c DCHECK_EQ(entry->skeleton.immediate_bytes, 0); } +void X86Mir2Lir::EmitMemCond(const X86EncodingMap* entry, uint8_t base, int displacement, uint8_t condition) { + if (entry->skeleton.prefix1 != 0) { + code_buffer_.push_back(entry->skeleton.prefix1); + if (entry->skeleton.prefix2 != 0) { + code_buffer_.push_back(entry->skeleton.prefix2); + } + } else { + DCHECK_EQ(0, entry->skeleton.prefix2); + } + DCHECK_EQ(0, entry->skeleton.ax_opcode); + DCHECK_EQ(0x0F, entry->skeleton.opcode); + code_buffer_.push_back(0x0F); + DCHECK_EQ(0x90, entry->skeleton.extra_opcode1); + code_buffer_.push_back(0x90 | condition); + DCHECK_EQ(0, entry->skeleton.extra_opcode2); + EmitModrmDisp(entry->skeleton.modrm_opcode, base, displacement); + DCHECK_EQ(entry->skeleton.immediate_bytes, 0); +} + void X86Mir2Lir::EmitRegRegCond(const X86EncodingMap* entry, uint8_t reg1, uint8_t reg2, uint8_t condition) { // Generate prefix and opcode without the condition @@ -935,6 +995,24 @@ void X86Mir2Lir::EmitRegRegCond(const X86EncodingMap* entry, uint8_t reg1, uint8 code_buffer_.push_back(modrm); } +void X86Mir2Lir::EmitRegMemCond(const X86EncodingMap* entry, uint8_t reg1, uint8_t base, int displacement, uint8_t condition) { + // Generate prefix and opcode without the condition + EmitPrefixAndOpcode(entry); + + // Now add the condition. The last byte of opcode is the one that receives it. + DCHECK_LE(condition, 0xF); + code_buffer_.back() += condition; + + DCHECK_EQ(0, entry->skeleton.immediate_bytes); + DCHECK_EQ(0, entry->skeleton.modrm_opcode); + + // Check that registers requested for encoding are sane. + DCHECK_LT(reg1, 8); + DCHECK_LT(base, 8); + + EmitModrmDisp(reg1, base, displacement); +} + void X86Mir2Lir::EmitJmp(const X86EncodingMap* entry, int rel) { if (entry->opcode == kX86Jmp8) { DCHECK(IS_SIMM8(rel)); @@ -1254,6 +1332,10 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(CodeOffset start_addr) { case kMemImm: // lir operands - 0: base, 1: disp, 2: immediate EmitMemImm(entry, lir->operands[0], lir->operands[1], lir->operands[2]); break; + case kArrayImm: // lir operands - 0: base, 1: index, 2: disp, 3:scale, 4:immediate + EmitArrayImm(entry, lir->operands[0], lir->operands[1], lir->operands[2], + lir->operands[3], lir->operands[4]); + break; case kArrayReg: // lir operands - 0: base, 1: index, 2: scale, 3: disp, 4: reg EmitArrayReg(entry, lir->operands[0], lir->operands[1], lir->operands[2], lir->operands[3], lir->operands[4]); @@ -1277,6 +1359,10 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(CodeOffset start_addr) { case kRegRegImmRev: EmitRegRegImmRev(entry, lir->operands[0], lir->operands[1], lir->operands[2]); break; + case kMemRegImm: + EmitMemRegImm(entry, lir->operands[0], lir->operands[1], lir->operands[2], + lir->operands[3]); + break; case kRegRegImm: EmitRegRegImm(entry, lir->operands[0], lir->operands[1], lir->operands[2]); break; @@ -1296,6 +1382,9 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(CodeOffset start_addr) { case kShiftRegImm: // lir operands - 0: reg, 1: immediate EmitShiftRegImm(entry, lir->operands[0], lir->operands[1]); break; + case kShiftMemImm: // lir operands - 0: base, 1: disp, 2:immediate + EmitShiftMemImm(entry, lir->operands[0], lir->operands[1], lir->operands[2]); + break; case kShiftRegCl: // lir operands - 0: reg, 1: cl EmitShiftRegCl(entry, lir->operands[0], lir->operands[1]); break; @@ -1305,9 +1394,15 @@ AssemblerStatus X86Mir2Lir::AssembleInstructions(CodeOffset start_addr) { case kRegCond: // lir operands - 0: reg, 1: condition EmitRegCond(entry, lir->operands[0], lir->operands[1]); break; + case kMemCond: // lir operands - 0: base, 1: displacement, 2: condition + EmitMemCond(entry, lir->operands[0], lir->operands[1], lir->operands[2]); + break; case kRegRegCond: // lir operands - 0: reg, 1: reg, 2: condition EmitRegRegCond(entry, lir->operands[0], lir->operands[1], lir->operands[2]); break; + case kRegMemCond: // lir operands - 0: reg, 1: reg, displacement, 3: condition + EmitRegMemCond(entry, lir->operands[0], lir->operands[1], lir->operands[2], lir->operands[3]); + break; case kJmp: // lir operands - 0: rel if (entry->opcode == kX86JmpT) { // This works since the instruction format for jmp and call is basically the same and diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index c3ea55fb39..8f0490c361 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -331,19 +331,24 @@ class X86Mir2Lir FINAL : public Mir2Lir { int scale, int disp); void EmitArrayReg(const X86EncodingMap* entry, uint8_t base, uint8_t index, int scale, int disp, uint8_t reg); + void EmitArrayImm(const X86EncodingMap* entry, uint8_t base, uint8_t index, int scale, int disp, + int32_t imm); void EmitRegThread(const X86EncodingMap* entry, uint8_t reg, int disp); void EmitRegReg(const X86EncodingMap* entry, uint8_t reg1, uint8_t reg2); void EmitRegRegImm(const X86EncodingMap* entry, uint8_t reg1, uint8_t reg2, int32_t imm); void EmitRegRegImmRev(const X86EncodingMap* entry, uint8_t reg1, uint8_t reg2, int32_t imm); void EmitRegMemImm(const X86EncodingMap* entry, uint8_t reg1, uint8_t base, int disp, int32_t imm); + void EmitMemRegImm(const X86EncodingMap* entry, uint8_t base, int disp, uint8_t reg1, int32_t imm); void EmitRegImm(const X86EncodingMap* entry, uint8_t reg, int imm); void EmitThreadImm(const X86EncodingMap* entry, int disp, int imm); void EmitMovRegImm(const X86EncodingMap* entry, uint8_t reg, int imm); void EmitShiftRegImm(const X86EncodingMap* entry, uint8_t reg, int imm); + void EmitShiftMemImm(const X86EncodingMap* entry, uint8_t base, int disp, int imm); void EmitShiftMemCl(const X86EncodingMap* entry, uint8_t base, int displacement, uint8_t cl); void EmitShiftRegCl(const X86EncodingMap* entry, uint8_t reg, uint8_t cl); void EmitRegCond(const X86EncodingMap* entry, uint8_t reg, uint8_t condition); + void EmitMemCond(const X86EncodingMap* entry, uint8_t base, int displacement, uint8_t condition); /** * @brief Used for encoding conditional register to register operation. @@ -354,6 +359,16 @@ class X86Mir2Lir FINAL : public Mir2Lir { */ void EmitRegRegCond(const X86EncodingMap* entry, uint8_t reg1, uint8_t reg2, uint8_t condition); + /** + * @brief Used for encoding conditional register to memory operation. + * @param entry The entry in the encoding map for the opcode. + * @param reg1 The first physical register. + * @param base The memory base register. + * @param displacement The memory displacement. + * @param condition The condition code for operation. + */ + void EmitRegMemCond(const X86EncodingMap* entry, uint8_t reg1, uint8_t base, int displacement, uint8_t condition); + void EmitJmp(const X86EncodingMap* entry, int rel); void EmitJcc(const X86EncodingMap* entry, int rel, uint8_t cc); void EmitCallMem(const X86EncodingMap* entry, uint8_t base, int disp); diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h index 9bf49c358e..77d716fabc 100644 --- a/compiler/dex/quick/x86/x86_lir.h +++ b/compiler/dex/quick/x86/x86_lir.h @@ -343,6 +343,10 @@ enum X86OpCode { // RRC - Register Register ConditionCode - cond_opcode reg1, reg2 // - lir operands - 0: reg1, 1: reg2, 2: CC kX86Cmov32RRC, + // RMC - Register Memory ConditionCode - cond_opcode reg1, [base + disp] + // - lir operands - 0: reg1, 1: base, 2: disp 3: CC + kX86Cmov32RMC, + // RC - Register CL - opcode reg, CL // - lir operands - 0: reg, 1: CL // MC - Memory CL - opcode [base + disp], CL @@ -366,7 +370,9 @@ enum X86OpCode { #undef BinaryShiftOpcode kX86Cmc, kX86Shld32RRI, + kX86Shld32MRI, kX86Shrd32RRI, + kX86Shrd32MRI, #define UnaryOpcode(opcode, reg, mem, array) \ opcode ## 8 ## reg, opcode ## 8 ## mem, opcode ## 8 ## array, \ opcode ## 16 ## reg, opcode ## 16 ## mem, opcode ## 16 ## array, \ @@ -481,11 +487,13 @@ enum X86EncodingKind { kRegRegImm, kRegMemImm, kRegArrayImm, // RRI, RMI and RAI instruction kinds. kMovRegImm, // Shorter form move RI. kRegRegImmRev, // RRI with first reg in r/m + kMemRegImm, // MRI instruction kinds. kShiftRegImm, kShiftMemImm, kShiftArrayImm, // Shift opcode with immediate. kShiftRegCl, kShiftMemCl, kShiftArrayCl, // Shift opcode with register CL. kRegRegReg, kRegRegMem, kRegRegArray, // RRR, RRM, RRA instruction kinds. kRegCond, kMemCond, kArrayCond, // R, M, A instruction kinds following by a condition. kRegRegCond, // RR instruction kind followed by a condition. + kRegMemCond, // RM instruction kind followed by a condition. kJmp, kJcc, kCall, // Branch instruction kinds. kPcRel, // Operation with displacement that is PC relative kMacro, // An instruction composing multiple others diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index ff316e5b04..8b85d71dae 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -132,6 +132,12 @@ void CodeGenerator::AllocateRegistersLocally(HInstruction* instruction) const { } } + // Make all registers available for the return value. + for (size_t i = 0, e = GetNumberOfRegisters(); i < e; ++i) { + blocked_registers_[i] = false; + } + SetupBlockedRegisters(blocked_registers_); + Location result_location = locations->Out(); if (result_location.IsUnallocated()) { switch (result_location.GetPolicy()) { diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc index eddaa0bc5e..f81e2f9797 100644 --- a/runtime/arch/arm/fault_handler_arm.cc +++ b/runtime/arch/arm/fault_handler_arm.cc @@ -34,7 +34,7 @@ namespace art { extern "C" void art_quick_throw_null_pointer_exception(); -extern "C" void art_quick_throw_stack_overflow(void*); +extern "C" void art_quick_throw_stack_overflow_from_signal(); extern "C" void art_quick_implicit_suspend(); // Get the size of a thumb2 instruction in bytes. @@ -50,7 +50,7 @@ void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** struct ucontext *uc = (struct ucontext *)context; struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext); *out_sp = static_cast<uintptr_t>(sc->arm_sp); - LOG(DEBUG) << "sp: " << *out_sp; + VLOG(signals) << "sp: " << *out_sp; if (*out_sp == 0) { return; } @@ -74,7 +74,7 @@ void FaultManager::GetMethodAndReturnPCAndSP(void* context, mirror::ArtMethod** // Need to work out the size of the instruction that caused the exception. uint8_t* ptr = reinterpret_cast<uint8_t*>(sc->arm_pc); - LOG(DEBUG) << "pc: " << std::hex << static_cast<void*>(ptr); + VLOG(signals) << "pc: " << std::hex << static_cast<void*>(ptr); uint32_t instr_size = GetInstructionSize(ptr); *out_return_pc = (sc->arm_pc + instr_size) | 1; @@ -95,7 +95,7 @@ bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) { uint32_t instr_size = GetInstructionSize(ptr); sc->arm_lr = (sc->arm_pc + instr_size) | 1; // LR needs to point to gc map location sc->arm_pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception); - LOG(DEBUG) << "Generating null pointer exception"; + VLOG(signals) << "Generating null pointer exception"; return true; } @@ -117,10 +117,10 @@ bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) { struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext); uint8_t* ptr2 = reinterpret_cast<uint8_t*>(sc->arm_pc); uint8_t* ptr1 = ptr2 - 4; - LOG(DEBUG) << "checking suspend"; + VLOG(signals) << "checking suspend"; uint16_t inst2 = ptr2[0] | ptr2[1] << 8; - LOG(DEBUG) << "inst2: " << std::hex << inst2 << " checkinst2: " << checkinst2; + VLOG(signals) << "inst2: " << std::hex << inst2 << " checkinst2: " << checkinst2; if (inst2 != checkinst2) { // Second instruction is not good, not ours. return false; @@ -132,7 +132,7 @@ bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) { bool found = false; while (ptr1 > limit) { uint32_t inst1 = ((ptr1[0] | ptr1[1] << 8) << 16) | (ptr1[2] | ptr1[3] << 8); - LOG(DEBUG) << "inst1: " << std::hex << inst1 << " checkinst1: " << checkinst1; + VLOG(signals) << "inst1: " << std::hex << inst1 << " checkinst1: " << checkinst1; if (inst1 == checkinst1) { found = true; break; @@ -140,7 +140,7 @@ bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) { ptr1 -= 2; // Min instruction size is 2 bytes. } if (found) { - LOG(DEBUG) << "suspend check match"; + VLOG(signals) << "suspend check match"; // This is a suspend check. Arrange for the signal handler to return to // art_quick_implicit_suspend. Also set LR so that after the suspend check it // will resume the instruction (current PC + 2). PC points to the @@ -148,14 +148,14 @@ bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) { // NB: remember that we need to set the bottom bit of the LR register // to switch to thumb mode. - LOG(DEBUG) << "arm lr: " << std::hex << sc->arm_lr; - LOG(DEBUG) << "arm pc: " << std::hex << sc->arm_pc; + VLOG(signals) << "arm lr: " << std::hex << sc->arm_lr; + VLOG(signals) << "arm pc: " << std::hex << sc->arm_pc; sc->arm_lr = sc->arm_pc + 3; // +2 + 1 (for thumb) sc->arm_pc = reinterpret_cast<uintptr_t>(art_quick_implicit_suspend); // Now remove the suspend trigger that caused this fault. Thread::Current()->RemoveSuspendTrigger(); - LOG(DEBUG) << "removed suspend trigger invoking test suspend"; + VLOG(signals) << "removed suspend trigger invoking test suspend"; return true; } return false; @@ -174,103 +174,60 @@ bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) { // on the stack. // // If we determine this is a stack overflow we need to move the stack pointer -// to the overflow region below the protected region. Because we now have -// a gap in the stack (skips over protected region), we need to arrange -// for the rest of the system to be unaware of the new stack arrangement -// and behave as if there is a fully valid stack. We do this by placing -// a unique address onto the stack followed by -// the size of the gap. The stack walker will detect this and skip over the -// gap. - -// NB. We also need to be careful of stack alignment as the ARM EABI specifies that -// stack must be 8 byte aligned when making any calls. - -// NB. The size of the gap is the difference between the previous frame's SP and -// the SP at which the size word is pushed. +// to the overflow region below the protected region. bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { struct ucontext *uc = (struct ucontext *)context; struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext); - LOG(DEBUG) << "stack overflow handler with sp at " << std::hex << &uc; - LOG(DEBUG) << "sigcontext: " << std::hex << sc; + VLOG(signals) << "stack overflow handler with sp at " << std::hex << &uc; + VLOG(signals) << "sigcontext: " << std::hex << sc; - uint8_t* sp = reinterpret_cast<uint8_t*>(sc->arm_sp); - LOG(DEBUG) << "sp: " << static_cast<void*>(sp); + uintptr_t sp = sc->arm_sp; + VLOG(signals) << "sp: " << std::hex << sp; - uintptr_t* fault_addr = reinterpret_cast<uintptr_t*>(sc->fault_address); - LOG(DEBUG) << "fault_addr: " << std::hex << fault_addr; - LOG(DEBUG) << "checking for stack overflow, sp: " << std::hex << static_cast<void*>(sp) << + uintptr_t fault_addr = sc->fault_address; + VLOG(signals) << "fault_addr: " << std::hex << fault_addr; + VLOG(signals) << "checking for stack overflow, sp: " << std::hex << sp << ", fault_addr: " << fault_addr; - uintptr_t* overflow_addr = reinterpret_cast<uintptr_t*>(sp - Thread::kStackOverflowReservedBytes); + + uintptr_t overflow_addr = sp - Thread::kStackOverflowReservedBytes; + + Thread* self = reinterpret_cast<Thread*>(sc->arm_r9); + CHECK_EQ(self, Thread::Current()); + uintptr_t pregion = reinterpret_cast<uintptr_t>(self->GetStackEnd()) - + Thread::kStackOverflowProtectedSize; // Check that the fault address is the value expected for a stack overflow. if (fault_addr != overflow_addr) { - LOG(DEBUG) << "Not a stack overflow"; + VLOG(signals) << "Not a stack overflow"; return false; } // We know this is a stack overflow. We need to move the sp to the overflow region - // the exists below the protected region. R9 contains the current Thread* so - // we can read the stack_end from that and subtract the size of the - // protected region. This creates a gap in the stack that needs to be marked. - Thread* self = reinterpret_cast<Thread*>(sc->arm_r9); + // the exists below the protected region. Determine the address of the next + // available valid address below the protected region. + uintptr_t prevsp = sp; + sp = pregion; + VLOG(signals) << "setting sp to overflow region at " << std::hex << sp; - uint8_t* prevsp = sp; - sp = self->GetStackEnd() - Thread::kStackOverflowProtectedSize; - LOG(DEBUG) << "setting sp to overflow region at " << std::hex << static_cast<void*>(sp); - - // We need to find the previous frame. Remember that - // this has not yet been fully constructed because the SP has not been - // decremented. So we need to work out the size of the spill portion of the - // frame. This consists of something like: - // - // 0xb6a1d49c: e92d40e0 push {r5, r6, r7, lr} - // 0xb6a1d4a0: ed2d8a06 vpush.f32 {s16-s21} - // - // The first is encoded in the ArtMethod as the spill_mask, the second as the - // fp_spill_mask. A population count on each will give the number of registers - // in each mask. Each register is 4 bytes on ARM32. - - mirror::ArtMethod* method = reinterpret_cast<mirror::ArtMethod*>(sc->arm_r0); - uint32_t spill_mask = method->GetCoreSpillMask(); - uint32_t numcores = POPCOUNT(spill_mask); - uint32_t fp_spill_mask = method->GetFpSpillMask(); - uint32_t numfps = POPCOUNT(fp_spill_mask); - uint32_t spill_size = (numcores + numfps) * 4; - LOG(DEBUG) << "spill size: " << spill_size; - uint8_t* prevframe = prevsp + spill_size; - LOG(DEBUG) << "previous frame: " << static_cast<void*>(prevframe); - - // NOTE: the ARM EABI needs an 8 byte alignment. In the case of ARM32 a pointer - // is 4 bytes so that, together with the offset to the previous frame is 8 - // bytes. On other architectures we will need to align the stack. - - // Push a marker onto the stack to tell the stack walker that there is a stack - // overflow and the stack is not contiguous. - - // First the offset from SP to the previous frame. - sp -= sizeof(uint32_t); - LOG(DEBUG) << "push gap of " << static_cast<uint32_t>(prevframe - sp); - *reinterpret_cast<uint32_t*>(sp) = static_cast<uint32_t>(prevframe - sp); - - // Now the gap marker (pointer sized). - sp -= sizeof(mirror::ArtMethod*); - *reinterpret_cast<void**>(sp) = stack_overflow_gap_marker; + // Since the compiler puts the implicit overflow + // check before the callee save instructions, the SP is already pointing to + // the previous frame. + VLOG(signals) << "previous frame: " << std::hex << prevsp; // Now establish the stack pointer for the signal return. - sc->arm_sp = reinterpret_cast<uintptr_t>(sp); + sc->arm_sp = prevsp; - // Now arrange for the signal handler to return to art_quick_throw_stack_overflow. - // We need the LR to point to the GC map just after the fault instruction. - uint8_t* ptr = reinterpret_cast<uint8_t*>(sc->arm_pc); - uint32_t instr_size = GetInstructionSize(ptr); - sc->arm_lr = (sc->arm_pc + instr_size) | 1; // LR needs to point to gc map location - sc->arm_pc = reinterpret_cast<uintptr_t>(art_quick_throw_stack_overflow); + // Tell the stack overflow code where the new stack pointer should be. + sc->arm_ip = sp; // aka r12 + + // Now arrange for the signal handler to return to art_quick_throw_stack_overflow_from_signal. + // The value of LR must be the same as it was when we entered the code that + // caused this fault. This will be inserted into a callee save frame by + // the function to which this handler returns (art_quick_throw_stack_overflow_from_signal). + sc->arm_pc = reinterpret_cast<uintptr_t>(art_quick_throw_stack_overflow_from_signal); - // The kernel will now return to the address in sc->arm_pc. We have arranged the - // stack pointer to be in the overflow region. Throwing the exception will perform - // a longjmp which will restore the stack pointer to the correct location for the - // exception catch. + // The kernel will now return to the address in sc->arm_pc. return true; } } // namespace art diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index bc80644945..dcf4561a9f 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -235,6 +235,31 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFr */ ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFromCode + /* + * Invoke stack overflow exception from signal handler. + * On entry: + * r9: thread + * sp: address of last known frame + * r12: address of next valid SP below protected region in stack + * + * This is deceptively simple but hides some complexity. It is called in the case of + * a stack overflow condition during implicit checks. The signal handler has been + * called by the kernel due to a load from the protected stack region. The handler + * works out the address of the previous frame and passes this in SP. However there + * is a piece of memory somewhere below the current SP that is not accessible (the + * memory that caused the signal). The signal handler works out the next + * accessible value of SP and passes this in r12. This code then sets up the SP + * to be this new value and calls the code to create and throw the stack overflow + * exception. + */ +ENTRY art_quick_throw_stack_overflow_from_signal + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context + mov r0, r9 @ pass Thread::Current + mov r1, sp @ pass SP + mov sp, r12 @ move SP down to below protected region. + b artThrowStackOverflowFromCode @ artThrowStackOverflowFromCode(Thread*, SP) +END art_quick_throw_stack_overflow_from_signal + /* * All generated callsites for interface invokes and invocation slow paths will load arguments * as usual - except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain diff --git a/runtime/base/logging.h b/runtime/base/logging.h index bd5ae85f5c..c4461fab07 100644 --- a/runtime/base/logging.h +++ b/runtime/base/logging.h @@ -296,6 +296,7 @@ struct LogVerbosity { bool startup; bool third_party_jni; // Enabled with "-verbose:third-party-jni". bool threads; + bool signals; }; extern LogVerbosity gLogVerbosity; diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc index b8093bc288..1304b04b90 100644 --- a/runtime/fault_handler.cc +++ b/runtime/fault_handler.cc @@ -106,23 +106,23 @@ void FaultManager::RemoveHandler(FaultHandler* handler) { bool FaultManager::IsInGeneratedCode(void* context, bool check_dex_pc) { // We can only be running Java code in the current thread if it // is in Runnable state. - LOG(DEBUG) << "Checking for generated code"; + VLOG(signals) << "Checking for generated code"; Thread* thread = Thread::Current(); if (thread == nullptr) { - LOG(DEBUG) << "no current thread"; + VLOG(signals) << "no current thread"; return false; } ThreadState state = thread->GetState(); if (state != kRunnable) { - LOG(DEBUG) << "not runnable"; + VLOG(signals) << "not runnable"; return false; } // Current thread is runnable. // Make sure it has the mutator lock. if (!Locks::mutator_lock_->IsSharedHeld(thread)) { - LOG(DEBUG) << "no lock"; + VLOG(signals) << "no lock"; return false; } @@ -135,9 +135,9 @@ bool FaultManager::IsInGeneratedCode(void* context, bool check_dex_pc) { GetMethodAndReturnPCAndSP(context, &method_obj, &return_pc, &sp); // If we don't have a potential method, we're outta here. - LOG(DEBUG) << "potential method: " << method_obj; + VLOG(signals) << "potential method: " << method_obj; if (method_obj == 0 || !IsAligned<kObjectAlignment>(method_obj)) { - LOG(DEBUG) << "no method"; + VLOG(signals) << "no method"; return false; } @@ -147,36 +147,36 @@ bool FaultManager::IsInGeneratedCode(void* context, bool check_dex_pc) { // TODO: Method might be not a heap address, and GetClass could fault. mirror::Class* cls = method_obj->GetClass<kVerifyNone>(); if (cls == nullptr) { - LOG(DEBUG) << "not a class"; + VLOG(signals) << "not a class"; return false; } if (!IsAligned<kObjectAlignment>(cls)) { - LOG(DEBUG) << "not aligned"; + VLOG(signals) << "not aligned"; return false; } if (!VerifyClassClass(cls)) { - LOG(DEBUG) << "not a class class"; + VLOG(signals) << "not a class class"; return false; } // Now make sure the class is a mirror::ArtMethod. if (!cls->IsArtMethodClass()) { - LOG(DEBUG) << "not a method"; + VLOG(signals) << "not a method"; return false; } // We can be certain that this is a method now. Check if we have a GC map // at the return PC address. if (true || kIsDebugBuild) { - LOG(DEBUG) << "looking for dex pc for return pc " << std::hex << return_pc; + VLOG(signals) << "looking for dex pc for return pc " << std::hex << return_pc; const void* code = Runtime::Current()->GetInstrumentation()->GetQuickCodeFor(method_obj); uint32_t sought_offset = return_pc - reinterpret_cast<uintptr_t>(code); - LOG(DEBUG) << "pc offset: " << std::hex << sought_offset; + VLOG(signals) << "pc offset: " << std::hex << sought_offset; } uint32_t dexpc = method_obj->ToDexPc(return_pc, false); - LOG(DEBUG) << "dexpc: " << dexpc; + VLOG(signals) << "dexpc: " << dexpc; return !check_dex_pc || dexpc != DexFile::kDexNoIndex; } diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 9cf878535e..f6a98e1e9d 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -232,6 +232,7 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni // gLogVerbosity.startup = true; // TODO: don't check this in! // gLogVerbosity.third_party_jni = true; // TODO: don't check this in! // gLogVerbosity.threads = true; // TODO: don't check this in! +// gLogVerbosity.signals = true; // TODO: don't check this in! method_trace_ = false; method_trace_file_ = "/data/method-trace-file.bin"; @@ -464,6 +465,8 @@ bool ParsedOptions::Parse(const Runtime::Options& options, bool ignore_unrecogni gLogVerbosity.third_party_jni = true; } else if (verbose_options[i] == "threads") { gLogVerbosity.threads = true; + } else if (verbose_options[i] == "signals") { + gLogVerbosity.signals = true; } else { Usage("Unknown -verbose option %s\n", verbose_options[i].c_str()); return false; diff --git a/runtime/stack.cc b/runtime/stack.cc index 9c709ae505..5e64e597f9 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -32,14 +32,6 @@ namespace art { -// Define a piece of memory, the address of which can be used as a marker -// for the gap in the stack added during stack overflow handling. -static uint32_t stack_overflow_object; - -// The stack overflow gap marker is simply a valid unique address. -void* stack_overflow_gap_marker = &stack_overflow_object; - - mirror::Object* ShadowFrame::GetThisObject() const { mirror::ArtMethod* m = GetMethod(); if (m->IsStatic()) { @@ -305,56 +297,23 @@ void StackVisitor::WalkStack(bool include_transitions) { bool exit_stubs_installed = Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled(); uint32_t instrumentation_stack_depth = 0; - bool kDebugStackWalk = false; - bool kDebugStackWalkVeryVerbose = false; // The name says it all. - - if (kDebugStackWalk) { - LOG(INFO) << "walking stack"; - } for (const ManagedStack* current_fragment = thread_->GetManagedStack(); current_fragment != NULL; current_fragment = current_fragment->GetLink()) { cur_shadow_frame_ = current_fragment->GetTopShadowFrame(); cur_quick_frame_ = current_fragment->GetTopQuickFrame(); cur_quick_frame_pc_ = current_fragment->GetTopQuickFramePc(); - if (kDebugStackWalkVeryVerbose) { - LOG(INFO) << "cur_quick_frame: " << cur_quick_frame_; - LOG(INFO) << "cur_quick_frame_pc: " << std::hex << cur_quick_frame_pc_; - } if (cur_quick_frame_ != NULL) { // Handle quick stack frames. // Can't be both a shadow and a quick fragment. DCHECK(current_fragment->GetTopShadowFrame() == NULL); mirror::ArtMethod* method = *cur_quick_frame_; while (method != NULL) { - // Check for a stack overflow gap marker. - if (method == reinterpret_cast<mirror::ArtMethod*>(stack_overflow_gap_marker)) { - // Marker for a stack overflow. This is followed by the offset from the - // current SP to the next frame. There is a gap in the stack here. Jump - // the gap silently. - // Caveat coder: the layout of the overflow marker depends on the architecture. - // The first element is address sized (8 bytes on a 64 bit machine). The second - // element is 32 bits. So be careful with those address calculations. - - // Get the address of the offset, just beyond the marker pointer. - byte* gapsizeaddr = reinterpret_cast<byte*>(cur_quick_frame_) + sizeof(uintptr_t); - uint32_t gap = *reinterpret_cast<uint32_t*>(gapsizeaddr); - CHECK_GT(gap, Thread::kStackOverflowProtectedSize); - mirror::ArtMethod** next_frame = reinterpret_cast<mirror::ArtMethod**>( - reinterpret_cast<byte*>(gapsizeaddr) + gap); - if (kDebugStackWalk) { - LOG(INFO) << "stack overflow marker hit, gap: " << gap << ", next_frame: " << - next_frame; - } - cur_quick_frame_ = next_frame; - method = *next_frame; - CHECK(method != nullptr); - } else { - SanityCheckFrame(); - bool should_continue = VisitFrame(); - if (UNLIKELY(!should_continue)) { - return; - } + SanityCheckFrame(); + bool should_continue = VisitFrame(); + if (UNLIKELY(!should_continue)) { + return; } + if (context_ != NULL) { context_->FillCalleeSaves(*this); } @@ -363,9 +322,6 @@ void StackVisitor::WalkStack(bool include_transitions) { size_t return_pc_offset = method->GetReturnPcOffsetInBytes(); byte* return_pc_addr = reinterpret_cast<byte*>(cur_quick_frame_) + return_pc_offset; uintptr_t return_pc = *reinterpret_cast<uintptr_t*>(return_pc_addr); - if (kDebugStackWalkVeryVerbose) { - LOG(INFO) << "frame size: " << frame_size << ", return_pc: " << std::hex << return_pc; - } if (UNLIKELY(exit_stubs_installed)) { // While profiling, the return pc is restored from the side stack, except when walking // the stack for an exception where the side stack will be unwound in VisitFrame. @@ -398,10 +354,6 @@ void StackVisitor::WalkStack(bool include_transitions) { cur_quick_frame_ = reinterpret_cast<mirror::ArtMethod**>(next_frame); cur_depth_++; method = *cur_quick_frame_; - if (kDebugStackWalkVeryVerbose) { - LOG(INFO) << "new cur_quick_frame_: " << cur_quick_frame_; - LOG(INFO) << "new cur_quick_frame_pc_: " << std::hex << cur_quick_frame_pc_; - } } } else if (cur_shadow_frame_ != NULL) { do { diff --git a/runtime/stack.h b/runtime/stack.h index 73a823aac7..88ef78f4b2 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -102,14 +102,6 @@ enum VRegBaseRegNum : int { kVRegNonSpecialTempBaseReg = -3, }; -// Special object used to mark the gap in the stack placed when a stack -// overflow fault occurs during implicit stack checking. This is not -// a real object - it is used simply as a valid address to which a -// mirror::ArtMethod* can be compared during a stack walk. It is inserted -// into the stack during the stack overflow signal handling to mark the gap -// in which the memory is protected against read and write. -extern void* stack_overflow_gap_marker; - // A reference from the shadow stack to a MirrorType object within the Java heap. template<class MirrorType> class MANAGED StackReference : public mirror::ObjectReference<false, MirrorType> { diff --git a/runtime/thread.cc b/runtime/thread.cc index 23a6779639..3a62cd5823 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -243,10 +243,16 @@ void Thread::InstallImplicitProtection(bool is_main_stack) { pregion -= kStackOverflowProtectedSize; // Touch the pages in the region to map them in. Otherwise mprotect fails. Only - // need to do this on the main stack. + // need to do this on the main stack. We only need to touch one byte per page. if (is_main_stack) { - memset(pregion, 0x55, kStackOverflowProtectedSize); + byte* start = pregion; + byte* end = pregion + kStackOverflowProtectedSize; + while (start < end) { + *start = static_cast<byte>(0); + start += kPageSize; + } } + VLOG(threads) << "installing stack protected region at " << std::hex << static_cast<void*>(pregion) << " to " << static_cast<void*>(pregion + kStackOverflowProtectedSize - 1); @@ -255,6 +261,11 @@ void Thread::InstallImplicitProtection(bool is_main_stack) { LOG(FATAL) << "Unable to create protected region in stack for implicit overflow check. Reason:" << strerror(errno); } + + // Tell the kernel that we won't be needing these pages any more. + if (is_main_stack) { + madvise(pregion, kStackOverflowProtectedSize, MADV_DONTNEED); + } } void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) { diff --git a/runtime/utils.cc b/runtime/utils.cc index ee2cca4b64..c332bdf815 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1169,10 +1169,12 @@ const char* GetAndroidData() { std::string GetDalvikCacheOrDie(const char* subdir, const bool create_if_absent) { CHECK(subdir != nullptr); - const std::string dalvik_cache_root(StringPrintf("%s/dalvik-cache/", GetAndroidData())); + const char* android_data = GetAndroidData(); + const std::string dalvik_cache_root(StringPrintf("%s/dalvik-cache/", android_data)); const std::string dalvik_cache = dalvik_cache_root + subdir; if (create_if_absent && !OS::DirectoryExists(dalvik_cache.c_str())) { - if (StartsWith(dalvik_cache_root, "/tmp/")) { + // Don't create the system's /data/dalvik-cache/... because it needs special permissions. + if (strcmp(android_data, "/data") != 0) { int result = mkdir(dalvik_cache_root.c_str(), 0700); if (result != 0 && errno != EEXIST) { PLOG(FATAL) << "Failed to create dalvik-cache directory " << dalvik_cache_root; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 03ceed357c..bf1de86f2d 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3126,9 +3126,10 @@ mirror::ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst return nullptr; } mirror::ObjectArray<mirror::ArtMethod>* vtable = actual_arg_type.GetClass()->GetVTable(); - CHECK(vtable != nullptr); + CHECK(vtable != nullptr) << PrettyDescriptor(actual_arg_type.GetClass()); uint16_t vtable_index = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); - CHECK_LT(static_cast<int32_t>(vtable_index), vtable->GetLength()); + CHECK_LT(static_cast<int32_t>(vtable_index), vtable->GetLength()) + << PrettyDescriptor(actual_arg_type.GetClass()); mirror::ArtMethod* res_method = vtable->Get(vtable_index); CHECK(!Thread::Current()->IsExceptionPending()); return res_method; diff --git a/test/etc/push-and-run-test-jar b/test/etc/push-and-run-test-jar index e0d2f1decf..6cf79985a9 100755 --- a/test/etc/push-and-run-test-jar +++ b/test/etc/push-and-run-test-jar @@ -150,7 +150,7 @@ fi JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni" -cmdline="cd $DEX_LOCATION && mkdir -p dalvik-cache/{arm,arm64,mips,x86,x86_64} && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \ +cmdline="cd $DEX_LOCATION && export ANDROID_DATA=$DEX_LOCATION && export DEX_LOCATION=$DEX_LOCATION && \ $INVOKE_WITH $gdb /system/bin/dalvikvm$TARGET_SUFFIX $FLAGS $gdbargs -XXlib:$LIB $ZYGOTE $JNI_OPTS $INT_OPTS $DEBUGGER_OPTS $BOOT_OPT -cp $DEX_LOCATION/$TEST_NAME.jar Main" if [ "$DEV_MODE" = "y" ]; then echo $cmdline "$@" |