This repository has been archived by the owner on Mar 8, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 110
/
bytecodeInterpreter.cpp
3459 lines (3084 loc) · 143 KB
/
bytecodeInterpreter.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright (c) 2002, 2014, 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.
*
*/
// no precompiled headers
#include "classfile/vmSymbols.hpp"
#include "gc_interface/collectedHeap.hpp"
#include "interpreter/bytecodeHistogram.hpp"
#include "interpreter/bytecodeInterpreter.hpp"
#include "interpreter/bytecodeInterpreter.inline.hpp"
#include "interpreter/bytecodeInterpreterProfiling.hpp"
#include "interpreter/interpreter.hpp"
#include "interpreter/interpreterRuntime.hpp"
#include "memory/resourceArea.hpp"
#include "oops/methodCounters.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/oop.inline.hpp"
#include "prims/jvmtiExport.hpp"
#include "prims/jvmtiThreadState.hpp"
#include "runtime/biasedLocking.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/interfaceSupport.hpp"
#include "runtime/orderAccess.inline.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/threadCritical.hpp"
#include "utilities/exceptions.hpp"
// no precompiled headers
#ifdef CC_INTERP
/*
* USELABELS - If using GCC, then use labels for the opcode dispatching
* rather -then a switch statement. This improves performance because it
* gives us the oportunity to have the instructions that calculate the
* next opcode to jump to be intermixed with the rest of the instructions
* that implement the opcode (see UPDATE_PC_AND_TOS_AND_CONTINUE macro).
*/
#undef USELABELS
#ifdef __GNUC__
/*
ASSERT signifies debugging. It is much easier to step thru bytecodes if we
don't use the computed goto approach.
*/
#ifndef ASSERT
#define USELABELS
#endif
#endif
#undef CASE
#ifdef USELABELS
#define CASE(opcode) opc ## opcode
#define DEFAULT opc_default
#else
#define CASE(opcode) case Bytecodes:: opcode
#define DEFAULT default
#endif
/*
* PREFETCH_OPCCODE - Some compilers do better if you prefetch the next
* opcode before going back to the top of the while loop, rather then having
* the top of the while loop handle it. This provides a better opportunity
* for instruction scheduling. Some compilers just do this prefetch
* automatically. Some actually end up with worse performance if you
* force the prefetch. Solaris gcc seems to do better, but cc does worse.
*/
#undef PREFETCH_OPCCODE
#define PREFETCH_OPCCODE
/*
Interpreter safepoint: it is expected that the interpreter will have no live
handles of its own creation live at an interpreter safepoint. Therefore we
run a HandleMarkCleaner and trash all handles allocated in the call chain
since the JavaCalls::call_helper invocation that initiated the chain.
There really shouldn't be any handles remaining to trash but this is cheap
in relation to a safepoint.
*/
#define SAFEPOINT \
if ( SafepointSynchronize::is_synchronizing()) { \
{ \
/* zap freed handles rather than GC'ing them */ \
HandleMarkCleaner __hmc(THREAD); \
} \
CALL_VM(SafepointSynchronize::block(THREAD), handle_exception); \
}
/*
* VM_JAVA_ERROR - Macro for throwing a java exception from
* the interpreter loop. Should really be a CALL_VM but there
* is no entry point to do the transition to vm so we just
* do it by hand here.
*/
#define VM_JAVA_ERROR_NO_JUMP(name, msg, note_a_trap) \
DECACHE_STATE(); \
SET_LAST_JAVA_FRAME(); \
{ \
InterpreterRuntime::note_a_trap(THREAD, istate->method(), BCI()); \
ThreadInVMfromJava trans(THREAD); \
Exceptions::_throw_msg(THREAD, __FILE__, __LINE__, name, msg); \
} \
RESET_LAST_JAVA_FRAME(); \
CACHE_STATE();
// Normal throw of a java error.
#define VM_JAVA_ERROR(name, msg, note_a_trap) \
VM_JAVA_ERROR_NO_JUMP(name, msg, note_a_trap) \
goto handle_exception;
#ifdef PRODUCT
#define DO_UPDATE_INSTRUCTION_COUNT(opcode)
#else
#define DO_UPDATE_INSTRUCTION_COUNT(opcode) \
{ \
BytecodeCounter::_counter_value++; \
BytecodeHistogram::_counters[(Bytecodes::Code)opcode]++; \
if (StopInterpreterAt && StopInterpreterAt == BytecodeCounter::_counter_value) os::breakpoint(); \
if (TraceBytecodes) { \
CALL_VM((void)SharedRuntime::trace_bytecode(THREAD, 0, \
topOfStack[Interpreter::expr_index_at(1)], \
topOfStack[Interpreter::expr_index_at(2)]), \
handle_exception); \
} \
}
#endif
#undef DEBUGGER_SINGLE_STEP_NOTIFY
#ifdef VM_JVMTI
/* NOTE: (kbr) This macro must be called AFTER the PC has been
incremented. JvmtiExport::at_single_stepping_point() may cause a
breakpoint opcode to get inserted at the current PC to allow the
debugger to coalesce single-step events.
As a result if we call at_single_stepping_point() we refetch opcode
to get the current opcode. This will override any other prefetching
that might have occurred.
*/
#define DEBUGGER_SINGLE_STEP_NOTIFY() \
{ \
if (_jvmti_interp_events) { \
if (JvmtiExport::should_post_single_step()) { \
DECACHE_STATE(); \
SET_LAST_JAVA_FRAME(); \
ThreadInVMfromJava trans(THREAD); \
JvmtiExport::at_single_stepping_point(THREAD, \
istate->method(), \
pc); \
RESET_LAST_JAVA_FRAME(); \
CACHE_STATE(); \
if (THREAD->pop_frame_pending() && \
!THREAD->pop_frame_in_process()) { \
goto handle_Pop_Frame; \
} \
if (THREAD->jvmti_thread_state() && \
THREAD->jvmti_thread_state()->is_earlyret_pending()) { \
goto handle_Early_Return; \
} \
opcode = *pc; \
} \
} \
}
#else
#define DEBUGGER_SINGLE_STEP_NOTIFY()
#endif
/*
* CONTINUE - Macro for executing the next opcode.
*/
#undef CONTINUE
#ifdef USELABELS
// Have to do this dispatch this way in C++ because otherwise gcc complains about crossing an
// initialization (which is is the initialization of the table pointer...)
#define DISPATCH(opcode) goto *(void*)dispatch_table[opcode]
#define CONTINUE { \
opcode = *pc; \
DO_UPDATE_INSTRUCTION_COUNT(opcode); \
DEBUGGER_SINGLE_STEP_NOTIFY(); \
DISPATCH(opcode); \
}
#else
#ifdef PREFETCH_OPCCODE
#define CONTINUE { \
opcode = *pc; \
DO_UPDATE_INSTRUCTION_COUNT(opcode); \
DEBUGGER_SINGLE_STEP_NOTIFY(); \
continue; \
}
#else
#define CONTINUE { \
DO_UPDATE_INSTRUCTION_COUNT(opcode); \
DEBUGGER_SINGLE_STEP_NOTIFY(); \
continue; \
}
#endif
#endif
#define UPDATE_PC(opsize) {pc += opsize; }
/*
* UPDATE_PC_AND_TOS - Macro for updating the pc and topOfStack.
*/
#undef UPDATE_PC_AND_TOS
#define UPDATE_PC_AND_TOS(opsize, stack) \
{pc += opsize; MORE_STACK(stack); }
/*
* UPDATE_PC_AND_TOS_AND_CONTINUE - Macro for updating the pc and topOfStack,
* and executing the next opcode. It's somewhat similar to the combination
* of UPDATE_PC_AND_TOS and CONTINUE, but with some minor optimizations.
*/
#undef UPDATE_PC_AND_TOS_AND_CONTINUE
#ifdef USELABELS
#define UPDATE_PC_AND_TOS_AND_CONTINUE(opsize, stack) { \
pc += opsize; opcode = *pc; MORE_STACK(stack); \
DO_UPDATE_INSTRUCTION_COUNT(opcode); \
DEBUGGER_SINGLE_STEP_NOTIFY(); \
DISPATCH(opcode); \
}
#define UPDATE_PC_AND_CONTINUE(opsize) { \
pc += opsize; opcode = *pc; \
DO_UPDATE_INSTRUCTION_COUNT(opcode); \
DEBUGGER_SINGLE_STEP_NOTIFY(); \
DISPATCH(opcode); \
}
#else
#ifdef PREFETCH_OPCCODE
#define UPDATE_PC_AND_TOS_AND_CONTINUE(opsize, stack) { \
pc += opsize; opcode = *pc; MORE_STACK(stack); \
DO_UPDATE_INSTRUCTION_COUNT(opcode); \
DEBUGGER_SINGLE_STEP_NOTIFY(); \
goto do_continue; \
}
#define UPDATE_PC_AND_CONTINUE(opsize) { \
pc += opsize; opcode = *pc; \
DO_UPDATE_INSTRUCTION_COUNT(opcode); \
DEBUGGER_SINGLE_STEP_NOTIFY(); \
goto do_continue; \
}
#else
#define UPDATE_PC_AND_TOS_AND_CONTINUE(opsize, stack) { \
pc += opsize; MORE_STACK(stack); \
DO_UPDATE_INSTRUCTION_COUNT(opcode); \
DEBUGGER_SINGLE_STEP_NOTIFY(); \
goto do_continue; \
}
#define UPDATE_PC_AND_CONTINUE(opsize) { \
pc += opsize; \
DO_UPDATE_INSTRUCTION_COUNT(opcode); \
DEBUGGER_SINGLE_STEP_NOTIFY(); \
goto do_continue; \
}
#endif /* PREFETCH_OPCCODE */
#endif /* USELABELS */
// About to call a new method, update the save the adjusted pc and return to frame manager
#define UPDATE_PC_AND_RETURN(opsize) \
DECACHE_TOS(); \
istate->set_bcp(pc+opsize); \
return;
#define METHOD istate->method()
#define GET_METHOD_COUNTERS(res) \
res = METHOD->method_counters(); \
if (res == NULL) { \
CALL_VM(res = InterpreterRuntime::build_method_counters(THREAD, METHOD), handle_exception); \
}
#define OSR_REQUEST(res, branch_pc) \
CALL_VM(res=InterpreterRuntime::frequency_counter_overflow(THREAD, branch_pc), handle_exception);
/*
* For those opcodes that need to have a GC point on a backwards branch
*/
// Backedge counting is kind of strange. The asm interpreter will increment
// the backedge counter as a separate counter but it does it's comparisons
// to the sum (scaled) of invocation counter and backedge count to make
// a decision. Seems kind of odd to sum them together like that
// skip is delta from current bcp/bci for target, branch_pc is pre-branch bcp
#define DO_BACKEDGE_CHECKS(skip, branch_pc) \
if ((skip) <= 0) { \
MethodCounters* mcs; \
GET_METHOD_COUNTERS(mcs); \
if (UseLoopCounter) { \
bool do_OSR = UseOnStackReplacement; \
mcs->backedge_counter()->increment(); \
if (ProfileInterpreter) { \
BI_PROFILE_GET_OR_CREATE_METHOD_DATA(handle_exception); \
/* Check for overflow against MDO count. */ \
do_OSR = do_OSR \
&& (mdo_last_branch_taken_count >= (uint)InvocationCounter::InterpreterBackwardBranchLimit)\
/* When ProfileInterpreter is on, the backedge_count comes */ \
/* from the methodDataOop, which value does not get reset on */ \
/* the call to frequency_counter_overflow(). To avoid */ \
/* excessive calls to the overflow routine while the method is */ \
/* being compiled, add a second test to make sure the overflow */ \
/* function is called only once every overflow_frequency. */ \
&& (!(mdo_last_branch_taken_count & 1023)); \
} else { \
/* check for overflow of backedge counter */ \
do_OSR = do_OSR \
&& mcs->invocation_counter()->reached_InvocationLimit(mcs->backedge_counter()); \
} \
if (do_OSR) { \
nmethod* osr_nmethod; \
OSR_REQUEST(osr_nmethod, branch_pc); \
if (osr_nmethod != NULL && osr_nmethod->osr_entry_bci() != InvalidOSREntryBci) { \
intptr_t* buf; \
/* Call OSR migration with last java frame only, no checks. */ \
CALL_VM_NAKED_LJF(buf=SharedRuntime::OSR_migration_begin(THREAD)); \
istate->set_msg(do_osr); \
istate->set_osr_buf((address)buf); \
istate->set_osr_entry(osr_nmethod->osr_entry()); \
return; \
} \
} \
} /* UseCompiler ... */ \
SAFEPOINT; \
}
/*
* For those opcodes that need to have a GC point on a backwards branch
*/
/*
* Macros for caching and flushing the interpreter state. Some local
* variables need to be flushed out to the frame before we do certain
* things (like pushing frames or becomming gc safe) and some need to
* be recached later (like after popping a frame). We could use one
* macro to cache or decache everything, but this would be less then
* optimal because we don't always need to cache or decache everything
* because some things we know are already cached or decached.
*/
#undef DECACHE_TOS
#undef CACHE_TOS
#undef CACHE_PREV_TOS
#define DECACHE_TOS() istate->set_stack(topOfStack);
#define CACHE_TOS() topOfStack = (intptr_t *)istate->stack();
#undef DECACHE_PC
#undef CACHE_PC
#define DECACHE_PC() istate->set_bcp(pc);
#define CACHE_PC() pc = istate->bcp();
#define CACHE_CP() cp = istate->constants();
#define CACHE_LOCALS() locals = istate->locals();
#undef CACHE_FRAME
#define CACHE_FRAME()
// BCI() returns the current bytecode-index.
#undef BCI
#define BCI() ((int)(intptr_t)(pc - (intptr_t)istate->method()->code_base()))
/*
* CHECK_NULL - Macro for throwing a NullPointerException if the object
* passed is a null ref.
* On some architectures/platforms it should be possible to do this implicitly
*/
#undef CHECK_NULL
#define CHECK_NULL(obj_) \
if ((obj_) == NULL) { \
VM_JAVA_ERROR(vmSymbols::java_lang_NullPointerException(), NULL, note_nullCheck_trap); \
} \
VERIFY_OOP(obj_)
#define VMdoubleConstZero() 0.0
#define VMdoubleConstOne() 1.0
#define VMlongConstZero() (max_jlong-max_jlong)
#define VMlongConstOne() ((max_jlong-max_jlong)+1)
/*
* Alignment
*/
#define VMalignWordUp(val) (((uintptr_t)(val) + 3) & ~3)
// Decache the interpreter state that interpreter modifies directly (i.e. GC is indirect mod)
#define DECACHE_STATE() DECACHE_PC(); DECACHE_TOS();
// Reload interpreter state after calling the VM or a possible GC
#define CACHE_STATE() \
CACHE_TOS(); \
CACHE_PC(); \
CACHE_CP(); \
CACHE_LOCALS();
// Call the VM with last java frame only.
#define CALL_VM_NAKED_LJF(func) \
DECACHE_STATE(); \
SET_LAST_JAVA_FRAME(); \
func; \
RESET_LAST_JAVA_FRAME(); \
CACHE_STATE();
// Call the VM. Don't check for pending exceptions.
#define CALL_VM_NOCHECK(func) \
CALL_VM_NAKED_LJF(func) \
if (THREAD->pop_frame_pending() && \
!THREAD->pop_frame_in_process()) { \
goto handle_Pop_Frame; \
} \
if (THREAD->jvmti_thread_state() && \
THREAD->jvmti_thread_state()->is_earlyret_pending()) { \
goto handle_Early_Return; \
}
// Call the VM and check for pending exceptions
#define CALL_VM(func, label) { \
CALL_VM_NOCHECK(func); \
if (THREAD->has_pending_exception()) goto label; \
}
/*
* BytecodeInterpreter::run(interpreterState istate)
* BytecodeInterpreter::runWithChecks(interpreterState istate)
*
* The real deal. This is where byte codes actually get interpreted.
* Basically it's a big while loop that iterates until we return from
* the method passed in.
*
* The runWithChecks is used if JVMTI is enabled.
*
*/
#if defined(VM_JVMTI)
void
BytecodeInterpreter::runWithChecks(interpreterState istate) {
#else
void
BytecodeInterpreter::run(interpreterState istate) {
#endif
// In order to simplify some tests based on switches set at runtime
// we invoke the interpreter a single time after switches are enabled
// and set simpler to to test variables rather than method calls or complex
// boolean expressions.
static int initialized = 0;
static int checkit = 0;
static intptr_t* c_addr = NULL;
static intptr_t c_value;
if (checkit && *c_addr != c_value) {
os::breakpoint();
}
#ifdef VM_JVMTI
static bool _jvmti_interp_events = 0;
#endif
static int _compiling; // (UseCompiler || CountCompiledCalls)
#ifdef ASSERT
if (istate->_msg != initialize) {
// We have a problem here if we are running with a pre-hsx24 JDK (for example during bootstrap)
// because in that case, EnableInvokeDynamic is true by default but will be later switched off
// if java_lang_invoke_MethodHandle::compute_offsets() detects that the JDK only has the classes
// for the old JSR292 implementation.
// This leads to a situation where 'istate->_stack_limit' always accounts for
// methodOopDesc::extra_stack_entries() because it is computed in
// CppInterpreterGenerator::generate_compute_interpreter_state() which was generated while
// EnableInvokeDynamic was still true. On the other hand, istate->_method->max_stack() doesn't
// account for extra_stack_entries() anymore because at the time when it is called
// EnableInvokeDynamic was already set to false.
// So we have a second version of the assertion which handles the case where EnableInvokeDynamic was
// switched off because of the wrong classes.
if (EnableInvokeDynamic || FLAG_IS_CMDLINE(EnableInvokeDynamic)) {
assert(labs(istate->_stack_base - istate->_stack_limit) == (istate->_method->max_stack() + 1), "bad stack limit");
} else {
const int extra_stack_entries = Method::extra_stack_entries_for_jsr292;
assert(labs(istate->_stack_base - istate->_stack_limit) == (istate->_method->max_stack() + extra_stack_entries
+ 1), "bad stack limit");
}
#ifndef SHARK
IA32_ONLY(assert(istate->_stack_limit == istate->_thread->last_Java_sp() + 1, "wrong"));
#endif // !SHARK
}
// Verify linkages.
interpreterState l = istate;
do {
assert(l == l->_self_link, "bad link");
l = l->_prev_link;
} while (l != NULL);
// Screwups with stack management usually cause us to overwrite istate
// save a copy so we can verify it.
interpreterState orig = istate;
#endif
register intptr_t* topOfStack = (intptr_t *)istate->stack(); /* access with STACK macros */
register address pc = istate->bcp();
register jubyte opcode;
register intptr_t* locals = istate->locals();
register ConstantPoolCache* cp = istate->constants(); // method()->constants()->cache()
#ifdef LOTS_OF_REGS
register JavaThread* THREAD = istate->thread();
#else
#undef THREAD
#define THREAD istate->thread()
#endif
#ifdef USELABELS
const static void* const opclabels_data[256] = {
/* 0x00 */ &&opc_nop, &&opc_aconst_null,&&opc_iconst_m1,&&opc_iconst_0,
/* 0x04 */ &&opc_iconst_1,&&opc_iconst_2, &&opc_iconst_3, &&opc_iconst_4,
/* 0x08 */ &&opc_iconst_5,&&opc_lconst_0, &&opc_lconst_1, &&opc_fconst_0,
/* 0x0C */ &&opc_fconst_1,&&opc_fconst_2, &&opc_dconst_0, &&opc_dconst_1,
/* 0x10 */ &&opc_bipush, &&opc_sipush, &&opc_ldc, &&opc_ldc_w,
/* 0x14 */ &&opc_ldc2_w, &&opc_iload, &&opc_lload, &&opc_fload,
/* 0x18 */ &&opc_dload, &&opc_aload, &&opc_iload_0,&&opc_iload_1,
/* 0x1C */ &&opc_iload_2,&&opc_iload_3,&&opc_lload_0,&&opc_lload_1,
/* 0x20 */ &&opc_lload_2,&&opc_lload_3,&&opc_fload_0,&&opc_fload_1,
/* 0x24 */ &&opc_fload_2,&&opc_fload_3,&&opc_dload_0,&&opc_dload_1,
/* 0x28 */ &&opc_dload_2,&&opc_dload_3,&&opc_aload_0,&&opc_aload_1,
/* 0x2C */ &&opc_aload_2,&&opc_aload_3,&&opc_iaload, &&opc_laload,
/* 0x30 */ &&opc_faload, &&opc_daload, &&opc_aaload, &&opc_baload,
/* 0x34 */ &&opc_caload, &&opc_saload, &&opc_istore, &&opc_lstore,
/* 0x38 */ &&opc_fstore, &&opc_dstore, &&opc_astore, &&opc_istore_0,
/* 0x3C */ &&opc_istore_1,&&opc_istore_2,&&opc_istore_3,&&opc_lstore_0,
/* 0x40 */ &&opc_lstore_1,&&opc_lstore_2,&&opc_lstore_3,&&opc_fstore_0,
/* 0x44 */ &&opc_fstore_1,&&opc_fstore_2,&&opc_fstore_3,&&opc_dstore_0,
/* 0x48 */ &&opc_dstore_1,&&opc_dstore_2,&&opc_dstore_3,&&opc_astore_0,
/* 0x4C */ &&opc_astore_1,&&opc_astore_2,&&opc_astore_3,&&opc_iastore,
/* 0x50 */ &&opc_lastore,&&opc_fastore,&&opc_dastore,&&opc_aastore,
/* 0x54 */ &&opc_bastore,&&opc_castore,&&opc_sastore,&&opc_pop,
/* 0x58 */ &&opc_pop2, &&opc_dup, &&opc_dup_x1, &&opc_dup_x2,
/* 0x5C */ &&opc_dup2, &&opc_dup2_x1,&&opc_dup2_x2,&&opc_swap,
/* 0x60 */ &&opc_iadd,&&opc_ladd,&&opc_fadd,&&opc_dadd,
/* 0x64 */ &&opc_isub,&&opc_lsub,&&opc_fsub,&&opc_dsub,
/* 0x68 */ &&opc_imul,&&opc_lmul,&&opc_fmul,&&opc_dmul,
/* 0x6C */ &&opc_idiv,&&opc_ldiv,&&opc_fdiv,&&opc_ddiv,
/* 0x70 */ &&opc_irem, &&opc_lrem, &&opc_frem,&&opc_drem,
/* 0x74 */ &&opc_ineg, &&opc_lneg, &&opc_fneg,&&opc_dneg,
/* 0x78 */ &&opc_ishl, &&opc_lshl, &&opc_ishr,&&opc_lshr,
/* 0x7C */ &&opc_iushr,&&opc_lushr,&&opc_iand,&&opc_land,
/* 0x80 */ &&opc_ior, &&opc_lor,&&opc_ixor,&&opc_lxor,
/* 0x84 */ &&opc_iinc,&&opc_i2l,&&opc_i2f, &&opc_i2d,
/* 0x88 */ &&opc_l2i, &&opc_l2f,&&opc_l2d, &&opc_f2i,
/* 0x8C */ &&opc_f2l, &&opc_f2d,&&opc_d2i, &&opc_d2l,
/* 0x90 */ &&opc_d2f, &&opc_i2b, &&opc_i2c, &&opc_i2s,
/* 0x94 */ &&opc_lcmp, &&opc_fcmpl,&&opc_fcmpg,&&opc_dcmpl,
/* 0x98 */ &&opc_dcmpg,&&opc_ifeq, &&opc_ifne, &&opc_iflt,
/* 0x9C */ &&opc_ifge, &&opc_ifgt, &&opc_ifle, &&opc_if_icmpeq,
/* 0xA0 */ &&opc_if_icmpne,&&opc_if_icmplt,&&opc_if_icmpge, &&opc_if_icmpgt,
/* 0xA4 */ &&opc_if_icmple,&&opc_if_acmpeq,&&opc_if_acmpne, &&opc_goto,
/* 0xA8 */ &&opc_jsr, &&opc_ret, &&opc_tableswitch,&&opc_lookupswitch,
/* 0xAC */ &&opc_ireturn, &&opc_lreturn, &&opc_freturn, &&opc_dreturn,
/* 0xB0 */ &&opc_areturn, &&opc_return, &&opc_getstatic, &&opc_putstatic,
/* 0xB4 */ &&opc_getfield, &&opc_putfield, &&opc_invokevirtual,&&opc_invokespecial,
/* 0xB8 */ &&opc_invokestatic,&&opc_invokeinterface,&&opc_invokedynamic,&&opc_new,
/* 0xBC */ &&opc_newarray, &&opc_anewarray, &&opc_arraylength, &&opc_athrow,
/* 0xC0 */ &&opc_checkcast, &&opc_instanceof, &&opc_monitorenter, &&opc_monitorexit,
/* 0xC4 */ &&opc_wide, &&opc_multianewarray, &&opc_ifnull, &&opc_ifnonnull,
/* 0xC8 */ &&opc_goto_w, &&opc_jsr_w, &&opc_breakpoint, &&opc_default,
/* 0xCC */ &&opc_default, &&opc_default, &&opc_default, &&opc_default,
/* 0xD0 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default,
/* 0xD4 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default,
/* 0xD8 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default,
/* 0xDC */ &&opc_default, &&opc_default, &&opc_default, &&opc_default,
/* 0xE0 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default,
/* 0xE4 */ &&opc_default, &&opc_fast_aldc, &&opc_fast_aldc_w, &&opc_return_register_finalizer,
/* 0xE8 */ &&opc_invokehandle,&&opc_default, &&opc_default, &&opc_default,
/* 0xEC */ &&opc_default, &&opc_default, &&opc_default, &&opc_default,
/* 0xF0 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default,
/* 0xF4 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default,
/* 0xF8 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default,
/* 0xFC */ &&opc_default, &&opc_default, &&opc_default, &&opc_default
};
register uintptr_t *dispatch_table = (uintptr_t*)&opclabels_data[0];
#endif /* USELABELS */
#ifdef ASSERT
// this will trigger a VERIFY_OOP on entry
if (istate->msg() != initialize && ! METHOD->is_static()) {
oop rcvr = LOCALS_OBJECT(0);
VERIFY_OOP(rcvr);
}
#endif
// #define HACK
#ifdef HACK
bool interesting = false;
#endif // HACK
/* QQQ this should be a stack method so we don't know actual direction */
guarantee(istate->msg() == initialize ||
topOfStack >= istate->stack_limit() &&
topOfStack < istate->stack_base(),
"Stack top out of range");
#ifdef CC_INTERP_PROFILE
// MethodData's last branch taken count.
uint mdo_last_branch_taken_count = 0;
#else
const uint mdo_last_branch_taken_count = 0;
#endif
switch (istate->msg()) {
case initialize: {
if (initialized++) ShouldNotReachHere(); // Only one initialize call.
_compiling = (UseCompiler || CountCompiledCalls);
#ifdef VM_JVMTI
_jvmti_interp_events = JvmtiExport::can_post_interpreter_events();
#endif
return;
}
break;
case method_entry: {
THREAD->set_do_not_unlock();
// count invocations
assert(initialized, "Interpreter not initialized");
if (_compiling) {
MethodCounters* mcs;
GET_METHOD_COUNTERS(mcs);
if (ProfileInterpreter) {
METHOD->increment_interpreter_invocation_count(THREAD);
}
mcs->invocation_counter()->increment();
if (mcs->invocation_counter()->reached_InvocationLimit(mcs->backedge_counter())) {
CALL_VM((void)InterpreterRuntime::frequency_counter_overflow(THREAD, NULL), handle_exception);
// We no longer retry on a counter overflow.
}
// Get or create profile data. Check for pending (async) exceptions.
BI_PROFILE_GET_OR_CREATE_METHOD_DATA(handle_exception);
SAFEPOINT;
}
if ((istate->_stack_base - istate->_stack_limit) != istate->method()->max_stack() + 1) {
// initialize
os::breakpoint();
}
#ifdef HACK
{
ResourceMark rm;
char *method_name = istate->method()->name_and_sig_as_C_string();
if (strstr(method_name, "runThese$TestRunner.run()V") != NULL) {
tty->print_cr("entering: depth %d bci: %d",
(istate->_stack_base - istate->_stack),
istate->_bcp - istate->_method->code_base());
interesting = true;
}
}
#endif // HACK
// Lock method if synchronized.
if (METHOD->is_synchronized()) {
// oop rcvr = locals[0].j.r;
oop rcvr;
if (METHOD->is_static()) {
rcvr = METHOD->constants()->pool_holder()->java_mirror();
} else {
rcvr = LOCALS_OBJECT(0);
VERIFY_OOP(rcvr);
}
// The initial monitor is ours for the taking.
// Monitor not filled in frame manager any longer as this caused race condition with biased locking.
BasicObjectLock* mon = &istate->monitor_base()[-1];
mon->set_obj(rcvr);
bool success = false;
uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place;
markOop mark = rcvr->mark();
intptr_t hash = (intptr_t) markOopDesc::no_hash;
// Implies UseBiasedLocking.
if (mark->has_bias_pattern()) {
uintptr_t thread_ident;
uintptr_t anticipated_bias_locking_value;
thread_ident = (uintptr_t)istate->thread();
anticipated_bias_locking_value =
(((uintptr_t)rcvr->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &
~((uintptr_t) markOopDesc::age_mask_in_place);
if (anticipated_bias_locking_value == 0) {
// Already biased towards this thread, nothing to do.
if (PrintBiasedLockingStatistics) {
(* BiasedLocking::biased_lock_entry_count_addr())++;
}
success = true;
} else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {
// Try to revoke bias.
markOop header = rcvr->klass()->prototype_header();
if (hash != markOopDesc::no_hash) {
header = header->copy_set_hash(hash);
}
if (Atomic::cmpxchg_ptr(header, rcvr->mark_addr(), mark) == mark) {
if (PrintBiasedLockingStatistics)
(*BiasedLocking::revoked_lock_entry_count_addr())++;
}
} else if ((anticipated_bias_locking_value & epoch_mask_in_place) != 0) {
// Try to rebias.
markOop new_header = (markOop) ( (intptr_t) rcvr->klass()->prototype_header() | thread_ident);
if (hash != markOopDesc::no_hash) {
new_header = new_header->copy_set_hash(hash);
}
if (Atomic::cmpxchg_ptr((void*)new_header, rcvr->mark_addr(), mark) == mark) {
if (PrintBiasedLockingStatistics) {
(* BiasedLocking::rebiased_lock_entry_count_addr())++;
}
} else {
CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception);
}
success = true;
} else {
// Try to bias towards thread in case object is anonymously biased.
markOop header = (markOop) ((uintptr_t) mark &
((uintptr_t)markOopDesc::biased_lock_mask_in_place |
(uintptr_t)markOopDesc::age_mask_in_place | epoch_mask_in_place));
if (hash != markOopDesc::no_hash) {
header = header->copy_set_hash(hash);
}
markOop new_header = (markOop) ((uintptr_t) header | thread_ident);
// Debugging hint.
DEBUG_ONLY(mon->lock()->set_displaced_header((markOop) (uintptr_t) 0xdeaddead);)
if (Atomic::cmpxchg_ptr((void*)new_header, rcvr->mark_addr(), header) == header) {
if (PrintBiasedLockingStatistics) {
(* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
}
} else {
CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception);
}
success = true;
}
}
// Traditional lightweight locking.
if (!success) {
markOop displaced = rcvr->mark()->set_unlocked();
mon->lock()->set_displaced_header(displaced);
bool call_vm = UseHeavyMonitors;
if (call_vm || Atomic::cmpxchg_ptr(mon, rcvr->mark_addr(), displaced) != displaced) {
// Is it simple recursive case?
if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
mon->lock()->set_displaced_header(NULL);
} else {
CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception);
}
}
}
}
THREAD->clr_do_not_unlock();
// Notify jvmti
#ifdef VM_JVMTI
if (_jvmti_interp_events) {
// Whenever JVMTI puts a thread in interp_only_mode, method
// entry/exit events are sent for that thread to track stack depth.
if (THREAD->is_interp_only_mode()) {
CALL_VM(InterpreterRuntime::post_method_entry(THREAD),
handle_exception);
}
}
#endif /* VM_JVMTI */
goto run;
}
case popping_frame: {
// returned from a java call to pop the frame, restart the call
// clear the message so we don't confuse ourselves later
assert(THREAD->pop_frame_in_process(), "wrong frame pop state");
istate->set_msg(no_request);
if (_compiling) {
// Set MDX back to the ProfileData of the invoke bytecode that will be
// restarted.
SET_MDX(NULL);
BI_PROFILE_GET_OR_CREATE_METHOD_DATA(handle_exception);
}
THREAD->clr_pop_frame_in_process();
goto run;
}
case method_resume: {
if ((istate->_stack_base - istate->_stack_limit) != istate->method()->max_stack() + 1) {
// resume
os::breakpoint();
}
#ifdef HACK
{
ResourceMark rm;
char *method_name = istate->method()->name_and_sig_as_C_string();
if (strstr(method_name, "runThese$TestRunner.run()V") != NULL) {
tty->print_cr("resume: depth %d bci: %d",
(istate->_stack_base - istate->_stack) ,
istate->_bcp - istate->_method->code_base());
interesting = true;
}
}
#endif // HACK
// returned from a java call, continue executing.
if (THREAD->pop_frame_pending() && !THREAD->pop_frame_in_process()) {
goto handle_Pop_Frame;
}
if (THREAD->jvmti_thread_state() &&
THREAD->jvmti_thread_state()->is_earlyret_pending()) {
goto handle_Early_Return;
}
if (THREAD->has_pending_exception()) goto handle_exception;
// Update the pc by the saved amount of the invoke bytecode size
UPDATE_PC(istate->bcp_advance());
if (_compiling) {
// Get or create profile data. Check for pending (async) exceptions.
BI_PROFILE_GET_OR_CREATE_METHOD_DATA(handle_exception);
}
goto run;
}
case deopt_resume2: {
// Returned from an opcode that will reexecute. Deopt was
// a result of a PopFrame request.
//
if (_compiling) {
// Get or create profile data. Check for pending (async) exceptions.
BI_PROFILE_GET_OR_CREATE_METHOD_DATA(handle_exception);
}
goto run;
}
case deopt_resume: {
// Returned from an opcode that has completed. The stack has
// the result all we need to do is skip across the bytecode
// and continue (assuming there is no exception pending)
//
// compute continuation length
//
// Note: it is possible to deopt at a return_register_finalizer opcode
// because this requires entering the vm to do the registering. While the
// opcode is complete we can't advance because there are no more opcodes
// much like trying to deopt at a poll return. In that has we simply
// get out of here
//
if ( Bytecodes::code_at(METHOD, pc) == Bytecodes::_return_register_finalizer) {
// this will do the right thing even if an exception is pending.
goto handle_return;
}
UPDATE_PC(Bytecodes::length_at(METHOD, pc));
if (THREAD->has_pending_exception()) goto handle_exception;
if (_compiling) {
// Get or create profile data. Check for pending (async) exceptions.
BI_PROFILE_GET_OR_CREATE_METHOD_DATA(handle_exception);
}
goto run;
}
case got_monitors: {
// continue locking now that we have a monitor to use
// we expect to find newly allocated monitor at the "top" of the monitor stack.
oop lockee = STACK_OBJECT(-1);
VERIFY_OOP(lockee);
// derefing's lockee ought to provoke implicit null check
// find a free monitor
BasicObjectLock* entry = (BasicObjectLock*) istate->stack_base();
assert(entry->obj() == NULL, "Frame manager didn't allocate the monitor");
entry->set_obj(lockee);
bool success = false;
uintptr_t epoch_mask_in_place = (uintptr_t)markOopDesc::epoch_mask_in_place;
markOop mark = lockee->mark();
intptr_t hash = (intptr_t) markOopDesc::no_hash;
// implies UseBiasedLocking
if (mark->has_bias_pattern()) {
uintptr_t thread_ident;
uintptr_t anticipated_bias_locking_value;
thread_ident = (uintptr_t)istate->thread();
anticipated_bias_locking_value =
(((uintptr_t)lockee->klass()->prototype_header() | thread_ident) ^ (uintptr_t)mark) &
~((uintptr_t) markOopDesc::age_mask_in_place);
if (anticipated_bias_locking_value == 0) {
// already biased towards this thread, nothing to do
if (PrintBiasedLockingStatistics) {
(* BiasedLocking::biased_lock_entry_count_addr())++;
}
success = true;
} else if ((anticipated_bias_locking_value & markOopDesc::biased_lock_mask_in_place) != 0) {
// try revoke bias
markOop header = lockee->klass()->prototype_header();
if (hash != markOopDesc::no_hash) {
header = header->copy_set_hash(hash);
}
if (Atomic::cmpxchg_ptr(header, lockee->mark_addr(), mark) == mark) {
if (PrintBiasedLockingStatistics) {
(*BiasedLocking::revoked_lock_entry_count_addr())++;
}
}
} else if ((anticipated_bias_locking_value & epoch_mask_in_place) !=0) {
// try rebias
markOop new_header = (markOop) ( (intptr_t) lockee->klass()->prototype_header() | thread_ident);
if (hash != markOopDesc::no_hash) {
new_header = new_header->copy_set_hash(hash);
}
if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), mark) == mark) {
if (PrintBiasedLockingStatistics) {
(* BiasedLocking::rebiased_lock_entry_count_addr())++;
}
} else {
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
success = true;
} else {
// try to bias towards thread in case object is anonymously biased
markOop header = (markOop) ((uintptr_t) mark & ((uintptr_t)markOopDesc::biased_lock_mask_in_place |
(uintptr_t)markOopDesc::age_mask_in_place | epoch_mask_in_place));
if (hash != markOopDesc::no_hash) {
header = header->copy_set_hash(hash);
}
markOop new_header = (markOop) ((uintptr_t) header | thread_ident);
// debugging hint
DEBUG_ONLY(entry->lock()->set_displaced_header((markOop) (uintptr_t) 0xdeaddead);)
if (Atomic::cmpxchg_ptr((void*)new_header, lockee->mark_addr(), header) == header) {
if (PrintBiasedLockingStatistics) {
(* BiasedLocking::anonymously_biased_lock_entry_count_addr())++;
}
} else {
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
success = true;
}
}
// traditional lightweight locking
if (!success) {
markOop displaced = lockee->mark()->set_unlocked();
entry->lock()->set_displaced_header(displaced);
bool call_vm = UseHeavyMonitors;
if (call_vm || Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) {
// Is it simple recursive case?
if (!call_vm && THREAD->is_lock_owned((address) displaced->clear_lock_bits())) {
entry->lock()->set_displaced_header(NULL);
} else {
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
}
}
UPDATE_PC_AND_TOS(1, -1);
goto run;
}
default: {
fatal("Unexpected message from frame manager");
}
}
run:
DO_UPDATE_INSTRUCTION_COUNT(*pc)
DEBUGGER_SINGLE_STEP_NOTIFY();
#ifdef PREFETCH_OPCCODE
opcode = *pc; /* prefetch first opcode */
#endif
#ifndef USELABELS
while (1)
#endif
{
#ifndef PREFETCH_OPCCODE
opcode = *pc;
#endif
// Seems like this happens twice per opcode. At worst this is only
// need at entry to the loop.
// DEBUGGER_SINGLE_STEP_NOTIFY();
/* Using this labels avoids double breakpoints when quickening and
* when returing from transition frames.
*/