view src/share/vm/graal/graalCodeInstaller.cpp @ 9126:bc26f978b0ce

HotSpotResolvedObjectType: implement hasFinalizeSubclass() correctly don't use the (wrong) cached value, but ask the runtime on each request. Fixes regression on xml.* benchmarks @ specjvm2008. The problem was: After the constructor of Object was deoptimized due to an assumption violation, it was recompiled again after some time. However, on recompilation, the value of hasFinalizeSubclass for the class was not updated and it was compiled again with a, now wrong, assumption, which then triggers deoptimization again. This was repeated until it hit the recompilation limit (defined by PerMethodRecompilationCutoff), and therefore only executed by the interpreter from now on, causing the performance regression.
author Bernhard Urban <bernhard.urban@jku.at>
date Mon, 15 Apr 2013 19:54:58 +0200
parents 2979aaac95af
children 700f6a63763a
line wrap: on
line source

/*
 * Copyright (c) 2011, 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
 * 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 "precompiled.hpp"
#include "compiler/disassembler.hpp"
#include "runtime/javaCalls.hpp"
#include "graal/graalEnv.hpp"
#include "graal/graalCompiler.hpp"
#include "graal/graalCodeInstaller.hpp"
#include "graal/graalJavaAccess.hpp"
#include "graal/graalCompilerToVM.hpp"
#include "graal/graalRuntime.hpp"
#include "asm/register.hpp"
#include "classfile/vmSymbols.hpp"
#include "code/vmreg.hpp"

#ifdef TARGET_ARCH_x86
# include "vmreg_x86.inline.hpp"
#endif
#ifdef TARGET_ARCH_sparc
# include "vmreg_sparc.inline.hpp"
#endif
#ifdef TARGET_ARCH_zero
# include "vmreg_zero.inline.hpp"
#endif
#ifdef TARGET_ARCH_arm
# include "vmreg_arm.inline.hpp"
#endif
#ifdef TARGET_ARCH_ppc
# include "vmreg_ppc.inline.hpp"
#endif

// convert Graal register indices (as used in oop maps) to HotSpot registers
VMReg get_hotspot_reg(jint graal_reg) {
  if (graal_reg < RegisterImpl::number_of_registers) {
    return as_Register(graal_reg)->as_VMReg();
  } else {
    int remainder = graal_reg - RegisterImpl::number_of_registers;
#ifdef TARGET_ARCH_x86
    if (remainder < XMMRegisterImpl::number_of_registers) {
      return as_XMMRegister(remainder)->as_VMReg();
    }
#endif
    ShouldNotReachHere();
    return NULL;
  }
}

const int MapWordBits = 64;

static bool is_bit_set(oop bit_map, int i) {
  jint extra_idx = i / MapWordBits;
  arrayOop extra = (arrayOop) GraalBitMap::words(bit_map);
  assert(extra_idx >= 0 && extra_idx < extra->length(), "unexpected index");
  jlong word = ((jlong*) extra->base(T_LONG))[extra_idx];
  return (word & (1LL << (i % MapWordBits))) != 0;
}

static int bitmap_size(oop bit_map) {
  arrayOop arr = (arrayOop) GraalBitMap::words(bit_map);
  return arr->length() * MapWordBits;
}

// creates a HotSpot oop map out of the byte arrays provided by DebugInfo
static OopMap* create_oop_map(jint total_frame_size, jint parameter_count, oop debug_info) {
  OopMap* map = new OopMap(total_frame_size, parameter_count);
  oop register_map = (oop) DebugInfo::registerRefMap(debug_info);
  oop frame_map = (oop) DebugInfo::frameRefMap(debug_info);

  if (register_map != NULL) {
    for (jint i = 0; i < RegisterImpl::number_of_registers; i++) {
      bool is_oop = is_bit_set(register_map, i);
      VMReg reg = get_hotspot_reg(i);
      if (is_oop) {
        map->set_oop(reg);
      } else {
        map->set_value(reg);
      }
    }
  }

  for (jint i = 0; i < bitmap_size(frame_map); i++) {
    bool is_oop = is_bit_set(frame_map, i);
    // HotSpot stack slots are 4 bytes
    VMReg reg = VMRegImpl::stack2reg(i * 2);
    if (is_oop) {
      map->set_oop(reg);
    } else {
      map->set_value(reg);
    }
  }

  return map;
}

// Records any Metadata values embedded in a Constant (e.g., the value returned by HotSpotResolvedObjectType.klass()).
static void record_metadata_in_constant(oop constant, OopRecorder* oop_recorder) {
  char kind = Kind::typeChar(Constant::kind(constant));
  char wordKind = 'j';
  if (kind == wordKind) {
    oop obj = Constant::object(constant);
    jlong prim = Constant::primitive(constant);
    if (obj != NULL) {
      if (obj->is_a(HotSpotResolvedObjectType::klass())) {
        Klass* klass = (Klass*) (address) HotSpotResolvedObjectType::metaspaceKlass(obj);
        assert((Klass*) prim == klass, err_msg("%s @ %p != %p", klass->name()->as_C_string(), klass, prim));
        int index = oop_recorder->find_index(klass);
        TRACE_graal_3("metadata[%d of %d] = %s", index, oop_recorder->metadata_count(), klass->name()->as_C_string());
      } else if (obj->is_a(HotSpotResolvedJavaMethod::klass())) {
        Method* method = (Method*) (address) HotSpotResolvedJavaMethod::metaspaceMethod(obj);
        int index = oop_recorder->find_index(method);
        TRACE_graal_3("metadata[%d of %d] = %s", index, oop_recorder->metadata_count(), method->name()->as_C_string());
      } else {
        assert(java_lang_String::is_instance(obj),
            err_msg("unexpected annotation type (%s) for constant %ld (%p) of kind %c", obj->klass()->name()->as_C_string(), prim, prim, kind));
      }
    }
  }
}

static ScopeValue* get_hotspot_value(oop value, int total_frame_size, GrowableArray<ScopeValue*>* objects, ScopeValue* &second, OopRecorder* oop_recorder) {
  second = NULL;
  if (value == Value::ILLEGAL()) {
    return new LocationValue(Location::new_stk_loc(Location::invalid, 0));
  }

  BasicType type = GraalCompiler::kindToBasicType(Kind::typeChar(Value::kind(value)));
  Location::Type locationType = Location::normal;
  if (type == T_OBJECT || type == T_ARRAY) locationType = Location::oop;

  if (value->is_a(RegisterValue::klass())) {
    jint number = code_Register::number(RegisterValue::reg(value));
    if (number < 16) {
      if (type == T_INT || type == T_FLOAT || type == T_SHORT || type == T_CHAR || type == T_BOOLEAN || type == T_BYTE || type == T_ADDRESS) {
        locationType = Location::int_in_long;
      } else if (type == T_LONG) {
        locationType = Location::lng;
      } else {
        assert(type == T_OBJECT || type == T_ARRAY, "unexpected type in cpu register");
      }
      ScopeValue* value = new LocationValue(Location::new_reg_loc(locationType, as_Register(number)->as_VMReg()));
      if (type == T_LONG) {
        second = value;
      }
      return value;
    } else {
      assert(type == T_FLOAT || type == T_DOUBLE, "only float and double expected in xmm register");
      if (type == T_FLOAT) {
        // this seems weird, but the same value is used in c1_LinearScan
        locationType = Location::normal;
      } else {
        locationType = Location::dbl;
      }
#ifdef TARGET_ARCH_x86
      ScopeValue* value = new LocationValue(Location::new_reg_loc(locationType, as_XMMRegister(number - 16)->as_VMReg()));
      if (type == T_DOUBLE) {
        second = value;
      }
      return value;
#else
      ShouldNotReachHere("Platform currently does not support floating point values.");
#endif
    }
  } else if (value->is_a(StackSlot::klass())) {
    if (type == T_DOUBLE) {
      locationType = Location::dbl;
    } else if (type == T_LONG) {
      locationType = Location::lng;
    }
    jint offset = StackSlot::offset(value);
    if (StackSlot::addFrameSize(value)) {
      offset += total_frame_size;
    }
    ScopeValue* value = new LocationValue(Location::new_stk_loc(locationType, offset));
    if (type == T_DOUBLE || type == T_LONG) {
      second = value;
    }
    return value;
  } else if (value->is_a(Constant::klass())){
    record_metadata_in_constant(value, oop_recorder);
    jlong prim = Constant::primitive(value);
    if (type == T_INT || type == T_FLOAT || type == T_SHORT || type == T_CHAR || type == T_BOOLEAN || type == T_BYTE) {
      return new ConstantIntValue(*(jint*)&prim);
    } else if (type == T_LONG || type == T_DOUBLE) {
      second = new ConstantIntValue(0);
      return new ConstantLongValue(prim);
    } else if (type == T_OBJECT) {
      oop obj = Constant::object(value);
      if (obj == NULL) {
        return new ConstantOopWriteValue(NULL);
      } else {
        return new ConstantOopWriteValue(JNIHandles::make_local(obj));
      }
    } else if (type == T_ADDRESS) {
      ShouldNotReachHere();
    }
    tty->print("%i", type);
  } else if (value->is_a(VirtualObject::klass())) {
    oop type = VirtualObject::type(value);
    int id = VirtualObject::id(value);
    oop javaMirror = HotSpotResolvedObjectType::javaMirror(type);
    Klass* klass = java_lang_Class::as_Klass(javaMirror);
    bool isLongArray = klass == Universe::longArrayKlassObj();

    for (jint i = 0; i < objects->length(); i++) {
      ObjectValue* obj = (ObjectValue*) objects->at(i);
      if (obj->id() == id) {
        return obj;
      }
    }

    ObjectValue* sv = new ObjectValue(id, new ConstantOopWriteValue(JNIHandles::make_local(Thread::current(), javaMirror)));
    objects->append(sv);

    arrayOop values = (arrayOop) VirtualObject::values(value);
    for (jint i = 0; i < values->length(); i++) {
      ScopeValue* cur_second = NULL;
      ScopeValue* value = get_hotspot_value(((oop*) values->base(T_OBJECT))[i], total_frame_size, objects, cur_second, oop_recorder);

      if (isLongArray && cur_second == NULL) {
        // we're trying to put ints into a long array... this isn't really valid, but it's used for some optimizations.
        // add an int 0 constant
#ifdef VM_LITTLE_ENDIAN
        cur_second = new ConstantIntValue(0);
#else
        cur_second = value;
        value = new ConstantIntValue(0);
#endif
      }

      if (cur_second != NULL) {
        sv->field_values()->append(cur_second);
      }
      sv->field_values()->append(value);
    }
    return sv;
  } else {
    value->klass()->print();
    value->print();
  }
  ShouldNotReachHere();
  return NULL;
}

static MonitorValue* get_monitor_value(oop value, int total_frame_size, GrowableArray<ScopeValue*>* objects, OopRecorder* oop_recorder) {
  guarantee(value->is_a(HotSpotMonitorValue::klass()), "Monitors must be of type MonitorValue");

  ScopeValue* second = NULL;
  ScopeValue* owner_value = get_hotspot_value(HotSpotMonitorValue::owner(value), total_frame_size, objects, second, oop_recorder);
  assert(second == NULL, "monitor cannot occupy two stack slots");

  ScopeValue* lock_data_value = get_hotspot_value(HotSpotMonitorValue::slot(value), total_frame_size, objects, second, oop_recorder);
  assert(second == lock_data_value, "monitor is LONG value that occupies two stack slots");
  assert(lock_data_value->is_location(), "invalid monitor location");
  Location lock_data_loc = ((LocationValue*)lock_data_value)->location();

  bool eliminated = false;
  if (HotSpotMonitorValue::eliminated(value)) {
    eliminated = true;
  }

  return new MonitorValue(owner_value, lock_data_loc, eliminated);
}

void CodeInstaller::initialize_assumptions(oop target_method) {
  _oop_recorder = new OopRecorder(&_arena);
  _dependencies = new Dependencies(&_arena, _oop_recorder);
  Handle assumptions_handle = CompilationResult::assumptions(HotSpotCompilationResult::comp(target_method));
  if (!assumptions_handle.is_null()) {
    objArrayHandle assumptions(Thread::current(), (objArrayOop)Assumptions::list(assumptions_handle()));
    int length = assumptions->length();
    for (int i = 0; i < length; ++i) {
      Handle assumption = assumptions->obj_at(i);
      if (!assumption.is_null()) {
        if (assumption->klass() == Assumptions_MethodContents::klass()) {
          assumption_MethodContents(assumption);
        } else if (assumption->klass() == Assumptions_NoFinalizableSubclass::klass()) {
          assumption_NoFinalizableSubclass(assumption);
        } else if (assumption->klass() == Assumptions_ConcreteSubtype::klass()) {
          assumption_ConcreteSubtype(assumption);
        } else if (assumption->klass() == Assumptions_ConcreteMethod::klass()) {
          assumption_ConcreteMethod(assumption);
        } else if (assumption->klass() == Assumptions_CallSiteTargetValue::klass()) {
          assumption_CallSiteTargetValue(assumption);
        } else {
          assumption->print();
          fatal("unexpected Assumption subclass");
        }
      }
    }
  }
}

GrowableArray<jlong>* get_leaf_graph_ids(Handle& comp_result) {
  arrayOop leafGraphArray = (arrayOop) CompilationResult::leafGraphIds(HotSpotCompilationResult::comp(comp_result));

  jint length;
  if (leafGraphArray == NULL) {
    length = 0;
  } else {
    length = leafGraphArray->length();
  }

  GrowableArray<jlong>* result = new GrowableArray<jlong>(length);
  for (int i = 0; i < length; i++) {
    result->append(((jlong*) leafGraphArray->base(T_LONG))[i]);
  }

  return result;
}

// constructor used to create a method
CodeInstaller::CodeInstaller(Handle& comp_result, methodHandle method, GraalEnv::CodeInstallResult& result, nmethod*& nm, Handle installed_code, Handle triggered_deoptimizations) {
  GraalCompiler::initialize_buffer_blob();
  CodeBuffer buffer(JavaThread::current()->get_buffer_blob());
  jobject comp_result_obj = JNIHandles::make_local(comp_result());
  jint entry_bci = HotSpotCompilationResult::entryBCI(comp_result);
  initialize_assumptions(JNIHandles::resolve(comp_result_obj));

  {
    No_Safepoint_Verifier no_safepoint;
    initialize_fields(JNIHandles::resolve(comp_result_obj), method);
    initialize_buffer(buffer);
    process_exception_handlers();
  }

  int stack_slots = _total_frame_size / HeapWordSize; // conversion to words
  GrowableArray<jlong>* leaf_graph_ids = get_leaf_graph_ids(comp_result);

  result = GraalEnv::register_method(method, nm, entry_bci, &_offsets, _custom_stack_area_offset, &buffer, stack_slots, _debug_recorder->_oopmaps, &_exception_handler_table,
    GraalCompiler::instance(), _debug_recorder, _dependencies, NULL, -1, true, false, leaf_graph_ids, installed_code, triggered_deoptimizations);
}

// constructor used to create a stub
CodeInstaller::CodeInstaller(Handle& target_method, BufferBlob*& blob, jlong& id) {
  No_Safepoint_Verifier no_safepoint;

  _oop_recorder = new OopRecorder(&_arena);
  initialize_fields(target_method(), NULL);
  assert(_name != NULL, "installMethod needs NON-NULL name");

  // (very) conservative estimate: each site needs a relocation
  GraalCompiler::initialize_buffer_blob();
  CodeBuffer buffer(JavaThread::current()->get_buffer_blob());
  initialize_buffer(buffer);

  const char* cname = java_lang_String::as_utf8_string(_name);
  blob = BufferBlob::create(strdup(cname), &buffer); // this is leaking strings... but only a limited number of stubs will be created
  IF_TRACE_graal_3 Disassembler::decode((CodeBlob*) blob);
  id = (jlong)blob->code_begin();
}

void CodeInstaller::initialize_fields(oop comp_result, methodHandle method) {
  _comp_result = HotSpotCompilationResult::comp(comp_result);
  if (!method.is_null()) {
    _parameter_count = method->size_of_parameters();
    TRACE_graal_1("installing code for %s", method->name_and_sig_as_C_string());
  }
  _name = HotSpotCompilationResult::name(comp_result);
  _sites = (arrayOop) HotSpotCompilationResult::sites(comp_result);
  _exception_handlers = (arrayOop) HotSpotCompilationResult::exceptionHandlers(comp_result);

  _code = (arrayOop) CompilationResult::targetCode(_comp_result);
  _code_size = CompilationResult::targetCodeSize(_comp_result);
  // The frame size we get from the target method does not include the return address, so add one word for it here.
  _total_frame_size = CompilationResult::frameSize(_comp_result) + HeapWordSize;
  _custom_stack_area_offset = CompilationResult::customStackAreaOffset(_comp_result);

  // (very) conservative estimate: each site needs a constant section entry
  _constants_size = _sites->length() * (BytesPerLong*2);

  _next_call_type = MARK_INVOKE_INVALID;
}

// perform data and call relocation on the CodeBuffer
void CodeInstaller::initialize_buffer(CodeBuffer& buffer) {
  int locs_buffer_size = _sites->length() * (relocInfo::length_limit + sizeof(relocInfo));
  char* locs_buffer = NEW_RESOURCE_ARRAY(char, locs_buffer_size);
  buffer.insts()->initialize_shared_locs((relocInfo*)locs_buffer, locs_buffer_size / sizeof(relocInfo));
  buffer.initialize_stubs_size(256);
  buffer.initialize_consts_size(_constants_size);

  _debug_recorder = new DebugInformationRecorder(_oop_recorder);
  _debug_recorder->set_oopmaps(new OopMapSet());

  buffer.initialize_oop_recorder(_oop_recorder);

  _instructions = buffer.insts();
  _constants = buffer.consts();

  // copy the code into the newly created CodeBuffer
  memcpy(_instructions->start(), _code->base(T_BYTE), _code_size);
  _instructions->set_end(_instructions->start() + _code_size);

  oop* sites = (oop*) _sites->base(T_OBJECT);
  for (int i = 0; i < _sites->length(); i++) {
    oop site = sites[i];
    jint pc_offset = CompilationResult_Site::pcOffset(site);

    if (site->is_a(CompilationResult_Call::klass())) {
      TRACE_graal_4("call at %i", pc_offset);
      site_Call(buffer, pc_offset, site);
    } else if (site->is_a(CompilationResult_Infopoint::klass())) {
      // three reasons for infopoints denote actual safepoints
      oop reason = CompilationResult_Infopoint::reason(site);
      if (InfopointReason::SAFEPOINT() == reason || InfopointReason::CALL() == reason || InfopointReason::IMPLICIT_EXCEPTION() == reason) {
        TRACE_graal_4("safepoint at %i", pc_offset);
        site_Safepoint(buffer, pc_offset, site);
      } else {
        // if the infopoint is not an actual safepoint, it must have one of the other reasons
        // (safeguard against new safepoint types that require handling above)
        assert(InfopointReason::METHOD_START() == reason || InfopointReason::METHOD_END() == reason || InfopointReason::LINE_NUMBER() == reason, "");
      }
    } else if (site->is_a(CompilationResult_DataPatch::klass())) {
      TRACE_graal_4("datapatch at %i", pc_offset);
      site_DataPatch(buffer, pc_offset, site);
    } else if (site->is_a(CompilationResult_Mark::klass())) {
      TRACE_graal_4("mark at %i", pc_offset);
      site_Mark(buffer, pc_offset, site);
    } else {
      fatal("unexpected Site subclass");
    }
  }
}

void CodeInstaller::assumption_MethodContents(Handle assumption) {
  Handle method_handle = Assumptions_MethodContents::method(assumption());
  methodHandle method = getMethodFromHotSpotMethod(method_handle());
  _dependencies->assert_evol_method(method());
}

void CodeInstaller::assumption_NoFinalizableSubclass(Handle assumption) {
  Handle receiverType_handle = Assumptions_NoFinalizableSubclass::receiverType(assumption());
  Klass* receiverType = asKlass(HotSpotResolvedObjectType::metaspaceKlass(receiverType_handle));
  _dependencies->assert_has_no_finalizable_subclasses(receiverType);
}

void CodeInstaller::assumption_ConcreteSubtype(Handle assumption) {
  Handle context_handle = Assumptions_ConcreteSubtype::context(assumption());
  Handle subtype_handle = Assumptions_ConcreteSubtype::subtype(assumption());
  Klass* context = asKlass(HotSpotResolvedObjectType::metaspaceKlass(context_handle));
  Klass* subtype = asKlass(HotSpotResolvedObjectType::metaspaceKlass(subtype_handle));

  _dependencies->assert_leaf_type(subtype);
  if (context != subtype) {
    assert(context->is_abstract(), "");
    _dependencies->assert_abstract_with_unique_concrete_subtype(context, subtype);
  }
}

void CodeInstaller::assumption_ConcreteMethod(Handle assumption) {
  Handle impl_handle = Assumptions_ConcreteMethod::impl(assumption());
  Handle context_handle = Assumptions_ConcreteMethod::context(assumption());

  methodHandle impl = getMethodFromHotSpotMethod(impl_handle());
  Klass* context = asKlass(HotSpotResolvedObjectType::metaspaceKlass(context_handle));

  _dependencies->assert_unique_concrete_method(context, impl());
}

void CodeInstaller::assumption_CallSiteTargetValue(Handle assumption) {
  Handle callSite = Assumptions_CallSiteTargetValue::callSite(assumption());
  Handle methodHandle = Assumptions_CallSiteTargetValue::methodHandle(assumption());

  _dependencies->assert_call_site_target_value(callSite(), methodHandle());
}

void CodeInstaller::process_exception_handlers() {
  // allocate some arrays for use by the collection code.
  const int num_handlers = 5;
  GrowableArray<intptr_t>* bcis = new GrowableArray<intptr_t> (num_handlers);
  GrowableArray<intptr_t>* scope_depths = new GrowableArray<intptr_t> (num_handlers);
  GrowableArray<intptr_t>* pcos = new GrowableArray<intptr_t> (num_handlers);

  if (_exception_handlers != NULL) {
    oop* exception_handlers = (oop*) _exception_handlers->base(T_OBJECT);
    for (int i = 0; i < _exception_handlers->length(); i++) {
      oop exc = exception_handlers[i];
      jint pc_offset = CompilationResult_Site::pcOffset(exc);
      jint handler_offset = CompilationResult_ExceptionHandler::handlerPos(exc);

      // Subtable header
      _exception_handler_table.add_entry(HandlerTableEntry(1, pc_offset, 0));

      // Subtable entry
      _exception_handler_table.add_entry(HandlerTableEntry(-1, handler_offset, 0));
    }
  }
}

// If deoptimization happens, the interpreter should reexecute these bytecodes.
// This function mainly helps the compilers to set up the reexecute bit.
static bool bytecode_should_reexecute(Bytecodes::Code code) {
  switch (code) {
    case Bytecodes::_invokedynamic:
    case Bytecodes::_invokevirtual:
    case Bytecodes::_invokeinterface:
    case Bytecodes::_invokespecial:
    case Bytecodes::_invokestatic:
      return false;
    default:
      return true;
    }
  return true;
}

void CodeInstaller::record_scope(jint pc_offset, oop frame, GrowableArray<ScopeValue*>* objects) {
  assert(frame->klass() == BytecodeFrame::klass(), "BytecodeFrame expected");
  oop caller_frame = BytecodePosition::caller(frame);
  if (caller_frame != NULL) {
    record_scope(pc_offset, caller_frame, objects);
  }

  oop hotspot_method = BytecodePosition::method(frame);
  Method* method = getMethodFromHotSpotMethod(hotspot_method);
  jint bci = BytecodePosition::bci(frame);
  bool reexecute;
  if (bci == -1 || bci == -2){
     reexecute = false;
  } else {
    Bytecodes::Code code = Bytecodes::java_code_at(method, method->bcp_from(bci));
    reexecute = bytecode_should_reexecute(code);
    if (frame != NULL) {
      reexecute = (BytecodeFrame::duringCall(frame) == JNI_FALSE);
    }
  }

  if (TraceGraal >= 2) {
    tty->print_cr("Recording scope pc_offset=%d bci=%d frame=%d", pc_offset, bci, frame);
  }

  jint local_count = BytecodeFrame::numLocals(frame);
  jint expression_count = BytecodeFrame::numStack(frame);
  jint monitor_count = BytecodeFrame::numLocks(frame);
  arrayOop values = (arrayOop) BytecodeFrame::values(frame);

  assert(local_count + expression_count + monitor_count == values->length(), "unexpected values length");

  GrowableArray<ScopeValue*>* locals = new GrowableArray<ScopeValue*> ();
  GrowableArray<ScopeValue*>* expressions = new GrowableArray<ScopeValue*> ();
  GrowableArray<MonitorValue*>* monitors = new GrowableArray<MonitorValue*> ();

  if (TraceGraal >= 2) {
    tty->print_cr("Scope at bci %d with %d values", bci, values->length());
    tty->print_cr("%d locals %d expressions, %d monitors", local_count, expression_count, monitor_count);
  }

  for (jint i = 0; i < values->length(); i++) {
    ScopeValue* second = NULL;
    oop value = ((oop*) values->base(T_OBJECT))[i];

    if (i < local_count) {
      ScopeValue* first = get_hotspot_value(value, _total_frame_size, objects, second, _oop_recorder);
      if (second != NULL) {
        locals->append(second);
      }
      locals->append(first);
    } else if (i < local_count + expression_count) {
      ScopeValue* first = get_hotspot_value(value, _total_frame_size, objects, second, _oop_recorder);
      if (second != NULL) {
        expressions->append(second);
      }
      expressions->append(first);
    } else {
      monitors->append(get_monitor_value(value, _total_frame_size, objects, _oop_recorder));
    }
    if (second != NULL) {
      i++;
      assert(i < values->length(), "double-slot value not followed by Value.ILLEGAL");
      assert(((oop*) values->base(T_OBJECT))[i] == Value::ILLEGAL(), "double-slot value not followed by Value.ILLEGAL");
    }
  }

  _debug_recorder->dump_object_pool(objects);

  DebugToken* locals_token = _debug_recorder->create_scope_values(locals);
  DebugToken* expressions_token = _debug_recorder->create_scope_values(expressions);
  DebugToken* monitors_token = _debug_recorder->create_monitor_values(monitors);

  bool throw_exception = BytecodeFrame::rethrowException(frame) == JNI_TRUE;

  _debug_recorder->describe_scope(pc_offset, method, NULL, bci, reexecute, throw_exception, false, false, locals_token, expressions_token, monitors_token);
}

void CodeInstaller::site_Safepoint(CodeBuffer& buffer, jint pc_offset, oop site) {
  oop debug_info = CompilationResult_Infopoint::debugInfo(site);
  assert(debug_info != NULL, "debug info expected");

  // address instruction = _instructions->start() + pc_offset;
  // jint next_pc_offset = Assembler::locate_next_instruction(instruction) - _instructions->start();
  _debug_recorder->add_safepoint(pc_offset, create_oop_map(_total_frame_size, _parameter_count, debug_info));

  oop frame = DebugInfo::bytecodePosition(debug_info);
  if (frame != NULL) {
    record_scope(pc_offset, frame, new GrowableArray<ScopeValue*>());
  } else {
    // Stubs do not record scope info, just oop maps
  }

  _debug_recorder->end_safepoint(pc_offset);
}

void CodeInstaller::site_Call(CodeBuffer& buffer, jint pc_offset, oop site) {
  oop target = CompilationResult_Call::target(site);
  InstanceKlass* target_klass = InstanceKlass::cast(target->klass());

  oop hotspot_method = NULL; // JavaMethod
  oop global_stub = NULL;

  if (target_klass->is_subclass_of(SystemDictionary::HotSpotRuntimeCallTarget_klass())) {
    global_stub = target;
  } else {
    hotspot_method = target;
  }

  oop debug_info = CompilationResult_Call::debugInfo(site);

  assert((hotspot_method ? 1 : 0) + (global_stub ? 1 : 0) == 1, "Call site needs exactly one type");

  NativeInstruction* inst = nativeInstruction_at(_instructions->start() + pc_offset);
  jint next_pc_offset = 0x0;
  if (inst->is_call() || inst->is_jump()) {
    assert(NativeCall::instruction_size == (int)NativeJump::instruction_size, "unexpected size");
    next_pc_offset = pc_offset + NativeCall::instruction_size;
  } else if (inst->is_mov_literal64()) {
    // mov+call instruction pair
    next_pc_offset = pc_offset + NativeMovConstReg::instruction_size;
    u_char* call = (u_char*) (_instructions->start() + next_pc_offset);
    assert((call[0] == 0x40 || call[0] == 0x41) && call[1] == 0xFF, "expected call with rex/rexb prefix byte");
    next_pc_offset += 3; /* prefix byte + opcode byte + modrm byte */
  } else if (inst->is_call_reg()) {
    // the inlined vtable stub contains a "call register" instruction
    assert(hotspot_method != NULL, "only valid for virtual calls");
    next_pc_offset = pc_offset + ((NativeCallReg *) inst)->next_instruction_offset();
  } else {
    fatal("unsupported type of instruction for call site");
  }

  if (target->is_a(SystemDictionary::HotSpotInstalledCode_klass())) {
    assert(inst->is_jump(), "jump expected");

    nmethod* nm = (nmethod*) HotSpotInstalledCode::nmethod(target);
    nativeJump_at((address)inst)->set_jump_destination(nm->verified_entry_point());
    _instructions->relocate((address)inst, runtime_call_Relocation::spec(), Assembler::call32_operand);

    return;
  }

  if (debug_info != NULL) {
    oop frame = DebugInfo::bytecodePosition(debug_info);
    _debug_recorder->add_safepoint(next_pc_offset, create_oop_map(_total_frame_size, _parameter_count, debug_info));
    if (frame != NULL) {
      record_scope(next_pc_offset, frame, new GrowableArray<ScopeValue*>());
    } else {
      // Stubs do not record scope info, just oop maps
    }
  }

  if (global_stub != NULL) {
    jlong global_stub_destination = HotSpotRuntimeCallTarget::address(global_stub);
    if (inst->is_call()) {
      // NOTE: for call without a mov, the offset must fit a 32-bit immediate
      //       see also CompilerToVM.getMaxCallTargetOffset()
      NativeCall* call = nativeCall_at((address) (inst));
      call->set_destination((address) global_stub_destination);
      _instructions->relocate(call->instruction_address(), runtime_call_Relocation::spec(), Assembler::call32_operand);
    } else if (inst->is_mov_literal64()) {
      NativeMovConstReg* mov = nativeMovConstReg_at((address) (inst));
      mov->set_data((intptr_t) global_stub_destination);
      _instructions->relocate(mov->instruction_address(), runtime_call_Relocation::spec(), Assembler::imm_operand);
    } else {
      NativeJump* jump = nativeJump_at((address) (inst));
      jump->set_jump_destination((address) global_stub_destination);
      _instructions->relocate((address)inst, runtime_call_Relocation::spec(), Assembler::call32_operand);
    }
    TRACE_graal_3("relocating (stub)  at %p", inst);
  } else { // method != NULL
    assert(hotspot_method != NULL, "unexpected JavaMethod");
#ifdef ASSERT
    Method* method = NULL;
    // we need to check, this might also be an unresolved method
    if (hotspot_method->is_a(HotSpotResolvedJavaMethod::klass())) {
      method = getMethodFromHotSpotMethod(hotspot_method);
    }
#endif
    assert(debug_info != NULL, "debug info expected");

    TRACE_graal_3("method call");
    switch (_next_call_type) {
      case MARK_INLINE_INVOKE:
        break;
      case MARK_INVOKEVIRTUAL:
      case MARK_INVOKEINTERFACE: {
        assert(method == NULL || !method->is_static(), "cannot call static method with invokeinterface");

        NativeCall* call = nativeCall_at(_instructions->start() + pc_offset);
        call->set_destination(SharedRuntime::get_resolve_virtual_call_stub());
        _instructions->relocate(call->instruction_address(), virtual_call_Relocation::spec(_invoke_mark_pc), Assembler::call32_operand);
        break;
      }
      case MARK_INVOKESTATIC: {
        assert(method == NULL || method->is_static(), "cannot call non-static method with invokestatic");

        NativeCall* call = nativeCall_at(_instructions->start() + pc_offset);
        call->set_destination(SharedRuntime::get_resolve_static_call_stub());
        _instructions->relocate(call->instruction_address(), relocInfo::static_call_type, Assembler::call32_operand);
        break;
      }
      case MARK_INVOKESPECIAL: {
        assert(method == NULL || !method->is_static(), "cannot call static method with invokespecial");
        NativeCall* call = nativeCall_at(_instructions->start() + pc_offset);
        call->set_destination(SharedRuntime::get_resolve_opt_virtual_call_stub());
        _instructions->relocate(call->instruction_address(), relocInfo::opt_virtual_call_type, Assembler::call32_operand);
        break;
      }
      default:
        fatal("invalid _next_call_type value");
        break;
    }
  }
  _next_call_type = MARK_INVOKE_INVALID;
  if (debug_info != NULL) {
    _debug_recorder->end_safepoint(next_pc_offset);
  }
}

