Mon, 04 Jan 2010 15:52:40 +0100
6894206: JVM needs a way to traverse method handle structures
Summary: We need a way to walk chained method handles in the JVM to call the right methods and to generate required bytecode adapters for the compilers.
Reviewed-by: kvn
twisti@1568 | 1 | /* |
twisti@1568 | 2 | * Copyright 2008-2010 Sun Microsystems, Inc. All Rights Reserved. |
twisti@1568 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
twisti@1568 | 4 | * |
twisti@1568 | 5 | * This code is free software; you can redistribute it and/or modify it |
twisti@1568 | 6 | * under the terms of the GNU General Public License version 2 only, as |
twisti@1568 | 7 | * published by the Free Software Foundation. |
twisti@1568 | 8 | * |
twisti@1568 | 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
twisti@1568 | 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
twisti@1568 | 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
twisti@1568 | 12 | * version 2 for more details (a copy is included in the LICENSE file that |
twisti@1568 | 13 | * accompanied this code). |
twisti@1568 | 14 | * |
twisti@1568 | 15 | * You should have received a copy of the GNU General Public License version |
twisti@1568 | 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
twisti@1568 | 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
twisti@1568 | 18 | * |
twisti@1568 | 19 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
twisti@1568 | 20 | * CA 95054 USA or visit www.sun.com if you need additional information or |
twisti@1568 | 21 | * have any questions. |
twisti@1568 | 22 | * |
twisti@1568 | 23 | */ |
twisti@1568 | 24 | |
twisti@1568 | 25 | // Low-level parser for method handle chains. |
twisti@1568 | 26 | class MethodHandleChain : StackObj { |
twisti@1568 | 27 | public: |
twisti@1568 | 28 | typedef MethodHandles::EntryKind EntryKind; |
twisti@1568 | 29 | |
twisti@1568 | 30 | private: |
twisti@1568 | 31 | Handle _root; // original target |
twisti@1568 | 32 | Handle _method_handle; // current target |
twisti@1568 | 33 | bool _is_last; // final guy in chain |
twisti@1568 | 34 | bool _is_bound; // has a bound argument |
twisti@1568 | 35 | BasicType _arg_type; // if is_bound, the bound argument type |
twisti@1568 | 36 | int _arg_slot; // if is_bound or is_adapter, affected argument slot |
twisti@1568 | 37 | jint _conversion; // conversion field of AMH or -1 |
twisti@1568 | 38 | methodHandle _last_method; // if is_last, which method we target |
twisti@1568 | 39 | Bytecodes::Code _last_invoke; // if is_last, type of invoke |
twisti@1568 | 40 | const char* _lose_message; // saved argument to lose() |
twisti@1568 | 41 | |
twisti@1568 | 42 | void set_method_handle(Handle target, TRAPS); |
twisti@1568 | 43 | void set_last_method(oop target, TRAPS); |
twisti@1568 | 44 | static BasicType compute_bound_arg_type(oop target, methodOop m, int arg_slot, TRAPS); |
twisti@1568 | 45 | |
twisti@1568 | 46 | oop MethodHandle_type_oop() { return java_dyn_MethodHandle::type(method_handle_oop()); } |
twisti@1568 | 47 | oop MethodHandle_vmtarget_oop() { return java_dyn_MethodHandle::vmtarget(method_handle_oop()); } |
twisti@1568 | 48 | int MethodHandle_vmslots() { return java_dyn_MethodHandle::vmslots(method_handle_oop()); } |
twisti@1568 | 49 | int DirectMethodHandle_vmindex() { return sun_dyn_DirectMethodHandle::vmindex(method_handle_oop()); } |
twisti@1568 | 50 | oop BoundMethodHandle_argument_oop() { return sun_dyn_BoundMethodHandle::argument(method_handle_oop()); } |
twisti@1568 | 51 | int BoundMethodHandle_vmargslot() { return sun_dyn_BoundMethodHandle::vmargslot(method_handle_oop()); } |
twisti@1568 | 52 | int AdapterMethodHandle_conversion() { return sun_dyn_AdapterMethodHandle::conversion(method_handle_oop()); } |
twisti@1568 | 53 | |
twisti@1568 | 54 | public: |
twisti@1568 | 55 | MethodHandleChain(Handle root, TRAPS) |
twisti@1568 | 56 | : _root(root) |
twisti@1568 | 57 | { set_method_handle(root, THREAD); } |
twisti@1568 | 58 | |
twisti@1568 | 59 | bool is_adapter() { return _conversion != -1; } |
twisti@1568 | 60 | bool is_bound() { return _is_bound; } |
twisti@1568 | 61 | bool is_last() { return _is_last; } |
twisti@1568 | 62 | |
twisti@1568 | 63 | void next(TRAPS) { |
twisti@1568 | 64 | assert(!is_last(), ""); |
twisti@1568 | 65 | set_method_handle(MethodHandle_vmtarget_oop(), THREAD); |
twisti@1568 | 66 | } |
twisti@1568 | 67 | |
twisti@1568 | 68 | Handle method_handle() { return _method_handle; } |
twisti@1568 | 69 | oop method_handle_oop() { return _method_handle(); } |
twisti@1568 | 70 | oop method_type_oop() { return MethodHandle_type_oop(); } |
twisti@1568 | 71 | |
twisti@1568 | 72 | jint adapter_conversion() { assert(is_adapter(), ""); return _conversion; } |
twisti@1568 | 73 | int adapter_conversion_op() { return MethodHandles::adapter_conversion_op(adapter_conversion()); } |
twisti@1568 | 74 | BasicType adapter_conversion_src_type() |
twisti@1568 | 75 | { return MethodHandles::adapter_conversion_src_type(adapter_conversion()); } |
twisti@1568 | 76 | BasicType adapter_conversion_dest_type() |
twisti@1568 | 77 | { return MethodHandles::adapter_conversion_dest_type(adapter_conversion()); } |
twisti@1568 | 78 | int adapter_conversion_stack_move() |
twisti@1568 | 79 | { return MethodHandles::adapter_conversion_stack_move(adapter_conversion()); } |
twisti@1568 | 80 | int adapter_conversion_stack_pushes() |
twisti@1568 | 81 | { return adapter_conversion_stack_move() / MethodHandles::stack_move_unit(); } |
twisti@1568 | 82 | int adapter_conversion_vminfo() |
twisti@1568 | 83 | { return MethodHandles::adapter_conversion_vminfo(adapter_conversion()); } |
twisti@1568 | 84 | int adapter_arg_slot() { assert(is_adapter(), ""); return _arg_slot; } |
twisti@1568 | 85 | oop adapter_arg_oop() { assert(is_adapter(), ""); return BoundMethodHandle_argument_oop(); } |
twisti@1568 | 86 | |
twisti@1568 | 87 | BasicType bound_arg_type() { assert(is_bound(), ""); return _arg_type; } |
twisti@1568 | 88 | int bound_arg_slot() { assert(is_bound(), ""); return _arg_slot; } |
twisti@1568 | 89 | oop bound_arg_oop() { assert(is_bound(), ""); return BoundMethodHandle_argument_oop(); } |
twisti@1568 | 90 | |
twisti@1568 | 91 | methodOop last_method_oop() { assert(is_last(), ""); return _last_method(); } |
twisti@1568 | 92 | Bytecodes::Code last_invoke_code() { assert(is_last(), ""); return _last_invoke; } |
twisti@1568 | 93 | |
twisti@1568 | 94 | void lose(const char* msg, TRAPS); |
twisti@1568 | 95 | const char* lose_message() { return _lose_message; } |
twisti@1568 | 96 | }; |
twisti@1568 | 97 | |
twisti@1568 | 98 | |
twisti@1568 | 99 | // Structure walker for method handles. |
twisti@1568 | 100 | // Does abstract interpretation on top of low-level parsing. |
twisti@1568 | 101 | // You supply the tokens shuffled by the abstract interpretation. |
twisti@1568 | 102 | class MethodHandleWalker : StackObj { |
twisti@1568 | 103 | public: |
twisti@1568 | 104 | struct _ArgToken { }; // dummy struct |
twisti@1568 | 105 | typedef _ArgToken* ArgToken; |
twisti@1568 | 106 | |
twisti@1568 | 107 | // Abstract interpretation state: |
twisti@1568 | 108 | struct SlotState { |
twisti@1568 | 109 | BasicType _type; |
twisti@1568 | 110 | ArgToken _arg; |
twisti@1568 | 111 | SlotState() : _type(), _arg() {} |
twisti@1568 | 112 | }; |
twisti@1568 | 113 | static SlotState make_state(BasicType type, ArgToken arg) { |
twisti@1568 | 114 | SlotState ss; |
twisti@1568 | 115 | ss._type = type; ss._arg = arg; |
twisti@1568 | 116 | return ss; |
twisti@1568 | 117 | } |
twisti@1568 | 118 | |
twisti@1568 | 119 | private: |
twisti@1568 | 120 | MethodHandleChain _chain; |
twisti@1568 | 121 | |
twisti@1568 | 122 | GrowableArray<SlotState> _outgoing; // current outgoing parameter slots |
twisti@1568 | 123 | int _outgoing_argc; // # non-empty outgoing slots |
twisti@1568 | 124 | |
twisti@1568 | 125 | // Replace a value of type old_type at slot (and maybe slot+1) with the new value. |
twisti@1568 | 126 | // If old_type != T_VOID, remove the old argument at that point. |
twisti@1568 | 127 | // If new_type != T_VOID, insert the new argument at that point. |
twisti@1568 | 128 | // Insert or delete a second empty slot as needed. |
twisti@1568 | 129 | void change_argument(BasicType old_type, int slot, BasicType new_type, ArgToken new_arg); |
twisti@1568 | 130 | |
twisti@1568 | 131 | SlotState* slot_state(int slot) { |
twisti@1568 | 132 | if (slot < 0 || slot >= _outgoing.length()) |
twisti@1568 | 133 | return NULL; |
twisti@1568 | 134 | return _outgoing.adr_at(slot); |
twisti@1568 | 135 | } |
twisti@1568 | 136 | BasicType slot_type(int slot) { |
twisti@1568 | 137 | SlotState* ss = slot_state(slot); |
twisti@1568 | 138 | if (ss == NULL) |
twisti@1568 | 139 | return T_ILLEGAL; |
twisti@1568 | 140 | return ss->_type; |
twisti@1568 | 141 | } |
twisti@1568 | 142 | bool slot_has_argument(int slot) { |
twisti@1568 | 143 | return slot_type(slot) < T_VOID; |
twisti@1568 | 144 | } |
twisti@1568 | 145 | |
twisti@1568 | 146 | #ifdef ASSERT |
twisti@1568 | 147 | int argument_count_slow(); |
twisti@1568 | 148 | #endif |
twisti@1568 | 149 | |
twisti@1568 | 150 | // Return a bytecode for converting src to dest, if one exists. |
twisti@1568 | 151 | Bytecodes::Code conversion_code(BasicType src, BasicType dest); |
twisti@1568 | 152 | |
twisti@1568 | 153 | void walk_incoming_state(TRAPS); |
twisti@1568 | 154 | |
twisti@1568 | 155 | public: |
twisti@1568 | 156 | MethodHandleWalker(Handle root, TRAPS) |
twisti@1568 | 157 | : _chain(root, THREAD), |
twisti@1568 | 158 | _outgoing(THREAD, 10), |
twisti@1568 | 159 | _outgoing_argc(0) |
twisti@1568 | 160 | { } |
twisti@1568 | 161 | |
twisti@1568 | 162 | MethodHandleChain& chain() { return _chain; } |
twisti@1568 | 163 | |
twisti@1568 | 164 | // plug-in abstract interpretation steps: |
twisti@1568 | 165 | virtual ArgToken make_parameter( BasicType type, klassOop tk, int argnum, TRAPS ) = 0; |
twisti@1568 | 166 | virtual ArgToken make_prim_constant( BasicType type, jvalue* con, TRAPS ) = 0; |
twisti@1568 | 167 | virtual ArgToken make_oop_constant( oop con, TRAPS ) = 0; |
twisti@1568 | 168 | virtual ArgToken make_conversion( BasicType type, klassOop tk, Bytecodes::Code op, ArgToken src, TRAPS ) = 0; |
twisti@1568 | 169 | virtual ArgToken make_fetch( BasicType type, klassOop tk, Bytecodes::Code op, ArgToken base, ArgToken offset, TRAPS ) = 0; |
twisti@1568 | 170 | virtual ArgToken make_invoke( methodOop m, vmIntrinsics::ID iid, Bytecodes::Code op, bool tailcall, int argc, ArgToken* argv, TRAPS ) = 0; |
twisti@1568 | 171 | |
twisti@1568 | 172 | // For make_invoke, the methodOop can be NULL if the intrinsic ID |
twisti@1568 | 173 | // is something other than vmIntrinsics::_none. |
twisti@1568 | 174 | |
twisti@1568 | 175 | // and in case anyone cares to related the previous actions to the chain: |
twisti@1568 | 176 | virtual void set_method_handle(oop mh) { } |
twisti@1568 | 177 | |
twisti@1568 | 178 | void lose(const char* msg, TRAPS) { chain().lose(msg, THREAD); } |
twisti@1568 | 179 | const char* lose_message() { return chain().lose_message(); } |
twisti@1568 | 180 | |
twisti@1568 | 181 | ArgToken walk(TRAPS); |
twisti@1568 | 182 | }; |
twisti@1568 | 183 | |
twisti@1568 | 184 | |
twisti@1568 | 185 | // An abstract interpreter for method handle chains. |
twisti@1568 | 186 | // Produces an account of the semantics of a chain, in terms of a static IR. |
twisti@1568 | 187 | // The IR happens to be JVM bytecodes. |
twisti@1568 | 188 | class MethodHandleCompiler : public MethodHandleWalker { |
twisti@1568 | 189 | private: |
twisti@1568 | 190 | Thread* _thread; |
twisti@1568 | 191 | |
twisti@1568 | 192 | struct PrimCon { |
twisti@1568 | 193 | BasicType _type; |
twisti@1568 | 194 | jvalue _value; |
twisti@1568 | 195 | }; |
twisti@1568 | 196 | |
twisti@1568 | 197 | // Accumulated compiler state: |
twisti@1568 | 198 | stringStream _bytes; |
twisti@1568 | 199 | GrowableArray<Handle> _constant_oops; |
twisti@1568 | 200 | GrowableArray<PrimCon*> _constant_prims; |
twisti@1568 | 201 | int _max_stack; |
twisti@1568 | 202 | int _num_params; |
twisti@1568 | 203 | int _max_locals; |
twisti@1568 | 204 | int _name_index; |
twisti@1568 | 205 | int _signature_index; |
twisti@1568 | 206 | |
twisti@1568 | 207 | // Stack values: |
twisti@1568 | 208 | enum TokenType { |
twisti@1568 | 209 | tt_void, |
twisti@1568 | 210 | tt_parameter, |
twisti@1568 | 211 | tt_temporary, |
twisti@1568 | 212 | tt_constant |
twisti@1568 | 213 | }; |
twisti@1568 | 214 | |
twisti@1568 | 215 | ArgToken make_stack_value(TokenType tt, BasicType type, int id) { |
twisti@1568 | 216 | return ArgToken( ((intptr_t)id << 8) | ((intptr_t)type << 4) | (intptr_t)tt ); |
twisti@1568 | 217 | } |
twisti@1568 | 218 | |
twisti@1568 | 219 | public: |
twisti@1568 | 220 | virtual ArgToken make_parameter(BasicType type, klassOop tk, int argnum, TRAPS) { |
twisti@1568 | 221 | return make_stack_value(tt_parameter, type, argnum); |
twisti@1568 | 222 | } |
twisti@1568 | 223 | virtual ArgToken make_oop_constant(oop con, TRAPS) { |
twisti@1568 | 224 | return make_stack_value(tt_constant, T_OBJECT, find_oop_constant(con)); |
twisti@1568 | 225 | } |
twisti@1568 | 226 | virtual ArgToken make_prim_constant(BasicType type, jvalue* con, TRAPS) { |
twisti@1568 | 227 | return make_stack_value(tt_constant, type, find_prim_constant(type, con)); |
twisti@1568 | 228 | } |
twisti@1568 | 229 | virtual ArgToken make_conversion(BasicType type, klassOop tk, Bytecodes::Code op, ArgToken src, TRAPS); |
twisti@1568 | 230 | virtual ArgToken make_fetch(BasicType type, klassOop tk, Bytecodes::Code op, ArgToken base, ArgToken offset, TRAPS); |
twisti@1568 | 231 | virtual ArgToken make_invoke(methodOop m, vmIntrinsics::ID iid, Bytecodes::Code op, bool tailcall, int argc, ArgToken* argv, TRAPS); |
twisti@1568 | 232 | |
twisti@1568 | 233 | int find_oop_constant(oop con); |
twisti@1568 | 234 | int find_prim_constant(BasicType type, jvalue* con); |
twisti@1568 | 235 | |
twisti@1568 | 236 | public: |
twisti@1568 | 237 | MethodHandleCompiler(Handle root, TRAPS) |
twisti@1568 | 238 | : MethodHandleWalker(root, THREAD), |
twisti@1568 | 239 | _thread(THREAD), |
twisti@1568 | 240 | _bytes(50), |
twisti@1568 | 241 | _constant_oops(THREAD, 10), |
twisti@1568 | 242 | _constant_prims(THREAD, 10), |
twisti@1568 | 243 | _max_stack(0), _max_locals(0), |
twisti@1568 | 244 | _name_index(0), _signature_index(0) |
twisti@1568 | 245 | { } |
twisti@1568 | 246 | const char* bytes() { return _bytes.as_string(); } |
twisti@1568 | 247 | int constant_length() { return _constant_oops.length(); } |
twisti@1568 | 248 | int max_stack() { return _max_stack; } |
twisti@1568 | 249 | int max_locals() { return _max_locals; } |
twisti@1568 | 250 | int name_index() { return _name_index; } |
twisti@1568 | 251 | int signature_index() { return _signature_index; } |
twisti@1568 | 252 | symbolHandle name() { return symbolHandle(_thread, (symbolOop)constant_oop_at(_name_index)()); } |
twisti@1568 | 253 | symbolHandle signature() { return symbolHandle(_thread, (symbolOop)constant_oop_at(_signature_index)()); } |
twisti@1568 | 254 | |
twisti@1568 | 255 | bool constant_is_oop_at(int i) { |
twisti@1568 | 256 | return (_constant_prims.at(i) == NULL); |
twisti@1568 | 257 | } |
twisti@1568 | 258 | Handle constant_oop_at(int i) { |
twisti@1568 | 259 | assert(constant_is_oop_at(i), ""); |
twisti@1568 | 260 | return _constant_oops.at(i); |
twisti@1568 | 261 | } |
twisti@1568 | 262 | PrimCon* constant_prim_at(int i) { |
twisti@1568 | 263 | assert(!constant_is_oop_at(i), ""); |
twisti@1568 | 264 | return _constant_prims.at(i); |
twisti@1568 | 265 | } |
twisti@1568 | 266 | |
twisti@1568 | 267 | // Compile the given MH chain into bytecode. |
twisti@1568 | 268 | void compile(TRAPS); |
twisti@1568 | 269 | }; |