--- /dev/null 2010-06-22 09:23:59.198002222 +0100 +++ new/hotspot/src/share/vm/shark/sharkInliner.cpp 2010-06-22 09:53:13.000000000 +0100 @@ -0,0 +1,749 @@ +/* + * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright 2009 Red Hat, Inc. + * 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 "incls/_precompiled.incl" +#include "incls/_sharkInliner.cpp.incl" + +using namespace llvm; + +class SharkInlineBlock : public SharkBlock { + public: + SharkInlineBlock(ciMethod* target, SharkState* state) + : SharkBlock(state, target), + _outer_state(state), + _entry_state(new SharkState(this)) { + for (int i = target->max_locals() - 1; i >= 0; i--) { + SharkValue *value = NULL; + if (i < target->arg_size()) + value = outer_state()->pop(); + entry_state()->set_local(i, value); + } + } + + private: + SharkState* _outer_state; + SharkState* _entry_state; + + private: + SharkState* outer_state() { + return _outer_state; + } + SharkState* entry_state() { + return _entry_state; + } + + public: + void emit_IR() { + parse_bytecode(0, target()->code_size()); + } + + private: + void do_return(BasicType type) { + if (type != T_VOID) { + SharkValue *result = pop_result(type); + outer_state()->push(result); + if (result->is_two_word()) + outer_state()->push(NULL); + } + } +}; + +class SharkInlinerHelper : public StackObj { + public: + SharkInlinerHelper(ciMethod* target, SharkState* entry_state) + : _target(target), + _entry_state(entry_state), + _iter(target) {} + + private: + ciBytecodeStream _iter; + SharkState* _entry_state; + ciMethod* _target; + + public: + ciBytecodeStream* iter() { + return &_iter; + } + SharkState* entry_state() const { + return _entry_state; + } + ciMethod* target() const { + return _target; + } + + public: + Bytecodes::Code bc() { + return iter()->cur_bc(); + } + int max_locals() const { + return target()->max_locals(); + } + int max_stack() const { + return target()->max_stack(); + } + + // Inlinability check + public: + bool is_inlinable(); + + private: + void initialize_for_check(); + + bool do_getstatic() { + return do_field_access(true, false); + } + bool do_getfield() { + return do_field_access(true, true); + } + bool do_putfield() { + return do_field_access(false, true); + } + bool do_field_access(bool is_get, bool is_field); + + // Local variables for inlinability check + private: + bool* _locals; + + public: + bool* local_addr(int index) const { + assert(index >= 0 && index < max_locals(), "bad local variable index"); + return &_locals[index]; + } + bool local(int index) const { + return *local_addr(index); + } + void set_local(int index, bool value) { + *local_addr(index) = value; + } + + // Expression stack for inlinability check + private: + bool* _stack; + bool* _sp; + + public: + int stack_depth() const { + return _sp - _stack; + } + bool* stack_addr(int slot) const { + assert(slot >= 0 && slot < stack_depth(), "bad stack slot"); + return &_sp[-(slot + 1)]; + } + void push(bool value) { + assert(stack_depth() < max_stack(), "stack overrun"); + *(_sp++) = value; + } + bool pop() { + assert(stack_depth() > 0, "stack underrun"); + return *(--_sp); + } + + // Methods for two-word locals + public: + void push_pair_local(int index) { + push(local(index)); + push(local(index + 1)); + } + void pop_pair_local(int index) { + set_local(index + 1, pop()); + set_local(index, pop()); + } + + // Code generation + public: + void do_inline() { + (new SharkInlineBlock(target(), entry_state()))->emit_IR(); + } +}; + +// Quick checks so we can bail out before doing too much +bool SharkInliner::may_be_inlinable(ciMethod *target) { + // We can't inline native methods + if (target->is_native()) + return false; + + // Not much point inlining abstract ones, and in any + // case we'd need a stack frame to throw the exception + if (target->is_abstract()) + return false; + + // Don't inline anything huge + if (target->code_size() > SharkMaxInlineSize) + return false; + + // Monitors aren't allowed without a frame to put them in + if (target->is_synchronized() || target->has_monitor_bytecodes()) + return false; + + // We don't do control flow + if (target->has_exception_handlers() || target->has_jsrs()) + return false; + + // Don't try to inline constructors, as they must + // eventually call Object. which we can't inline. + // Note that this catches too, but why would + // we be compiling that? + if (target->is_initializer()) + return false; + + // Mustn't inline Object. + // Should be caught by the above, but just in case... + if (target->intrinsic_id() == vmIntrinsics::_Object_init) + return false; + + return true; +} + +// Full-on detailed check, for methods that pass the quick checks +// Inlined methods have no stack frame, so we can't do anything +// that would require one. This means no safepoints (and hence +// no loops) and no VM calls. No VM calls means, amongst other +// things, that no exceptions can be created, which means no null +// checks or divide-by-zero checks are allowed. The lack of null +// checks in particular would eliminate practically everything, +// but we can get around that restriction by relying on the zero- +// check eliminator to strip the checks. To do that, we need to +// walk through the method, tracking which values are and are not +// zero-checked. +bool SharkInlinerHelper::is_inlinable() { + ResourceMark rm; + initialize_for_check(); + + SharkConstant *sc; + bool a, b, c, d; + + iter()->reset_to_bci(0); + while (iter()->next() != ciBytecodeStream::EOBC()) { + switch (bc()) { + case Bytecodes::_nop: + break; + + case Bytecodes::_aconst_null: + push(false); + break; + + case Bytecodes::_iconst_0: + push(false); + break; + case Bytecodes::_iconst_m1: + case Bytecodes::_iconst_1: + case Bytecodes::_iconst_2: + case Bytecodes::_iconst_3: + case Bytecodes::_iconst_4: + case Bytecodes::_iconst_5: + push(true); + break; + + case Bytecodes::_lconst_0: + push(false); + push(false); + break; + case Bytecodes::_lconst_1: + push(true); + push(false); + break; + + case Bytecodes::_fconst_0: + case Bytecodes::_fconst_1: + case Bytecodes::_fconst_2: + push(false); + break; + + case Bytecodes::_dconst_0: + case Bytecodes::_dconst_1: + push(false); + push(false); + break; + + case Bytecodes::_bipush: + push(iter()->get_constant_u1() != 0); + break; + case Bytecodes::_sipush: + push(iter()->get_constant_u2() != 0); + break; + + case Bytecodes::_ldc: + case Bytecodes::_ldc_w: + case Bytecodes::_ldc2_w: + sc = SharkConstant::for_ldc(iter()); + if (!sc->is_loaded()) + return false; + push(sc->is_nonzero()); + if (sc->is_two_word()) + push(false); + break; + + case Bytecodes::_iload_0: + case Bytecodes::_fload_0: + case Bytecodes::_aload_0: + push(local(0)); + break; + case Bytecodes::_lload_0: + case Bytecodes::_dload_0: + push_pair_local(0); + break; + + case Bytecodes::_iload_1: + case Bytecodes::_fload_1: + case Bytecodes::_aload_1: + push(local(1)); + break; + case Bytecodes::_lload_1: + case Bytecodes::_dload_1: + push_pair_local(1); + break; + + case Bytecodes::_iload_2: + case Bytecodes::_fload_2: + case Bytecodes::_aload_2: + push(local(2)); + break; + case Bytecodes::_lload_2: + case Bytecodes::_dload_2: + push_pair_local(2); + break; + + case Bytecodes::_iload_3: + case Bytecodes::_fload_3: + case Bytecodes::_aload_3: + push(local(3)); + break; + case Bytecodes::_lload_3: + case Bytecodes::_dload_3: + push_pair_local(3); + break; + + case Bytecodes::_iload: + case Bytecodes::_fload: + case Bytecodes::_aload: + push(local(iter()->get_index())); + break; + case Bytecodes::_lload: + case Bytecodes::_dload: + push_pair_local(iter()->get_index()); + break; + + case Bytecodes::_istore_0: + case Bytecodes::_fstore_0: + case Bytecodes::_astore_0: + set_local(0, pop()); + break; + case Bytecodes::_lstore_0: + case Bytecodes::_dstore_0: + pop_pair_local(0); + break; + + case Bytecodes::_istore_1: + case Bytecodes::_fstore_1: + case Bytecodes::_astore_1: + set_local(1, pop()); + break; + case Bytecodes::_lstore_1: + case Bytecodes::_dstore_1: + pop_pair_local(1); + break; + + case Bytecodes::_istore_2: + case Bytecodes::_fstore_2: + case Bytecodes::_astore_2: + set_local(2, pop()); + break; + case Bytecodes::_lstore_2: + case Bytecodes::_dstore_2: + pop_pair_local(2); + break; + + case Bytecodes::_istore_3: + case Bytecodes::_fstore_3: + case Bytecodes::_astore_3: + set_local(3, pop()); + break; + case Bytecodes::_lstore_3: + case Bytecodes::_dstore_3: + pop_pair_local(3); + break; + + case Bytecodes::_istore: + case Bytecodes::_fstore: + case Bytecodes::_astore: + set_local(iter()->get_index(), pop()); + break; + case Bytecodes::_lstore: + case Bytecodes::_dstore: + pop_pair_local(iter()->get_index()); + break; + + case Bytecodes::_pop: + pop(); + break; + case Bytecodes::_pop2: + pop(); + pop(); + break; + case Bytecodes::_swap: + a = pop(); + b = pop(); + push(a); + push(b); + break; + case Bytecodes::_dup: + a = pop(); + push(a); + push(a); + break; + case Bytecodes::_dup_x1: + a = pop(); + b = pop(); + push(a); + push(b); + push(a); + break; + case Bytecodes::_dup_x2: + a = pop(); + b = pop(); + c = pop(); + push(a); + push(c); + push(b); + push(a); + break; + case Bytecodes::_dup2: + a = pop(); + b = pop(); + push(b); + push(a); + push(b); + push(a); + break; + case Bytecodes::_dup2_x1: + a = pop(); + b = pop(); + c = pop(); + push(b); + push(a); + push(c); + push(b); + push(a); + break; + case Bytecodes::_dup2_x2: + a = pop(); + b = pop(); + c = pop(); + d = pop(); + push(b); + push(a); + push(d); + push(c); + push(b); + push(a); + break; + + case Bytecodes::_getfield: + if (!do_getfield()) + return false; + break; + case Bytecodes::_getstatic: + if (!do_getstatic()) + return false; + break; + case Bytecodes::_putfield: + if (!do_putfield()) + return false; + break; + + case Bytecodes::_iadd: + case Bytecodes::_isub: + case Bytecodes::_imul: + case Bytecodes::_iand: + case Bytecodes::_ixor: + case Bytecodes::_ishl: + case Bytecodes::_ishr: + case Bytecodes::_iushr: + pop(); + pop(); + push(false); + break; + case Bytecodes::_ior: + a = pop(); + b = pop(); + push(a && b); + break; + case Bytecodes::_idiv: + case Bytecodes::_irem: + if (!pop()) + return false; + pop(); + push(false); + break; + case Bytecodes::_ineg: + break; + + case Bytecodes::_ladd: + case Bytecodes::_lsub: + case Bytecodes::_lmul: + case Bytecodes::_land: + case Bytecodes::_lxor: + pop(); + pop(); + pop(); + pop(); + push(false); + push(false); + break; + case Bytecodes::_lor: + a = pop(); + b = pop(); + push(a && b); + break; + case Bytecodes::_ldiv: + case Bytecodes::_lrem: + pop(); + if (!pop()) + return false; + pop(); + pop(); + push(false); + push(false); + break; + case Bytecodes::_lneg: + break; + case Bytecodes::_lshl: + case Bytecodes::_lshr: + case Bytecodes::_lushr: + pop(); + pop(); + pop(); + push(false); + push(false); + break; + + case Bytecodes::_fadd: + case Bytecodes::_fsub: + case Bytecodes::_fmul: + case Bytecodes::_fdiv: + case Bytecodes::_frem: + pop(); + pop(); + push(false); + break; + case Bytecodes::_fneg: + break; + + case Bytecodes::_dadd: + case Bytecodes::_dsub: + case Bytecodes::_dmul: + case Bytecodes::_ddiv: + case Bytecodes::_drem: + pop(); + pop(); + pop(); + pop(); + push(false); + push(false); + break; + case Bytecodes::_dneg: + break; + + case Bytecodes::_iinc: + set_local(iter()->get_index(), false); + break; + + case Bytecodes::_lcmp: + pop(); + pop(); + pop(); + pop(); + push(false); + break; + + case Bytecodes::_fcmpl: + case Bytecodes::_fcmpg: + pop(); + pop(); + push(false); + break; + + case Bytecodes::_dcmpl: + case Bytecodes::_dcmpg: + pop(); + pop(); + pop(); + pop(); + push(false); + break; + + case Bytecodes::_i2l: + push(false); + break; + case Bytecodes::_i2f: + pop(); + push(false); + break; + case Bytecodes::_i2d: + pop(); + push(false); + push(false); + break; + + case Bytecodes::_l2i: + case Bytecodes::_l2f: + pop(); + pop(); + push(false); + break; + case Bytecodes::_l2d: + pop(); + pop(); + push(false); + push(false); + break; + + case Bytecodes::_f2i: + pop(); + push(false); + break; + case Bytecodes::_f2l: + case Bytecodes::_f2d: + pop(); + push(false); + push(false); + break; + + case Bytecodes::_d2i: + case Bytecodes::_d2f: + pop(); + pop(); + push(false); + break; + case Bytecodes::_d2l: + pop(); + pop(); + push(false); + push(false); + break; + + case Bytecodes::_i2b: + case Bytecodes::_i2c: + case Bytecodes::_i2s: + pop(); + push(false); + break; + + case Bytecodes::_return: + case Bytecodes::_ireturn: + case Bytecodes::_lreturn: + case Bytecodes::_freturn: + case Bytecodes::_dreturn: + case Bytecodes::_areturn: + break; + + default: + return false; + } + } + + return true; +} + +void SharkInlinerHelper::initialize_for_check() { + _locals = NEW_RESOURCE_ARRAY(bool, max_locals()); + _stack = NEW_RESOURCE_ARRAY(bool, max_stack()); + + memset(_locals, 0, max_locals() * sizeof(bool)); + for (int i = 0; i < target()->arg_size(); i++) { + SharkValue *arg = entry_state()->stack(target()->arg_size() - 1 - i); + if (arg && arg->zero_checked()) + set_local(i, true); + } + + _sp = _stack; +} + +bool SharkInlinerHelper::do_field_access(bool is_get, bool is_field) { + assert(is_get || is_field, "can't inline putstatic"); + + // If the holder isn't linked then there isn't a lot we can do + if (!target()->holder()->is_linked()) + return false; + + // Get the field + bool will_link; + ciField *field = iter()->get_field(will_link); + if (!will_link) + return false; + + // If the field is mismatched then an exception needs throwing + if (is_field == field->is_static()) + return false; + + // Pop the value off the stack if necessary + if (!is_get) { + pop(); + if (field->type()->is_two_word()) + pop(); + } + + // Pop and null-check the receiver if necessary + if (is_field) { + if (!pop()) + return false; + } + + // Push the result if necessary + if (is_get) { + bool result_pushed = false; + if (field->is_constant()) { + SharkConstant *sc = SharkConstant::for_field(iter()); + if (sc->is_loaded()) { + push(sc->is_nonzero()); + result_pushed = true; + } + } + + if (!result_pushed) + push(false); + + if (field->type()->is_two_word()) + push(false); + } + + return true; +} + +bool SharkInliner::attempt_inline(ciMethod *target, SharkState *state) { + if (SharkIntrinsics::is_intrinsic(target)) { + SharkIntrinsics::inline_intrinsic(target, state); + return true; + } + + if (may_be_inlinable(target)) { + SharkInlinerHelper inliner(target, state); + if (inliner.is_inlinable()) { + inliner.do_inline(); + return true; + } + } + return false; +}