void CodeInstaller::site_DataPatch(CodeBuffer& buffer, jint pc_offset, oop site) {
  oop constant = CompilationResult_DataPatch::constant(site);
  int alignment = CompilationResult_DataPatch::alignment(site);
  bool inlined = CompilationResult_DataPatch::inlined(site) == JNI_TRUE;
  oop kind = Constant::kind(constant);

  address instruction = _instructions->start() + pc_offset;

  char typeChar = Kind::typeChar(kind);
  switch (typeChar) {
    case 'z':
    case 'b':
    case 's':
    case 'c':
    case 'i':
      fatal("int-sized values not expected in DataPatch");
      break;
    case 'f':
    case 'j':
    case 'd': {
      record_metadata_in_constant(constant, _oop_recorder);
      if (inlined) {
        address operand = Assembler::locate_operand(instruction, Assembler::imm_operand);
        *((jlong*) operand) = Constant::primitive(constant);
      } else {
        address operand = Assembler::locate_operand(instruction, Assembler::disp32_operand);
        address next_instruction = Assembler::locate_next_instruction(instruction);
        int size = _constants->size();
        if (alignment > 0) {
          guarantee(alignment <= _constants->alignment(), "Alignment inside constants section is restricted by alignment of section begin");
          size = align_size_up(size, alignment);
        }
        // we don't care if this is a long/double/etc., the primitive field contains the right bits
        address dest = _constants->start() + size;
        _constants->set_end(dest + BytesPerLong);
        *(jlong*) dest = Constant::primitive(constant);

        long disp = dest - next_instruction;
        assert(disp == (jint) disp, "disp doesn't fit in 32 bits");
        *((jint*) operand) = (jint) disp;

        _instructions->relocate(instruction, section_word_Relocation::spec((address) dest, CodeBuffer::SECT_CONSTS), Assembler::disp32_operand);
        TRACE_graal_3("relocating (%c) at %p/%p with destination at %p (%d)", typeChar, instruction, operand, dest, size);
      }
      break;
    }
    case 'a': {
      address operand = Assembler::locate_operand(instruction, Assembler::imm_operand);
      Handle obj = Constant::object(constant);

      jobject value = JNIHandles::make_local(obj());
      *((jobject*) operand) = value;
      _instructions->relocate(instruction, oop_Relocation::spec_for_immediate(), Assembler::imm_operand);
      TRACE_graal_3("relocating (oop constant) at %p/%p", instruction, operand);
      break;
    }
    default:
      fatal(err_msg("unexpected Kind (%d) in DataPatch", typeChar));
      break;
  }
}

