src/hotspot/cpu/s390/disassembler_s390.cpp
changeset 54960 e46fe26d7f77
child 58959 b7b170ba3ba9
--- /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);
+  }
+}