8213084: Rework and enhance Print[Opto]Assembly output
Reviewed-by: kvn, thartmann
--- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp Tue May 21 15:51:35 2019 +0200
@@ -629,6 +629,14 @@
enum { instruction_size = 4 };
+ //---< calculate length of instruction >---
+ // We just use the values set above.
+ // instruction must start at passed address
+ static unsigned int instr_len(unsigned char *instr) { return instruction_size; }
+
+ //---< longest instructions >---
+ static unsigned int instr_maxlen() { return instruction_size; }
+
Address adjust(Register base, int offset, bool preIncrement) {
if (preIncrement)
return Address(Pre(base, offset));
--- a/src/hotspot/cpu/aarch64/disassembler_aarch64.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/aarch64/disassembler_aarch64.hpp Tue May 21 15:51:35 2019 +0200
@@ -34,4 +34,24 @@
return "";
}
+ // Returns address of n-th instruction preceding addr,
+ // NULL if no preceding instruction can be found.
+ // On ARM(aarch64), we assume a constant instruction length.
+ // It might be beneficial to check "is_readable" as we do on ppc and s390.
+ static address find_prev_instr(address addr, int n_instr) {
+ return addr - Assembler::instruction_size*n_instr;
+ }
+
+ // special-case instruction decoding.
+ // There may be cases where the binutils disassembler doesn't do
+ // the perfect job. In those cases, decode_instruction0 may kick in
+ // and do it right.
+ // If nothing had to be done, just return "here", otherwise return "here + instr_len(here)"
+ static address decode_instruction0(address here, outputStream* st, address virtual_begin = NULL) {
+ return here;
+ }
+
+ // platform-specific instruction annotations (like value of loaded constants)
+ static void annotate(address pc, outputStream* st) { };
+
#endif // CPU_AARCH64_DISASSEMBLER_AARCH64_HPP
--- a/src/hotspot/cpu/arm/assembler_arm_32.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/arm/assembler_arm_32.hpp Tue May 21 15:51:35 2019 +0200
@@ -199,6 +199,14 @@
static const int LogInstructionSize = 2;
static const int InstructionSize = 1 << LogInstructionSize;
+ //---< calculate length of instruction >---
+ // We just use the values set above.
+ // instruction must start at passed address
+ static unsigned int instr_len(unsigned char *instr) { return InstructionSize; }
+
+ //---< longest instructions >---
+ static unsigned int instr_maxlen() { return InstructionSize; }
+
static inline AsmCondition inverse(AsmCondition cond) {
assert ((cond != al) && (cond != nv), "AL and NV conditions cannot be inversed");
return (AsmCondition)((int)cond ^ 1);
--- a/src/hotspot/cpu/arm/disassembler_arm.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/arm/disassembler_arm.hpp Tue May 21 15:51:35 2019 +0200
@@ -33,4 +33,24 @@
return "";
}
+ // Returns address of n-th instruction preceding addr,
+ // NULL if no preceding instruction can be found.
+ // On ARM, we assume a constant instruction length.
+ // It might be beneficial to check "is_readable" as we do on ppc and s390.
+ static address find_prev_instr(address addr, int n_instr) {
+ return addr - Assembler::InstructionSize*n_instr;
+ }
+
+ // special-case instruction decoding.
+ // There may be cases where the binutils disassembler doesn't do
+ // the perfect job. In those cases, decode_instruction0 may kick in
+ // and do it right.
+ // If nothing had to be done, just return "here", otherwise return "here + instr_len(here)"
+ static address decode_instruction0(address here, outputStream* st, address virtual_begin = NULL) {
+ return here;
+ }
+
+ // platform-specific instruction annotations (like value of loaded constants)
+ static void annotate(address pc, outputStream* st) { };
+
#endif // CPU_ARM_DISASSEMBLER_ARM_HPP
--- a/src/hotspot/cpu/ppc/assembler_ppc.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/ppc/assembler_ppc.hpp Tue May 21 15:51:35 2019 +0200
@@ -929,11 +929,13 @@
enum Predict { pt = 1, pn = 0 }; // pt = predict taken
- // Instruction must start at passed address.
- static int instr_len(unsigned char *instr) { return BytesPerInstWord; }
+ //---< calculate length of instruction >---
+ // With PPC64 being a RISC architecture, this always is BytesPerInstWord
+ // instruction must start at passed address
+ static unsigned int instr_len(unsigned char *instr) { return BytesPerInstWord; }
- // longest instructions
- static int instr_maxlen() { return BytesPerInstWord; }
+ //---< longest instructions >---
+ static unsigned int instr_maxlen() { return BytesPerInstWord; }
// Test if x is within signed immediate range for nbits.
static bool is_simm(int x, unsigned int nbits) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/cpu/ppc/disassembler_ppc.cpp Tue May 21 15:51:35 2019 +0200
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "asm/macroAssembler.inline.hpp"
+#include "code/codeCache.hpp"
+#include "compiler/disassembler.hpp"
+#include "depChecker_ppc.hpp"
+#include "gc/cms/concurrentMarkSweepGeneration.inline.hpp"
+#include "gc/cms/parOopClosures.inline.hpp"
+#include "gc/shared/collectedHeap.hpp"
+#include "gc/shared/cardTableBarrierSet.hpp"
+#include "gc/shared/genOopClosures.inline.hpp"
+#include "oops/oop.inline.hpp"
+#include "runtime/handles.inline.hpp"
+#include "runtime/stubCodeGenerator.hpp"
+#include "runtime/stubRoutines.hpp"
+
+// Macro to print instruction bits.
+// numbering of instruction bits on ppc64 is (highest) 0 1 ... 30 31 (lowest).
+#define print_instruction_bits(st, instruction, start_bit, end_bit) \
+ { assert((start_bit) <= (end_bit), "sanity check"); \
+ for (int i=(31-(start_bit));i>=(31-(end_bit));i--) { \
+ (st)->print("%d", ((instruction) >> i) & 0x1); \
+ } \
+ }
+
+// Macro to decode "bo" instruction bits.
+#define print_decoded_bo_bits(env, instruction, end_bit) \
+ { int bo_bits = (instruction >> (31 - (end_bit))) & 0x1f; \
+ if ( ((bo_bits & 0x1c) == 0x4) || ((bo_bits & 0x1c) == 0xc) ) { \
+ switch (bo_bits & 0x3) { \
+ case (0 << 1) | (0 << 0): env->print("[no_hint]"); break; \
+ case (0 << 1) | (1 << 0): env->print("[reserved]"); break; \
+ case (1 << 1) | (0 << 0): env->print("[not_taken]"); break; \
+ case (1 << 1) | (1 << 0): env->print("[taken]"); break; \
+ default: break; \
+ } \
+ } else if ( ((bo_bits & 0x14) == 0x10) ) { \
+ switch (bo_bits & 0x9) { \
+ case (0 << 3) | (0 << 0): env->print("[no_hint]"); break; \
+ case (0 << 3) | (1 << 0): env->print("[reserved]"); break; \
+ case (1 << 3) | (0 << 0): env->print("[not_taken]"); break; \
+ case (1 << 3) | (1 << 0): env->print("[taken]"); break; \
+ default: break; \
+ } \
+ } \
+ }
+
+// Macro to decode "bh" instruction bits.
+#define print_decoded_bh_bits(env, instruction, end_bit, is_bclr) \
+ { int bh_bits = (instruction >> (31 - (end_bit))) & 0x3; \
+ if (is_bclr) { \
+ switch (bh_bits) { \
+ case (0 << 1) | (0 << 0): env->print("[subroutine_return]"); break; \
+ case (0 << 1) | (1 << 0): env->print("[not_return_but_same]"); break; \
+ case (1 << 1) | (0 << 0): env->print("[reserved]"); break; \
+ case (1 << 1) | (1 << 0): env->print("[not_predictable]"); break; \
+ default: break; \
+ } \
+ } else { \
+ switch (bh_bits) { \
+ case (0 << 1) | (0 << 0): env->print("[not_return_but_same]"); break; \
+ case (0 << 1) | (1 << 0): env->print("[reserved]"); break; \
+ case (1 << 1) | (0 << 0): env->print("[reserved]"); break; \
+ case (1 << 1) | (1 << 0): env->print("[not_predictable]"); break; \
+ default: break; \
+ } \
+ } \
+ }
+
+address Disassembler::find_prev_instr(address here, int n_instr) {
+ if (!os::is_readable_pointer(here)) return NULL; // obviously a bad location to decode
+
+ // Find most distant possible starting point.
+ // Narrow down because we don't want to SEGV while printing.
+ address start = here - n_instr*Assembler::instr_maxlen(); // starting point can't be further away.
+ while ((start < here) && !os::is_readable_range(start, here)) {
+ start = align_down(start, os::min_page_size()) + os::min_page_size();
+ }
+ if (start >= here) {
+ // Strange. Can only happen with here on page boundary.
+ return NULL;
+ }
+ return start;
+}
+
+address Disassembler::decode_instruction0(address here, outputStream * st, address virtual_begin ) {
+ if (is_abstract()) {
+ // The disassembler library was not loaded (yet),
+ // use AbstractDisassembler's decode method.
+ return decode_instruction_abstract(here, st, Assembler::instr_len(here), Assembler::instr_maxlen());
+ }
+
+ // Currently, "special decoding" doesn't work when decoding error files.
+ // When decoding an instruction from a hs_err file, the given
+ // instruction address 'start' points to the instruction's virtual address
+ // which is not equal to the address where the instruction is located.
+ // Therefore, we will either crash or decode garbage.
+ if (is_decode_error_file()) {
+ return here;
+ }
+
+ //---< Decode some well-known "instructions" >---
+
+ address next;
+ uint32_t instruction = *(uint32_t*)here;
+
+ // Align at next tab position.
+ const uint tabspacing = 8;
+ const uint pos = st->position();
+ const uint aligned_pos = ((pos+tabspacing-1)/tabspacing)*tabspacing;
+ st->fill_to(aligned_pos);
+
+ if (instruction == 0x0) {
+ st->print("illtrap .data 0x0");
+ next = here + Assembler::instr_len(here);
+ } else if (instruction == 0xbadbabe) {
+ st->print(".data 0xbadbabe");
+ next = here + Assembler::instr_len(here);
+ } else if (Assembler::is_endgroup(instruction)) {
+ st->print("endgroup");
+ next = here + Assembler::instr_len(here);
+ } else {
+ next = here;
+ }
+ return next;
+}
+
+// print annotations (instruction control bits)
+void Disassembler::annotate(address here, outputStream* st) {
+ // Currently, annotation doesn't work when decoding error files.
+ // When decoding an instruction from a hs_err file, the given
+ // instruction address 'start' points to the instruction's virtual address
+ // which is not equal to the address where the instruction is located.
+ // Therefore, we will either crash or decode garbage.
+ if (is_decode_error_file()) {
+ return;
+ }
+
+ uint32_t instruction = *(uint32_t*)here;
+
+ // Align at next tab position.
+ const uint tabspacing = 8;
+ const uint pos = st->position();
+ const uint aligned_pos = ((pos+tabspacing-1)/tabspacing)*tabspacing;
+
+ if (MacroAssembler::is_bcxx(instruction)) {
+ st->print(",bo=0b");
+ print_instruction_bits(st, instruction, 6, 10);
+ print_decoded_bo_bits(st, instruction, 10);
+ } else if (MacroAssembler::is_bctr(instruction) ||
+ MacroAssembler::is_bctrl(instruction) ||
+ MacroAssembler::is_bclr(instruction)) {
+ st->fill_to(aligned_pos);
+ st->print("bo=0b");
+ print_instruction_bits(st, instruction, 6, 10);
+ print_decoded_bo_bits(st, instruction, 10);
+ st->print(",bh=0b");
+ print_instruction_bits(st, instruction, 19, 20);
+ print_decoded_bh_bits(st, instruction, 20,
+ !(MacroAssembler::is_bctr(instruction) ||
+ MacroAssembler::is_bctrl(instruction)));
+ } else if (MacroAssembler::is_trap_should_not_reach_here(instruction)) {
+ st->fill_to(aligned_pos + tabspacing);
+ st->print(";trap: should not reach here");
+ } else if (MacroAssembler::is_trap_null_check(instruction)) {
+ st->fill_to(aligned_pos + tabspacing);
+ st->print(";trap: null check");
+ } else if (MacroAssembler::is_trap_range_check(instruction)) {
+ st->fill_to(aligned_pos + tabspacing);
+ st->print(";trap: range check");
+ } else if (MacroAssembler::is_trap_ic_miss_check(instruction)) {
+ st->fill_to(aligned_pos + tabspacing);
+ st->print(";trap: ic miss check");
+ } else if (MacroAssembler::is_trap_zombie_not_entrant(instruction)) {
+ st->fill_to(aligned_pos + tabspacing);
+ st->print(";trap: zombie");
+ }
+}
--- a/src/hotspot/cpu/ppc/disassembler_ppc.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/ppc/disassembler_ppc.hpp Tue May 21 15:51:35 2019 +0200
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2013 SAP SE. All rights reserved.
+ * Copyright (c) 2012, 2019 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -34,4 +34,23 @@
return "ppc64";
}
+ // Find preceding instruction.
+ //
+ // Starting at the passed location, the n-th preceding (towards lower addresses)
+ // location is searched, the contents of which - if interpreted as
+ // instructions - has the passed location as n-th successor.
+ // - If no such location exists, NULL is returned. The caller should then
+ // terminate its search and react properly.
+ static address find_prev_instr(address here, int n_instr);
+
+ // special-case instruction decoding.
+ // There may be cases where the binutils disassembler doesn't do
+ // the perfect job. In those cases, decode_instruction0 may kick in
+ // and do it right.
+ // If nothing had to be done, just return "here", otherwise return "here + instr_len(here)"
+ static address decode_instruction0(address here, outputStream* st, address virtual_begin = NULL);
+
+ // platform-specific instruction annotations (like value of loaded constants)
+ static void annotate(address pc, outputStream* st);
+
#endif // CPU_PPC_DISASSEMBLER_PPC_HPP
--- a/src/hotspot/cpu/ppc/vm_version_ppc.cpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/ppc/vm_version_ppc.cpp Tue May 21 15:51:35 2019 +0200
@@ -708,6 +708,8 @@
uint32_t *code_end = (uint32_t *)a->pc();
a->flush();
+ cb.insts()->set_end((u_char*)code_end);
+
double loop1_seconds,loop2_seconds, rel_diff;
uint64_t start1, stop1;
@@ -725,10 +727,11 @@
rel_diff = (loop2_seconds - loop1_seconds) / loop1_seconds *100;
- if (PrintAssembly) {
+ if (PrintAssembly || PrintStubCode) {
ttyLocker ttyl;
tty->print_cr("Decoding section size detection stub at " INTPTR_FORMAT " before execution:", p2i(code));
- Disassembler::decode((u_char*)code, (u_char*)code_end, tty);
+ // Use existing decode function. This enables the [MachCode] format which is needed to DecodeErrorFile.
+ Disassembler::decode(&cb, (u_char*)code, (u_char*)code_end, tty);
tty->print_cr("Time loop1 :%f", loop1_seconds);
tty->print_cr("Time loop2 :%f", loop2_seconds);
tty->print_cr("(time2 - time1) / time1 = %f %%", rel_diff);
--- a/src/hotspot/cpu/s390/assembler_s390.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/s390/assembler_s390.hpp Tue May 21 15:51:35 2019 +0200
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2016, 2017 SAP SE. All rights reserved.
+ * Copyright (c) 2016, 2019 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1531,16 +1531,16 @@
//-----------------------------------------------
// Calculate length of instruction.
- static int instr_len(unsigned char *instr);
+ static unsigned int instr_len(unsigned char *instr);
// Longest instructions are 6 bytes on z/Architecture.
- static int instr_maxlen() { return 6; }
+ static unsigned int instr_maxlen() { return 6; }
// Average instruction is 4 bytes on z/Architecture (just a guess).
- static int instr_avglen() { return 4; }
+ static unsigned int instr_avglen() { return 4; }
// Shortest instructions are 2 bytes on z/Architecture.
- static int instr_minlen() { return 2; }
+ static unsigned int instr_minlen() { return 2; }
// Move instruction at pc right-justified into passed long int.
// Return instr len in bytes as function result.
--- a/src/hotspot/cpu/s390/assembler_s390.inline.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/s390/assembler_s390.inline.hpp Tue May 21 15:51:35 2019 +0200
@@ -1344,7 +1344,7 @@
// Instruction must start at passed address.
// Extra check for illtraps with ID.
-inline int Assembler::instr_len(unsigned char *instr) {
+inline unsigned int Assembler::instr_len(unsigned char *instr) {
switch ((*instr) >> 6) {
case 0: return 2;
case 1: // fallthru
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/cpu/s390/disassembler_s390.cpp Tue May 21 15:51:35 2019 +0200
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "asm/assembler.inline.hpp"
+#include "asm/macroAssembler.hpp"
+#include "code/codeCache.hpp"
+#include "compiler/disassembler.hpp"
+#include "depChecker_s390.hpp"
+#include "gc/cms/concurrentMarkSweepGeneration.inline.hpp"
+#include "gc/cms/parOopClosures.inline.hpp"
+#include "gc/shared/collectedHeap.hpp"
+#include "gc/shared/cardTableBarrierSet.hpp"
+#include "gc/shared/genOopClosures.inline.hpp"
+#include "oops/oop.inline.hpp"
+#include "runtime/handles.inline.hpp"
+#include "runtime/stubCodeGenerator.hpp"
+#include "runtime/stubRoutines.hpp"
+#include "utilities/align.hpp"
+
+// List of all major opcodes, as of
+// Principles of Operation, Eleventh Edition, March 2015
+bool Disassembler::valid_opcodes[] =
+{ true, true, false, false, true, true, true, true, // 0x00..07
+ false, false, true, true, true, true, true, true, // 0x08..0f
+ true, true, true, true, true, true, true, true, // 0x10..17
+ true, true, true, true, true, true, true, true, // 0x18..1f
+ true, true, true, true, true, true, true, true, // 0x20..27
+ true, true, true, true, true, true, true, true, // 0x28..2f
+ true, true, true, true, true, true, true, true, // 0x30..37
+ true, true, true, true, true, true, true, true, // 0x38..3f
+ true, true, true, true, true, true, true, true, // 0x40..47
+ true, true, true, true, true, true, true, true, // 0x48..4f
+ true, true, false, false, true, true, true, true, // 0x50..57
+ true, true, true, true, true, true, true, true, // 0x58..5f
+ true, false, false, false, false, false, false, true, // 0x60..67
+ true, true, true, true, true, true, true, true, // 0x68..6f
+ true, true, false, false, false, false, false, false, // 0x70..77
+ true, true, true, true, true, true, true, true, // 0x78..7f
+ true, false, true, true, true, true, true, true, // 0x80..87
+ true, true, true, true, true, true, true, true, // 0x88..8f
+ true, true, true, true, true, true, true, true, // 0x90..97
+ true, true, true, true, false, false, false, false, // 0x98..9f
+ false, false, false, false, false, true, false, true, // 0xa0..a7
+ true, true, false, false, true, true, true, true, // 0xa8..af
+ false, true, true, true, false, false, true, true, // 0xb0..b7
+ false, true, true, true, false, true, true, true, // 0xb8..bf
+ true, false, true, false, true, false, true, false, // 0xc0..c7
+ true, false, false, false, true, false, false, false, // 0xc8..cf
+ true, true, true, true, true, true, true, true, // 0xd0..d7
+ false, true, true, true, true, true, true, true, // 0xd8..df
+ false, true, true, true, false, true, false, true, // 0xe0..e7
+ true, true, true, true, true, true, true, true, // 0xe8..ef
+ true, true, true, true, false, false, false, false, // 0xf0..f7
+ true, true, true, true, true, true, false, false, // 0xf8..ff
+};
+// Check for valid opcodes.
+//
+// The major opcode (one byte) at the passed location is inspected.
+// If the opcode found is assigned, the function returns true, false otherwise.
+// The true indication is not reliable. It may well be that the major opcode is
+// assigned, but there exists a minor opcode field in the instruction which
+// which has unassigned values.
+bool Disassembler::is_valid_opcode_at(address here) {
+ return valid_opcodes[*here];
+}
+
+// This method does plain instruction decoding, no frills.
+// It may be called before the binutils disassembler kicks in
+// to handle special cases the binutils disassembler does not.
+// Instruction address, comments, and the like have to be output by caller.
+address Disassembler::decode_instruction0(address here, outputStream * st, address virtual_begin) {
+ if (is_abstract()) {
+ // The disassembler library was not loaded (yet),
+ // use AbstractDisassembler's decode-method.
+ return decode_instruction_abstract(here, st, Assembler::instr_len(here), Assembler::instr_maxlen());
+ }
+
+ // Currently, "special decoding" doesn't work when decoding error files.
+ // When decoding an instruction from a hs_err file, the given
+ // instruction address 'start' points to the instruction's virtual address
+ // which is not equal to the address where the instruction is located.
+ // Therefore, we will either crash or decode garbage.
+ if (is_decode_error_file()) {
+ return here;
+ }
+
+ //---< Decode some well-known "instructions" >---
+
+ address next;
+ uint16_t instruction_2bytes = *(uint16_t*)here;
+
+ if (Assembler::is_z_nop((long)instruction_2bytes)) {
+#if 1
+ st->print("nop "); // fill up to operand column, leads to better code comment alignment
+ next = here + 2;
+#else
+ // Compact disassembler output. Does not work the easy way.
+ // Currently unusable, search does not terminate, risk of crash.
+ // TODO: rework required.
+ // Terminate search loop when reaching CodeEntryAlignment-aligned offset
+ // or, at the latest, when reaching the next page boundary.
+ int n_nops = 0;
+ while(is_same_page(here, here+2*n_nops) && Assembler::is_z_nop((long)instruction_2bytes)) {
+ n_nops++;
+ instruction_2bytes = *(uint16_t*)(here+2*n_nops);
+ }
+ if (n_nops <= 4) { // do not group few subsequent nops
+ st->print("nop "); // fill up to operand column, leads to better code comment alignment
+ next = here + 2;
+ } else {
+ st->print("nop count=%d", n_nops);
+ next = here + 2*n_nops;
+ }
+#endif
+ } else if (Assembler::is_z_sync((long)instruction_2bytes)) {
+ // Specific names. Make use of lightweight sync.
+ st->print("sync ");
+ if (Assembler::is_z_sync_full((long)instruction_2bytes) ) st->print("heavyweight");
+ if (Assembler::is_z_sync_light((long)instruction_2bytes)) st->print("lightweight");
+ next = here + 2;
+ } else if (instruction_2bytes == 0x0000) {
+#if 1
+ st->print("illtrap .nodata");
+ next = here + 2;
+#else
+ // Compact disassembler output. Does not work the easy way.
+ // Currently unusable, search does not terminate, risk of crash.
+ // TODO: rework required.
+ // Terminate search loop when reaching CodeEntryAlignment-aligned offset
+ // or, at the latest, when reaching the next page boundary.
+ int n_traps = 0;
+ while(is_same_page(here, here+2*n_nops) && (instruction_2bytes == 0x0000)) {
+ n_traps++;
+ instruction_2bytes = *(uint16_t*)(here+2*n_traps);
+ }
+ if (n_traps <= 4) { // do not group few subsequent illtraps
+ st->print("illtrap .nodata");
+ next = here + 2;
+ } else {
+ st->print("illtrap .nodata count=%d", n_traps);
+ next = here + 2*n_traps;
+ }
+#endif
+ } else if ((instruction_2bytes & 0xff00) == 0x0000) {
+ st->print("illtrap .data 0x%2.2x", instruction_2bytes & 0x00ff);
+ next = here + 2;
+ } else {
+ next = here;
+ }
+ return next;
+}
+
+// Count the instructions contained in the range [begin..end).
+// The range must exactly contain the instructions, i.e.
+// - the first instruction starts @begin
+// - the last instruction ends @(end-1)
+// The caller has to make sure that the given range is readable.
+// This function performs no safety checks!
+// Return value:
+// - The number of instructions, if there was exact containment.
+// - If there is no exact containment, a negative value is returned.
+// Its absolute value is the number of instructions from begin to end,
+// where the last instruction counted runs over the range end.
+// - 0 (zero) is returned if there was a parameter error
+// (inverted range, bad starting point).
+int Disassembler::count_instr(address begin, address end) {
+ if (end < begin+2) return 0; // no instructions in range
+ if (!Disassembler::is_valid_opcode_at(begin)) return 0; // bad starting point
+
+ address p = begin;
+ int n = 0;
+ while(p < end) {
+ p += Assembler::instr_len(p);
+ n++;
+ }
+ return (p == end) ? n : -n;
+}
+
+// Find preceding instruction.
+//
+// Starting at the passed location, the n-th preceding (towards lower addresses)
+// instruction is searched. With variable length instructions, there may be
+// more than one solution, or no solution at all (if the passed location
+// does not point to the start of an instruction or if the storage area
+// does not contain instructions at all).
+// instructions - has the passed location as n-th successor.
+// - If multiple such locations exist between (here-n*instr_maxlen()) and here,
+// the most distant location is selected.
+// - If no such location exists, NULL is returned. The caller should then
+// terminate its search and react properly.
+// Must be placed here in disassembler_s390.cpp. It does not compile
+// in the header. There the class 'Assembler' is not available.
+address Disassembler::find_prev_instr(address here, int n_instr) {
+ if (!os::is_readable_pointer(here)) return NULL; // obviously a bad location to decode
+
+ // Find most distant possible starting point.
+ // Narrow down because we don't want to SEGV while printing.
+ address start = here - n_instr*Assembler::instr_maxlen(); // starting point can't be further away.
+ while ((start < here) && !os::is_readable_range(start, here)) {
+ start = align_down(start, os::min_page_size()) + os::min_page_size();
+ }
+ if (start >= here) {
+ // Strange. Can only happen with here on page boundary.
+ return NULL;
+ }
+
+ //---< Find a starting point >---
+ int i_count = 0;
+ while ((start < here) && ((i_count = count_instr(start, here)) <= 0)) start += 2;
+ if (i_count == 0) return NULL; // There is something seriously wrong
+
+ //---< Narrow down distance (estimate was too large) >---
+ while(i_count-- > n_instr) {
+ start += Assembler::instr_len(start);
+ }
+ assert(n_instr >= count_instr(start, here), "just checking");
+ return start;
+}
+
+
+// Print annotations (value of loaded constant)
+void Disassembler::annotate(address here, outputStream* st) {
+ // Currently, annotation doesn't work when decoding error files.
+ // When decoding an instruction from a hs_err file, the given
+ // instruction address 'start' points to the instruction's virtual address
+ // which is not equal to the address where the instruction is located.
+ // Therefore, we will either crash or decode garbage.
+ if (is_decode_error_file()) {
+ return;
+ }
+
+ if (MacroAssembler::is_load_const(here)) {
+ long value = MacroAssembler::get_const(here);
+ const int tsize = 8;
+
+ st->fill_to(60);
+ st->print(";const %p | %ld | %23.15e", (void *)value, value, (double)value);
+ }
+}
--- a/src/hotspot/cpu/s390/disassembler_s390.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/s390/disassembler_s390.hpp Tue May 21 15:51:35 2019 +0200
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2016 SAP SE. All rights reserved.
+ * Copyright (c) 2016, 2019 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,11 +27,36 @@
#define CPU_S390_DISASSEMBLER_S390_HPP
static int pd_instruction_alignment() {
- return 1;
+ return 2;
}
static const char* pd_cpu_opts() {
- return "zarch";
+ return "s390";
}
+ static bool valid_opcodes[256];
+ static bool is_valid_opcode_at(address here);
+
+ // Find preceding instruction.
+ //
+ // Starting at the passed location, the n-th preceding (towards lower addresses)
+ // location is searched, the contents of which - if interpreted as
+ // instructions - has the passed location as n-th successor.
+ // - If multiple such locations exist between (here-n*instr_maxlen()) and here,
+ // the most distant location is selected.
+ // - If no such location exists, NULL is returned. The caller should then
+ // terminate its search and react properly.
+ static address find_prev_instr(address here, int n_instr);
+ static int count_instr(address begin, address end);
+
+ // special-case instruction decoding.
+ // There may be cases where the binutils disassembler doesn't do
+ // the perfect job. In those cases, decode_instruction0 may kick in
+ // and do it right.
+ // If nothing had to be done, just return "here", otherwise return "here + instr_len(here)"
+ static address decode_instruction0(address here, outputStream* st, address virtual_begin = NULL);
+
+ // platform-specific instruction annotations (like value of loaded constants)
+ static void annotate(address pc, outputStream* st);
+
#endif // CPU_S390_DISASSEMBLER_S390_HPP
--- a/src/hotspot/cpu/s390/s390.ad Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/s390/s390.ad Tue May 21 15:51:35 2019 +0200
@@ -1,6 +1,6 @@
//
-// Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
-// Copyright (c) 2017, SAP SE. All rights reserved.
+// Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
+// Copyright (c) 2017, 2019 SAP SE. All rights reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
//
// This code is free software; you can redistribute it and/or modify it
@@ -1388,7 +1388,6 @@
__ z_br(R1_ic_miss_stub_addr);
__ bind(valid);
}
-
}
uint MachUEPNode::size(PhaseRegAlloc *ra_) const {
@@ -4318,7 +4317,7 @@
match(Set dst src);
ins_cost(DEFAULT_COST);
size(6);
- format %{ "LGFI $dst,$src\t # (int)" %}
+ format %{ "LGFI $dst,$src\t # (int)" %}
ins_encode %{ __ z_lgfi($dst$$Register, $src$$constant); %} // Sign-extend to 64 bit, it's at no cost.
ins_pipe(pipe_class_dummy);
%}
@@ -4327,7 +4326,7 @@
match(Set dst src);
ins_cost(DEFAULT_COST_LOW);
size(4);
- format %{ "LGHI $dst,$src\t # (int)" %}
+ format %{ "LGHI $dst,$src\t # (int)" %}
ins_encode %{ __ z_lghi($dst$$Register, $src$$constant); %} // Sign-extend to 64 bit, it's at no cost.
ins_pipe(pipe_class_dummy);
%}
@@ -4723,7 +4722,7 @@
match(Set dst (LoadN mem));
ins_cost(MEMORY_REF_COST);
size(Z_DISP3_SIZE);
- format %{ "LoadN $dst,$mem\t# (cOop)" %}
+ format %{ "LoadN $dst,$mem\t # (cOop)" %}
opcode(LLGF_ZOPC, LLGF_ZOPC);
ins_encode(z_form_rt_mem_opt(dst, mem));
ins_pipe(pipe_class_dummy);
@@ -4734,7 +4733,7 @@
match(Set dst (LoadNKlass mem));
ins_cost(MEMORY_REF_COST);
size(Z_DISP3_SIZE);
- format %{ "LoadNKlass $dst,$mem\t# (klass cOop)" %}
+ format %{ "LoadNKlass $dst,$mem\t # (klass cOop)" %}
opcode(LLGF_ZOPC, LLGF_ZOPC);
ins_encode(z_form_rt_mem_opt(dst, mem));
ins_pipe(pipe_class_dummy);
@@ -4787,7 +4786,7 @@
predicate(false && (CompressedOops::base()==NULL)&&(CompressedOops::shift()==0));
ins_cost(MEMORY_REF_COST);
size(Z_DISP3_SIZE);
- format %{ "DecodeLoadN $dst,$mem\t# (cOop Load+Decode)" %}
+ format %{ "DecodeLoadN $dst,$mem\t # (cOop Load+Decode)" %}
opcode(LLGF_ZOPC, LLGF_ZOPC);
ins_encode(z_form_rt_mem_opt(dst, mem));
ins_pipe(pipe_class_dummy);
@@ -4798,7 +4797,7 @@
predicate(false && (CompressedKlassPointers::base()==NULL)&&(CompressedKlassPointers::shift()==0));
ins_cost(MEMORY_REF_COST);
size(Z_DISP3_SIZE);
- format %{ "DecodeLoadNKlass $dst,$mem\t# (load/decode NKlass)" %}
+ format %{ "DecodeLoadNKlass $dst,$mem\t # (load/decode NKlass)" %}
opcode(LLGF_ZOPC, LLGF_ZOPC);
ins_encode(z_form_rt_mem_opt(dst, mem));
ins_pipe(pipe_class_dummy);
@@ -4826,7 +4825,7 @@
predicate(CompressedOops::base() == NULL || !ExpandLoadingBaseDecode);
ins_cost(MEMORY_REF_COST+3 * DEFAULT_COST + BRANCH_COST);
// TODO: s390 port size(VARIABLE_SIZE);
- format %{ "decodeN $dst,$src\t# (decode cOop)" %}
+ format %{ "decodeN $dst,$src\t # (decode cOop)" %}
ins_encode %{ __ oop_decoder($dst$$Register, $src$$Register, true); %}
ins_pipe(pipe_class_dummy);
%}
@@ -4850,7 +4849,7 @@
(CompressedOops::base()== NULL || !ExpandLoadingBaseDecode_NN));
ins_cost(MEMORY_REF_COST+2 * DEFAULT_COST);
// TODO: s390 port size(VARIABLE_SIZE);
- format %{ "decodeN $dst,$src\t# (decode cOop NN)" %}
+ format %{ "decodeN $dst,$src\t # (decode cOop NN)" %}
ins_encode %{ __ oop_decoder($dst$$Register, $src$$Register, false); %}
ins_pipe(pipe_class_dummy);
%}
@@ -4873,7 +4872,7 @@
effect(KILL cr);
predicate(false);
// TODO: s390 port size(VARIABLE_SIZE);
- format %{ "decodeN $dst = ($src == 0) ? NULL : ($src << 3) + $base + pow2_offset\t# (decode cOop)" %}
+ format %{ "decodeN $dst = ($src == 0) ? NULL : ($src << 3) + $base + pow2_offset\t # (decode cOop)" %}
ins_encode %{
__ oop_decoder($dst$$Register, $src$$Register, true, $base$$Register,
(jlong)MacroAssembler::get_oop_base_pow2_offset((uint64_t)(intptr_t)CompressedOops::base()));
@@ -4887,7 +4886,7 @@
effect(KILL cr);
predicate(false);
// TODO: s390 port size(VARIABLE_SIZE);
- format %{ "decodeN $dst = ($src << 3) + $base + pow2_offset\t# (decode cOop)" %}
+ format %{ "decodeN $dst = ($src << 3) + $base + pow2_offset\t # (decode cOop)" %}
ins_encode %{
__ oop_decoder($dst$$Register, $src$$Register, false, $base$$Register,
(jlong)MacroAssembler::get_oop_base_pow2_offset((uint64_t)(intptr_t)CompressedOops::base()));
@@ -4937,7 +4936,7 @@
!ExpandLoadingBaseEncode));
ins_cost(MEMORY_REF_COST+3 * DEFAULT_COST);
// TODO: s390 port size(VARIABLE_SIZE);
- format %{ "encodeP $dst,$src\t# (encode cOop)" %}
+ format %{ "encodeP $dst,$src\t # (encode cOop)" %}
ins_encode %{ __ oop_encoder($dst$$Register, $src$$Register, true, Z_R1_scratch, -1, all_outs_are_Stores(this)); %}
ins_pipe(pipe_class_dummy);
%}
@@ -4960,7 +4959,7 @@
!ExpandLoadingBaseEncode_NN));
ins_cost(MEMORY_REF_COST+3 * DEFAULT_COST);
// TODO: s390 port size(VARIABLE_SIZE);
- format %{ "encodeP $dst,$src\t# (encode cOop)" %}
+ format %{ "encodeP $dst,$src\t # (encode cOop)" %}
ins_encode %{ __ oop_encoder($dst$$Register, $src$$Register, false, Z_R1_scratch, -1, all_outs_are_Stores(this)); %}
ins_pipe(pipe_class_dummy);
%}
@@ -4972,7 +4971,7 @@
predicate(false);
ins_cost(MEMORY_REF_COST+2 * DEFAULT_COST);
// TODO: s390 port size(VARIABLE_SIZE);
- format %{ "encodeP $dst = ($src>>3) +$base + pow2_offset\t# (encode cOop)" %}
+ format %{ "encodeP $dst = ($src>>3) +$base + pow2_offset\t # (encode cOop)" %}
ins_encode %{
jlong offset = -(jlong)MacroAssembler::get_oop_base_pow2_offset
(((uint64_t)(intptr_t)CompressedOops::base()) >> CompressedOops::shift());
@@ -4988,7 +4987,7 @@
predicate(false);
ins_cost(MEMORY_REF_COST+2 * DEFAULT_COST);
// TODO: s390 port size(VARIABLE_SIZE);
- format %{ "encodeP $dst = ($src>>3) +$base + $pow2_offset\t# (encode cOop)" %}
+ format %{ "encodeP $dst = ($src>>3) +$base + $pow2_offset\t # (encode cOop)" %}
ins_encode %{ __ oop_encoder($dst$$Register, $src$$Register, false, $base$$Register, $pow2_offset$$constant); %}
ins_pipe(pipe_class_dummy);
%}
@@ -5041,7 +5040,7 @@
match(Set mem (StoreN mem src));
ins_cost(MEMORY_REF_COST);
size(Z_DISP_SIZE);
- format %{ "ST $src,$mem\t# (cOop)" %}
+ format %{ "ST $src,$mem\t # (cOop)" %}
opcode(STY_ZOPC, ST_ZOPC);
ins_encode(z_form_rt_mem_opt(src, mem));
ins_pipe(pipe_class_dummy);
@@ -5052,7 +5051,7 @@
match(Set mem (StoreNKlass mem src));
ins_cost(MEMORY_REF_COST);
size(Z_DISP_SIZE);
- format %{ "ST $src,$mem\t# (cKlass)" %}
+ format %{ "ST $src,$mem\t # (cKlass)" %}
opcode(STY_ZOPC, ST_ZOPC);
ins_encode(z_form_rt_mem_opt(src, mem));
ins_pipe(pipe_class_dummy);
@@ -5064,7 +5063,7 @@
match(Set cr (CmpN src1 src2));
ins_cost(DEFAULT_COST);
size(2);
- format %{ "CLR $src1,$src2\t# (cOop)" %}
+ format %{ "CLR $src1,$src2\t # (cOop)" %}
opcode(CLR_ZOPC);
ins_encode(z_rrform(src1, src2));
ins_pipe(pipe_class_dummy);
@@ -5074,7 +5073,7 @@
match(Set cr (CmpN src1 src2));
ins_cost(DEFAULT_COST);
size(6);
- format %{ "CLFI $src1,$src2\t# (cOop) compare immediate narrow" %}
+ format %{ "CLFI $src1,$src2\t # (cOop) compare immediate narrow" %}
ins_encode %{
AddressLiteral cOop = __ constant_oop_address((jobject)$src2$$constant);
__ relocate(cOop.rspec(), 1);
@@ -5087,7 +5086,7 @@
match(Set cr (CmpN src1 src2));
ins_cost(DEFAULT_COST);
size(6);
- format %{ "CLFI $src1,$src2\t# (NKlass) compare immediate narrow" %}
+ format %{ "CLFI $src1,$src2\t # (NKlass) compare immediate narrow" %}
ins_encode %{
AddressLiteral NKlass = __ constant_metadata_address((Metadata*)$src2$$constant);
__ relocate(NKlass.rspec(), 1);
@@ -5100,7 +5099,7 @@
match(Set cr (CmpN src1 src2));
ins_cost(DEFAULT_COST);
size(2);
- format %{ "LTR $src1,$src2\t# (cOop) LTR because comparing against zero" %}
+ format %{ "LTR $src1,$src2\t # (cOop) LTR because comparing against zero" %}
opcode(LTR_ZOPC);
ins_encode(z_rrform(src1, src1));
ins_pipe(pipe_class_dummy);
@@ -6795,7 +6794,7 @@
effect(KILL cr); // R1 is killed, too.
ins_cost(3 * DEFAULT_COST);
size(14);
- format %{ "SLL $dst,$src,[$nbits] & 31\t# use RISC-like SLLG also for int" %}
+ format %{ "SLL $dst,$src,[$nbits] & 31\t # use RISC-like SLLG also for int" %}
ins_encode %{
__ z_lgr(Z_R1_scratch, $nbits$$Register);
__ z_nill(Z_R1_scratch, BitsPerJavaInteger-1);
@@ -6809,7 +6808,7 @@
instruct sllI_reg_imm(iRegI dst, iRegI src, immI nbits) %{
match(Set dst (LShiftI src nbits));
size(6);
- format %{ "SLL $dst,$src,$nbits\t# use RISC-like SLLG also for int" %}
+ format %{ "SLL $dst,$src,$nbits\t # use RISC-like SLLG also for int" %}
ins_encode %{
int Nbit = $nbits$$constant;
assert((Nbit & (BitsPerJavaInteger - 1)) == Nbit, "Check shift mask in ideal graph");
@@ -7125,7 +7124,7 @@
instruct overflowNegI_rReg(flagsReg cr, immI_0 zero, iRegI op2) %{
match(Set cr (OverflowSubI zero op2));
effect(DEF cr, USE op2);
- format %{ "NEG $op2\t# overflow check int" %}
+ format %{ "NEG $op2\t # overflow check int" %}
ins_encode %{
__ clear_reg(Z_R0_scratch, false, false);
__ z_sr(Z_R0_scratch, $op2$$Register);
@@ -7136,7 +7135,7 @@
instruct overflowNegL_rReg(flagsReg cr, immL_0 zero, iRegL op2) %{
match(Set cr (OverflowSubL zero op2));
effect(DEF cr, USE op2);
- format %{ "NEGG $op2\t# overflow check long" %}
+ format %{ "NEGG $op2\t # overflow check long" %}
ins_encode %{
__ clear_reg(Z_R0_scratch, true, false);
__ z_sgr(Z_R0_scratch, $op2$$Register);
@@ -9191,7 +9190,7 @@
effect(USE lbl);
ins_cost(BRANCH_COST);
size(4);
- format %{ "branch_con_short,$cmp $cr, $lbl" %}
+ format %{ "branch_con_short,$cmp $lbl" %}
ins_encode(z_enc_branch_con_short(cmp, lbl));
ins_pipe(pipe_class_dummy);
// If set to 1 this indicates that the current instruction is a
@@ -9213,7 +9212,7 @@
// Make more expensive to prefer compare_and_branch over separate instructions.
ins_cost(2 * BRANCH_COST);
size(6);
- format %{ "branch_con_far,$cmp $cr, $lbl" %}
+ format %{ "branch_con_far,$cmp $lbl" %}
ins_encode(z_enc_branch_con_far(cmp, lbl));
ins_pipe(pipe_class_dummy);
// This is not a short variant of a branch, but the long variant..
@@ -9782,7 +9781,7 @@
match(TailCall jump_target method_oop);
ins_cost(CALL_COST);
size(2);
- format %{ "Jmp $jump_target\t# $method_oop holds method oop" %}
+ format %{ "Jmp $jump_target\t # $method_oop holds method oop" %}
ins_encode %{ __ z_br($jump_target$$Register); %}
ins_pipe(pipe_class_dummy);
%}
@@ -10790,7 +10789,7 @@
predicate(UseByteReverseInstruction); // See Matcher::match_rule_supported
ins_cost(DEFAULT_COST);
size(4);
- format %{ "LRVR $dst,$src\t# byte reverse int" %}
+ format %{ "LRVR $dst,$src\t # byte reverse int" %}
opcode(LRVR_ZOPC);
ins_encode(z_rreform(dst, src));
ins_pipe(pipe_class_dummy);
@@ -10801,7 +10800,7 @@
predicate(UseByteReverseInstruction); // See Matcher::match_rule_supported
ins_cost(DEFAULT_COST);
// TODO: s390 port size(FIXED_SIZE);
- format %{ "LRVGR $dst,$src\t# byte reverse long" %}
+ format %{ "LRVGR $dst,$src\t # byte reverse long" %}
opcode(LRVGR_ZOPC);
ins_encode(z_rreform(dst, src));
ins_pipe(pipe_class_dummy);
@@ -10821,8 +10820,8 @@
effect(KILL tmp, KILL cr);
ins_cost(3 * DEFAULT_COST);
size(14);
- format %{ "SLLG $dst,$src,32\t# no need to always count 32 zeroes first\n\t"
- "IILH $dst,0x8000 \t# insert \"stop bit\" to force result 32 for zero src.\n\t"
+ format %{ "SLLG $dst,$src,32\t # no need to always count 32 zeroes first\n\t"
+ "IILH $dst,0x8000 \t # insert \"stop bit\" to force result 32 for zero src.\n\t"
"FLOGR $dst,$dst"
%}
ins_encode %{
@@ -10859,7 +10858,7 @@
effect(KILL tmp, KILL cr);
ins_cost(DEFAULT_COST);
size(4);
- format %{ "FLOGR $dst,$src \t# count leading zeros (long)\n\t" %}
+ format %{ "FLOGR $dst,$src \t # count leading zeros (long)\n\t" %}
ins_encode %{ __ z_flogr($dst$$Register, $src$$Register); %}
ins_pipe(pipe_class_dummy);
%}
@@ -10884,14 +10883,14 @@
effect(TEMP_DEF dst, TEMP tmp, KILL cr);
ins_cost(8 * DEFAULT_COST);
// TODO: s390 port size(FIXED_SIZE); // Emitted code depends on PreferLAoverADD being on/off.
- format %{ "LLGFR $dst,$src \t# clear upper 32 bits (we are dealing with int)\n\t"
- "LCGFR $tmp,$src \t# load 2's complement (32->64 bit)\n\t"
- "AGHI $dst,-1 \t# tmp1 = src-1\n\t"
- "AGHI $tmp,-1 \t# tmp2 = -src-1 = ~src\n\t"
- "NGR $dst,$tmp \t# tmp3 = tmp1&tmp2\n\t"
- "FLOGR $dst,$dst \t# count trailing zeros (int)\n\t"
- "AHI $dst,-64 \t# tmp4 = 64-(trailing zeroes)-64\n\t"
- "LCR $dst,$dst \t# res = -tmp4"
+ format %{ "LLGFR $dst,$src \t # clear upper 32 bits (we are dealing with int)\n\t"
+ "LCGFR $tmp,$src \t # load 2's complement (32->64 bit)\n\t"
+ "AGHI $dst,-1 \t # tmp1 = src-1\n\t"
+ "AGHI $tmp,-1 \t # tmp2 = -src-1 = ~src\n\t"
+ "NGR $dst,$tmp \t # tmp3 = tmp1&tmp2\n\t"
+ "FLOGR $dst,$dst \t # count trailing zeros (int)\n\t"
+ "AHI $dst,-64 \t # tmp4 = 64-(trailing zeroes)-64\n\t"
+ "LCR $dst,$dst \t # res = -tmp4"
%}
ins_encode %{
Register Rdst = $dst$$Register;
@@ -10937,12 +10936,12 @@
effect(TEMP_DEF dst, KILL tmp, KILL cr);
ins_cost(8 * DEFAULT_COST);
// TODO: s390 port size(FIXED_SIZE); // Emitted code depends on PreferLAoverADD being on/off.
- format %{ "LCGR $dst,$src \t# preserve src\n\t"
- "NGR $dst,$src \t#"
- "AGHI $dst,-1 \t# tmp1 = src-1\n\t"
- "FLOGR $dst,$dst \t# count trailing zeros (long), kill $tmp\n\t"
- "AHI $dst,-64 \t# tmp4 = 64-(trailing zeroes)-64\n\t"
- "LCR $dst,$dst \t#"
+ format %{ "LCGR $dst,$src \t # preserve src\n\t"
+ "NGR $dst,$src \t #\n\t"
+ "AGHI $dst,-1 \t # tmp1 = src-1\n\t"
+ "FLOGR $dst,$dst \t # count trailing zeros (long), kill $tmp\n\t"
+ "AHI $dst,-64 \t # tmp4 = 64-(trailing zeroes)-64\n\t"
+ "LCR $dst,$dst \t #"
%}
ins_encode %{
Register Rdst = $dst$$Register;
@@ -10969,7 +10968,7 @@
predicate(UsePopCountInstruction && VM_Version::has_PopCount());
ins_cost(DEFAULT_COST);
size(24);
- format %{ "POPCNT $dst,$src\t# pop count int" %}
+ format %{ "POPCNT $dst,$src\t # pop count int" %}
ins_encode %{
Register Rdst = $dst$$Register;
Register Rsrc = $src$$Register;
@@ -10996,7 +10995,7 @@
predicate(UsePopCountInstruction && VM_Version::has_PopCount());
ins_cost(DEFAULT_COST);
// TODO: s390 port size(FIXED_SIZE);
- format %{ "POPCNT $dst,$src\t# pop count long" %}
+ format %{ "POPCNT $dst,$src\t # pop count long" %}
ins_encode %{
Register Rdst = $dst$$Register;
Register Rsrc = $src$$Register;
--- a/src/hotspot/cpu/s390/vm_version_s390.cpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/s390/vm_version_s390.cpp Tue May 21 15:51:35 2019 +0200
@@ -804,6 +804,8 @@
address code_end = a->pc();
a->flush();
+ cbuf.insts()->set_end(code_end);
+
// Print the detection code.
bool printVerbose = Verbose || PrintAssembly || PrintStubCode;
if (printVerbose) {
@@ -812,8 +814,8 @@
tty->print_cr("Stub length is %ld bytes, codebuffer reserves %d bytes, %ld bytes spare.",
code_end-code, cbuf_size, cbuf_size-(code_end-code));
- // Use existing decode function. This enables the [Code] format which is needed to DecodeErrorFile.
- Disassembler::decode((u_char*)code, (u_char*)code_end, tty);
+ // Use existing decode function. This enables the [MachCode] format which is needed to DecodeErrorFile.
+ Disassembler::decode(&cbuf, code, code_end, tty);
}
// Prepare for detection code execution and clear work buffer.
--- a/src/hotspot/cpu/sparc/assembler_sparc.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/sparc/assembler_sparc.hpp Tue May 21 15:51:35 2019 +0200
@@ -335,6 +335,14 @@
Lookaside = 1 << 4
};
+ //---< calculate length of instruction >---
+ // With SPARC being a RISC architecture, this always is BytesPerInstWord
+ // instruction must start at passed address
+ static unsigned int instr_len(unsigned char *instr) { return BytesPerInstWord; }
+
+ //---< longest instructions >---
+ static unsigned int instr_maxlen() { return BytesPerInstWord; }
+
static bool is_in_wdisp_range(address a, address b, int nbits) {
intptr_t d = intptr_t(b) - intptr_t(a);
return is_simm(d, nbits + 2);
--- a/src/hotspot/cpu/sparc/disassembler_sparc.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/sparc/disassembler_sparc.hpp Tue May 21 15:51:35 2019 +0200
@@ -33,4 +33,24 @@
return "v9only";
}
+ // Returns address of n-th instruction preceding addr,
+ // NULL if no preceding instruction can be found.
+ // With SPARC being a RISC architecture, this always is BytesPerInstWord
+ // It might be beneficial to check "is_readable" as we do on ppc and s390.
+ static address find_prev_instr(address addr, int n_instr) {
+ return addr - BytesPerInstWord*n_instr;
+ }
+
+ // special-case instruction decoding.
+ // There may be cases where the binutils disassembler doesn't do
+ // the perfect job. In those cases, decode_instruction0 may kick in
+ // and do it right.
+ // If nothing had to be done, just return "here", otherwise return "here + instr_len(here)"
+ static address decode_instruction0(address here, outputStream* st, address virtual_begin = NULL) {
+ return here;
+ }
+
+ // platform-specific instruction annotations (like value of loaded constants)
+ static void annotate(address pc, outputStream* st) { };
+
#endif // CPU_SPARC_DISASSEMBLER_SPARC_HPP
--- a/src/hotspot/cpu/sparc/sparc.ad Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/sparc/sparc.ad Tue May 21 15:51:35 2019 +0200
@@ -1,5 +1,5 @@
//
-// Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
+// Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
//
// This code is free software; you can redistribute it and/or modify it
@@ -1295,7 +1295,8 @@
#ifndef PRODUCT
ATTRIBUTE_PRINTF(2, 3)
static void print_helper(outputStream* st, const char* format, ...) {
- if (st->position() > 0) {
+ const int tab_size = 8;
+ if (st->position() > tab_size) {
st->cr();
st->sp();
}
--- a/src/hotspot/cpu/x86/assembler_x86.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/x86/assembler_x86.hpp Tue May 21 15:51:35 2019 +0200
@@ -630,6 +630,17 @@
_true = 7
};
+ //---< calculate length of instruction >---
+ // As instruction size can't be found out easily on x86/x64,
+ // we just use '4' for len and maxlen.
+ // instruction must start at passed address
+ static unsigned int instr_len(unsigned char *instr) { return 4; }
+
+ //---< longest instructions >---
+ // Max instruction length is not specified in architecture documentation.
+ // We could use a "safe enough" estimate (15), but just default to
+ // instruction length guess from above.
+ static unsigned int instr_maxlen() { return 4; }
// NOTE: The general philopsophy of the declarations here is that 64bit versions
// of instructions are freely declared without the need for wrapping them an ifdef.
--- a/src/hotspot/cpu/x86/disassembler_x86.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/x86/disassembler_x86.hpp Tue May 21 15:51:35 2019 +0200
@@ -33,4 +33,25 @@
return "";
}
+ // Returns address of n-th instruction preceding addr,
+ // NULL if no preceding instruction can be found.
+ // On CISC architectures, it is difficult to impossible to step
+ // backwards in the instruction stream. Therefore just return NULL.
+ // It might be beneficial to check "is_readable" as we do on ppc and s390.
+ static address find_prev_instr(address addr, int n_instr) {
+ return NULL;
+ }
+
+ // special-case instruction decoding.
+ // There may be cases where the binutils disassembler doesn't do
+ // the perfect job. In those cases, decode_instruction0 may kick in
+ // and do it right.
+ // If nothing had to be done, just return "here", otherwise return "here + instr_len(here)"
+ static address decode_instruction0(address here, outputStream* st, address virtual_begin = NULL) {
+ return here;
+ }
+
+ // platform-specific instruction annotations (like value of loaded constants)
+ static void annotate(address pc, outputStream* st) { };
+
#endif // CPU_X86_DISASSEMBLER_X86_HPP
--- a/src/hotspot/cpu/x86/x86_64.ad Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/x86/x86_64.ad Tue May 21 15:51:35 2019 +0200
@@ -918,19 +918,19 @@
st->print("\t");
}
- st->print_cr("popq rbp");
+ st->print_cr("popq rbp");
if (do_polling() && C->is_method_compilation()) {
st->print("\t");
if (SafepointMechanism::uses_thread_local_poll()) {
- st->print_cr("movq rscratch1, poll_offset[r15_thread] #polling_page_address\n\t"
- "testl rax, [rscratch1]\t"
+ st->print_cr("movq rscratch1, poll_offset[r15_thread] #polling_page_address\n\t"
+ "testl rax, [rscratch1]\t"
"# Safepoint: poll for GC");
} else if (Assembler::is_polling_page_far()) {
- st->print_cr("movq rscratch1, #polling_page_address\n\t"
- "testl rax, [rscratch1]\t"
+ st->print_cr("movq rscratch1, #polling_page_address\n\t"
+ "testl rax, [rscratch1]\t"
"# Safepoint: poll for GC");
} else {
- st->print_cr("testl rax, [rip + #offset_to_poll_page]\t"
+ st->print_cr("testl rax, [rip + #offset_to_poll_page]\t"
"# Safepoint: poll for GC");
}
}
@@ -10303,10 +10303,10 @@
match(Set p (AddI (AndI (CmpLTMask p q) y) (SubI p q)));
effect(KILL cr);
ins_cost(300);
- format %{ "subl $p,$q\t# cadd_cmpLTMask\n\t"
- "jge done\n\t"
- "addl $p,$y\n"
- "done: " %}
+ format %{ "subl $p,$q\t# cadd_cmpLTMask\n\t"
+ "jge done\n\t"
+ "addl $p,$y\n"
+ "done: " %}
ins_encode %{
Register Rp = $p$$Register;
Register Rq = $q$$Register;
@@ -10328,10 +10328,10 @@
ins_cost(300);
- format %{ "cmpl $p, $q\t# and_cmpLTMask\n\t"
- "jlt done\n\t"
- "xorl $y, $y\n"
- "done: " %}
+ format %{ "cmpl $p, $q\t# and_cmpLTMask\n\t"
+ "jlt done\n\t"
+ "xorl $y, $y\n"
+ "done: " %}
ins_encode %{
Register Rp = $p$$Register;
Register Rq = $q$$Register;
@@ -11888,7 +11888,7 @@
%{
match(Set cr (CmpU src zero));
- format %{ "testl $src, $src\t# unsigned" %}
+ format %{ "testl $src, $src\t# unsigned" %}
opcode(0x85);
ins_encode(REX_reg_reg(src, src), OpcP, reg_reg(src, src));
ins_pipe(ialu_cr_reg_imm);
@@ -12431,7 +12431,7 @@
effect(USE labl);
ins_cost(300);
- format %{ "j$cop,u $labl" %}
+ format %{ "j$cop,u $labl" %}
size(6);
ins_encode %{
Label* L = $labl$$label;
@@ -12445,7 +12445,7 @@
effect(USE labl);
ins_cost(200);
- format %{ "j$cop,u $labl" %}
+ format %{ "j$cop,u $labl" %}
size(6);
ins_encode %{
Label* L = $labl$$label;
@@ -12461,10 +12461,10 @@
ins_cost(200);
format %{ $$template
if ($cop$$cmpcode == Assembler::notEqual) {
- $$emit$$"jp,u $labl\n\t"
+ $$emit$$"jp,u $labl\n\t"
$$emit$$"j$cop,u $labl"
} else {
- $$emit$$"jp,u done\n\t"
+ $$emit$$"jp,u done\n\t"
$$emit$$"j$cop,u $labl\n\t"
$$emit$$"done:"
}
@@ -12666,10 +12666,10 @@
ins_cost(300);
format %{ $$template
if ($cop$$cmpcode == Assembler::notEqual) {
- $$emit$$"jp,u,s $labl\n\t"
- $$emit$$"j$cop,u,s $labl"
+ $$emit$$"jp,u,s $labl\n\t"
+ $$emit$$"j$cop,u,s $labl"
} else {
- $$emit$$"jp,u,s done\n\t"
+ $$emit$$"jp,u,s done\n\t"
$$emit$$"j$cop,u,s $labl\n\t"
$$emit$$"done:"
}
@@ -12745,7 +12745,7 @@
match(SafePoint);
effect(KILL cr);
- format %{ "testl rax, [rip + #offset_to_poll_page]\t"
+ format %{ "testl rax, [rip + #offset_to_poll_page]\t"
"# Safepoint: poll for GC" %}
ins_cost(125);
ins_encode %{
@@ -12761,7 +12761,7 @@
match(SafePoint poll);
effect(KILL cr, USE poll);
- format %{ "testl rax, [$poll]\t"
+ format %{ "testl rax, [$poll]\t"
"# Safepoint: poll for GC" %}
ins_cost(125);
ins_encode %{
@@ -12777,7 +12777,7 @@
match(SafePoint poll);
effect(KILL cr, USE poll);
- format %{ "testl rax, [$poll]\t"
+ format %{ "testl rax, [$poll]\t"
"# Safepoint: poll for GC" %}
ins_cost(125);
size(4); /* setting an explicit size will cause debug builds to assert if size is incorrect */
--- a/src/hotspot/cpu/zero/assembler_zero.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/zero/assembler_zero.hpp Tue May 21 15:51:35 2019 +0200
@@ -37,6 +37,12 @@
public:
void pd_patch_instruction(address branch, address target, const char* file, int line);
+
+ //---< calculate length of instruction >---
+ static unsigned int instr_len(unsigned char *instr) { return 1; }
+
+ //---< longest instructions >---
+ static unsigned int instr_maxlen() { return 1; }
};
class MacroAssembler : public Assembler {
--- a/src/hotspot/cpu/zero/disassembler_zero.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/cpu/zero/disassembler_zero.hpp Tue May 21 15:51:35 2019 +0200
@@ -34,4 +34,24 @@
return "";
}
+ // Returns address of n-th instruction preceding addr,
+ // NULL if no preceding instruction can be found.
+ // On ZERO, we assume a constant instruction length of 1 byte.
+ // It might be beneficial to check "is_readable" as we do on ppc and s390.
+ static address find_prev_instr(address addr, int n_instr) {
+ return addr - 1*n_instr;
+ }
+
+ // special-case instruction decoding.
+ // There may be cases where the binutils disassembler doesn't do
+ // the perfect job. In those cases, decode_instruction0 may kick in
+ // and do it right.
+ // If nothing had to be done, just return "here", otherwise return "here + instr_len(here)"
+ static address decode_instruction0(address here, outputStream* st, address virtual_begin = NULL) {
+ return here;
+ }
+
+ // platform-specific instruction annotations (like value of loaded constants)
+ static void annotate(address pc, outputStream* st) { };
+
#endif // CPU_ZERO_DISASSEMBLER_ZERO_HPP
--- a/src/hotspot/share/asm/codeBuffer.cpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/asm/codeBuffer.cpp Tue May 21 15:51:35 2019 +0200
@@ -86,7 +86,8 @@
// External buffer, in a predefined CodeBlob.
// Important: The code_start must be taken exactly, and not realigned.
CodeBuffer::CodeBuffer(CodeBlob* blob) {
- initialize_misc("static buffer");
+ // Provide code buffer with meaningful name
+ initialize_misc(blob->name());
initialize(blob->content_begin(), blob->content_size());
verify_section_allocation();
}
@@ -1035,7 +1036,9 @@
}
void CodeBuffer::block_comment(intptr_t offset, const char * comment) {
- _code_strings.add_comment(offset, comment);
+ if (_collect_comments) {
+ _code_strings.add_comment(offset, comment);
+ }
}
const char* CodeBuffer::code_string(const char* str) {
@@ -1148,15 +1151,23 @@
const char* CodeStrings::_prefix = " ;; "; // default: can be changed via set_prefix
+// Check if any block comments are pending for the given offset.
+bool CodeStrings::has_block_comment(intptr_t offset) const {
+ if (_strings == NULL) return false;
+ CodeString* c = find(offset);
+ return c != NULL;
+}
+
void CodeStrings::print_block_comment(outputStream* stream, intptr_t offset) const {
- check_valid();
- if (_strings != NULL) {
+ check_valid();
+ if (_strings != NULL) {
CodeString* c = find(offset);
while (c && c->offset() == offset) {
stream->bol();
stream->print("%s", _prefix);
// Don't interpret as format strings since it could contain %
- stream->print_raw_cr(c->string());
+ stream->print_raw(c->string());
+ stream->bol(); // advance to next line only if string didn't contain a cr() at the end.
c = c->next_comment();
}
}
@@ -1186,7 +1197,7 @@
void CodeBuffer::decode() {
ttyLocker ttyl;
- Disassembler::decode(decode_begin(), insts_end());
+ Disassembler::decode(decode_begin(), insts_end(), tty);
_decode_begin = insts_end();
}
@@ -1217,4 +1228,10 @@
}
}
+// Directly disassemble code buffer.
+void CodeBuffer::decode(address start, address end) {
+ ttyLocker ttyl;
+ Disassembler::decode(this, start, end, tty);
+}
+
#endif // PRODUCT
--- a/src/hotspot/share/asm/codeBuffer.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/asm/codeBuffer.hpp Tue May 21 15:51:35 2019 +0200
@@ -289,6 +289,7 @@
const char* add_string(const char * string) PRODUCT_RETURN_(return NULL;);
void add_comment(intptr_t offset, const char * comment) PRODUCT_RETURN;
+ bool has_block_comment(intptr_t offset) const;
void print_block_comment(outputStream* stream, intptr_t offset) const PRODUCT_RETURN;
// MOVE strings from other to this; invalidate other.
void assign(CodeStrings& other) PRODUCT_RETURN;
@@ -296,6 +297,7 @@
void copy(CodeStrings& other) PRODUCT_RETURN;
// FREE strings; invalidate this.
void free() PRODUCT_RETURN;
+
// Guarantee that _strings are used at most once; assign and free invalidate a buffer.
inline void check_valid() const {
#ifdef ASSERT
@@ -377,6 +379,7 @@
OopRecorder* _oop_recorder;
CodeStrings _code_strings;
+ bool _collect_comments; // Indicate if we need to collect block comments at all.
OopRecorder _default_oop_recorder; // override with initialize_oop_recorder
Arena* _overflow_arena;
@@ -403,6 +406,14 @@
#if INCLUDE_AOT
_immutable_PIC = false;
#endif
+
+ // Collect block comments, but restrict collection to cases where a disassembly is output.
+ _collect_comments = ( PrintAssembly
+ || PrintStubCode
+ || PrintMethodHandleStubs
+ || PrintInterpreter
+ || PrintSignatureHandlers
+ );
}
void initialize(address code_start, csize_t code_size) {
@@ -604,6 +615,23 @@
}
}
+ // Directly disassemble code buffer.
+ // Print the comment associated with offset on stream, if there is one.
+ virtual void print_block_comment(outputStream* stream, address block_begin) {
+#ifndef PRODUCT
+ intptr_t offset = (intptr_t)(block_begin - _total_start); // I assume total_start is not correct for all code sections.
+ _code_strings.print_block_comment(stream, offset);
+#endif
+ }
+ bool has_block_comment(address block_begin) {
+#ifndef PRODUCT
+ intptr_t offset = (intptr_t)(block_begin - _total_start); // I assume total_start is not correct for all code sections.
+ return _code_strings.has_block_comment(offset);
+#else
+ return false;
+#endif
+ }
+
// Code generation
void relocate(address at, RelocationHolder const& rspec, int format = 0) {
_insts.relocate(at, rspec, format);
@@ -650,7 +678,8 @@
void decode();
void print();
#endif
-
+ // Directly disassemble code buffer.
+ void decode(address start, address end);
// The following header contains architecture-specific implementations
#include CPU_HEADER(codeBuffer)
--- a/src/hotspot/share/code/codeBlob.cpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/code/codeBlob.cpp Tue May 21 15:51:35 2019 +0200
@@ -183,8 +183,14 @@
jio_snprintf(stub_id, sizeof(stub_id), "%s%s", name1, name2);
if (PrintStubCode) {
ttyLocker ttyl;
+ tty->print_cr("- - - [BEGIN] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
tty->print_cr("Decoding %s " INTPTR_FORMAT, stub_id, (intptr_t) stub);
- Disassembler::decode(stub->code_begin(), stub->code_end());
+ Disassembler::decode(stub->code_begin(), stub->code_end(), tty);
+ if ((stub->oop_maps() != NULL) && AbstractDisassembler::show_structs()) {
+ tty->print_cr("- - - [OOP MAPS]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
+ stub->oop_maps()->print();
+ }
+ tty->print_cr("- - - [END] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
tty->cr();
}
Forte::register_stub(stub_id, stub->code_begin(), stub->code_end());
@@ -263,6 +269,7 @@
}
void BufferBlob::free(BufferBlob *blob) {
+ assert(blob != NULL, "caller must check for NULL");
ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock
blob->flush();
{
--- a/src/hotspot/share/code/codeBlob.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/code/codeBlob.hpp Tue May 21 15:51:35 2019 +0200
@@ -211,7 +211,7 @@
const ImmutableOopMap* oop_map_for_return_address(address return_address);
virtual void preserve_callee_argument_oops(frame fr, const RegisterMap* reg_map, OopClosure* f) = 0;
- // Frame support
+ // Frame support. Sizes are in word units.
int frame_size() const { return _frame_size; }
void set_frame_size(int size) { _frame_size = size; }
@@ -230,6 +230,10 @@
void dump_for_addr(address addr, outputStream* st, bool verbose) const;
void print_code();
+ bool has_block_comment(address block_begin) const {
+ intptr_t offset = (intptr_t)(block_begin - code_begin());
+ return _strings.has_block_comment(offset);
+ }
// Print the comment associated with offset on stream, if there is one
virtual void print_block_comment(outputStream* stream, address block_begin) const {
intptr_t offset = (intptr_t)(block_begin - code_begin());
--- a/src/hotspot/share/code/exceptionHandlerTable.cpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/code/exceptionHandlerTable.cpp Tue May 21 15:51:35 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -185,10 +185,24 @@
}
void ImplicitExceptionTable::print(address base) const {
- tty->print("{");
- for( uint i=0; i<len(); i++ )
- tty->print("< " INTPTR_FORMAT ", " INTPTR_FORMAT " > ", p2i(base + *adr(i)), p2i(base + *(adr(i)+1)));
- tty->print_cr("}");
+ const uint n = len();
+ if (n > 0) {
+ const uint items_per_line = 3;
+ uint i;
+ tty->print_cr("ImplicitExceptionTable (size = %d entries, %d bytes):", n, size_in_bytes());
+ tty->print("{");
+ for (i = 0; i < n; i++) {
+ if (i%items_per_line == 0) {
+ tty->cr();
+ tty->fill_to(3);
+ }
+ tty->print("< " INTPTR_FORMAT ", " INTPTR_FORMAT " > ", p2i(base + *adr(i)), p2i(base + *(adr(i)+1)));
+ }
+ tty->bol();
+ tty->print_cr("}");
+ } else {
+ tty->print_cr("ImplicitExceptionTable is empty");
+ }
}
ImplicitExceptionTable::ImplicitExceptionTable(const nmethod* nm) {
--- a/src/hotspot/share/code/nmethod.cpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/code/nmethod.cpp Tue May 21 15:51:35 2019 +0200
@@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "jvm.h"
+#include "asm/assembler.inline.hpp"
#include "code/codeCache.hpp"
#include "code/compiledIC.hpp"
#include "code/compiledMethod.inline.hpp"
@@ -456,14 +457,17 @@
{
MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
int native_nmethod_size = CodeBlob::allocation_size(code_buffer, sizeof(nmethod));
+
CodeOffsets offsets;
offsets.set_value(CodeOffsets::Verified_Entry, vep_offset);
offsets.set_value(CodeOffsets::Frame_Complete, frame_complete);
- nm = new (native_nmethod_size, CompLevel_none) nmethod(method(), compiler_none, native_nmethod_size,
- compile_id, &offsets,
- code_buffer, frame_size,
- basic_lock_owner_sp_offset,
- basic_lock_sp_offset, oop_maps);
+ nm = new (native_nmethod_size, CompLevel_none)
+ nmethod(method(), compiler_none, native_nmethod_size,
+ compile_id, &offsets,
+ code_buffer, frame_size,
+ basic_lock_owner_sp_offset,
+ basic_lock_sp_offset,
+ oop_maps);
NOT_PRODUCT(if (nm != NULL) native_nmethod_stats.note_native_nmethod(nm));
}
@@ -593,9 +597,9 @@
_native_basic_lock_sp_offset(basic_lock_sp_offset)
{
{
- int scopes_data_offset = 0;
- int deoptimize_offset = 0;
- int deoptimize_mh_offset = 0;
+ int scopes_data_offset = 0;
+ int deoptimize_offset = 0;
+ int deoptimize_mh_offset = 0;
debug_only(NoSafepointVerifier nsv;)
assert_locked_or_safepoint(CodeCache_lock);
@@ -658,18 +662,32 @@
xtty->stamp();
xtty->end_head(" address='" INTPTR_FORMAT "'", (intptr_t) this);
}
- // print the header part first
- print();
- // then print the requested information
+ // Print the header part, then print the requested information.
+ // This is both handled in decode2(), called via print_code() -> decode()
if (PrintNativeNMethods) {
+ tty->print_cr("-------------------------- Assembly (native nmethod) ---------------------------");
print_code();
- if (oop_maps != NULL) {
- oop_maps->print();
+ tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+#if defined(SUPPORT_DATA_STRUCTS)
+ if (AbstractDisassembler::show_structs()) {
+ if (oop_maps != NULL) {
+ tty->print("oop maps:"); // oop_maps->print_on(tty) outputs a cr() at the beginning
+ oop_maps->print_on(tty);
+ tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+ }
+ }
+#endif
+ } else {
+ print(); // print the header part only.
+ }
+#if defined(SUPPORT_DATA_STRUCTS)
+ if (AbstractDisassembler::show_structs()) {
+ if (PrintRelocations) {
+ print_relocations();
+ tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
}
}
- if (PrintRelocations) {
- print_relocations();
- }
+#endif
if (xtty != NULL) {
xtty->tail("print_native_nmethod");
}
@@ -746,22 +764,21 @@
} else {
_deopt_mh_handler_begin = NULL;
}
- } else {
+ } else
#endif
- // Exception handler and deopt handler are in the stub section
- assert(offsets->value(CodeOffsets::Exceptions) != -1, "must be set");
- assert(offsets->value(CodeOffsets::Deopt ) != -1, "must be set");
-
- _exception_offset = _stub_offset + offsets->value(CodeOffsets::Exceptions);
- _deopt_handler_begin = (address) this + _stub_offset + offsets->value(CodeOffsets::Deopt);
- if (offsets->value(CodeOffsets::DeoptMH) != -1) {
- _deopt_mh_handler_begin = (address) this + _stub_offset + offsets->value(CodeOffsets::DeoptMH);
- } else {
- _deopt_mh_handler_begin = NULL;
+ {
+ // Exception handler and deopt handler are in the stub section
+ assert(offsets->value(CodeOffsets::Exceptions) != -1, "must be set");
+ assert(offsets->value(CodeOffsets::Deopt ) != -1, "must be set");
+
+ _exception_offset = _stub_offset + offsets->value(CodeOffsets::Exceptions);
+ _deopt_handler_begin = (address) this + _stub_offset + offsets->value(CodeOffsets::Deopt);
+ if (offsets->value(CodeOffsets::DeoptMH) != -1) {
+ _deopt_mh_handler_begin = (address) this + _stub_offset + offsets->value(CodeOffsets::DeoptMH);
+ } else {
+ _deopt_mh_handler_begin = NULL;
+ }
}
-#if INCLUDE_JVMCI
- }
-#endif
if (offsets->value(CodeOffsets::UnwindHandler) != -1) {
_unwind_handler_offset = code_offset() + offsets->value(CodeOffsets::UnwindHandler);
} else {
@@ -787,8 +804,7 @@
_verified_entry_point = code_begin() + offsets->value(CodeOffsets::Verified_Entry);
_osr_entry_point = code_begin() + offsets->value(CodeOffsets::OSR_Entry);
_exception_cache = NULL;
-
- _scopes_data_begin = (address) this + scopes_data_offset;
+ _scopes_data_begin = (address) this + scopes_data_offset;
_pc_desc_container.reset_to(scopes_pcs_begin());
@@ -909,33 +925,72 @@
xtty->stamp();
xtty->end_head();
}
- // print the header part first
- print();
- // then print the requested information
+ // Print the header part, then print the requested information.
+ // This is both handled in decode2().
if (printmethod) {
- print_code();
- print_pcs();
- if (oop_maps()) {
- oop_maps()->print();
+ HandleMark hm;
+ ResourceMark m;
+ if (is_compiled_by_c1()) {
+ tty->cr();
+ tty->print_cr("============================= C1-compiled nmethod ==============================");
+ }
+ if (is_compiled_by_jvmci()) {
+ tty->cr();
+ tty->print_cr("=========================== JVMCI-compiled nmethod =============================");
+ }
+ tty->print_cr("----------------------------------- Assembly -----------------------------------");
+ decode2(tty);
+#if defined(SUPPORT_DATA_STRUCTS)
+ if (AbstractDisassembler::show_structs()) {
+ // Print the oops from the underlying CodeBlob as well.
+ tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+ print_oops(tty);
+ tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+ print_metadata(tty);
+ tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+ print_pcs();
+ tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+ if (oop_maps() != NULL) {
+ tty->print("oop maps:"); // oop_maps()->print_on(tty) outputs a cr() at the beginning
+ oop_maps()->print_on(tty);
+ tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+ }
+ }
+#endif
+ } else {
+ print(); // print the header part only.
+ }
+
+#if defined(SUPPORT_DATA_STRUCTS)
+ if (AbstractDisassembler::show_structs()) {
+ if (printmethod || PrintDebugInfo || CompilerOracle::has_option_string(_method, "PrintDebugInfo")) {
+ print_scopes();
+ tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+ }
+ if (printmethod || PrintRelocations || CompilerOracle::has_option_string(_method, "PrintRelocations")) {
+ print_relocations();
+ tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+ }
+ if (printmethod || PrintDependencies || CompilerOracle::has_option_string(_method, "PrintDependencies")) {
+ print_dependencies();
+ tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+ }
+ if (printmethod || PrintExceptionHandlers) {
+ print_handler_table();
+ tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+ print_nul_chk_table();
+ tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+ }
+
+ if (printmethod) {
+ print_recorded_oops();
+ tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
+ print_recorded_metadata();
+ tty->print_cr("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
}
}
- if (printmethod || PrintDebugInfo || CompilerOracle::has_option_string(_method, "PrintDebugInfo")) {
- print_scopes();
- }
- if (printmethod || PrintRelocations || CompilerOracle::has_option_string(_method, "PrintRelocations")) {
- print_relocations();
- }
- if (printmethod || PrintDependencies || CompilerOracle::has_option_string(_method, "PrintDependencies")) {
- print_dependencies();
- }
- if (printmethod || PrintExceptionHandlers) {
- print_handler_table();
- print_nul_chk_table();
- }
- if (printmethod) {
- print_recorded_oops();
- print_recorded_metadata();
- }
+#endif
+
if (xtty != NULL) {
xtty->tail("print_nmethod");
}
@@ -2062,10 +2117,9 @@
assert(cb != NULL && cb == this, "");
ttyLocker ttyl;
tty->print_cr("implicit exception happened at " INTPTR_FORMAT, p2i(pc));
- print();
+ // Print all available nmethod info.
+ print_nmethod(true);
method()->print_codes();
- print_code();
- print_pcs();
}
#endif
if (cont_offset == 0) {
@@ -2076,7 +2130,6 @@
}
-
void nmethod_init() {
// make sure you didn't forget to adjust the filler fields
assert(sizeof(nmethod) % oopSize == 0, "nmethod size must be multiple of a word");
@@ -2124,12 +2177,14 @@
bool ok() { return _ok; }
virtual void do_oop(oop* p) {
if (oopDesc::is_oop_or_null(*p)) return;
+ // Print diagnostic information before calling print_nmethod().
+ // Assertions therein might prevent call from returning.
+ tty->print_cr("*** non-oop " PTR_FORMAT " found at " PTR_FORMAT " (offset %d)",
+ p2i(*p), p2i(p), (int)((intptr_t)p - (intptr_t)_nm));
if (_ok) {
_nm->print_nmethod(true);
_ok = false;
}
- tty->print_cr("*** non-oop " PTR_FORMAT " found at " PTR_FORMAT " (offset %d)",
- p2i(*p), p2i(p), (int)((intptr_t)p - (intptr_t)_nm));
}
virtual void do_oop(narrowOop* p) { ShouldNotReachHere(); }
};
@@ -2238,107 +2293,104 @@
// Printing operations
void nmethod::print() const {
+ ttyLocker ttyl; // keep the following output all in one block
+ print(tty);
+}
+
+void nmethod::print(outputStream* st) const {
ResourceMark rm;
- ttyLocker ttyl; // keep the following output all in one block
-
- tty->print("Compiled method ");
+
+ st->print("Compiled method ");
if (is_compiled_by_c1()) {
- tty->print("(c1) ");
+ st->print("(c1) ");
} else if (is_compiled_by_c2()) {
- tty->print("(c2) ");
+ st->print("(c2) ");
} else if (is_compiled_by_jvmci()) {
- tty->print("(JVMCI) ");
+ st->print("(JVMCI) ");
} else {
- tty->print("(nm) ");
+ st->print("(n/a) ");
}
print_on(tty, NULL);
if (WizardMode) {
- tty->print("((nmethod*) " INTPTR_FORMAT ") ", p2i(this));
- tty->print(" for method " INTPTR_FORMAT , p2i(method()));
- tty->print(" { ");
- tty->print_cr("%s ", state());
- tty->print_cr("}:");
+ st->print("((nmethod*) " INTPTR_FORMAT ") ", p2i(this));
+ st->print(" for method " INTPTR_FORMAT , p2i(method()));
+ st->print(" { ");
+ st->print_cr("%s ", state());
+ st->print_cr("}:");
}
- if (size () > 0) tty->print_cr(" total in heap [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
- p2i(this),
- p2i(this) + size(),
- size());
- if (relocation_size () > 0) tty->print_cr(" relocation [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
- p2i(relocation_begin()),
- p2i(relocation_end()),
- relocation_size());
- if (consts_size () > 0) tty->print_cr(" constants [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
- p2i(consts_begin()),
- p2i(consts_end()),
- consts_size());
- if (insts_size () > 0) tty->print_cr(" main code [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
- p2i(insts_begin()),
- p2i(insts_end()),
- insts_size());
- if (stub_size () > 0) tty->print_cr(" stub code [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
- p2i(stub_begin()),
- p2i(stub_end()),
- stub_size());
- if (oops_size () > 0) tty->print_cr(" oops [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
- p2i(oops_begin()),
- p2i(oops_end()),
- oops_size());
- if (metadata_size () > 0) tty->print_cr(" metadata [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
- p2i(metadata_begin()),
- p2i(metadata_end()),
- metadata_size());
- if (scopes_data_size () > 0) tty->print_cr(" scopes data [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
- p2i(scopes_data_begin()),
- p2i(scopes_data_end()),
- scopes_data_size());
- if (scopes_pcs_size () > 0) tty->print_cr(" scopes pcs [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
- p2i(scopes_pcs_begin()),
- p2i(scopes_pcs_end()),
- scopes_pcs_size());
- if (dependencies_size () > 0) tty->print_cr(" dependencies [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
- p2i(dependencies_begin()),
- p2i(dependencies_end()),
- dependencies_size());
- if (handler_table_size() > 0) tty->print_cr(" handler table [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
- p2i(handler_table_begin()),
- p2i(handler_table_end()),
- handler_table_size());
- if (nul_chk_table_size() > 0) tty->print_cr(" nul chk table [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
- p2i(nul_chk_table_begin()),
- p2i(nul_chk_table_end()),
- nul_chk_table_size());
+ if (size () > 0) st->print_cr(" total in heap [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
+ p2i(this),
+ p2i(this) + size(),
+ size());
+ if (relocation_size () > 0) st->print_cr(" relocation [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
+ p2i(relocation_begin()),
+ p2i(relocation_end()),
+ relocation_size());
+ if (consts_size () > 0) st->print_cr(" constants [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
+ p2i(consts_begin()),
+ p2i(consts_end()),
+ consts_size());
+ if (insts_size () > 0) st->print_cr(" main code [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
+ p2i(insts_begin()),
+ p2i(insts_end()),
+ insts_size());
+ if (stub_size () > 0) st->print_cr(" stub code [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
+ p2i(stub_begin()),
+ p2i(stub_end()),
+ stub_size());
+ if (oops_size () > 0) st->print_cr(" oops [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
+ p2i(oops_begin()),
+ p2i(oops_end()),
+ oops_size());
+ if (metadata_size () > 0) st->print_cr(" metadata [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
+ p2i(metadata_begin()),
+ p2i(metadata_end()),
+ metadata_size());
+ if (scopes_data_size () > 0) st->print_cr(" scopes data [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
+ p2i(scopes_data_begin()),
+ p2i(scopes_data_end()),
+ scopes_data_size());
+ if (scopes_pcs_size () > 0) st->print_cr(" scopes pcs [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
+ p2i(scopes_pcs_begin()),
+ p2i(scopes_pcs_end()),
+ scopes_pcs_size());
+ if (dependencies_size () > 0) st->print_cr(" dependencies [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
+ p2i(dependencies_begin()),
+ p2i(dependencies_end()),
+ dependencies_size());
+ if (handler_table_size() > 0) st->print_cr(" handler table [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
+ p2i(handler_table_begin()),
+ p2i(handler_table_end()),
+ handler_table_size());
+ if (nul_chk_table_size() > 0) st->print_cr(" nul chk table [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
+ p2i(nul_chk_table_begin()),
+ p2i(nul_chk_table_end()),
+ nul_chk_table_size());
#if INCLUDE_JVMCI
- if (speculations_size () > 0) tty->print_cr(" speculations [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
- p2i(speculations_begin()),
- p2i(speculations_end()),
- speculations_size());
- if (jvmci_data_size () > 0) tty->print_cr(" JVMCI data [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
- p2i(jvmci_data_begin()),
- p2i(jvmci_data_end()),
- jvmci_data_size());
+ if (speculations_size () > 0) st->print_cr(" speculations [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
+ p2i(speculations_begin()),
+ p2i(speculations_end()),
+ speculations_size());
+ if (jvmci_data_size () > 0) st->print_cr(" JVMCI data [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
+ p2i(jvmci_data_begin()),
+ p2i(jvmci_data_end()),
+ jvmci_data_size());
#endif
}
-#ifndef PRODUCT
-
-void nmethod::print_scopes() {
- // Find the first pc desc for all scopes in the code and print it.
- ResourceMark rm;
- for (PcDesc* p = scopes_pcs_begin(); p < scopes_pcs_end(); p++) {
- if (p->scope_decode_offset() == DebugInformationRecorder::serialized_null)
- continue;
-
- ScopeDesc* sd = scope_desc_at(p->real_pc(this));
- while (sd != NULL) {
- sd->print_on(tty, p);
- sd = sd->sender();
- }
- }
+void nmethod::print_code() {
+ HandleMark hm;
+ ResourceMark m;
+ ttyLocker ttyl;
+ // Call the specialized decode method of this class.
+ decode(tty);
}
+#ifndef PRODUCT // called InstanceKlass methods are available only then. Declared as PRODUCT_RETURN
+
void nmethod::print_dependencies() {
ResourceMark rm;
ttyLocker ttyl; // keep the following output all in one block
@@ -2354,57 +2406,379 @@
deps.log_dependency(); // put it into the xml log also
}
}
-
-
+#endif
+
+#if defined(SUPPORT_DATA_STRUCTS)
+
+// Print the oops from the underlying CodeBlob.
+void nmethod::print_oops(outputStream* st) {
+ HandleMark hm;
+ ResourceMark m;
+ st->print("Oops:");
+ if (oops_begin() < oops_end()) {
+ st->cr();
+ for (oop* p = oops_begin(); p < oops_end(); p++) {
+ Disassembler::print_location((unsigned char*)p, (unsigned char*)oops_begin(), (unsigned char*)oops_end(), st, true, false);
+ st->print(PTR_FORMAT " ", *((uintptr_t*)p));
+ if (*p == Universe::non_oop_word()) {
+ st->print_cr("NON_OOP");
+ continue; // skip non-oops
+ }
+ if (*p == NULL) {
+ st->print_cr("NULL-oop");
+ continue; // skip non-oops
+ }
+ (*p)->print_value_on(st);
+ st->cr();
+ }
+ } else {
+ st->print_cr(" <list empty>");
+ }
+}
+
+// Print metadata pool.
+void nmethod::print_metadata(outputStream* st) {
+ HandleMark hm;
+ ResourceMark m;
+ st->print("Metadata:");
+ if (metadata_begin() < metadata_end()) {
+ st->cr();
+ for (Metadata** p = metadata_begin(); p < metadata_end(); p++) {
+ Disassembler::print_location((unsigned char*)p, (unsigned char*)metadata_begin(), (unsigned char*)metadata_end(), st, true, false);
+ st->print(PTR_FORMAT " ", *((uintptr_t*)p));
+ if (*p && *p != Universe::non_oop_word()) {
+ (*p)->print_value_on(st);
+ }
+ st->cr();
+ }
+ } else {
+ st->print_cr(" <list empty>");
+ }
+}
+
+#ifndef PRODUCT // ScopeDesc::print_on() is available only then. Declared as PRODUCT_RETURN
+void nmethod::print_scopes_on(outputStream* st) {
+ // Find the first pc desc for all scopes in the code and print it.
+ ResourceMark rm;
+ st->print("scopes:");
+ if (scopes_pcs_begin() < scopes_pcs_end()) {
+ st->cr();
+ for (PcDesc* p = scopes_pcs_begin(); p < scopes_pcs_end(); p++) {
+ if (p->scope_decode_offset() == DebugInformationRecorder::serialized_null)
+ continue;
+
+ ScopeDesc* sd = scope_desc_at(p->real_pc(this));
+ while (sd != NULL) {
+ sd->print_on(st, p); // print output ends with a newline
+ sd = sd->sender();
+ }
+ }
+ } else {
+ st->print_cr(" <list empty>");
+ }
+}
+#endif
+
+#ifndef PRODUCT // RelocIterator does support printing only then.
void nmethod::print_relocations() {
ResourceMark m; // in case methods get printed via the debugger
tty->print_cr("relocations:");
RelocIterator iter(this);
iter.print();
}
-
-
-void nmethod::print_pcs() {
+#endif
+
+void nmethod::print_pcs_on(outputStream* st) {
ResourceMark m; // in case methods get printed via debugger
- tty->print_cr("pc-bytecode offsets:");
- for (PcDesc* p = scopes_pcs_begin(); p < scopes_pcs_end(); p++) {
- p->print(this);
+ st->print("pc-bytecode offsets:");
+ if (scopes_pcs_begin() < scopes_pcs_end()) {
+ st->cr();
+ for (PcDesc* p = scopes_pcs_begin(); p < scopes_pcs_end(); p++) {
+ p->print_on(st, this); // print output ends with a newline
+ }
+ } else {
+ st->print_cr(" <list empty>");
}
}
+void nmethod::print_handler_table() {
+ ExceptionHandlerTable(this).print();
+}
+
+void nmethod::print_nul_chk_table() {
+ ImplicitExceptionTable(this).print(code_begin());
+}
+
void nmethod::print_recorded_oops() {
- tty->print_cr("Recorded oops:");
- for (int i = 0; i < oops_count(); i++) {
- oop o = oop_at(i);
- tty->print("#%3d: " INTPTR_FORMAT " ", i, p2i(o));
- if (o == Universe::non_oop_word()) {
- tty->print("non-oop word");
- } else {
- if (o != NULL) {
- o->print_value();
+ const int n = oops_count();
+ const int log_n = (n<10) ? 1 : (n<100) ? 2 : (n<1000) ? 3 : (n<10000) ? 4 : 6;
+ tty->print("Recorded oops:");
+ if (n > 0) {
+ tty->cr();
+ for (int i = 0; i < n; i++) {
+ oop o = oop_at(i);
+ tty->print("#%*d: " INTPTR_FORMAT " ", log_n, i, p2i(o));
+ if (o == (oop)Universe::non_oop_word()) {
+ tty->print("non-oop word");
+ } else if (o == NULL) {
+ tty->print("NULL-oop");
} else {
- tty->print_cr("NULL");
+ o->print_value_on(tty);
}
+ tty->cr();
}
- tty->cr();
+ } else {
+ tty->print_cr(" <list empty>");
}
}
void nmethod::print_recorded_metadata() {
- tty->print_cr("Recorded metadata:");
- for (int i = 0; i < metadata_count(); i++) {
- Metadata* m = metadata_at(i);
- tty->print("#%3d: " INTPTR_FORMAT " ", i, p2i(m));
- if (m == (Metadata*)Universe::non_oop_word()) {
- tty->print("non-metadata word");
- } else {
- Metadata::print_value_on_maybe_null(tty, m);
+ const int n = metadata_count();
+ const int log_n = (n<10) ? 1 : (n<100) ? 2 : (n<1000) ? 3 : (n<10000) ? 4 : 6;
+ tty->print("Recorded metadata:");
+ if (n > 0) {
+ tty->cr();
+ for (int i = 0; i < n; i++) {
+ Metadata* m = metadata_at(i);
+ tty->print("#%*d: " INTPTR_FORMAT " ", log_n, i, p2i(m));
+ if (m == (Metadata*)Universe::non_oop_word()) {
+ tty->print("non-metadata word");
+ } else if (m == NULL) {
+ tty->print("NULL-oop");
+ } else {
+ Metadata::print_value_on_maybe_null(tty, m);
+ }
+ tty->cr();
}
- tty->cr();
+ } else {
+ tty->print_cr(" <list empty>");
}
}
-
-#endif // PRODUCT
+#endif
+
+#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY)
+
+void nmethod::print_constant_pool(outputStream* st) {
+ //-----------------------------------
+ //---< Print the constant pool >---
+ //-----------------------------------
+ int consts_size = this->consts_size();
+ if ( consts_size > 0 ) {
+ unsigned char* cstart = this->consts_begin();
+ unsigned char* cp = cstart;
+ unsigned char* cend = cp + consts_size;
+ unsigned int bytes_per_line = 4;
+ unsigned int CP_alignment = 8;
+ unsigned int n;
+
+ st->cr();
+
+ //---< print CP header to make clear what's printed >---
+ if( ((uintptr_t)cp&(CP_alignment-1)) == 0 ) {
+ n = bytes_per_line;
+ st->print_cr("[Constant Pool]");
+ Disassembler::print_location(cp, cstart, cend, st, true, true);
+ Disassembler::print_hexdata(cp, n, st, true);
+ st->cr();
+ } else {
+ n = (uintptr_t)cp&(bytes_per_line-1);
+ st->print_cr("[Constant Pool (unaligned)]");
+ }
+
+ //---< print CP contents, bytes_per_line at a time >---
+ while (cp < cend) {
+ Disassembler::print_location(cp, cstart, cend, st, true, false);
+ Disassembler::print_hexdata(cp, n, st, false);
+ cp += n;
+ n = bytes_per_line;
+ st->cr();
+ }
+
+ //---< Show potential alignment gap between constant pool and code >---
+ cend = code_begin();
+ if( cp < cend ) {
+ n = 4;
+ st->print_cr("[Code entry alignment]");
+ while (cp < cend) {
+ Disassembler::print_location(cp, cstart, cend, st, false, false);
+ cp += n;
+ st->cr();
+ }
+ }
+ } else {
+ st->print_cr("[Constant Pool (empty)]");
+ }
+ st->cr();
+}
+
+#endif
+
+// Disassemble this nmethod.
+// Print additional debug information, if requested. This could be code
+// comments, block comments, profiling counters, etc.
+// The undisassembled format is useful no disassembler library is available.
+// The resulting hex dump (with markers) can be disassembled later, or on
+// another system, when/where a disassembler library is available.
+void nmethod::decode2(outputStream* ost) const {
+
+ // Called from frame::back_trace_with_decode without ResourceMark.
+ ResourceMark rm;
+
+ // Make sure we have a valid stream to print on.
+ outputStream* st = ost ? ost : tty;
+
+#if defined(SUPPORT_ABSTRACT_ASSEMBLY) && ! defined(SUPPORT_ASSEMBLY)
+ const bool use_compressed_format = true;
+ const bool compressed_with_comments = use_compressed_format && (AbstractDisassembler::show_comment() ||
+ AbstractDisassembler::show_block_comment());
+#else
+ const bool use_compressed_format = Disassembler::is_abstract();
+ const bool compressed_with_comments = use_compressed_format && (AbstractDisassembler::show_comment() ||
+ AbstractDisassembler::show_block_comment());
+#endif
+
+ st->cr();
+ this->print(st);
+ st->cr();
+
+#if defined(SUPPORT_ASSEMBLY)
+ //----------------------------------
+ //---< Print real disassembly >---
+ //----------------------------------
+ if (! use_compressed_format) {
+ Disassembler::decode(const_cast<nmethod*>(this), st);
+ return;
+ }
+#endif
+
+#if defined(SUPPORT_ABSTRACT_ASSEMBLY)
+
+ // Compressed undisassembled disassembly format.
+ // The following stati are defined/supported:
+ // = 0 - currently at bol() position, nothing printed yet on current line.
+ // = 1 - currently at position after print_location().
+ // > 1 - in the midst of printing instruction stream bytes.
+ int compressed_format_idx = 0;
+ int code_comment_column = 0;
+ const int instr_maxlen = Assembler::instr_maxlen();
+ const uint tabspacing = 8;
+ unsigned char* start = this->code_begin();
+ unsigned char* p = this->code_begin();
+ unsigned char* end = this->code_end();
+ unsigned char* pss = p; // start of a code section (used for offsets)
+
+ if ((start == NULL) || (end == NULL)) {
+ st->print_cr("PrintAssembly not possible due to uninitialized section pointers");
+ return;
+ }
+#endif
+
+#if defined(SUPPORT_ABSTRACT_ASSEMBLY)
+ //---< plain abstract disassembly, no comments or anything, just section headers >---
+ if (use_compressed_format && ! compressed_with_comments) {
+ const_cast<nmethod*>(this)->print_constant_pool(st);
+
+ //---< Open the output (Marker for post-mortem disassembler) >---
+ st->print_cr("[MachCode]");
+ const char* header = NULL;
+ address p0 = p;
+ while (p < end) {
+ address pp = p;
+ while ((p < end) && (header == NULL)) {
+ header = nmethod_section_label(p);
+ pp = p;
+ p += Assembler::instr_len(p);
+ }
+ if (pp > p0) {
+ AbstractDisassembler::decode_range_abstract(p0, pp, start, end, st, Assembler::instr_maxlen());
+ p0 = pp;
+ p = pp;
+ header = NULL;
+ } else if (header != NULL) {
+ st->bol();
+ st->print_cr("%s", header);
+ header = NULL;
+ }
+ }
+ //---< Close the output (Marker for post-mortem disassembler) >---
+ st->bol();
+ st->print_cr("[/MachCode]");
+ return;
+ }
+#endif
+
+#if defined(SUPPORT_ABSTRACT_ASSEMBLY)
+ //---< abstract disassembly with comments and section headers merged in >---
+ if (compressed_with_comments) {
+ const_cast<nmethod*>(this)->print_constant_pool(st);
+
+ //---< Open the output (Marker for post-mortem disassembler) >---
+ st->print_cr("[MachCode]");
+ while ((p < end) && (p != NULL)) {
+ const int instruction_size_in_bytes = Assembler::instr_len(p);
+
+ //---< Block comments for nmethod. Interrupts instruction stream, if any. >---
+ // Outputs a bol() before and a cr() after, but only if a comment is printed.
+ // Prints nmethod_section_label as well.
+ if (AbstractDisassembler::show_block_comment()) {
+ print_block_comment(st, p);
+ if (st->position() == 0) {
+ compressed_format_idx = 0;
+ }
+ }
+
+ //---< New location information after line break >---
+ if (compressed_format_idx == 0) {
+ code_comment_column = Disassembler::print_location(p, pss, end, st, false, false);
+ compressed_format_idx = 1;
+ }
+
+ //---< Code comment for current instruction. Address range [p..(p+len)) >---
+ unsigned char* p_end = p + (ssize_t)instruction_size_in_bytes;
+ S390_ONLY(if (p_end > end) p_end = end;) // avoid getting past the end
+
+ if (AbstractDisassembler::show_comment() && const_cast<nmethod*>(this)->has_code_comment(p, p_end)) {
+ //---< interrupt instruction byte stream for code comment >---
+ if (compressed_format_idx > 1) {
+ st->cr(); // interrupt byte stream
+ st->cr(); // add an empty line
+ code_comment_column = Disassembler::print_location(p, pss, end, st, false, false);
+ }
+ const_cast<nmethod*>(this)->print_code_comment_on(st, code_comment_column, p, p_end );
+ st->bol();
+ compressed_format_idx = 0;
+ }
+
+ //---< New location information after line break >---
+ if (compressed_format_idx == 0) {
+ code_comment_column = Disassembler::print_location(p, pss, end, st, false, false);
+ compressed_format_idx = 1;
+ }
+
+ //---< Nicely align instructions for readability >---
+ if (compressed_format_idx > 1) {
+ Disassembler::print_delimiter(st);
+ }
+
+ //---< Now, finally, print the actual instruction bytes >---
+ unsigned char* p0 = p;
+ p = Disassembler::decode_instruction_abstract(p, st, instruction_size_in_bytes, instr_maxlen);
+ compressed_format_idx += p - p0;
+
+ if (Disassembler::start_newline(compressed_format_idx-1)) {
+ st->cr();
+ compressed_format_idx = 0;
+ }
+ }
+ //---< Close the output (Marker for post-mortem disassembler) >---
+ st->bol();
+ st->print_cr("[/MachCode]");
+ return;
+ }
+#endif
+}
+
+#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY)
const char* nmethod::reloc_string_for(u_char* begin, u_char* end) {
RelocIterator iter(this, begin, end);
@@ -2414,7 +2788,9 @@
switch (iter.type()) {
case relocInfo::none: return "no_reloc";
case relocInfo::oop_type: {
- stringStream st;
+ // Get a non-resizable resource-allocated stringStream.
+ // Our callees make use of (nested) ResourceMarks.
+ stringStream st(NEW_RESOURCE_ARRAY(char, 1024), 1024);
oop_Relocation* r = iter.oop_reloc();
oop obj = r->oop_value();
st.print("oop(");
@@ -2516,17 +2892,28 @@
return NULL;
}
-void nmethod::print_nmethod_labels(outputStream* stream, address block_begin) const {
- if (block_begin == entry_point()) stream->print_cr("[Entry Point]");
- if (block_begin == verified_entry_point()) stream->print_cr("[Verified Entry Point]");
- if (JVMCI_ONLY(_exception_offset >= 0 &&) block_begin == exception_begin()) stream->print_cr("[Exception Handler]");
- if (block_begin == stub_begin()) stream->print_cr("[Stub Code]");
- if (JVMCI_ONLY(_deopt_handler_begin != NULL &&) block_begin == deopt_handler_begin()) stream->print_cr("[Deopt Handler Code]");
-
- if (has_method_handle_invokes())
- if (block_begin == deopt_mh_handler_begin()) stream->print_cr("[Deopt MH Handler Code]");
-
- if (block_begin == consts_begin()) stream->print_cr("[Constants]");
+const char* nmethod::nmethod_section_label(address pos) const {
+ const char* label = NULL;
+ if (pos == code_begin()) label = "[Instructions begin]";
+ if (pos == entry_point()) label = "[Entry Point]";
+ if (pos == verified_entry_point()) label = "[Verified Entry Point]";
+ if (has_method_handle_invokes() && (pos == deopt_mh_handler_begin())) label = "[Deopt MH Handler Code]";
+ if (pos == consts_begin() && pos != insts_begin()) label = "[Constants]";
+ // Check stub_code before checking exception_handler or deopt_handler.
+ if (pos == this->stub_begin()) label = "[Stub Code]";
+ if (JVMCI_ONLY(_exception_offset >= 0 &&) pos == exception_begin()) label = "[Exception Handler]";
+ if (JVMCI_ONLY(_deopt_handler_begin != NULL &&) pos == deopt_handler_begin()) label = "[Deopt Handler Code]";
+ return label;
+}
+
+void nmethod::print_nmethod_labels(outputStream* stream, address block_begin, bool print_section_labels) const {
+ if (print_section_labels) {
+ const char* label = nmethod_section_label(block_begin);
+ if (label != NULL) {
+ stream->bol();
+ stream->print_cr("%s", label);
+ }
+ }
if (block_begin == entry_point()) {
methodHandle m = method();
@@ -2623,7 +3010,24 @@
}
}
-void nmethod::print_code_comment_on(outputStream* st, int column, u_char* begin, u_char* end) {
+// Returns whether this nmethod has code comments.
+bool nmethod::has_code_comment(address begin, address end) {
+ // scopes?
+ ScopeDesc* sd = scope_desc_in(begin, end);
+ if (sd != NULL) return true;
+
+ // relocations?
+ const char* str = reloc_string_for(begin, end);
+ if (str != NULL) return true;
+
+ // implicit exceptions?
+ int cont_offset = ImplicitExceptionTable(this).at(begin - code_begin());
+ if (cont_offset != 0) return true;
+
+ return false;
+}
+
+void nmethod::print_code_comment_on(outputStream* st, int column, address begin, address end) {
// First, find an oopmap in (begin, end].
// We use the odd half-closed interval so that oop maps and scope descs
// which are tied to the byte after a call are printed with the call itself.
@@ -2636,7 +3040,7 @@
address pc = base + pair->pc_offset();
if (pc > begin) {
if (pc <= end) {
- st->move_to(column);
+ st->move_to(column, 6, 0);
st->print("; ");
om->print_on(st);
}
@@ -2648,7 +3052,7 @@
// Print any debug info present at this pc.
ScopeDesc* sd = scope_desc_in(begin, end);
if (sd != NULL) {
- st->move_to(column);
+ st->move_to(column, 6, 0);
if (sd->bci() == SynchronizationEntryBCI) {
st->print(";*synchronization entry");
} else if (sd->bci() == AfterBci) {
@@ -2704,8 +3108,11 @@
// Print all scopes
for (;sd != NULL; sd = sd->sender()) {
- st->move_to(column);
+ st->move_to(column, 6, 0);
st->print("; -");
+ if (sd->should_reexecute()) {
+ st->print(" (reexecute)");
+ }
if (sd->method() == NULL) {
st->print("method is NULL");
} else {
@@ -2722,20 +3129,24 @@
}
// Print relocation information
+ // Prevent memory leak: allocating without ResourceMark.
+ ResourceMark rm;
const char* str = reloc_string_for(begin, end);
if (str != NULL) {
if (sd != NULL) st->cr();
- st->move_to(column);
+ st->move_to(column, 6, 0);
st->print("; {%s}", str);
}
int cont_offset = ImplicitExceptionTable(this).at(begin - code_begin());
if (cont_offset != 0) {
- st->move_to(column);
+ st->move_to(column, 6, 0);
st->print("; implicit exception: dispatches to " INTPTR_FORMAT, p2i(code_begin() + cont_offset));
}
}
+#endif
+
class DirectNativeCallWrapper: public NativeCallWrapper {
private:
NativeCall* _call;
@@ -2842,12 +3253,14 @@
return CompiledDirectStaticCall::before(return_addr);
}
-#ifndef PRODUCT
-
+#if defined(SUPPORT_DATA_STRUCTS)
void nmethod::print_value_on(outputStream* st) const {
st->print("nmethod");
print_on(st, NULL);
}
+#endif
+
+#ifndef PRODUCT
void nmethod::print_calls(outputStream* st) {
RelocIterator iter(this);
@@ -2869,14 +3282,6 @@
}
}
-void nmethod::print_handler_table() {
- ExceptionHandlerTable(this).print();
-}
-
-void nmethod::print_nul_chk_table() {
- ImplicitExceptionTable(this).print(code_begin());
-}
-
void nmethod::print_statistics() {
ttyLocker ttyl;
if (xtty != NULL) xtty->head("statistics type='nmethod'");
--- a/src/hotspot/share/code/nmethod.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/code/nmethod.hpp Tue May 21 15:51:35 2019 +0200
@@ -377,6 +377,7 @@
void make_unloaded();
bool has_dependencies() { return dependencies_size() != 0; }
+ void print_dependencies() PRODUCT_RETURN;
void flush_dependencies(bool delete_immediately);
bool has_flushed_dependencies() { return _has_flushed_dependencies; }
void set_has_flushed_dependencies() {
@@ -505,18 +506,40 @@
void verify_scopes();
void verify_interrupt_point(address interrupt_point);
+ // Disassemble this nmethod with additional debug information, e.g. information about blocks.
+ void decode2(outputStream* st) const;
+ void print_constant_pool(outputStream* st);
+
+ // Avoid hiding of parent's 'decode(outputStream*)' method.
+ void decode(outputStream* st) const { decode2(st); } // just delegate here.
+
// printing support
void print() const;
+ void print(outputStream* st) const;
+ void print_code();
+
+#if defined(SUPPORT_DATA_STRUCTS)
+ // print output in opt build for disassembler library
void print_relocations() PRODUCT_RETURN;
- void print_pcs() PRODUCT_RETURN;
- void print_scopes() PRODUCT_RETURN;
- void print_dependencies() PRODUCT_RETURN;
- void print_value_on(outputStream* st) const PRODUCT_RETURN;
+ void print_pcs() { print_pcs_on(tty); }
+ void print_pcs_on(outputStream* st);
+ void print_scopes() { print_scopes_on(tty); }
+ void print_scopes_on(outputStream* st) PRODUCT_RETURN;
+ void print_value_on(outputStream* st) const;
+ void print_handler_table();
+ void print_nul_chk_table();
+ void print_recorded_oops();
+ void print_recorded_metadata();
+
+ void print_oops(outputStream* st); // oops from the underlying CodeBlob.
+ void print_metadata(outputStream* st); // metadata in metadata pool.
+#else
+ // void print_pcs() PRODUCT_RETURN;
+ void print_pcs() { return; }
+#endif
+
void print_calls(outputStream* st) PRODUCT_RETURN;
- void print_handler_table() PRODUCT_RETURN;
- void print_nul_chk_table() PRODUCT_RETURN;
- void print_recorded_oops() PRODUCT_RETURN;
- void print_recorded_metadata() PRODUCT_RETURN;
+ static void print_statistics() PRODUCT_RETURN;
void maybe_print_nmethod(DirectiveSet* directive);
void print_nmethod(bool print_code);
@@ -532,14 +555,21 @@
// Prints block-level comments, including nmethod specific block labels:
virtual void print_block_comment(outputStream* stream, address block_begin) const {
+#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY)
print_nmethod_labels(stream, block_begin);
CodeBlob::print_block_comment(stream, block_begin);
+#endif
}
- void print_nmethod_labels(outputStream* stream, address block_begin) const;
+ bool has_block_comment(address block_begin) {
+ return CodeBlob::has_block_comment(block_begin);
+ }
+ void print_nmethod_labels(outputStream* stream, address block_begin, bool print_section_labels=true) const;
+ const char* nmethod_section_label(address pos) const;
+ // returns whether this nmethod has code comments.
+ bool has_code_comment(address begin, address end);
// Prints a comment for one native instruction (reloc info, pc desc)
void print_code_comment_on(outputStream* st, int column, address begin, address end);
- static void print_statistics() PRODUCT_RETURN;
// Compiler task identification. Note that all OSR methods
// are numbered in an independent sequence if CICountOSR is true,
--- a/src/hotspot/share/code/pcDesc.cpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/code/pcDesc.cpp Tue May 21 15:51:35 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -40,19 +40,24 @@
return code->code_begin() + pc_offset();
}
-void PcDesc::print(CompiledMethod* code) {
+void PcDesc::print_on(outputStream* st, CompiledMethod* code) {
#ifndef PRODUCT
ResourceMark rm;
- tty->print_cr("PcDesc(pc=" PTR_FORMAT " offset=%x bits=%x):", p2i(real_pc(code)), pc_offset(), _flags);
+ st->print("PcDesc(pc=" PTR_FORMAT " offset=%x bits=%x):", p2i(real_pc(code)), pc_offset(), _flags);
if (scope_decode_offset() == DebugInformationRecorder::serialized_null) {
+ st->cr();
return;
}
+ int tab = 8;
+ int pos = st->position() + 2; // current column plus two spaces
+ pos = ((pos+tab-1)/tab)*tab;
+
for (ScopeDesc* sd = code->scope_desc_at(real_pc(code));
sd != NULL;
sd = sd->sender()) {
- sd->print_on(tty);
+ sd->print_on(st);
}
#endif
}
--- a/src/hotspot/share/code/pcDesc.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/code/pcDesc.hpp Tue May 21 15:51:35 2019 +0200
@@ -92,7 +92,8 @@
// Returns the real pc
address real_pc(const CompiledMethod* code) const;
- void print(CompiledMethod* code);
+ void print(CompiledMethod* code) { print_on(tty, code); }
+ void print_on(outputStream* st, CompiledMethod* code);
bool verify(CompiledMethod* code);
};
--- a/src/hotspot/share/code/vmreg.cpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/code/vmreg.cpp Tue May 21 15:51:35 2019 +0200
@@ -39,7 +39,7 @@
void VMRegImpl::print_on(outputStream* st) const {
if( is_reg() ) {
- assert( VMRegImpl::regName[value()], "" );
+ assert(VMRegImpl::regName[value()], "VMRegImpl::regName[" INTPTR_FORMAT "] returns NULL", value());
st->print("%s",VMRegImpl::regName[value()]);
} else if (is_stack()) {
int stk = value() - stack0->value();
--- a/src/hotspot/share/code/vtableStubs.cpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/code/vtableStubs.cpp Tue May 21 15:51:35 2019 +0200
@@ -80,7 +80,7 @@
void VtableStub::print_on(outputStream* st) const {
- st->print("vtable stub (index = %d, receiver_location = " INTX_FORMAT ", code = [" INTPTR_FORMAT ", " INTPTR_FORMAT "[)",
+ st->print("vtable stub (index = %d, receiver_location = " INTX_FORMAT ", code = [" INTPTR_FORMAT ", " INTPTR_FORMAT "])",
index(), p2i(receiver_location()), p2i(code_begin()), p2i(code_end()));
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/compiler/abstractDisassembler.cpp Tue May 21 15:51:35 2019 +0200
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+// AbstractDisassembler is the base class for
+// platform-specific Disassembler classes.
+
+#include "precompiled.hpp"
+#include "asm/assembler.inline.hpp"
+#include "compiler/abstractDisassembler.hpp"
+#include "oops/oop.inline.hpp"
+#include "utilities/debug.hpp"
+#include "utilities/ostream.hpp"
+
+// Default values for what is being printed as line prefix when disassembling a single instruction.
+// Can be overridden by command line parameter PrintAssemblyOptions.
+bool AbstractDisassembler::_show_data_hex = true;
+bool AbstractDisassembler::_show_data_int = false;
+bool AbstractDisassembler::_show_data_float = false;
+bool AbstractDisassembler::_align_instr = false;
+bool AbstractDisassembler::_show_pc = true;
+bool AbstractDisassembler::_show_offset = false;
+bool AbstractDisassembler::_show_structs = false;
+bool AbstractDisassembler::_show_comment = false;
+bool AbstractDisassembler::_show_block_comment = false;
+#if defined(ARM) || defined(AARCH64)
+bool AbstractDisassembler::_show_bytes = false; // set "true" to see what's in memory bit by bit
+ // might prove cumbersome because instr_len is hard to find on arm
+#endif
+#if defined(PPC)
+bool AbstractDisassembler::_show_bytes = false; // set "true" to see what's in memory bit by bit
+#endif
+#if defined(S390)
+bool AbstractDisassembler::_show_bytes = false; // set "true" to see what's in memory bit by bit
+#endif
+#if defined(SPARC)
+bool AbstractDisassembler::_show_bytes = false; // set "true" to see what's in memory bit by bit
+#endif
+#if defined(X86)
+bool AbstractDisassembler::_show_bytes = false; // set "true" to see what's in memory bit by bit
+ // might prove cumbersome because instr_len is hard to find on x86
+#endif
+#if defined(ZERO)
+bool AbstractDisassembler::_show_bytes = false; // set "true" to see what's in memory bit by bit
+#endif
+
+// Return #bytes printed. Callers may use that for output alignment.
+// Print instruction address, and offset from blob begin.
+// Offset width (2, 4, 6, 8 bytes) is adapted to size of blob.
+// Working assumption: we are at st->bol() upon entry. If not, it's the
+// caller's responsibility to guarantee proper alignment.
+int AbstractDisassembler::print_location(address here, address begin, address end, outputStream* st, bool align, bool print_header) {
+ const int pos_0 = st->position();
+
+ if (show_pc() || show_offset()) {
+ st->print(" ");
+ }
+
+ if (show_pc()) {
+ if (print_header) {
+ st->print(" %*s", 18, "Address");
+ } else {
+ st->print(" " PTR_FORMAT, p2i(here));
+ }
+ }
+
+ if (show_offset()) {
+#ifdef ASSERT
+ if ((uintptr_t)begin > (uintptr_t)here) st->print(">>begin(" PTR_FORMAT ") > here(" PTR_FORMAT ")<<", p2i(begin), p2i(here));
+ if ((uintptr_t)end < (uintptr_t)here) st->print(">> end(" PTR_FORMAT ") < here(" PTR_FORMAT ")<<", p2i(end), p2i(here));
+ assert((uintptr_t)begin <= (uintptr_t)end, "inverted address range");
+#endif
+ const int blob_len = end - begin;
+ const int offset = here - begin;
+ const int width = (blob_len < (1<< 8)) ? 2 : (blob_len < (1<<16)) ? 4 : (blob_len < (1<<24)) ? 6 : 8;
+ if (print_header) {
+ st->print(" %*s", width+5, "offset");
+ } else {
+ st->print(" (+0x%*.*x)", width, width, offset);
+ }
+ }
+
+ if ((show_pc() || show_offset()) && !print_header) {
+ st->print(": ");
+ }
+
+ if (align) {
+ const uint tabspacing = 8;
+ const uint pos = st->position();
+ const uint aligned_pos = ((pos+tabspacing-1)/tabspacing)*tabspacing /* - 1 */;
+ st->fill_to(aligned_pos);
+ }
+
+ return st->position() - pos_0;
+}
+
+
+// Return #bytes printed. Callers may use that for output alignment.
+// Print instruction in hexadecimal representation, using 2-byte blocks.
+// Used with real disassemblies. Not so useful with abstract disassemblies.
+int AbstractDisassembler::print_instruction(address here, int len, int max_len, outputStream* st, bool align, bool print_header) {
+ if (show_bytes()) {
+ const int block_bytes = 2;
+ const int pos_0 = st->position();
+ address pos = here;
+
+ //---< print instruction bytes in blocks >---
+ // must print byte by byte: address might be unaligned.
+ for (; pos <= here + len - block_bytes; pos += block_bytes) {
+ for (address byte = pos; byte < pos + block_bytes; byte++) {
+ st->print("%2.2x", *byte);
+ }
+ st->print(" ");
+ }
+
+ //---< Print the remaining bytes of the instruction >---
+ if ((len & (block_bytes - 1)) != 0) {
+ for (; pos < here + len; pos++) {
+ st->print("%2.2x", *pos);
+ }
+ }
+
+ //---< filler for shorter than max_len instructions >---
+ for (int i = len+1; i < max_len; i++) {
+ st->print(" ");
+ }
+
+ st->print(" "); // separator space.
+ print_delimiter(st);
+ return st->position() - pos_0;
+ }
+
+ if (align) {
+ const uint tabspacing = 8;
+ const uint pos = st->position();
+ const uint aligned_pos = ((pos+tabspacing-1)/tabspacing)*tabspacing /* - 1 */;
+ st->fill_to(aligned_pos);
+ }
+
+ return 0;
+}
+
+
+// Return #bytes printed. Callers may use that for output alignment.
+// Print data (e.g. constant pool entries) in hex format.
+// Depending on the alignment, short, int, and long entities are printed.
+// If selected, data is formatted as int/long and float/double values in addition.
+int AbstractDisassembler::print_hexdata(address here, int len, outputStream* st, bool print_header) {
+ const int tsize = 8;
+ const int pos_0 = st->position();
+ int pos = pos_0;
+ int align = ((pos+tsize-1)/tsize)*tsize;
+ st->fill_to(align);
+
+ //---< printing hex data >---
+ if (show_data_hex()) {
+ switch (len) {
+ case 1: if (print_header) {
+ st->print("hex1");
+ } else {
+ st->print("0x%02x", *here);
+ }
+ st->fill_to(align += tsize);
+ case 2: if (print_header) {
+ st->print(" hex2");
+ } else {
+ if (((uintptr_t)(here)&0x01) == 0) {
+ st->print("0x%04x", *((jushort*)here));
+ }
+ }
+ st->fill_to(align += tsize);
+ case 4: if (print_header) {
+ st->print(" hex4");
+ } else {
+ if (((uintptr_t)(here)&0x03) == 0) {
+ st->print("0x%08x", *((juint*)here));
+ }
+ }
+ st->fill_to(align += 2*tsize);
+ case 8: if (print_header) {
+ st->print(" hex8");
+ } else {
+ if (((uintptr_t)(here)&0x07) == 0) {
+ st->print(PTR_FORMAT, *((uintptr_t*)here));
+ }
+ }
+ st->fill_to(align += 3*tsize);
+ break;
+ default: ;
+ }
+ pos = st->position();
+ align = ((pos+tsize-1)/tsize)*tsize;
+ st->fill_to(align);
+ }
+
+ //---< printing int/long data >---
+ if (show_data_int()) {
+ switch (len) {
+ case 4: if (print_header) {
+ st->print(" int");
+ } else {
+ if (((uintptr_t)(here)&0x03) == 0) {
+ st->print("%12.1d", *((jint*)here));
+ }
+ }
+ st->fill_to(align += 2*tsize);
+ case 8: if (print_header) {
+ st->print(" long");
+ } else {
+ if (((uintptr_t)(here)&0x07) == 0) {
+ st->print("%23.1ld", *((jlong*)here));
+ }
+ }
+ st->fill_to(align += 3*tsize);
+ break;
+ default: ;
+ }
+ pos = st->position();
+ align = ((pos+tsize-1)/tsize)*tsize;
+ st->fill_to(align);
+ }
+
+ //---< printing float/double data >---
+ if (show_data_float()) {
+ switch (len) {
+ case 4: if (print_header) {
+ st->print(" float");
+ } else {
+ if (((uintptr_t)(here)&0x03) == 0) {
+ st->print("%15.7e", (double)*((float*)here));
+ }
+ }
+ st->fill_to(align += 2*tsize);
+ case 8: if (print_header) {
+ st->print(" double");
+ } else {
+ if (((uintptr_t)(here)&0x07) == 0) {
+ st->print("%23.15e", *((double*)here));
+ }
+ }
+ st->fill_to(align += 3*tsize);
+ break;
+ default: ;
+ }
+ }
+
+ return st->position() - pos_0;
+}
+
+
+// Return #bytes printed. Callers may use that for output alignment.
+// Print an instruction delimiter.
+int AbstractDisassembler::print_delimiter(outputStream* st) {
+ if (align_instr()) { st->print("| "); return 2; }
+ else return 0;
+}
+
+
+// Decodes the one instruction at address start in a platform-independent format.
+// Returns the start of the next instruction (which is 'start' plus 'instruction_size_in_bytes').
+// The parameter max_instr_size_in_bytes is used for output alignment purposes only.
+address AbstractDisassembler::decode_instruction_abstract(address start,
+ outputStream* st,
+ const int instruction_size_in_bytes,
+ const int max_instr_size_in_bytes) {
+ assert(instruction_size_in_bytes > 0, "no zero-size instructions!");
+ assert(max_instr_size_in_bytes >= instruction_size_in_bytes, "inconsistent call parameters");
+
+ //---< current instruction is at the start address >---
+ unsigned char* current = (unsigned char*) start;
+ int filler_limit = align_instr() ? max_instr_size_in_bytes : ((instruction_size_in_bytes+abstract_instruction_bytes_per_block-1)/abstract_instruction_bytes_per_block)
+ *abstract_instruction_bytes_per_block;
+
+ //---< print the instruction's bytes >---
+ for (int i = 1; i <= instruction_size_in_bytes; i++) {
+ st->print("%02x", *current);
+ ++current;
+ if (abstract_instruction_bytes_per_block <= max_instr_size_in_bytes) {
+ if (i%abstract_instruction_bytes_per_block == 0) st->print(" ");
+ } else {
+ if (i == instruction_size_in_bytes) st->print(" ");
+ }
+ }
+
+ //---< print some filler spaces to column-align instructions >---
+ for (int i = instruction_size_in_bytes+1; i <= filler_limit; i++) {
+ st->print(" ");
+ if (abstract_instruction_bytes_per_block <= max_instr_size_in_bytes) {
+ if (i%abstract_instruction_bytes_per_block == 0) st->print(" ");
+ } else {
+ if (i == instruction_size_in_bytes) st->print(" ");
+ }
+ }
+
+ //---< the address of the next instruction >---
+ return (address) current;
+}
+
+
+// Decodes all instructions in the given range [start..end)
+// calling decode_instruction_abstract for each instruction.
+// The format is platform dependent only to the extend that
+// it respects the actual instruction length where possible.
+// Does not print any markers or decorators.
+void AbstractDisassembler::decode_range_abstract(address range_start, address range_end,
+ address start, address end,
+ outputStream* st,
+ const int max_instr_size_in_bytes) {
+ assert(st != NULL, "need an output stream (no default)!");
+ int idx = 0;
+ address pos = range_start;
+
+ while ((pos != NULL) && (pos < range_end)) {
+ int instr_size_in_bytes = Assembler::instr_len(pos);
+
+ if (idx == 0) print_location(pos, start, end, st, false, false);
+ else print_delimiter(st);
+
+ //---< print the instruction's bytes >---
+ // don't access storage beyond end of range
+ if (pos + instr_size_in_bytes <= range_end) {
+ pos = decode_instruction_abstract(pos, st, instr_size_in_bytes, max_instr_size_in_bytes);
+ } else {
+ // If the range to be decoded contains garbage at the end (e.g. 0xcc initializer bytes),
+ // instruction size calculation may run out of sync. Just terminate in that case.
+ pos = range_end;
+ }
+
+ idx += instr_size_in_bytes;
+ if (start_newline(idx)) {
+ st->cr();
+ idx = 0;
+ }
+ }
+}
+
+
+// Decodes all instructions in the given range [start..end).
+// The output is enclosed in [MachCode] and [/MachCode] tags for later recognition.
+// The format is platform dependent only to the extend that
+// it respects the actual instruction length where possible.
+void AbstractDisassembler::decode_abstract(address start, address end, outputStream* ost,
+ const int max_instr_size_in_bytes) {
+ int idx = 0;
+ address pos = start;
+
+ outputStream* st = (ost == NULL) ? tty : ost;
+
+ //---< Open the output (Marker for post-mortem disassembler) >---
+ st->bol();
+ st->print_cr("[MachCode]");
+
+ decode_range_abstract(start, end, start, end, st, max_instr_size_in_bytes);
+
+ //---< Close the output (Marker for post-mortem disassembler) >---
+ st->bol();
+ st->print_cr("[/MachCode]");
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/compiler/abstractDisassembler.hpp Tue May 21 15:51:35 2019 +0200
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_COMPILER_ABSTRACTDISASSEMBLER_HPP
+#define SHARE_COMPILER_ABSTRACTDISASSEMBLER_HPP
+
+// AbstractDisassembler is the base class for
+// platform-specific Disassembler classes.
+
+#include "utilities/globalDefinitions.hpp"
+
+class AbstractDisassembler {
+
+ private:
+ // These are some general settings which control
+ // abstract disassembly output.
+ enum {
+ // that many bytes are dumped in one line.
+ abstract_instruction_bytes_per_line = 32,
+ // instruction bytes are grouped in blocks of that many bytes.
+ abstract_instruction_bytes_per_block = 2,
+ // instructions have this default len.
+ abstract_instruction_size_in_bytes = 1,
+ // instructions have this maximum len.
+ abstract_instruction_maxsize_in_bytes = 1
+ };
+
+ static bool _align_instr; // vertical alignment of instructions in abstract disassembly
+ static bool _show_pc; // print the instruction address
+ static bool _show_offset; // print the instruction offset (from start of blob)
+ static bool _show_bytes; // print instruction bytes
+ static bool _show_data_hex; // print instruction bytes
+ static bool _show_data_int; // print instruction bytes
+ static bool _show_data_float; // print instruction bytes
+ static bool _show_structs; // print compiler data structures (relocations, oop maps, scopes, metadata, ...)
+ static bool _show_comment; // print instruction comments
+ static bool _show_block_comment; // print block comments
+
+ public:
+ // Platform-independent location and instruction formatting.
+ // All functions return #characters printed.
+ static int print_location(address here, address begin, address end, outputStream* st, bool align, bool print_header);
+ static int print_instruction(address here, int len, int max_len, outputStream* st, bool align, bool print_header);
+ static int print_hexdata(address here, int len, outputStream* st, bool print_header = false);
+ static int print_delimiter(outputStream* st);
+ static bool start_newline(int byte_count) { return byte_count >= abstract_instruction_bytes_per_line; }
+
+ static void toggle_align_instr() { _align_instr = !_align_instr; }
+ static void toggle_show_pc() { _show_pc = !_show_pc; }
+ static void toggle_show_offset() { _show_offset = !_show_offset; }
+ static void toggle_show_bytes() { _show_bytes = !_show_bytes; }
+ static void toggle_show_data_hex() { _show_data_hex = !_show_data_hex; }
+ static void toggle_show_data_int() { _show_data_int = !_show_data_int; }
+ static void toggle_show_data_float() { _show_data_float = !_show_data_float; }
+ static void toggle_show_structs() { _show_structs = !_show_structs; }
+ static void toggle_show_comment() { _show_comment = !_show_comment; }
+ static void toggle_show_block_comment() { _show_block_comment = !_show_block_comment; }
+
+ static bool align_instr() { return _align_instr; }
+ static bool show_pc() { return _show_pc; }
+ static bool show_offset() { return _show_offset; }
+ static bool show_bytes() { return _show_bytes; }
+ static bool show_data_hex() { return _show_data_hex; }
+ static bool show_data_int() { return _show_data_int; }
+ static bool show_data_float() { return _show_data_float; }
+ static bool show_structs() { return _show_structs; }
+ static bool show_comment() { return _show_comment; }
+ static bool show_block_comment() { return _show_block_comment; }
+
+ // Decodes the one instruction at address start in a platform-independent
+ // format. Returns the start of the next instruction (which is
+ // 'start' plus 'instruction_size_in_bytes'). The parameter max_instr_size_in_bytes
+ // is used for output alignment purposes only.
+ static address decode_instruction_abstract(address start,
+ outputStream* st,
+ const int instruction_size_in_bytes,
+ const int max_instr_size_in_bytes = abstract_instruction_maxsize_in_bytes);
+
+ // Decodes all instructions in the given range [start..end)
+ // calling decode_instruction_abstract for each instruction.
+ // The format is platform dependent only to the extend that
+ // it respects the actual instruction length where possible.
+ // Does not print any markers or decorators.
+ static void decode_range_abstract(address range_start, address range_end,
+ address start, address end,
+ outputStream* st,
+ const int max_instr_size_in_bytes = abstract_instruction_maxsize_in_bytes);
+
+ // Decodes all instructions in the given range in a platform-independent
+ // format, calling decode_instruction_abstract for each instruction.
+ static void decode_abstract(address start, address end,
+ outputStream* st,
+ const int max_instr_size_in_bytes = abstract_instruction_maxsize_in_bytes);
+};
+
+#endif // SHARE_COMPILER_ABSTRACTDISASSEMBLER_HPP
--- a/src/hotspot/share/compiler/disassembler.cpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/compiler/disassembler.cpp Tue May 21 15:51:35 2019 +0200
@@ -23,7 +23,7 @@
*/
#include "precompiled.hpp"
-#include "asm/macroAssembler.hpp"
+#include "asm/assembler.inline.hpp"
#include "ci/ciUtilities.hpp"
#include "classfile/javaClasses.hpp"
#include "code/codeCache.hpp"
@@ -43,6 +43,7 @@
void* Disassembler::_library = NULL;
bool Disassembler::_tried_to_load_library = false;
+bool Disassembler::_library_usable = false;
// This routine is in the shared library:
Disassembler::decode_func_virtual Disassembler::_decode_instructions_virtual = NULL;
@@ -55,127 +56,46 @@
#define COMMENT_COLUMN 52 LP64_ONLY(+8) /*could be an option*/
#define BYTES_COMMENT ";..." /* funky byte display comment */
-bool Disassembler::load_library() {
- if (_decode_instructions_virtual != NULL || _decode_instructions != NULL) {
- // Already succeeded.
- return true;
- }
- if (_tried_to_load_library) {
- // Do not try twice.
- // To force retry in debugger: assign _tried_to_load_library=0
- return false;
- }
- // Try to load it.
- char ebuf[1024];
- char buf[JVM_MAXPATHLEN];
- os::jvm_path(buf, sizeof(buf));
- int jvm_offset = -1;
- int lib_offset = -1;
-#ifdef STATIC_BUILD
- char* p = strrchr(buf, '/');
- *p = '\0';
- strcat(p, "/lib/");
- lib_offset = jvm_offset = strlen(buf);
-#else
- {
- // Match "jvm[^/]*" in jvm_path.
- const char* base = buf;
- const char* p = strrchr(buf, *os::file_separator());
- if (p != NULL) lib_offset = p - base + 1;
- p = strstr(p ? p : base, "jvm");
- if (p != NULL) jvm_offset = p - base;
- }
-#endif
- // Find the disassembler shared library.
- // Search for several paths derived from libjvm, in this order:
- // 1. <home>/jre/lib/<arch>/<vm>/libhsdis-<arch>.so (for compatibility)
- // 2. <home>/jre/lib/<arch>/<vm>/hsdis-<arch>.so
- // 3. <home>/jre/lib/<arch>/hsdis-<arch>.so
- // 4. hsdis-<arch>.so (using LD_LIBRARY_PATH)
- if (jvm_offset >= 0) {
- // 1. <home>/jre/lib/<arch>/<vm>/libhsdis-<arch>.so
- strcpy(&buf[jvm_offset], hsdis_library_name);
- strcat(&buf[jvm_offset], os::dll_file_extension());
- _library = os::dll_load(buf, ebuf, sizeof ebuf);
- if (_library == NULL && lib_offset >= 0) {
- // 2. <home>/jre/lib/<arch>/<vm>/hsdis-<arch>.so
- strcpy(&buf[lib_offset], hsdis_library_name);
- strcat(&buf[lib_offset], os::dll_file_extension());
- _library = os::dll_load(buf, ebuf, sizeof ebuf);
- }
- if (_library == NULL && lib_offset > 0) {
- // 3. <home>/jre/lib/<arch>/hsdis-<arch>.so
- buf[lib_offset - 1] = '\0';
- const char* p = strrchr(buf, *os::file_separator());
- if (p != NULL) {
- lib_offset = p - buf + 1;
- strcpy(&buf[lib_offset], hsdis_library_name);
- strcat(&buf[lib_offset], os::dll_file_extension());
- _library = os::dll_load(buf, ebuf, sizeof ebuf);
- }
- }
- }
- if (_library == NULL) {
- // 4. hsdis-<arch>.so (using LD_LIBRARY_PATH)
- strcpy(&buf[0], hsdis_library_name);
- strcat(&buf[0], os::dll_file_extension());
- _library = os::dll_load(buf, ebuf, sizeof ebuf);
- }
- if (_library != NULL) {
- _decode_instructions_virtual = CAST_TO_FN_PTR(Disassembler::decode_func_virtual,
- os::dll_lookup(_library, decode_instructions_virtual_name));
- }
- if (_decode_instructions_virtual == NULL && _library != NULL) {
- // could not spot in new version, try old version
- _decode_instructions = CAST_TO_FN_PTR(Disassembler::decode_func,
- os::dll_lookup(_library, decode_instructions_name));
- use_new_version = false;
- } else {
- use_new_version = true;
- }
- _tried_to_load_library = true;
- if (_decode_instructions_virtual == NULL && _decode_instructions == NULL) {
- tty->print_cr("Could not load %s; %s; %s", buf,
- ((_library != NULL)
- ? "entry point is missing"
- : (WizardMode || PrintMiscellaneous)
- ? (const char*)ebuf
- : "library not loadable"),
- "PrintAssembly is disabled");
- return false;
- }
-
- // Success.
- tty->print_cr("Loaded disassembler from %s", buf);
- return true;
-}
-
-
class decode_env {
private:
- nmethod* _nm;
- CodeBlob* _code;
+ outputStream* _output; // where the disassembly is directed to
+ CodeBuffer* _codeBuffer; // != NULL only when decoding a CodeBuffer
+ CodeBlob* _codeBlob; // != NULL only when decoding a CodeBlob
+ nmethod* _nm; // != NULL only when decoding a nmethod
CodeStrings _strings;
- outputStream* _output;
- address _start, _end;
- ptrdiff_t _offset;
+ address _start; // != NULL when decoding a range of unknown type
+ address _end; // != NULL when decoding a range of unknown type
char _option_buf[512];
char _print_raw;
- bool _print_pc;
- bool _print_bytes;
- address _cur_insn;
- int _bytes_per_line; // arch-specific formatting option
+ address _cur_insn; // address of instruction currently being decoded
+ int _bytes_per_line; // arch-specific formatting option
+ int _pre_decode_alignment;
+ int _post_decode_alignment;
bool _print_file_name;
+ bool _print_help;
+ bool _helpPrinted;
+ static bool _optionsParsed;
+ enum {
+ tabspacing = 8
+ };
+
+ // Check if the event matches the expected tag
+ // The tag must be a substring of the event, and
+ // the tag must be a token in the event, i.e. separated by delimiters
static bool match(const char* event, const char* tag) {
- size_t taglen = strlen(tag);
- if (strncmp(event, tag, taglen) != 0)
+ size_t eventlen = strlen(event);
+ size_t taglen = strlen(tag);
+ if (eventlen < taglen) // size mismatch
+ return false;
+ if (strncmp(event, tag, taglen) != 0) // string mismatch
return false;
char delim = event[taglen];
return delim == '\0' || delim == ' ' || delim == '/' || delim == '=';
}
+ // Merge new option string with previously recorded options
void collect_options(const char* p) {
if (p == NULL || p[0] == '\0') return;
size_t opt_so_far = strlen(_option_buf);
@@ -187,14 +107,56 @@
char* q = fillp;
while ((q = strpbrk(q, " \t\n")) != NULL)
*q++ = ',';
- // Note that multiple PrintAssemblyOptions flags accumulate with \n,
- // which we want to be changed to a comma...
}
+ void process_options(outputStream* ost);
+
void print_insn_labels();
- void print_insn_bytes(address pc0, address pc);
+ void print_insn_prefix();
void print_address(address value);
+ // Properly initializes _start/_end. Overwritten too often if
+ // printing of instructions is called for each instruction.
+ void set_start(address s) { _start = s; }
+ void set_end (address e) { _end = e; }
+ void set_nm (nmethod* nm) { _nm = nm; }
+ void set_output(outputStream* st) { _output = st; }
+
+#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY)
+ // The disassembler library (sometimes) uses tabs to nicely align the instruction operands.
+ // Depending on the mnemonic length and the column position where the
+ // mnemonic is printed, alignment may turn out to be not so nice.
+ // To improve, we assume 8-character tab spacing and left-align the mnemonic on a tab position.
+ // Instruction comments are aligned 4 tab positions to the right of the mnemonic.
+ void calculate_alignment() {
+ _pre_decode_alignment = ((output()->position()+tabspacing-1)/tabspacing)*tabspacing;
+ _post_decode_alignment = _pre_decode_alignment + 4*tabspacing;
+ }
+
+ void start_insn(address pc) {
+ _cur_insn = pc;
+ output()->bol();
+ print_insn_labels();
+ print_insn_prefix();
+ }
+
+ void end_insn(address pc) {
+ address pc0 = cur_insn();
+ outputStream* st = output();
+
+ if (AbstractDisassembler::show_comment()) {
+ if ((_nm != NULL) && _nm->has_code_comment(pc0, pc)) {
+ _nm->print_code_comment_on(st, _post_decode_alignment, pc0, pc);
+ // this calls reloc_string_for which calls oop::print_value_on
+ }
+ print_hook_comments(pc0, _nm != NULL);
+ }
+ Disassembler::annotate(pc0, output());
+ // follow each complete insn by a nice newline
+ st->bol();
+ }
+#endif
+
struct SourceFileInfo {
struct Link : public CHeapObj<mtCode> {
const char* file;
@@ -241,40 +203,28 @@
static GrowableArray<const char*>* _cached_src_lines;
public:
- decode_env(CodeBlob* code, outputStream* output,
- CodeStrings c = CodeStrings(), ptrdiff_t offset = 0);
-
- address decode_instructions(address start, address end);
-
- void start_insn(address pc) {
- _cur_insn = pc;
- output()->bol();
- print_insn_labels();
- }
+ decode_env(CodeBuffer* code, outputStream* output);
+ decode_env(CodeBlob* code, outputStream* output, CodeStrings c = CodeStrings() /* , ptrdiff_t offset */);
+ decode_env(nmethod* code, outputStream* output, CodeStrings c = CodeStrings());
+ // Constructor for a 'decode_env' to decode an arbitrary
+ // piece of memory, hopefully containing code.
+ decode_env(address start, address end, outputStream* output);
- void end_insn(address pc) {
- address pc0 = cur_insn();
- outputStream* st = output();
- if (_print_bytes && pc > pc0)
- print_insn_bytes(pc0, pc);
- if (_nm != NULL) {
- _nm->print_code_comment_on(st, COMMENT_COLUMN, pc0, pc);
- // this calls reloc_string_for which calls oop::print_value_on
- }
- print_hook_comments(pc0, _nm != NULL);
- // follow each complete insn by a nice newline
- st->cr();
- }
+ // Add 'original_start' argument which is the the original address
+ // the instructions were located at (if this is not equal to 'start').
+ address decode_instructions(address start, address end, address original_start = NULL);
address handle_event(const char* event, address arg);
- outputStream* output() { return _output; }
- address cur_insn() { return _cur_insn; }
- const char* options() { return _option_buf; }
- static void hook(const char* file, int line, address pc);
+ outputStream* output() { return _output; }
+ address cur_insn() { return _cur_insn; }
+ const char* options() { return _option_buf; }
+ static void hook(const char* file, int line, address pc);
void print_hook_comments(address pc, bool newline);
};
+bool decode_env::_optionsParsed = false;
+
decode_env::SourceFileInfoTable decode_env::_src_table;
const char* decode_env::_cached_src = NULL;
GrowableArray<const char*>* decode_env::_cached_src_lines = NULL;
@@ -361,50 +311,185 @@
}
}
-decode_env::decode_env(CodeBlob* code, outputStream* output, CodeStrings c,
- ptrdiff_t offset) : _nm(NULL),
- _start(NULL),
- _end(NULL),
- _option_buf(),
- _print_raw('\0'),
- _cur_insn(NULL) {
+decode_env::decode_env(CodeBuffer* code, outputStream* output) {
+ memset(this, 0, sizeof(*this));
+ _output = output ? output : tty;
+ _codeBlob = NULL;
+ _codeBuffer = code;
+ _helpPrinted = false;
+
+ process_options(_output);
+}
+
+decode_env::decode_env(CodeBlob* code, outputStream* output, CodeStrings c) {
+ memset(this, 0, sizeof(*this)); // Beware, this zeroes bits of fields.
+ _output = output ? output : tty;
+ _codeBlob = code;
+ _codeBuffer = NULL;
+ _helpPrinted = false;
+ if (_codeBlob != NULL && _codeBlob->is_nmethod()) {
+ _nm = (nmethod*) code;
+ }
+ _strings.copy(c);
+
+ process_options(_output);
+}
+
+decode_env::decode_env(nmethod* code, outputStream* output, CodeStrings c) {
+ memset(this, 0, sizeof(*this)); // Beware, this zeroes bits of fields.
_output = output ? output : tty;
- _code = code;
- if (code != NULL && code->is_nmethod())
- _nm = (nmethod*) code;
+ _codeBlob = NULL;
+ _codeBuffer = NULL;
+ _nm = code;
+ _start = _nm->code_begin();
+ _end = _nm->code_end();
+ _helpPrinted = false;
_strings.copy(c);
- _offset = offset;
+
+ process_options(_output);
+}
+// Constructor for a 'decode_env' to decode a memory range [start, end)
+// of unknown origin, assuming it contains code.
+decode_env::decode_env(address start, address end, outputStream* output) {
+ assert(start < end, "Range must have a positive size, [" PTR_FORMAT ".." PTR_FORMAT ").", p2i(start), p2i(end));
+ memset(this, 0, sizeof(*this));
+ _output = output ? output : tty;
+ _codeBlob = NULL;
+ _codeBuffer = NULL;
+ _start = start;
+ _end = end;
+ _helpPrinted = false;
+
+ process_options(_output);
+}
+
+void decode_env::process_options(outputStream* ost) {
// by default, output pc but not bytes:
- _print_pc = true;
- _print_bytes = false;
- _bytes_per_line = Disassembler::pd_instruction_alignment();
- _print_file_name= true;
+ _print_help = false;
+ _bytes_per_line = Disassembler::pd_instruction_alignment();
+ _print_file_name = true;
+
+ if (_optionsParsed) return; // parse only once
// parse the global option string:
collect_options(Disassembler::pd_cpu_opts());
collect_options(PrintAssemblyOptions);
- if (strstr(options(), "hsdis-")) {
- if (strstr(options(), "hsdis-print-raw"))
- _print_raw = (strstr(options(), "xml") ? 2 : 1);
- if (strstr(options(), "hsdis-print-pc"))
- _print_pc = !_print_pc;
- if (strstr(options(), "hsdis-print-bytes"))
- _print_bytes = !_print_bytes;
+ if (strstr(options(), "print-raw")) {
+ _print_raw = (strstr(options(), "xml") ? 2 : 1);
+ }
+
+ if (strstr(options(), "help")) {
+ _print_help = true;
+ }
+ if (strstr(options(), "align-instr")) {
+ AbstractDisassembler::toggle_align_instr();
+ }
+ if (strstr(options(), "show-pc")) {
+ AbstractDisassembler::toggle_show_pc();
+ }
+ if (strstr(options(), "show-offset")) {
+ AbstractDisassembler::toggle_show_offset();
+ }
+ if (strstr(options(), "show-bytes")) {
+ AbstractDisassembler::toggle_show_bytes();
+ }
+ if (strstr(options(), "show-data-hex")) {
+ AbstractDisassembler::toggle_show_data_hex();
+ }
+ if (strstr(options(), "show-data-int")) {
+ AbstractDisassembler::toggle_show_data_int();
+ }
+ if (strstr(options(), "show-data-float")) {
+ AbstractDisassembler::toggle_show_data_float();
}
- if (strstr(options(), "help")) {
- tty->print_cr("PrintAssemblyOptions help:");
- tty->print_cr(" hsdis-print-raw test plugin by requesting raw output");
- tty->print_cr(" hsdis-print-raw-xml test plugin by requesting raw xml");
- tty->print_cr(" hsdis-print-pc turn off PC printing (on by default)");
- tty->print_cr(" hsdis-print-bytes turn on instruction byte output");
- tty->print_cr("combined options: %s", options());
+ if (strstr(options(), "show-structs")) {
+ AbstractDisassembler::toggle_show_structs();
+ }
+ if (strstr(options(), "show-comment")) {
+ AbstractDisassembler::toggle_show_comment();
+ }
+ if (strstr(options(), "show-block-comment")) {
+ AbstractDisassembler::toggle_show_block_comment();
+ }
+ _optionsParsed = true;
+
+ if (_print_help && ! _helpPrinted) {
+ _helpPrinted = true;
+ ost->print_cr("PrintAssemblyOptions help:");
+ ost->print_cr(" print-raw test plugin by requesting raw output");
+ ost->print_cr(" print-raw-xml test plugin by requesting raw xml");
+ ost->cr();
+ ost->print_cr(" show-pc toggle printing current pc, currently %s", AbstractDisassembler::show_pc() ? "ON" : "OFF");
+ ost->print_cr(" show-offset toggle printing current offset, currently %s", AbstractDisassembler::show_offset() ? "ON" : "OFF");
+ ost->print_cr(" show-bytes toggle printing instruction bytes, currently %s", AbstractDisassembler::show_bytes() ? "ON" : "OFF");
+ ost->print_cr(" show-data-hex toggle formatting data as hex, currently %s", AbstractDisassembler::show_data_hex() ? "ON" : "OFF");
+ ost->print_cr(" show-data-int toggle formatting data as int, currently %s", AbstractDisassembler::show_data_int() ? "ON" : "OFF");
+ ost->print_cr(" show-data-float toggle formatting data as float, currently %s", AbstractDisassembler::show_data_float() ? "ON" : "OFF");
+ ost->print_cr(" show-structs toggle compiler data structures, currently %s", AbstractDisassembler::show_structs() ? "ON" : "OFF");
+ ost->print_cr(" show-comment toggle instruction comments, currently %s", AbstractDisassembler::show_comment() ? "ON" : "OFF");
+ ost->print_cr(" show-block-comment toggle block comments, currently %s", AbstractDisassembler::show_block_comment() ? "ON" : "OFF");
+ ost->print_cr(" align-instr toggle instruction alignment, currently %s", AbstractDisassembler::align_instr() ? "ON" : "OFF");
+ ost->print_cr("combined options: %s", options());
}
}
+// Disassembly Event Handler.
+// This method receives events from the disassembler library hsdis
+// via event_to_env for each decoding step (installed by
+// Disassembler::decode_instructions(), replacing the default
+// callback method). This enables dumping additional info
+// and custom line formatting.
+// In a future extension, calling a custom decode method will be
+// supported. We can use such a method to decode instructions the
+// binutils decoder does not handle to our liking (suboptimal
+// formatting, incomplete information, ...).
+// Returns:
+// - NULL for all standard invocations. The function result is not
+// examined (as of now, 20190409) by the hsdis decoder loop.
+// - next for 'insn0' invocations.
+// next == arg: the custom decoder didn't do anything.
+// next > arg: the custom decoder did decode the instruction.
+// next points to the next undecoded instruction
+// (continuation point for decoder loop).
+//
+// "Normal" sequence of events:
+// insns - start of instruction stream decoding
+// mach - display architecture
+// format - display bytes-per-line
+// for each instruction:
+// insn - start of instruction decoding
+// insn0 - custom decoder invocation (if any)
+// addr - print address value
+// /insn - end of instruction decoding
+// /insns - premature end of instruction stream due to no progress
+//
address decode_env::handle_event(const char* event, address arg) {
- if (match(event, "insn")) {
+
+#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY)
+
+ //---< Event: end decoding loop (error, no progress) >---
+ if (decode_env::match(event, "/insns")) {
+ // Nothing to be done here.
+ return NULL;
+ }
+
+ //---< Event: start decoding loop >---
+ if (decode_env::match(event, "insns")) {
+ // Nothing to be done here.
+ return NULL;
+ }
+
+ //---< Event: finish decoding an instruction >---
+ if (decode_env::match(event, "/insn")) {
+ output()->fill_to(_post_decode_alignment);
+ end_insn(arg);
+ return NULL;
+ }
+
+ //---< Event: start decoding an instruction >---
+ if (decode_env::match(event, "insn")) {
start_insn(arg);
} else if (match(event, "/insn")) {
end_insn(arg);
@@ -413,26 +498,59 @@
print_address(arg);
return arg;
}
- } else if (match(event, "mach")) {
- static char buffer[32] = { 0, };
- if (strcmp(buffer, (const char*)arg) != 0 ||
- strlen((const char*)arg) > sizeof(buffer) - 1) {
+ calculate_alignment();
+ output()->fill_to(_pre_decode_alignment);
+ return NULL;
+ }
+
+ //---< Event: call custom decoder (platform specific) >---
+ if (decode_env::match(event, "insn0")) {
+ return Disassembler::decode_instruction0(arg, output(), arg);
+ }
+
+ //---< Event: Print address >---
+ if (decode_env::match(event, "addr")) {
+ print_address(arg);
+ return arg;
+ }
+
+ //---< Event: mach (inform about machine architecture) >---
+ // This event is problematic because it messes up the output.
+ // The event is fired after the instruction address has already
+ // been printed. The decoded instruction (event "insn") is
+ // printed afterwards. That doesn't look nice.
+ if (decode_env::match(event, "mach")) {
+ guarantee(arg != NULL, "event_to_env - arg must not be NULL for event 'mach'");
+ static char buffer[64] = { 0, };
+ // Output suppressed because it messes up disassembly.
+ // Only print this when the mach changes.
+ if (false && (strcmp(buffer, (const char*)arg) != 0 ||
+ strlen((const char*)arg) > sizeof(buffer) - 1)) {
// Only print this when the mach changes
strncpy(buffer, (const char*)arg, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0';
- output()->print_cr("[Disassembling for mach='%s']", arg);
+ output()->print_cr("[Disassembling for mach='%s']", (const char*)arg);
}
- } else if (match(event, "format bytes-per-line")) {
+ return NULL;
+ }
+
+ //---< Event: format bytes-per-line >---
+ if (decode_env::match(event, "format bytes-per-line")) {
_bytes_per_line = (int) (intptr_t) arg;
- } else {
- // ignore unrecognized markup
+ return NULL;
}
+#endif
return NULL;
}
+static void* event_to_env(void* env_pv, const char* event, void* arg) {
+ decode_env* env = (decode_env*) env_pv;
+ return env->handle_event(event, (address) arg);
+}
+
// called by the disassembler to print out jump targets and data addresses
void decode_env::print_address(address adr) {
- outputStream* st = _output;
+ outputStream* st = output();
if (adr == NULL) {
st->print("NULL");
@@ -477,9 +595,11 @@
if (_nm == NULL) {
// Don't do this for native methods, as the function name will be printed in
// nmethod::reloc_string_for().
- ResourceMark rm;
+ // Allocate the buffer on the stack instead of as RESOURCE array.
+ // In case we do DecodeErrorFile, Thread will not be initialized,
+ // causing a "assert(current != __null) failed" failure.
const int buflen = 1024;
- char* buf = NEW_RESOURCE_ARRAY(char, buflen);
+ char buf[buflen];
int offset;
if (os::dll_address_to_function_name(adr, buf, buflen, &offset)) {
st->print(PTR_FORMAT " = %s", p2i(adr), buf);
@@ -495,54 +615,31 @@
}
void decode_env::print_insn_labels() {
- address p = cur_insn();
- outputStream* st = output();
- CodeBlob* cb = _code;
- if (cb != NULL) {
- cb->print_block_comment(st, p);
- }
- _strings.print_block_comment(st, (intptr_t)(p - _start + _offset));
- if (_print_pc) {
- st->print(" " PTR_FORMAT ": ", p2i(p));
+ if (AbstractDisassembler::show_block_comment()) {
+ address p = cur_insn();
+ outputStream* st = output();
+
+ //---< Block comments for nmethod >---
+ // Outputs a bol() before and a cr() after, but only if a comment is printed.
+ // Prints nmethod_section_label as well.
+ if (_nm != NULL) {
+ _nm->print_block_comment(st, p);
+ }
+ if (_codeBlob != NULL) {
+ _codeBlob->print_block_comment(st, p);
+ }
+ if (_codeBuffer != NULL) {
+ _codeBuffer->print_block_comment(st, p);
+ }
+ _strings.print_block_comment(st, (intptr_t)(p - _start));
}
}
-void decode_env::print_insn_bytes(address pc, address pc_limit) {
+void decode_env::print_insn_prefix() {
+ address p = cur_insn();
outputStream* st = output();
- size_t incr = 1;
- size_t perline = _bytes_per_line;
- if ((size_t) Disassembler::pd_instruction_alignment() >= sizeof(int)
- && !((uintptr_t)pc % sizeof(int))
- && !((uintptr_t)pc_limit % sizeof(int))) {
- incr = sizeof(int);
- if (perline % incr) perline += incr - (perline % incr);
- }
- while (pc < pc_limit) {
- // tab to the desired column:
- st->move_to(COMMENT_COLUMN);
- address pc0 = pc;
- address pc1 = pc + perline;
- if (pc1 > pc_limit) pc1 = pc_limit;
- for (; pc < pc1; pc += incr) {
- if (pc == pc0) {
- st->print(BYTES_COMMENT);
- } else if ((uint)(pc - pc0) % sizeof(int) == 0) {
- st->print(" "); // put out a space on word boundaries
- }
- if (incr == sizeof(int)) {
- st->print("%08x", *(int*)pc);
- } else {
- st->print("%02x", (*pc)&0xFF);
- }
- }
- st->cr();
- }
-}
-
-
-static void* event_to_env(void* env_pv, const char* event, void* arg) {
- decode_env* env = (decode_env*) env_pv;
- return env->handle_event(event, (address) arg);
+ AbstractDisassembler::print_location(p, _start, _end, st, false, false);
+ AbstractDisassembler::print_instruction(p, Assembler::instr_len(p), Assembler::instr_maxlen(), st, true, false);
}
ATTRIBUTE_PRINTF(2, 3)
@@ -575,16 +672,31 @@
return (int)(cnt1 - cnt0);
}
-address decode_env::decode_instructions(address start, address end) {
- _start = start; _end = end;
-
- assert(((((intptr_t)start | (intptr_t)end) % Disassembler::pd_instruction_alignment()) == 0), "misaligned insn addr");
+// The 'original_start' argument holds the the original address where
+// the instructions were located in the originating system. If zero (NULL)
+// is passed in, there is no original address.
+address decode_env::decode_instructions(address start, address end, address original_start /* = 0*/) {
+ // CodeComment in Stubs.
+ // Properly initialize _start/_end. Overwritten too often if
+ // printing of instructions is called for each instruction.
+ assert((_start == NULL) || (start == NULL) || (_start == start), "don't overwrite CTOR values");
+ assert((_end == NULL) || (end == NULL) || (_end == end ), "don't overwrite CTOR values");
+ if (start != NULL) set_start(start);
+ if (end != NULL) set_end(end);
+ if (original_start == NULL) {
+ original_start = start;
+ }
- const int show_bytes = false; // for disassembler debugging
+ //---< Check (and correct) alignment >---
+ // Don't check alignment of end, it is not aligned.
+ if (((uint64_t)start & ((uint64_t)Disassembler::pd_instruction_alignment() - 1)) != 0) {
+ output()->print_cr("Decode range start:" PTR_FORMAT ": ... (unaligned)", p2i(start));
+ start = (address)((uint64_t)start & ~((uint64_t)Disassembler::pd_instruction_alignment() - 1));
+ }
- //_version = Disassembler::pd_cpu_version();
-
- if (!Disassembler::can_decode()) {
+ // Trying to decode instructions doesn't make sense if we
+ // couldn't load the disassembler library.
+ if (Disassembler::is_abstract()) {
return NULL;
}
@@ -625,16 +737,177 @@
options());
}
+// ----------------------------------------------------------------------------
+// Disassembler
+// Used as a static wrapper for decode_env.
+// Each method will create a decode_env before decoding.
+// You can call the decode_env methods directly if you already have one.
-void Disassembler::decode(CodeBlob* cb, outputStream* st) {
- ttyLocker ttyl;
- if (!load_library()) return;
- if (cb->is_nmethod()) {
- decode((nmethod*)cb, st);
+
+bool Disassembler::load_library(outputStream* st) {
+ // Do not try to load multiple times. Failed once -> fails always.
+ // To force retry in debugger: assign _tried_to_load_library=0
+ if (_tried_to_load_library) {
+ return _library_usable;
+ }
+
+#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY)
+ // Print to given stream, if any.
+ // Print to tty if Verbose is on and no stream given.
+ st = ((st == NULL) && Verbose) ? tty : st;
+
+ // Compute fully qualified library name.
+ char ebuf[1024];
+ char buf[JVM_MAXPATHLEN];
+ os::jvm_path(buf, sizeof(buf));
+ int jvm_offset = -1;
+ int lib_offset = -1;
+#ifdef STATIC_BUILD
+ char* p = strrchr(buf, '/');
+ *p = '\0';
+ strcat(p, "/lib/");
+ lib_offset = jvm_offset = strlen(buf);
+#else
+ {
+ // Match "libjvm" instead of "jvm" on *nix platforms. Creates better matches.
+ // Match "[lib]jvm[^/]*" in jvm_path.
+ const char* base = buf;
+ const char* p = strrchr(buf, *os::file_separator());
+#ifdef _WIN32
+ p = strstr(p ? p : base, "jvm");
+#else
+ p = strstr(p ? p : base, "libjvm");
+#endif
+ if (p != NULL) lib_offset = p - base + 1;
+ if (p != NULL) jvm_offset = p - base;
+ }
+#endif
+
+ // Find the disassembler shared library.
+ // Search for several paths derived from libjvm, in this order:
+ // 1. <home>/jre/lib/<arch>/<vm>/libhsdis-<arch>.so (for compatibility)
+ // 2. <home>/jre/lib/<arch>/<vm>/hsdis-<arch>.so
+ // 3. <home>/jre/lib/<arch>/hsdis-<arch>.so
+ // 4. hsdis-<arch>.so (using LD_LIBRARY_PATH)
+ if (jvm_offset >= 0) {
+ // 1. <home>/jre/lib/<arch>/<vm>/libhsdis-<arch>.so
+ strcpy(&buf[jvm_offset], hsdis_library_name);
+ strcat(&buf[jvm_offset], os::dll_file_extension());
+ _library = os::dll_load(buf, ebuf, sizeof ebuf);
+ if (_library == NULL && lib_offset >= 0) {
+ // 2. <home>/jre/lib/<arch>/<vm>/hsdis-<arch>.so
+ strcpy(&buf[lib_offset], hsdis_library_name);
+ strcat(&buf[lib_offset], os::dll_file_extension());
+ _library = os::dll_load(buf, ebuf, sizeof ebuf);
+ }
+ if (_library == NULL && lib_offset > 0) {
+ // 3. <home>/jre/lib/<arch>/hsdis-<arch>.so
+ buf[lib_offset - 1] = '\0';
+ const char* p = strrchr(buf, *os::file_separator());
+ if (p != NULL) {
+ lib_offset = p - buf + 1;
+ strcpy(&buf[lib_offset], hsdis_library_name);
+ strcat(&buf[lib_offset], os::dll_file_extension());
+ _library = os::dll_load(buf, ebuf, sizeof ebuf);
+ }
+ }
+ }
+ if (_library == NULL) {
+ // 4. hsdis-<arch>.so (using LD_LIBRARY_PATH)
+ strcpy(&buf[0], hsdis_library_name);
+ strcat(&buf[0], os::dll_file_extension());
+ _library = os::dll_load(buf, ebuf, sizeof ebuf);
+ }
+
+ // load the decoder function to use (new or old version).
+ if (_library != NULL) {
+ _decode_instructions_virtual = CAST_TO_FN_PTR(Disassembler::decode_func_virtual,
+ os::dll_lookup(_library, decode_instructions_virtual_name));
+ }
+ if (_decode_instructions_virtual == NULL && _library != NULL) {
+ // could not spot in new version, try old version
+ _decode_instructions = CAST_TO_FN_PTR(Disassembler::decode_func,
+ os::dll_lookup(_library, decode_instructions_name));
+ use_new_version = false;
+ } else {
+ use_new_version = true;
+ }
+ _tried_to_load_library = true;
+ _library_usable = _decode_instructions_virtual != NULL || _decode_instructions != NULL;
+
+ // Create a dummy environment to initialize PrintAssemblyOptions.
+ // The PrintAssemblyOptions must be known for abstract disassemblies as well.
+ decode_env dummy((unsigned char*)(&buf[0]), (unsigned char*)(&buf[1]), st);
+
+ // Report problems during dll_load or dll_lookup, if any.
+ if (st != NULL) {
+ // Success.
+ if (_library_usable) {
+ st->print_cr("Loaded disassembler from %s", buf);
+ } else {
+ st->print_cr("Could not load %s; %s; %s",
+ buf,
+ ((_library != NULL)
+ ? "entry point is missing"
+ : ((WizardMode || PrintMiscellaneous)
+ ? (const char*)ebuf
+ : "library not loadable")),
+ "PrintAssembly defaults to abstract disassembly.");
+ }
+ }
+#endif
+ return _library_usable;
+}
+
+
+// Directly disassemble code buffer.
+void Disassembler::decode(CodeBuffer* cb, address start, address end, outputStream* st) {
+#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY)
+ //---< Test memory before decoding >---
+ if (!(cb->contains(start) && cb->contains(end))) {
+ //---< Allow output suppression, but prevent writing to a NULL stream. Could happen with +PrintStubCode. >---
+ if (st != NULL) {
+ st->print("Memory range [" PTR_FORMAT ".." PTR_FORMAT "] not contained in CodeBuffer", p2i(start), p2i(end));
+ }
return;
}
+ if (!os::is_readable_range(start, end)) {
+ //---< Allow output suppression, but prevent writing to a NULL stream. Could happen with +PrintStubCode. >---
+ if (st != NULL) {
+ st->print("Memory range [" PTR_FORMAT ".." PTR_FORMAT "] not readable", p2i(start), p2i(end));
+ }
+ return;
+ }
+
decode_env env(cb, st);
- env.output()->print_cr("----------------------------------------------------------------------");
+ env.output()->print_cr("--------------------------------------------------------------------------------");
+ env.output()->print("Decoding CodeBuffer (" PTR_FORMAT ")", p2i(cb));
+ if (cb->name() != NULL) {
+ env.output()->print(", name: %s,", cb->name());
+ }
+ env.output()->print_cr(" at [" PTR_FORMAT ", " PTR_FORMAT "] " JLONG_FORMAT " bytes", p2i(start), p2i(end), ((jlong)(end - start)));
+
+ if (is_abstract()) {
+ AbstractDisassembler::decode_abstract(start, end, env.output(), Assembler::instr_maxlen());
+ } else {
+ env.decode_instructions(start, end);
+ }
+ env.output()->print_cr("--------------------------------------------------------------------------------");
+#endif
+}
+
+// Directly disassemble code blob.
+void Disassembler::decode(CodeBlob* cb, outputStream* st, CodeStrings c) {
+#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY)
+ if (cb->is_nmethod()) {
+ // If we have an nmethod at hand,
+ // call the specialized decoder directly.
+ decode((nmethod*)cb, st, c);
+ return;
+ }
+
+ decode_env env(cb, st);
+ env.output()->print_cr("--------------------------------------------------------------------------------");
if (cb->is_aot()) {
env.output()->print("A ");
if (cb->is_compiled()) {
@@ -648,57 +921,78 @@
env.output()->print_cr("%s", cb->name());
}
} else {
- env.output()->print_cr("%s", cb->name());
+ env.output()->print("Decoding CodeBlob");
+ if (cb->name() != NULL) {
+ env.output()->print(", name: %s,", cb->name());
+ }
}
- env.output()->print_cr(" at [" PTR_FORMAT ", " PTR_FORMAT "] " JLONG_FORMAT " bytes", p2i(cb->code_begin()), p2i(cb->code_end()), ((jlong)(cb->code_end() - cb->code_begin())) * sizeof(unsigned char*));
- env.decode_instructions(cb->code_begin(), cb->code_end());
-}
+ env.output()->print_cr(" at [" PTR_FORMAT ", " PTR_FORMAT "] " JLONG_FORMAT " bytes", p2i(cb->code_begin()), p2i(cb->code_end()), ((jlong)(cb->code_end() - cb->code_begin())));
-void Disassembler::decode(address start, address end, outputStream* st, CodeStrings c,
- ptrdiff_t offset) {
- ttyLocker ttyl;
- if (!load_library()) return;
- decode_env env(CodeCache::find_blob_unsafe(start), st, c, offset);
- env.decode_instructions(start, end);
+ if (is_abstract()) {
+ AbstractDisassembler::decode_abstract(cb->code_begin(), cb->code_end(), env.output(), Assembler::instr_maxlen());
+ } else {
+ env.decode_instructions(cb->code_begin(), cb->code_end());
+ }
+ env.output()->print_cr("--------------------------------------------------------------------------------");
+#endif
}
-void Disassembler::decode(nmethod* nm, outputStream* st) {
+// Decode a nmethod.
+// This includes printing the constant pool and all code segments.
+// The nmethod data structures (oop maps, relocations and the like) are not printed.
+void Disassembler::decode(nmethod* nm, outputStream* st, CodeStrings c) {
+#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY)
ttyLocker ttyl;
- if (!load_library()) return;
+
decode_env env(nm, st);
- env.output()->print_cr("----------------------------------------------------------------------");
-
- unsigned char* p = nm->code_begin();
- unsigned char* end = nm->code_end();
+ env.output()->print_cr("--------------------------------------------------------------------------------");
+ nm->print_constant_pool(env.output());
+ env.output()->print_cr("--------------------------------------------------------------------------------");
+ env.output()->cr();
+ if (is_abstract()) {
+ AbstractDisassembler::decode_abstract(nm->code_begin(), nm->code_end(), env.output(), Assembler::instr_maxlen());
+ } else {
+ env.decode_instructions(nm->code_begin(), nm->code_end());
+ }
+ env.output()->print_cr("--------------------------------------------------------------------------------");
+#endif
+}
- nm->method()->method_holder()->name()->print_symbol_on(env.output());
- env.output()->print(".");
- nm->method()->name()->print_symbol_on(env.output());
- nm->method()->signature()->print_symbol_on(env.output());
-#if INCLUDE_JVMCI
+// Decode a range, given as [start address, end address)
+void Disassembler::decode(address start, address end, outputStream* st, CodeStrings c /*, ptrdiff_t offset */) {
+#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY)
+ //---< Test memory before decoding >---
+ if (!os::is_readable_range(start, end)) {
+ //---< Allow output suppression, but prevent writing to a NULL stream. Could happen with +PrintStubCode. >---
+ if (st != NULL) {
+ st->print("Memory range [" PTR_FORMAT ".." PTR_FORMAT "] not readable", p2i(start), p2i(end));
+ }
+ return;
+ }
+
+ if (is_abstract()) {
+ AbstractDisassembler::decode_abstract(start, end, st, Assembler::instr_maxlen());
+ return;
+ }
+
+// Don't do that fancy stuff. If we just have two addresses, live with it
+// and treat the memory contents as "amorphic" piece of code.
+#if 0
+ CodeBlob* cb = CodeCache::find_blob_unsafe(start);
+ if (cb != NULL) {
+ // If we have an CodeBlob at hand,
+ // call the specialized decoder directly.
+ decode(cb, st, c);
+ } else
+#endif
{
- const char* jvmciName = nm->jvmci_name();
- if (jvmciName != NULL) {
- env.output()->print(" (%s)", jvmciName);
- }
+ // This seems to be just a chunk of memory.
+ decode_env env(start, end, st);
+ env.output()->print_cr("--------------------------------------------------------------------------------");
+ env.decode_instructions(start, end);
+ env.output()->print_cr("--------------------------------------------------------------------------------");
}
#endif
- env.output()->print_cr(" [" PTR_FORMAT ", " PTR_FORMAT "] " JLONG_FORMAT " bytes", p2i(p), p2i(end), ((jlong)(end - p)));
-
- // Print constant table.
- if (nm->consts_size() > 0) {
- nm->print_nmethod_labels(env.output(), nm->consts_begin());
- int offset = 0;
- for (address p = nm->consts_begin(); p < nm->consts_end(); p += 4, offset += 4) {
- if ((offset % 8) == 0) {
- env.output()->print_cr(" " PTR_FORMAT " (offset: %4d): " PTR32_FORMAT " " PTR64_FORMAT, p2i(p), offset, *((int32_t*) p), *((int64_t*) p));
- } else {
- env.output()->print_cr(" " PTR_FORMAT " (offset: %4d): " PTR32_FORMAT, p2i(p), offset, *((int32_t*) p));
- }
- }
- }
-
- env.decode_instructions(p, end);
}
// To prevent excessive code expansion in the interpreter generator, we
--- a/src/hotspot/share/compiler/disassembler.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/compiler/disassembler.hpp Tue May 21 15:51:35 2019 +0200
@@ -25,7 +25,11 @@
#ifndef SHARE_COMPILER_DISASSEMBLER_HPP
#define SHARE_COMPILER_DISASSEMBLER_HPP
+#include "utilities/globalDefinitions.hpp"
+
+#include "asm/assembler.hpp"
#include "asm/codeBuffer.hpp"
+#include "compiler/abstractDisassembler.hpp"
#include "runtime/globals.hpp"
#include "utilities/macros.hpp"
@@ -34,7 +38,8 @@
// The disassembler prints out assembly code annotated
// with Java specific information.
-class Disassembler {
+// Disassembler inherits from AbstractDisassembler
+class Disassembler : public AbstractDisassembler {
friend class decode_env;
private:
// this is the type of the dll entry point:
@@ -57,26 +62,58 @@
static void* _library;
// bailout
static bool _tried_to_load_library;
+ static bool _library_usable;
// points to the decode function.
static decode_func_virtual _decode_instructions_virtual;
static decode_func _decode_instructions;
- // tries to load library and return whether it succedded.
- static bool load_library();
+
+ // tries to load library and return whether it succeeded.
+ // Allow (diagnostic) output redirection.
+ // No output at all if stream is NULL. Can be overridden
+ // with -Verbose flag, in which case output goes to tty.
+ static bool load_library(outputStream* st = NULL);
+
+ // Check if the two addresses are on the same page.
+ static bool is_same_page(address a1, address a2) {
+ return (((uintptr_t)a1 ^ (uintptr_t)a2) & (~0x0fffUL)) == 0L;
+ }
// Machine dependent stuff
#include CPU_HEADER(disassembler)
public:
- static bool can_decode() {
- ttyLocker tl;
- return (_decode_instructions_virtual != NULL) ||
- (_decode_instructions != NULL) ||
- load_library();
+ // We can always decode code blobs.
+ // Either we have a disassembler library available (successfully loaded)
+ // or we will resort to the abstract disassembler. This method informs
+ // about which decoding format is used.
+ // We can also enforce using the abstract disassembler.
+ static bool is_abstract() {
+ if (!_tried_to_load_library /* && !UseAbstractDisassembler */) {
+ load_library();
+ }
+ return ! _library_usable /* || UseAbstractDisassembler */; // Not available until DecodeErrorFile is supported.
}
- static void decode(CodeBlob *cb, outputStream* st = NULL);
- static void decode(nmethod* nm, outputStream* st = NULL);
- static void decode(address begin, address end, outputStream* st = NULL,
- CodeStrings c = CodeStrings(), ptrdiff_t offset = 0);
+
+ // Check out if we are doing a live disassembly or a post-mortem
+ // disassembly where the binary data was loaded from a hs_err file.
+ static bool is_decode_error_file() {
+// Activate once post-mortem disassembly (from hs-err file) is available.
+#if 0
+ return DecodeErrorFile && (strlen(DecodeErrorFile) != 0);
+#else
+ return false;
+#endif
+ }
+
+ // Directly disassemble code buffer.
+ static void decode(CodeBuffer* cb, address start, address end, outputStream* st = NULL);
+ // Directly disassemble code blob.
+ static void decode(CodeBlob *cb, outputStream* st = NULL, CodeStrings c = CodeStrings());
+ // Directly disassemble nmethod.
+ static void decode(nmethod* nm, outputStream* st = NULL, CodeStrings c = CodeStrings());
+ // Disassemble an arbitrary memory range.
+ static void decode(address start, address end, outputStream* st = NULL, CodeStrings c = CodeStrings() /* , ptrdiff_t offset */);
+
static void _hook(const char* file, int line, class MacroAssembler* masm);
// This functions makes it easy to generate comments in the generated
--- a/src/hotspot/share/compiler/oopMap.cpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/compiler/oopMap.cpp Tue May 21 15:51:35 2019 +0200
@@ -511,7 +511,7 @@
void ImmutableOopMap::print_on(outputStream* st) const {
OopMapValue omv;
- st->print("ImmutableOopMap{");
+ st->print("ImmutableOopMap {");
for(OopMapStream oms(this); !oms.is_done(); oms.next()) {
omv = oms.current();
omv.print_on(st);
@@ -523,44 +523,51 @@
void OopMap::print_on(outputStream* st) const {
OopMapValue omv;
- st->print("OopMap{");
+ st->print("OopMap {");
for(OopMapStream oms((OopMap*)this); !oms.is_done(); oms.next()) {
omv = oms.current();
omv.print_on(st);
}
- st->print("off=%d}", (int) offset());
+ // Print hex offset in addition.
+ st->print("off=%d/0x%x}", (int) offset(), (int) offset());
}
void OopMap::print() const { print_on(tty); }
void ImmutableOopMapSet::print_on(outputStream* st) const {
const ImmutableOopMap* last = NULL;
- for (int i = 0; i < _count; ++i) {
+ const int len = count();
+
+ st->print_cr("ImmutableOopMapSet contains %d OopMaps", len);
+
+ for (int i = 0; i < len; i++) {
const ImmutableOopMapPair* pair = pair_at(i);
const ImmutableOopMap* map = pair->get_from(this);
if (map != last) {
st->cr();
map->print_on(st);
- st->print("pc offsets: ");
+ st->print(" pc offsets: ");
}
last = map;
st->print("%d ", pair->pc_offset());
}
+ st->cr();
}
void ImmutableOopMapSet::print() const { print_on(tty); }
void OopMapSet::print_on(outputStream* st) const {
- int i, len = om_count();
+ const int len = om_count();
- st->print_cr("OopMapSet contains %d OopMaps\n",len);
+ st->print_cr("OopMapSet contains %d OopMaps", len);
- for( i = 0; i < len; i++) {
+ for( int i = 0; i < len; i++) {
OopMap* m = at(i);
st->print_cr("#%d ",i);
m->print_on(st);
st->cr();
}
+ st->cr();
}
void OopMapSet::print() const { print_on(tty); }
@@ -580,15 +587,17 @@
const ImmutableOopMap* ImmutableOopMapSet::find_map_at_offset(int pc_offset) const {
ImmutableOopMapPair* pairs = get_pairs();
+ ImmutableOopMapPair* last = NULL;
- int i;
- for (i = 0; i < _count; ++i) {
+ for (int i = 0; i < _count; ++i) {
if (pairs[i].pc_offset() >= pc_offset) {
+ last = &pairs[i];
break;
}
}
- ImmutableOopMapPair* last = &pairs[i];
+ // Heal Coverity issue: potential index out of bounds access.
+ guarantee(last != NULL, "last may not be null");
assert(last->pc_offset() == pc_offset, "oopmap not found");
return last->get_from(this);
}
--- a/src/hotspot/share/opto/block.cpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/opto/block.cpp Tue May 21 15:51:35 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -267,7 +267,7 @@
#ifndef PRODUCT
void Block::dump_bidx(const Block* orig, outputStream* st) const {
- if (_pre_order) st->print("B%d",_pre_order);
+ if (_pre_order) st->print("B%d", _pre_order);
else st->print("N%d", head()->_idx);
if (Verbose && orig != this) {
@@ -291,30 +291,36 @@
}
void Block::dump_head(const PhaseCFG* cfg, outputStream* st) const {
- // Print the basic block
+ // Print the basic block.
dump_bidx(this, st);
- st->print(": #\t");
+ st->print(": ");
- // Print the incoming CFG edges and the outgoing CFG edges
+ // Print the outgoing CFG edges.
+ st->print("#\tout( ");
for( uint i=0; i<_num_succs; i++ ) {
non_connector_successor(i)->dump_bidx(_succs[i], st);
st->print(" ");
}
- st->print("<- ");
+
+ // Print the incoming CFG edges.
+ st->print(") <- ");
if( head()->is_block_start() ) {
+ st->print("in( ");
for (uint i=1; i<num_preds(); i++) {
Node *s = pred(i);
if (cfg != NULL) {
Block *p = cfg->get_block_for_node(s);
p->dump_pred(cfg, p, st);
} else {
- while (!s->is_block_start())
+ while (!s->is_block_start()) {
s = s->in(0);
+ }
st->print("N%d ", s->_idx );
}
}
+ st->print(") ");
} else {
- st->print("BLOCK HEAD IS JUNK ");
+ st->print("BLOCK HEAD IS JUNK ");
}
// Print loop, if any
@@ -327,12 +333,15 @@
while (bx->is_connector()) {
bx = cfg->get_block_for_node(bx->pred(1));
}
- st->print("\tLoop: B%d-B%d ", bhead->_pre_order, bx->_pre_order);
+ st->print("Loop( B%d-B%d ", bhead->_pre_order, bx->_pre_order);
// Dump any loop-specific bits, especially for CountedLoops.
loop->dump_spec(st);
+ st->print(")");
} else if (has_loop_alignment()) {
- st->print(" top-of-loop");
+ st->print("top-of-loop");
}
+
+ // Print frequency and other optimization-relevant information
st->print(" Freq: %g",_freq);
if( Verbose || WizardMode ) {
st->print(" IDom: %d/#%d", _idom ? _idom->_pre_order : 0, _dom_depth);
--- a/src/hotspot/share/opto/compile.cpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/opto/compile.cpp Tue May 21 15:51:35 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -713,16 +713,19 @@
TraceTime t1("Total compilation time", &_t_totalCompilation, CITime, CITimeVerbose);
TraceTime t2(NULL, &_t_methodCompilation, CITime, false);
-#ifndef PRODUCT
+#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY)
bool print_opto_assembly = directive->PrintOptoAssemblyOption;
- if (!print_opto_assembly) {
- bool print_assembly = directive->PrintAssemblyOption;
- if (print_assembly && !Disassembler::can_decode()) {
- tty->print_cr("PrintAssembly request changed to PrintOptoAssembly");
- print_opto_assembly = true;
- }
- }
- set_print_assembly(print_opto_assembly);
+ // We can always print a disassembly, either abstract (hex dump) or
+ // with the help of a suitable hsdis library. Thus, we should not
+ // couple print_assembly and print_opto_assembly controls.
+ // But: always print opto and regular assembly on compile command 'print'.
+ bool print_assembly = directive->PrintAssemblyOption;
+ set_print_assembly(print_opto_assembly || print_assembly);
+#else
+ set_print_assembly(false); // must initialize.
+#endif
+
+#ifndef PRODUCT
set_parsed_irreducible_loop(false);
if (directive->ReplayInlineOption) {
@@ -1027,6 +1030,8 @@
#ifndef PRODUCT
set_print_assembly(PrintFrameConverterAssembly);
set_parsed_irreducible_loop(false);
+#else
+ set_print_assembly(false); // Must initialize.
#endif
set_has_irreducible_loop(false); // no loops
@@ -2552,12 +2557,25 @@
//------------------------------dump_asm---------------------------------------
// Dump formatted assembly
-#ifndef PRODUCT
-void Compile::dump_asm(int *pcs, uint pc_limit) {
+#if defined(SUPPORT_OPTO_ASSEMBLY)
+void Compile::dump_asm_on(outputStream* st, int* pcs, uint pc_limit) {
+
+ int pc_digits = 3; // #chars required for pc
+ int sb_chars = 3; // #chars for "start bundle" indicator
+ int tab_size = 8;
+ if (pcs != NULL) {
+ int max_pc = 0;
+ for (uint i = 0; i < pc_limit; i++) {
+ max_pc = (max_pc < pcs[i]) ? pcs[i] : max_pc;
+ }
+ pc_digits = ((max_pc < 4096) ? 3 : ((max_pc < 65536) ? 4 : ((max_pc < 65536*256) ? 6 : 8))); // #chars required for pc
+ }
+ int prefix_len = ((pc_digits + sb_chars + tab_size - 1)/tab_size)*tab_size;
+
bool cut_short = false;
- tty->print_cr("#");
- tty->print("# "); _tf->dump(); tty->cr();
- tty->print_cr("#");
+ st->print_cr("#");
+ st->print("# "); _tf->dump_on(st); st->cr();
+ st->print_cr("#");
// For all blocks
int pc = 0x0; // Program counter
@@ -2575,16 +2593,18 @@
continue;
}
n = block->head();
- if (pcs && n->_idx < pc_limit) {
- tty->print("%3.3x ", pcs[n->_idx]);
- } else {
- tty->print(" ");
+ if ((pcs != NULL) && (n->_idx < pc_limit)) {
+ pc = pcs[n->_idx];
+ st->print("%*.*x", pc_digits, pc_digits, pc);
}
- block->dump_head(_cfg);
+ st->fill_to(prefix_len);
+ block->dump_head(_cfg, st);
if (block->is_connector()) {
- tty->print_cr(" # Empty connector block");
+ st->fill_to(prefix_len);
+ st->print_cr("# Empty connector block");
} else if (block->num_preds() == 2 && block->pred(1)->is_CatchProj() && block->pred(1)->as_CatchProj()->_con == CatchProjNode::fall_through_index) {
- tty->print_cr(" # Block is sole successor of call");
+ st->fill_to(prefix_len);
+ st->print_cr("# Block is sole successor of call");
}
// For all instructions
@@ -2620,34 +2640,39 @@
!n->is_top() && // Debug info table constants
!(n->is_Con() && !n->is_Mach())// Debug info table constants
) {
- if (pcs && n->_idx < pc_limit)
- tty->print("%3.3x", pcs[n->_idx]);
- else
- tty->print(" ");
- tty->print(" %c ", starts_bundle);
+ if ((pcs != NULL) && (n->_idx < pc_limit)) {
+ pc = pcs[n->_idx];
+ st->print("%*.*x", pc_digits, pc_digits, pc);
+ } else {
+ st->fill_to(pc_digits);
+ }
+ st->print(" %c ", starts_bundle);
starts_bundle = ' ';
- tty->print("\t");
- n->format(_regalloc, tty);
- tty->cr();
+ st->fill_to(prefix_len);
+ n->format(_regalloc, st);
+ st->cr();
}
// If we have an instruction with a delay slot, and have seen a delay,
// then back up and print it
if (valid_bundle_info(n) && node_bundling(n)->use_unconditional_delay()) {
- assert(delay != NULL, "no unconditional delay instruction");
+ // Coverity finding - Explicit null dereferenced.
+ guarantee(delay != NULL, "no unconditional delay instruction");
if (WizardMode) delay->dump();
if (node_bundling(delay)->starts_bundle())
starts_bundle = '+';
- if (pcs && n->_idx < pc_limit)
- tty->print("%3.3x", pcs[n->_idx]);
- else
- tty->print(" ");
- tty->print(" %c ", starts_bundle);
+ if ((pcs != NULL) && (n->_idx < pc_limit)) {
+ pc = pcs[n->_idx];
+ st->print("%*.*x", pc_digits, pc_digits, pc);
+ } else {
+ st->fill_to(pc_digits);
+ }
+ st->print(" %c ", starts_bundle);
starts_bundle = ' ';
- tty->print("\t");
- delay->format(_regalloc, tty);
- tty->cr();
+ st->fill_to(prefix_len);
+ delay->format(_regalloc, st);
+ st->cr();
delay = NULL;
}
@@ -2656,19 +2681,13 @@
// Print the exception table for this offset
_handler_table.print_subtable_for(pc);
}
+ st->bol(); // Make sure we start on a new line
}
-
- if (pcs && n->_idx < pc_limit)
- tty->print_cr("%3.3x", pcs[n->_idx]);
- else
- tty->cr();
-
+ st->cr(); // one empty line between blocks
assert(cut_short || delay == NULL, "no unconditional delay branch");
-
} // End of per-block dump
- tty->cr();
-
- if (cut_short) tty->print_cr("*** disassembly is cut short ***");
+
+ if (cut_short) st->print_cr("*** disassembly is cut short ***");
}
#endif
--- a/src/hotspot/share/opto/compile.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/opto/compile.hpp Tue May 21 15:51:35 2019 +0200
@@ -1348,7 +1348,13 @@
static void print_statistics() PRODUCT_RETURN;
// Dump formatted assembly
- void dump_asm(int *pcs = NULL, uint pc_limit = 0) PRODUCT_RETURN;
+#if defined(SUPPORT_OPTO_ASSEMBLY)
+ void dump_asm_on(outputStream* ost, int* pcs, uint pc_limit);
+ void dump_asm(int* pcs = NULL, uint pc_limit = 0) { dump_asm_on(tty, pcs, pc_limit); }
+#else
+ void dump_asm_on(outputStream* ost, int* pcs, uint pc_limit) { return; }
+ void dump_asm(int* pcs = NULL, uint pc_limit = 0) { return; }
+#endif
void dump_pc(int *pcs, int pc_limit, Node *n);
// Verify ADLC assumptions during startup
--- a/src/hotspot/share/opto/output.cpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/opto/output.cpp Tue May 21 15:51:35 2019 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1106,12 +1106,17 @@
#endif
// Create an array of unused labels, one for each basic block, if printing is enabled
-#ifndef PRODUCT
+#if defined(SUPPORT_OPTO_ASSEMBLY)
int *node_offsets = NULL;
uint node_offset_limit = unique();
- if (print_assembly())
- node_offsets = NEW_RESOURCE_ARRAY(int, node_offset_limit);
+ if (print_assembly()) {
+ node_offsets = NEW_RESOURCE_ARRAY(int, node_offset_limit);
+ }
+ if (node_offsets != NULL) {
+ // We need to initialize. Unused array elements may contain garbage and mess up PrintOptoAssembly.
+ memset(node_offsets, 0, node_offset_limit*sizeof(int));
+ }
#endif
NonSafepointEmitter non_safepoints(this); // emit non-safepoints lazily
@@ -1381,9 +1386,10 @@
}
// Save the offset for the listing
-#ifndef PRODUCT
- if (node_offsets && n->_idx < node_offset_limit)
+#if defined(SUPPORT_OPTO_ASSEMBLY)
+ if ((node_offsets != NULL) && (n->_idx < node_offset_limit)) {
node_offsets[n->_idx] = cb->insts_size();
+ }
#endif
// "Normal" instruction case
@@ -1430,9 +1436,10 @@
cb->set_insts_end(cb->insts_end() - Pipeline::instr_unit_size());
// Save the offset for the listing
-#ifndef PRODUCT
- if (node_offsets && delay_slot->_idx < node_offset_limit)
+#if defined(SUPPORT_OPTO_ASSEMBLY)
+ if ((node_offsets != NULL) && (delay_slot->_idx < node_offset_limit)) {
node_offsets[delay_slot->_idx] = cb->insts_size();
+ }
#endif
// Support a SafePoint in the delay slot
@@ -1541,7 +1548,14 @@
return;
}
-#ifndef PRODUCT
+#if defined(SUPPORT_ABSTRACT_ASSEMBLY) || defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_OPTO_ASSEMBLY)
+ if (print_assembly()) {
+ tty->cr();
+ tty->print_cr("============================= C2-compiled nmethod ==============================");
+ }
+#endif
+
+#if defined(SUPPORT_OPTO_ASSEMBLY)
// Dump the assembly code, including basic-block numbers
if (print_assembly()) {
ttyLocker ttyl; // keep the following output all in one block
@@ -1555,11 +1569,15 @@
"");
}
if (method() != NULL) {
+ tty->print_cr("----------------------------------- MetaData -----------------------------------");
method()->print_metadata();
} else if (stub_name() != NULL) {
- tty->print_cr("Generating RuntimeStub - %s", stub_name());
+ tty->print_cr("----------------------------- RuntimeStub %s -------------------------------", stub_name());
}
+ tty->cr();
+ tty->print_cr("--------------------------------- OptoAssembly ---------------------------------");
dump_asm(node_offsets, node_offset_limit);
+ tty->print_cr("--------------------------------------------------------------------------------");
if (xtty != NULL) {
// print_metadata and dump_asm above may safepoint which makes us loose the ttylock.
// Retake lock too make sure the end tag is coherent, and that xmlStream->pop_tag is done
@@ -1570,7 +1588,6 @@
}
}
#endif
-
}
void Compile::FillExceptionTables(uint cnt, uint *call_returns, uint *inct_starts, Label *blk_labels) {
--- a/src/hotspot/share/runtime/stubCodeGenerator.cpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/runtime/stubCodeGenerator.cpp Tue May 21 15:51:35 2019 +0200
@@ -60,7 +60,7 @@
st->print("%s", group());
st->print("::");
st->print("%s", name());
- st->print(" [" INTPTR_FORMAT ", " INTPTR_FORMAT "[ (%d bytes)", p2i(begin()), p2i(end()), size_in_bytes());
+ st->print(" [" INTPTR_FORMAT ", " INTPTR_FORMAT "] (%d bytes)", p2i(begin()), p2i(end()), size_in_bytes());
}
void StubCodeDesc::print() const { print_on(tty); }
@@ -98,9 +98,12 @@
// of this stub.
offset = cdesc->begin() - outer_cbuf->insts()->start();
#endif
- cdesc->print();
+ ttyLocker ttyl;
+ tty->print_cr("- - - [BEGIN] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
+ cdesc->print_on(tty);
tty->cr();
- Disassembler::decode(cdesc->begin(), cdesc->end(), NULL, cs, offset);
+ Disassembler::decode(cdesc->begin(), cdesc->end(), tty, cs /*, offset */);
+ tty->print_cr("- - - [END] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
tty->cr();
}
}
--- a/src/hotspot/share/utilities/globalDefinitions.hpp Tue May 21 11:45:37 2019 +0200
+++ b/src/hotspot/share/utilities/globalDefinitions.hpp Tue May 21 15:51:35 2019 +0200
@@ -43,6 +43,26 @@
#define ATTRIBUTE_ALIGNED(x)
#endif
+// These are #defines to selectively turn on/off the Print(Opto)Assembly
+// capabilities. Choices should be led by a tradeoff between
+// code size and improved supportability.
+// if PRINT_ASSEMBLY then PRINT_ABSTRACT_ASSEMBLY must be true as well
+// to have a fallback in case hsdis is not available.
+#if defined(PRODUCT)
+ #define SUPPORT_ABSTRACT_ASSEMBLY
+ #define SUPPORT_ASSEMBLY
+ #undef SUPPORT_OPTO_ASSEMBLY // Can't activate. In PRODUCT, many dump methods are missing.
+ #undef SUPPORT_DATA_STRUCTS // Of limited use. In PRODUCT, many print methods are empty.
+#else
+ #define SUPPORT_ABSTRACT_ASSEMBLY
+ #define SUPPORT_ASSEMBLY
+ #define SUPPORT_OPTO_ASSEMBLY
+ #define SUPPORT_DATA_STRUCTS
+#endif
+#if defined(SUPPORT_ASSEMBLY) && !defined(SUPPORT_ABSTRACT_ASSEMBLY)
+ #define SUPPORT_ABSTRACT_ASSEMBLY
+#endif
+
// This file holds all globally used constants & types, class (forward)
// declarations and a few frequently used utility functions.