void CodeInstaller::site_Mark(CodeBuffer& buffer, jint pc_offset, oop site) {
  oop id_obj = CompilationResult_Mark::id(site);
  arrayOop references = (arrayOop) CompilationResult_Mark::references(site);

  if (id_obj != NULL) {
    assert(java_lang_boxing_object::is_instance(id_obj, T_INT), "Integer id expected");
    jint id = id_obj->int_field(java_lang_boxing_object::value_offset_in_bytes(T_INT));

    address instruction = _instructions->start() + pc_offset;

    switch (id) {
      case MARK_UNVERIFIED_ENTRY:
        _offsets.set_value(CodeOffsets::Entry, pc_offset);
        break;
      case MARK_VERIFIED_ENTRY:
        _offsets.set_value(CodeOffsets::Verified_Entry, pc_offset);
        break;
      case MARK_OSR_ENTRY:
        _offsets.set_value(CodeOffsets::OSR_Entry, pc_offset);
        break;
      case MARK_EXCEPTION_HANDLER_ENTRY:
        _offsets.set_value(CodeOffsets::Exceptions, pc_offset);
        break;
      case MARK_DEOPT_HANDLER_ENTRY:
        _offsets.set_value(CodeOffsets::Deopt, pc_offset);
        break;
      case MARK_INVOKEVIRTUAL:
      case MARK_INVOKEINTERFACE: {
        // Convert the initial value of the Klass* slot in an inline cache
        // from 0L to Universe::non_oop_word().
        NativeMovConstReg* n_copy = nativeMovConstReg_at(instruction);
        assert(n_copy->data() == 0, "inline cache Klass* initial value should be 0L");
        n_copy->set_data((intptr_t)Universe::non_oop_word());
      }
      case MARK_INLINE_INVOKE:
      case MARK_INVOKESTATIC:
      case MARK_INVOKESPECIAL:
        _next_call_type = (MarkId) id;
        _invoke_mark_pc = instruction;
        break;
      case MARK_POLL_NEAR: {
        NativeInstruction* ni = nativeInstruction_at(instruction);
        int32_t* disp = (int32_t*) Assembler::locate_operand(instruction, Assembler::disp32_operand);
        int32_t offset = *disp; // The Java code installed the polling page offset into the disp32 operand
        intptr_t new_disp = (intptr_t) (os::get_polling_page() + offset) - (intptr_t) ni;
        *disp = (int32_t)new_disp;
      }
      case MARK_POLL_FAR:
        _instructions->relocate(instruction, relocInfo::poll_type);
        break;
      case MARK_POLL_RETURN_NEAR: {
        NativeInstruction* ni = nativeInstruction_at(instruction);
        int32_t* disp = (int32_t*) Assembler::locate_operand(instruction, Assembler::disp32_operand);
        int32_t offset = *disp; // The Java code installed the polling page offset into the disp32 operand
        intptr_t new_disp = (intptr_t) (os::get_polling_page() + offset) - (intptr_t) ni;
        *disp = (int32_t)new_disp;
      }
      case MARK_POLL_RETURN_FAR:
        _instructions->relocate(instruction, relocInfo::poll_return_type);
        break;
      default:
        ShouldNotReachHere();
        break;
    }
  }
}