diff --git a/.github/workflows/build-cross-compile.yml b/.github/workflows/build-cross-compile.yml index fa5a8a61a0a..29750b485e4 100644 --- a/.github/workflows/build-cross-compile.yml +++ b/.github/workflows/build-cross-compile.yml @@ -84,7 +84,7 @@ jobs: - target-cpu: riscv64 gnu-arch: riscv64 debian-arch: riscv64 - debian-repository: https://httpredir.debian.org/debian/ + debian-repository: https://snapshot.debian.org/archive/debian/20240228T034848Z/ debian-version: sid tolerate-sysroot-errors: true diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp index 901ae502999..3117c751498 100644 --- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp @@ -2181,7 +2181,8 @@ void SharedRuntime::generate_deopt_blob() { pad += 512; // Increase the buffer size when compiling for JVMCI } #endif - CodeBuffer buffer("deopt_blob", 2048+pad, 1024); + const char* name = SharedRuntime::stub_name(SharedStubId::deopt_id); + CodeBuffer buffer(name, 2048+pad, 1024); MacroAssembler* masm = new MacroAssembler(&buffer); int frame_size_in_words; OopMap* map = nullptr; @@ -2565,20 +2566,23 @@ uint SharedRuntime::out_preserve_stack_slots() { // Generate a special Compile2Runtime blob that saves all registers, // and setup oopmap. // -SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_type) { +SafepointBlob* SharedRuntime::generate_handler_blob(SharedStubId id, address call_ptr) { + assert(is_polling_page_id(id), "expected a polling page stub id"); + ResourceMark rm; OopMapSet *oop_maps = new OopMapSet(); OopMap* map; // Allocate space for the code. Setup code generation tools. - CodeBuffer buffer("handler_blob", 2048, 1024); + const char* name = SharedRuntime::stub_name(id); + CodeBuffer buffer(name, 2048, 1024); MacroAssembler* masm = new MacroAssembler(&buffer); address start = __ pc(); address call_pc = nullptr; int frame_size_in_words; - bool cause_return = (poll_type == POLL_AT_RETURN); - RegisterSaver reg_save(poll_type == POLL_AT_VECTOR_LOOP /* save_vectors */); + bool cause_return = (id == SharedStubId::polling_page_return_handler_id); + RegisterSaver reg_save(id == SharedStubId::polling_page_vectors_safepoint_handler_id /* save_vectors */); // When the signal occurred, the LR was either signed and stored on the stack (in which // case it will be restored from the stack before being used) or unsigned and not stored @@ -2690,12 +2694,14 @@ SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_t // but since this is generic code we don't know what they are and the caller // must do any gc of the args. // -RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const char* name) { +RuntimeStub* SharedRuntime::generate_resolve_blob(SharedStubId id, address destination) { assert (StubRoutines::forward_exception_entry() != nullptr, "must be generated before"); + assert(is_resolve_id(id), "expected a resolve stub id"); // allocate space for the code ResourceMark rm; + const char* name = SharedRuntime::stub_name(id); CodeBuffer buffer(name, 1000, 512); MacroAssembler* masm = new MacroAssembler(&buffer); @@ -2787,7 +2793,11 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const cha // otherwise assume that stack unwinding will be initiated, so // caller saved registers were assumed volatile in the compiler. -RuntimeStub* SharedRuntime::generate_throw_exception(const char* name, address runtime_entry) { +RuntimeStub* SharedRuntime::generate_throw_exception(SharedStubId id, address runtime_entry) { + assert(is_throw_id(id), "expected a throw stub id"); + + const char* name = SharedRuntime::stub_name(id); + // Information about frame layout at time of blocking runtime call. // Note that we only have to preserve callee-saved registers since // the compilers are responsible for supplying a continuation point @@ -2896,7 +2906,8 @@ RuntimeStub* SharedRuntime::generate_jfr_write_checkpoint() { int insts_size = 1024; int locs_size = 64; - CodeBuffer code("jfr_write_checkpoint", insts_size, locs_size); + const char* name = SharedRuntime::stub_name(SharedStubId::jfr_write_checkpoint_id); + CodeBuffer code(name, insts_size, locs_size); OopMapSet* oop_maps = new OopMapSet(); MacroAssembler* masm = new MacroAssembler(&code); @@ -2915,7 +2926,7 @@ RuntimeStub* SharedRuntime::generate_jfr_write_checkpoint() { oop_maps->add_gc_map(the_pc - start, map); RuntimeStub* stub = // codeBlob framesize is in words (not VMRegImpl::slot_size) - RuntimeStub::new_runtime_stub("jfr_write_checkpoint", &code, frame_complete, + RuntimeStub::new_runtime_stub(name, &code, frame_complete, (framesize >> (LogBytesPerWord - LogBytesPerInt)), oop_maps, false); return stub; @@ -2934,7 +2945,8 @@ RuntimeStub* SharedRuntime::generate_jfr_return_lease() { int insts_size = 1024; int locs_size = 64; - CodeBuffer code("jfr_return_lease", insts_size, locs_size); + const char* name = SharedRuntime::stub_name(SharedStubId::jfr_return_lease_id); + CodeBuffer code(name, insts_size, locs_size); OopMapSet* oop_maps = new OopMapSet(); MacroAssembler* masm = new MacroAssembler(&code); @@ -2953,7 +2965,7 @@ RuntimeStub* SharedRuntime::generate_jfr_return_lease() { oop_maps->add_gc_map(the_pc - start, map); RuntimeStub* stub = // codeBlob framesize is in words (not VMRegImpl::slot_size) - RuntimeStub::new_runtime_stub("jfr_return_lease", &code, frame_complete, + RuntimeStub::new_runtime_stub(name, &code, frame_complete, (framesize >> (LogBytesPerWord - LogBytesPerInt)), oop_maps, false); return stub; diff --git a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp index f07c1f8e53c..7648e5c5d92 100644 --- a/src/hotspot/cpu/arm/sharedRuntime_arm.cpp +++ b/src/hotspot/cpu/arm/sharedRuntime_arm.cpp @@ -1360,7 +1360,8 @@ uint SharedRuntime::out_preserve_stack_slots() { //------------------------------generate_deopt_blob---------------------------- void SharedRuntime::generate_deopt_blob() { ResourceMark rm; - CodeBuffer buffer("deopt_blob", 1024, 1024); + const char* name = SharedRuntime::stub_name(SharedStubId::deopt_id); + CodeBuffer buffer(name, 1024, 1024); int frame_size_in_words; OopMapSet* oop_maps; int reexecute_offset; @@ -1601,15 +1602,17 @@ void SharedRuntime::generate_deopt_blob() { // setup oopmap, and calls safepoint code to stop the compiled code for // a safepoint. // -SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_type) { +SafepointBlob* SharedRuntime::generate_handler_blob(SharedStubId id, address call_ptr) { assert(StubRoutines::forward_exception_entry() != nullptr, "must be generated before"); + assert(is_polling_page_id(id), "expected a polling page stub id"); ResourceMark rm; - CodeBuffer buffer("handler_blob", 256, 256); + const char* name = SharedRuntime::stub_name(id); + CodeBuffer buffer(name, 256, 256); int frame_size_words; OopMapSet* oop_maps; - bool cause_return = (poll_type == POLL_AT_RETURN); + bool cause_return = (id == SharedStubId::polling_page_return_handler_id); MacroAssembler* masm = new MacroAssembler(&buffer); address start = __ pc(); @@ -1671,10 +1674,12 @@ SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_t return SafepointBlob::create(&buffer, oop_maps, frame_size_words); } -RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const char* name) { +RuntimeStub* SharedRuntime::generate_resolve_blob(SharedStubId id, address destination) { assert(StubRoutines::forward_exception_entry() != nullptr, "must be generated before"); + assert(is_resolve_id(id), "expected a resolve stub id"); ResourceMark rm; + const char* name = SharedRuntime::stub_name(id); CodeBuffer buffer(name, 1000, 512); int frame_size_words; OopMapSet *oop_maps; @@ -1733,7 +1738,11 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const cha // Continuation point for throwing of implicit exceptions that are not handled in // the current activation. Fabricates an exception oop and initiates normal // exception dispatching in this frame. -RuntimeStub* SharedRuntime::generate_throw_exception(const char* name, address runtime_entry) { +RuntimeStub* SharedRuntime::generate_throw_exception(SharedStubId id, address runtime_entry) { + assert(is_throw_id(id), "expected a throw stub id"); + + const char* name = SharedRuntime::stub_name(id); + int insts_size = 128; int locs_size = 32; @@ -1793,7 +1802,8 @@ RuntimeStub* SharedRuntime::generate_jfr_write_checkpoint() { framesize // inclusive of return address }; - CodeBuffer code("jfr_write_checkpoint", 512, 64); + const char* name = SharedRuntime::stub_name(SharedStubId::jfr_write_checkpoint_id); + CodeBuffer code(name, 512, 64); MacroAssembler* masm = new MacroAssembler(&code); address start = __ pc(); @@ -1818,7 +1828,7 @@ RuntimeStub* SharedRuntime::generate_jfr_write_checkpoint() { oop_maps->add_gc_map(frame_complete, map); RuntimeStub* stub = - RuntimeStub::new_runtime_stub(code.name(), + RuntimeStub::new_runtime_stub(name, &code, frame_complete, (framesize >> (LogBytesPerWord - LogBytesPerInt)), @@ -1836,7 +1846,8 @@ RuntimeStub* SharedRuntime::generate_jfr_return_lease() { framesize // inclusive of return address }; - CodeBuffer code("jfr_return_lease", 512, 64); + const char* name = SharedRuntime::stub_name(SharedStubId::jfr_return_lease_id); + CodeBuffer code(name, 512, 64); MacroAssembler* masm = new MacroAssembler(&code); address start = __ pc(); @@ -1858,7 +1869,7 @@ RuntimeStub* SharedRuntime::generate_jfr_return_lease() { oop_maps->add_gc_map(frame_complete, map); RuntimeStub* stub = - RuntimeStub::new_runtime_stub(code.name(), + RuntimeStub::new_runtime_stub(name, &code, frame_complete, (framesize >> (LogBytesPerWord - LogBytesPerInt)), diff --git a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp index 981f1c7afd2..5cf5f7cf73e 100644 --- a/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp +++ b/src/hotspot/cpu/ppc/sharedRuntime_ppc.cpp @@ -2856,7 +2856,8 @@ void SharedRuntime::generate_deopt_blob() { // Allocate space for the code ResourceMark rm; // Setup code generation tools - CodeBuffer buffer("deopt_blob", 2048, 1024); + const char* name = SharedRuntime::stub_name(SharedStubId::deopt_id); + CodeBuffer buffer(name, 2048, 1024); InterpreterMacroAssembler* masm = new InterpreterMacroAssembler(&buffer); Label exec_mode_initialized; int frame_size_in_words; @@ -3206,23 +3207,25 @@ void OptoRuntime::generate_uncommon_trap_blob() { #endif // COMPILER2 // Generate a special Compile2Runtime blob that saves all registers, and setup oopmap. -SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_type) { +SafepointBlob* SharedRuntime::generate_handler_blob(SharedStubId id, address call_ptr) { assert(StubRoutines::forward_exception_entry() != nullptr, "must be generated before"); + assert(is_polling_page_id(id), "expected a polling page stub id"); ResourceMark rm; OopMapSet *oop_maps = new OopMapSet(); OopMap* map; // Allocate space for the code. Setup code generation tools. - CodeBuffer buffer("handler_blob", 2048, 1024); + const char* name = SharedRuntime::stub_name(id); + CodeBuffer buffer(name, 2048, 1024); MacroAssembler* masm = new MacroAssembler(&buffer); address start = __ pc(); int frame_size_in_bytes = 0; RegisterSaver::ReturnPCLocation return_pc_location; - bool cause_return = (poll_type == POLL_AT_RETURN); + bool cause_return = (id == SharedStubId::polling_page_return_handler_id); if (cause_return) { // Nothing to do here. The frame has already been popped in MachEpilogNode. // Register LR already contains the return pc. @@ -3232,7 +3235,7 @@ SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_t return_pc_location = RegisterSaver::return_pc_is_thread_saved_exception_pc; } - bool save_vectors = (poll_type == POLL_AT_VECTOR_LOOP); + bool save_vectors = (id == SharedStubId::polling_page_vectors_safepoint_handler_id); // Save registers, fpu state, and flags. Set R31 = return pc. map = RegisterSaver::push_frame_reg_args_and_save_live_registers(masm, @@ -3319,11 +3322,13 @@ SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_t // but since this is generic code we don't know what they are and the caller // must do any gc of the args. // -RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const char* name) { +RuntimeStub* SharedRuntime::generate_resolve_blob(SharedStubId id, address destination) { + assert(is_resolve_id(id), "expected a resolve stub id"); // allocate space for the code ResourceMark rm; + const char* name = SharedRuntime::stub_name(id); CodeBuffer buffer(name, 1000, 512); MacroAssembler* masm = new MacroAssembler(&buffer); @@ -3421,7 +3426,11 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const cha // Note: the routine set_pc_not_at_call_for_caller in // SharedRuntime.cpp requires that this code be generated into a // RuntimeStub. -RuntimeStub* SharedRuntime::generate_throw_exception(const char* name, address runtime_entry) { +RuntimeStub* SharedRuntime::generate_throw_exception(SharedStubId id, address runtime_entry) { + assert(is_throw_id(id), "expected a throw stub id"); + + const char* name = SharedRuntime::stub_name(id); + ResourceMark rm; const char* timer_msg = "SharedRuntime generate_throw_exception"; TraceTime timer(timer_msg, TRACETIME_LOG(Info, startuptime)); @@ -3740,7 +3749,8 @@ void SharedRuntime::montgomery_square(jint *a_ints, jint *n_ints, // It returns a jobject handle to the event writer. // The handle is dereferenced and the return value is the event writer oop. RuntimeStub* SharedRuntime::generate_jfr_write_checkpoint() { - CodeBuffer code("jfr_write_checkpoint", 512, 64); + const char* name = SharedRuntime::stub_name(SharedStubId::jfr_write_checkpoint_id); + CodeBuffer code(name, 512, 64); MacroAssembler* masm = new MacroAssembler(&code); Register tmp1 = R10_ARG8; @@ -3768,8 +3778,7 @@ RuntimeStub* SharedRuntime::generate_jfr_write_checkpoint() { oop_maps->add_gc_map(calls_return_pc - start, map); RuntimeStub* stub = // codeBlob framesize is in words (not VMRegImpl::slot_size) - RuntimeStub::new_runtime_stub(code.name(), - &code, frame_complete, + RuntimeStub::new_runtime_stub(name, &code, frame_complete, (framesize >> (LogBytesPerWord - LogBytesPerInt)), oop_maps, false); return stub; @@ -3777,7 +3786,8 @@ RuntimeStub* SharedRuntime::generate_jfr_write_checkpoint() { // For c2: call to return a leased buffer. RuntimeStub* SharedRuntime::generate_jfr_return_lease() { - CodeBuffer code("jfr_return_lease", 512, 64); + const char* name = SharedRuntime::stub_name(SharedStubId::jfr_return_lease_id); + CodeBuffer code(name, 512, 64); MacroAssembler* masm = new MacroAssembler(&code); Register tmp1 = R10_ARG8; @@ -3803,8 +3813,7 @@ RuntimeStub* SharedRuntime::generate_jfr_return_lease() { oop_maps->add_gc_map(calls_return_pc - start, map); RuntimeStub* stub = // codeBlob framesize is in words (not VMRegImpl::slot_size) - RuntimeStub::new_runtime_stub(code.name(), - &code, frame_complete, + RuntimeStub::new_runtime_stub(name, &code, frame_complete, (framesize >> (LogBytesPerWord - LogBytesPerInt)), oop_maps, false); return stub; diff --git a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp index ffd904aed47..879fd922722 100644 --- a/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp +++ b/src/hotspot/cpu/riscv/sharedRuntime_riscv.cpp @@ -2057,7 +2057,8 @@ void SharedRuntime::generate_deopt_blob() { pad += 512; // Increase the buffer size when compiling for JVMCI } #endif - CodeBuffer buffer("deopt_blob", 2048 + pad, 1024); + const char* name = SharedRuntime::stub_name(SharedStubId::deopt_id); + CodeBuffer buffer(name, 2048 + pad, 1024); MacroAssembler* masm = new MacroAssembler(&buffer); int frame_size_in_words = -1; OopMap* map = nullptr; @@ -2435,22 +2436,25 @@ uint SharedRuntime::out_preserve_stack_slots() { // Generate a special Compile2Runtime blob that saves all registers, // and setup oopmap. // -SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_type) { +SafepointBlob* SharedRuntime::generate_handler_blob(SharedStubId id, address call_ptr) { + assert(is_polling_page_id(id), "expected a polling page stub id"); + ResourceMark rm; OopMapSet *oop_maps = new OopMapSet(); assert_cond(oop_maps != nullptr); OopMap* map = nullptr; // Allocate space for the code. Setup code generation tools. - CodeBuffer buffer("handler_blob", 2048, 1024); + const char* name = SharedRuntime::stub_name(id); + CodeBuffer buffer(name, 2048, 1024); MacroAssembler* masm = new MacroAssembler(&buffer); assert_cond(masm != nullptr); address start = __ pc(); address call_pc = nullptr; int frame_size_in_words = -1; - bool cause_return = (poll_type == POLL_AT_RETURN); - RegisterSaver reg_saver(poll_type == POLL_AT_VECTOR_LOOP /* save_vectors */); + bool cause_return = (id == SharedStubId::polling_page_return_handler_id); + RegisterSaver reg_saver(id == SharedStubId::polling_page_vectors_safepoint_handler_id /* save_vectors */); // Save Integer and Float registers. map = reg_saver.save_live_registers(masm, 0, &frame_size_in_words); @@ -2556,12 +2560,14 @@ SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_t // but since this is generic code we don't know what they are and the caller // must do any gc of the args. // -RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const char* name) { +RuntimeStub* SharedRuntime::generate_resolve_blob(SharedStubId id, address destination) { assert(StubRoutines::forward_exception_entry() != nullptr, "must be generated before"); + assert(is_resolve_id(id), "expected a resolve stub id"); // allocate space for the code ResourceMark rm; + const char* name = SharedRuntime::stub_name(id); CodeBuffer buffer(name, 1000, 512); MacroAssembler* masm = new MacroAssembler(&buffer); assert_cond(masm != nullptr); @@ -2652,7 +2658,11 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const cha // otherwise assume that stack unwinding will be initiated, so // caller saved registers were assumed volatile in the compiler. -RuntimeStub* SharedRuntime::generate_throw_exception(const char* name, address runtime_entry) { +RuntimeStub* SharedRuntime::generate_throw_exception(SharedStubId id, address runtime_entry) { + assert(is_throw_id(id), "expected a throw stub id"); + + const char* name = SharedRuntime::stub_name(id); + // Information about frame layout at time of blocking runtime call. // Note that we only have to preserve callee-saved registers since // the compilers are responsible for supplying a continuation point @@ -2759,7 +2769,8 @@ RuntimeStub* SharedRuntime::generate_jfr_write_checkpoint() { int insts_size = 1024; int locs_size = 64; - CodeBuffer code("jfr_write_checkpoint", insts_size, locs_size); + const char* name = SharedRuntime::stub_name(SharedStubId::jfr_write_checkpoint_id); + CodeBuffer code(name, insts_size, locs_size); OopMapSet* oop_maps = new OopMapSet(); MacroAssembler* masm = new MacroAssembler(&code); @@ -2779,7 +2790,7 @@ RuntimeStub* SharedRuntime::generate_jfr_write_checkpoint() { oop_maps->add_gc_map(the_pc - start, map); RuntimeStub* stub = // codeBlob framesize is in words (not VMRegImpl::slot_size) - RuntimeStub::new_runtime_stub("jfr_write_checkpoint", &code, frame_complete, + RuntimeStub::new_runtime_stub(name, &code, frame_complete, (framesize >> (LogBytesPerWord - LogBytesPerInt)), oop_maps, false); return stub; @@ -2797,7 +2808,8 @@ RuntimeStub* SharedRuntime::generate_jfr_return_lease() { int insts_size = 1024; int locs_size = 64; - CodeBuffer code("jfr_return_lease", insts_size, locs_size); + const char* name = SharedRuntime::stub_name(SharedStubId::jfr_return_lease_id); + CodeBuffer code(name, insts_size, locs_size); OopMapSet* oop_maps = new OopMapSet(); MacroAssembler* masm = new MacroAssembler(&code); @@ -2816,7 +2828,7 @@ RuntimeStub* SharedRuntime::generate_jfr_return_lease() { oop_maps->add_gc_map(the_pc - start, map); RuntimeStub* stub = // codeBlob framesize is in words (not VMRegImpl::slot_size) - RuntimeStub::new_runtime_stub("jfr_return_lease", &code, frame_complete, + RuntimeStub::new_runtime_stub(name, &code, frame_complete, (framesize >> (LogBytesPerWord - LogBytesPerInt)), oop_maps, false); return stub; diff --git a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp index 364ef948d91..9954c78ce1e 100644 --- a/src/hotspot/cpu/s390/sharedRuntime_s390.cpp +++ b/src/hotspot/cpu/s390/sharedRuntime_s390.cpp @@ -2488,7 +2488,8 @@ void SharedRuntime::generate_deopt_blob() { // Allocate space for the code. ResourceMark rm; // Setup code generation tools. - CodeBuffer buffer("deopt_blob", 2048, 1024); + const char* name = SharedRuntime::stub_name(SharedStubId::deopt_id); + CodeBuffer buffer(name, 2048, 1024); InterpreterMacroAssembler* masm = new InterpreterMacroAssembler(&buffer); Label exec_mode_initialized; OopMap* map = nullptr; @@ -2834,23 +2835,25 @@ void OptoRuntime::generate_uncommon_trap_blob() { // // Generate a special Compile2Runtime blob that saves all registers, // and setup oopmap. -SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_type) { +SafepointBlob* SharedRuntime::generate_handler_blob(SharedStubId id, address call_ptr) { assert(StubRoutines::forward_exception_entry() != nullptr, "must be generated before"); + assert(is_polling_page_id(id), "expected a polling page stub id"); ResourceMark rm; OopMapSet *oop_maps = new OopMapSet(); OopMap* map; // Allocate space for the code. Setup code generation tools. - CodeBuffer buffer("handler_blob", 2048, 1024); + const char* name = SharedRuntime::stub_name(id); + CodeBuffer buffer(name, 2048, 1024); MacroAssembler* masm = new MacroAssembler(&buffer); unsigned int start_off = __ offset(); address call_pc = nullptr; int frame_size_in_bytes; - bool cause_return = (poll_type == POLL_AT_RETURN); + bool cause_return = (id == SharedStubId::polling_page_return_handler_id); // Make room for return address (or push it again) if (!cause_return) { __ z_lg(Z_R14, Address(Z_thread, JavaThread::saved_exception_pc_offset())); @@ -2935,12 +2938,14 @@ SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_t // but since this is generic code we don't know what they are and the caller // must do any gc of the args. // -RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const char* name) { +RuntimeStub* SharedRuntime::generate_resolve_blob(SharedStubId id, address destination) { assert (StubRoutines::forward_exception_entry() != nullptr, "must be generated before"); + assert(is_resolve_id(id), "expected a resolve stub id"); // allocate space for the code ResourceMark rm; + const char* name = SharedRuntime::stub_name(id); CodeBuffer buffer(name, 1000, 512); MacroAssembler* masm = new MacroAssembler(&buffer); @@ -3032,7 +3037,10 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const cha // SharedRuntime.cpp requires that this code be generated into a // RuntimeStub. -RuntimeStub* SharedRuntime::generate_throw_exception(const char* name, address runtime_entry) { +RuntimeStub* SharedRuntime::generate_throw_exception(SharedStubId id, address runtime_entry) { + assert(is_throw_id(id), "expected a throw stub id"); + + const char* name = SharedRuntime::stub_name(id); int insts_size = 256; int locs_size = 0; diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp index 273bbcc6525..c6ab00cc9ee 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_32.cpp @@ -2062,7 +2062,8 @@ void SharedRuntime::generate_deopt_blob() { ResourceMark rm; // setup code generation tools // note: the buffer code size must account for StackShadowPages=50 - CodeBuffer buffer("deopt_blob", 1536, 1024); + const char* name = SharedRuntime::stub_name(SharedStubId::deopt_id); + CodeBuffer buffer(name, 1536, 1024); MacroAssembler* masm = new MacroAssembler(&buffer); int frame_size_in_words; OopMap* map = nullptr; @@ -2403,13 +2404,14 @@ void SharedRuntime::generate_deopt_blob() { // setup oopmap, and calls safepoint code to stop the compiled code for // a safepoint. // -SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_type) { +SafepointBlob* SharedRuntime::generate_handler_blob(SharedStubId id, address call_ptr) { // Account for thread arg in our frame const int additional_words = 1; int frame_size_in_words; assert (StubRoutines::forward_exception_entry() != nullptr, "must be generated before"); + assert(is_polling_page_id(id), "expected a polling page stub id"); ResourceMark rm; OopMapSet *oop_maps = new OopMapSet(); @@ -2417,14 +2419,15 @@ SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_t // allocate space for the code // setup code generation tools - CodeBuffer buffer("handler_blob", 2048, 1024); + const char* name = SharedRuntime::stub_name(id); + CodeBuffer buffer(name, 2048, 1024); MacroAssembler* masm = new MacroAssembler(&buffer); const Register java_thread = rdi; // callee-saved for VC++ address start = __ pc(); address call_pc = nullptr; - bool cause_return = (poll_type == POLL_AT_RETURN); - bool save_vectors = (poll_type == POLL_AT_VECTOR_LOOP); + bool cause_return = (id == SharedStubId::polling_page_return_handler_id); + bool save_vectors = (id == SharedStubId::polling_page_vectors_safepoint_handler_id); // If cause_return is true we are at a poll_return and there is // the return address on the stack to the caller on the nmethod @@ -2556,12 +2559,14 @@ SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_t // but since this is generic code we don't know what they are and the caller // must do any gc of the args. // -RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const char* name) { +RuntimeStub* SharedRuntime::generate_resolve_blob(SharedStubId id, address destination) { assert (StubRoutines::forward_exception_entry() != nullptr, "must be generated before"); + assert(is_resolve_id(id), "expected a resolve stub id"); // allocate space for the code ResourceMark rm; + const char* name = SharedRuntime::stub_name(id); CodeBuffer buffer(name, 1000, 512); MacroAssembler* masm = new MacroAssembler(&buffer); @@ -2662,7 +2667,10 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const cha // exceptions (e.g., NullPointerException or AbstractMethodError on entry) are // either at call sites or otherwise assume that stack unwinding will be initiated, // so caller saved registers were assumed volatile in the compiler. -RuntimeStub* SharedRuntime::generate_throw_exception(const char* name, address runtime_entry) { +RuntimeStub* SharedRuntime::generate_throw_exception(SharedStubId id, address runtime_entry) { + assert(is_throw_id(id), "expected a throw stub id"); + + const char* name = SharedRuntime::stub_name(id); // Information about frame layout at time of blocking runtime call. // Note that we only have to preserve callee-saved registers since @@ -2776,7 +2784,8 @@ RuntimeStub* SharedRuntime::generate_jfr_write_checkpoint() { int insts_size = 1024; int locs_size = 64; - CodeBuffer code("jfr_write_checkpoint", insts_size, locs_size); + const char* name = SharedRuntime::stub_name(SharedStubId::jfr_write_checkpoint_id); + CodeBuffer code(name, insts_size, locs_size); OopMapSet* oop_maps = new OopMapSet(); MacroAssembler* masm = new MacroAssembler(&code); @@ -2795,7 +2804,7 @@ RuntimeStub* SharedRuntime::generate_jfr_write_checkpoint() { oop_maps->add_gc_map(the_pc - start, map); RuntimeStub* stub = // codeBlob framesize is in words (not VMRegImpl::slot_size) - RuntimeStub::new_runtime_stub("jfr_write_checkpoint", &code, frame_complete, + RuntimeStub::new_runtime_stub(name, &code, frame_complete, (framesize >> (LogBytesPerWord - LogBytesPerInt)), oop_maps, false); return stub; @@ -2817,7 +2826,8 @@ RuntimeStub* SharedRuntime::generate_jfr_return_lease() { int insts_size = 1024; int locs_size = 64; - CodeBuffer code("jfr_return_lease", insts_size, locs_size); + const char* name = SharedRuntime::stub_name(SharedStubId::jfr_return_lease_id); + CodeBuffer code(name, insts_size, locs_size); OopMapSet* oop_maps = new OopMapSet(); MacroAssembler* masm = new MacroAssembler(&code); @@ -2835,7 +2845,7 @@ RuntimeStub* SharedRuntime::generate_jfr_return_lease() { oop_maps->add_gc_map(the_pc - start, map); RuntimeStub* stub = // codeBlob framesize is in words (not VMRegImpl::slot_size) - RuntimeStub::new_runtime_stub("jfr_return_lease", &code, frame_complete, + RuntimeStub::new_runtime_stub(name, &code, frame_complete, (framesize >> (LogBytesPerWord - LogBytesPerInt)), oop_maps, false); return stub; diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp index 05ec85ef09a..4bd91f640fc 100644 --- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp +++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp @@ -2623,7 +2623,8 @@ void SharedRuntime::generate_deopt_blob() { pad += 512; // Increase the buffer size when compiling for JVMCI } #endif - CodeBuffer buffer("deopt_blob", 2560+pad, 1024); + const char* name = SharedRuntime::stub_name(SharedStubId::deopt_id); + CodeBuffer buffer(name, 2560+pad, 1024); MacroAssembler* masm = new MacroAssembler(&buffer); int frame_size_in_words; OopMap* map = nullptr; @@ -2981,23 +2982,25 @@ void SharedRuntime::generate_deopt_blob() { // Generate a special Compile2Runtime blob that saves all registers, // and setup oopmap. // -SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_type) { +SafepointBlob* SharedRuntime::generate_handler_blob(SharedStubId id, address call_ptr) { assert(StubRoutines::forward_exception_entry() != nullptr, "must be generated before"); + assert(is_polling_page_id(id), "expected a polling page stub id"); ResourceMark rm; OopMapSet *oop_maps = new OopMapSet(); OopMap* map; // Allocate space for the code. Setup code generation tools. - CodeBuffer buffer("handler_blob", 2348, 1024); + const char* name = SharedRuntime::stub_name(id); + CodeBuffer buffer(name, 2348, 1024); MacroAssembler* masm = new MacroAssembler(&buffer); address start = __ pc(); address call_pc = nullptr; int frame_size_in_words; - bool cause_return = (poll_type == POLL_AT_RETURN); - bool save_wide_vectors = (poll_type == POLL_AT_VECTOR_LOOP); + bool cause_return = (id == SharedStubId::polling_page_return_handler_id); + bool save_wide_vectors = (id == SharedStubId::polling_page_vectors_safepoint_handler_id); // Make room for return address (or push it again) if (!cause_return) { @@ -3140,12 +3143,14 @@ SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_t // but since this is generic code we don't know what they are and the caller // must do any gc of the args. // -RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const char* name) { +RuntimeStub* SharedRuntime::generate_resolve_blob(SharedStubId id, address destination) { assert (StubRoutines::forward_exception_entry() != nullptr, "must be generated before"); + assert(is_resolve_id(id), "expected a resolve stub id"); // allocate space for the code ResourceMark rm; + const char* name = SharedRuntime::stub_name(id); CodeBuffer buffer(name, 1552, 512); MacroAssembler* masm = new MacroAssembler(&buffer); @@ -3232,7 +3237,11 @@ RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const cha // AbstractMethodError on entry) are either at call sites or // otherwise assume that stack unwinding will be initiated, so // caller saved registers were assumed volatile in the compiler. -RuntimeStub* SharedRuntime::generate_throw_exception(const char* name, address runtime_entry) { +RuntimeStub* SharedRuntime::generate_throw_exception(SharedStubId id, address runtime_entry) { + assert(is_throw_id(id), "expected a throw stub id"); + + const char* name = SharedRuntime::stub_name(id); + // Information about frame layout at time of blocking runtime call. // Note that we only have to preserve callee-saved registers since // the compilers are responsible for supplying a continuation point @@ -3591,7 +3600,8 @@ RuntimeStub* SharedRuntime::generate_jfr_write_checkpoint() { framesize // inclusive of return address }; - CodeBuffer code("jfr_write_checkpoint", 1024, 64); + const char* name = SharedRuntime::stub_name(SharedStubId::jfr_write_checkpoint_id); + CodeBuffer code(name, 1024, 64); MacroAssembler* masm = new MacroAssembler(&code); address start = __ pc(); @@ -3616,7 +3626,7 @@ RuntimeStub* SharedRuntime::generate_jfr_write_checkpoint() { oop_maps->add_gc_map(frame_complete, map); RuntimeStub* stub = - RuntimeStub::new_runtime_stub(code.name(), + RuntimeStub::new_runtime_stub(name, &code, frame_complete, (framesize >> (LogBytesPerWord - LogBytesPerInt)), @@ -3635,7 +3645,8 @@ RuntimeStub* SharedRuntime::generate_jfr_return_lease() { framesize // inclusive of return address }; - CodeBuffer code("jfr_return_lease", 1024, 64); + const char* name = SharedRuntime::stub_name(SharedStubId::jfr_return_lease_id); + CodeBuffer code(name, 1024, 64); MacroAssembler* masm = new MacroAssembler(&code); address start = __ pc(); @@ -3657,7 +3668,7 @@ RuntimeStub* SharedRuntime::generate_jfr_return_lease() { oop_maps->add_gc_map(frame_complete, map); RuntimeStub* stub = - RuntimeStub::new_runtime_stub(code.name(), + RuntimeStub::new_runtime_stub(name, &code, frame_complete, (framesize >> (LogBytesPerWord - LogBytesPerInt)), diff --git a/src/hotspot/cpu/zero/sharedRuntime_zero.cpp b/src/hotspot/cpu/zero/sharedRuntime_zero.cpp index 454f8e5f632..18ceb9514d3 100644 --- a/src/hotspot/cpu/zero/sharedRuntime_zero.cpp +++ b/src/hotspot/cpu/zero/sharedRuntime_zero.cpp @@ -105,15 +105,15 @@ void SharedRuntime::generate_deopt_blob() { _deopt_blob = generate_empty_deopt_blob(); } -SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_type) { +SafepointBlob* SharedRuntime::generate_handler_blob(SharedStubId id, address call_ptr) { return generate_empty_safepoint_blob(); } -RuntimeStub* SharedRuntime::generate_resolve_blob(address destination, const char* name) { +RuntimeStub* SharedRuntime::generate_resolve_blob(SharedStubId id, address destination) { return generate_empty_runtime_stub(); } -RuntimeStub* SharedRuntime::generate_throw_exception(const char* name, address runtime_entry) { +RuntimeStub* SharedRuntime::generate_throw_exception(SharedStubId id, address runtime_entry) { return generate_empty_runtime_stub(); } diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 0ea3bfd122a..58a716abd21 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -754,14 +754,14 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci, init_tf(TypeFunc::make(domain, range)); StartNode* s = new StartOSRNode(root(), domain); initial_gvn()->set_type_bottom(s); - init_start(s); + verify_start(s); cg = CallGenerator::for_osr(method(), entry_bci()); } else { // Normal case. init_tf(TypeFunc::make(method())); StartNode* s = new StartNode(root(), tf()->domain()); initial_gvn()->set_type_bottom(s); - init_start(s); + verify_start(s); if (method()->intrinsic_id() == vmIntrinsics::_Reference_get) { // With java.lang.ref.reference.get() we must go through the // intrinsic - even when get() is the root @@ -1105,13 +1105,12 @@ void Compile::Init(bool aliasing) { probe_alias_cache(nullptr)->_index = AliasIdxTop; } -//---------------------------init_start---------------------------------------- -// Install the StartNode on this compile object. -void Compile::init_start(StartNode* s) { - if (failing()) - return; // already failing - assert(s == start(), ""); +#ifdef ASSERT +// Verify that the current StartNode is valid. +void Compile::verify_start(StartNode* s) const { + assert(failing() || s == start(), "should be StartNode"); } +#endif /** * Return the 'StartNode'. We must not have a pending failure, since the ideal graph diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 15c91f6ad12..e1574da568b 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -864,7 +864,7 @@ class Compile : public Phase { RootNode* root() const { return _root; } void set_root(RootNode* r) { _root = r; } StartNode* start() const; // (Derived from root.) - void init_start(StartNode* s); + void verify_start(StartNode* s) const NOT_DEBUG_RETURN; Node* immutable_memory(); Node* recent_alloc_ctl() const { return _recent_alloc_ctl; } diff --git a/src/hotspot/share/opto/generateOptoStub.cpp b/src/hotspot/share/opto/generateOptoStub.cpp index e22f484c179..2f940439045 100644 --- a/src/hotspot/share/opto/generateOptoStub.cpp +++ b/src/hotspot/share/opto/generateOptoStub.cpp @@ -261,7 +261,7 @@ void GraphKit::gen_stub(address C_function, frameptr(), returnadr()); root()->add_req(_gvn.transform(to_exc)); // bind to root to keep live - C->init_start(start); + C->verify_start(start); //----------------------------- // If this is a normal subroutine return, issue the return and be done. diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp index f98d031a2cd..d9b38133f99 100644 --- a/src/hotspot/share/runtime/sharedRuntime.cpp +++ b/src/hotspot/share/runtime/sharedRuntime.cpp @@ -92,61 +92,61 @@ // Shared runtime stub routines reside in their own unique blob with a // single entry point -RuntimeStub* SharedRuntime::_wrong_method_blob; -RuntimeStub* SharedRuntime::_wrong_method_abstract_blob; -RuntimeStub* SharedRuntime::_ic_miss_blob; -RuntimeStub* SharedRuntime::_resolve_opt_virtual_call_blob; -RuntimeStub* SharedRuntime::_resolve_virtual_call_blob; -RuntimeStub* SharedRuntime::_resolve_static_call_blob; - -DeoptimizationBlob* SharedRuntime::_deopt_blob; -SafepointBlob* SharedRuntime::_polling_page_vectors_safepoint_handler_blob; -SafepointBlob* SharedRuntime::_polling_page_safepoint_handler_blob; -SafepointBlob* SharedRuntime::_polling_page_return_handler_blob; - -RuntimeStub* SharedRuntime::_throw_AbstractMethodError_blob; -RuntimeStub* SharedRuntime::_throw_IncompatibleClassChangeError_blob; -RuntimeStub* SharedRuntime::_throw_NullPointerException_at_call_blob; -RuntimeStub* SharedRuntime::_throw_StackOverflowError_blob; -RuntimeStub* SharedRuntime::_throw_delayed_StackOverflowError_blob; -#if INCLUDE_JFR -RuntimeStub* SharedRuntime::_jfr_write_checkpoint_blob = nullptr; -RuntimeStub* SharedRuntime::_jfr_return_lease_blob = nullptr; -#endif +#define SHARED_STUB_FIELD_DEFINE(name, type) \ + type SharedRuntime::BLOB_FIELD_NAME(name); + SHARED_STUBS_DO(SHARED_STUB_FIELD_DEFINE) +#undef SHARED_STUB_FIELD_DEFINE nmethod* SharedRuntime::_cont_doYield_stub; +#define SHARED_STUB_NAME_DECLARE(name, type) "Shared Runtime " # name "_blob", +const char *SharedRuntime::_stub_names[] = { + SHARED_STUBS_DO(SHARED_STUB_NAME_DECLARE) +}; + //----------------------------generate_stubs----------------------------------- void SharedRuntime::generate_initial_stubs() { // Build this early so it's available for the interpreter. _throw_StackOverflowError_blob = - generate_throw_exception("StackOverflowError throw_exception", + generate_throw_exception(SharedStubId::throw_StackOverflowError_id, CAST_FROM_FN_PTR(address, SharedRuntime::throw_StackOverflowError)); } void SharedRuntime::generate_stubs() { - _wrong_method_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::handle_wrong_method), "wrong_method_stub"); - _wrong_method_abstract_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::handle_wrong_method_abstract), "wrong_method_abstract_stub"); - _ic_miss_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::handle_wrong_method_ic_miss), "ic_miss_stub"); - _resolve_opt_virtual_call_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::resolve_opt_virtual_call_C), "resolve_opt_virtual_call"); - _resolve_virtual_call_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::resolve_virtual_call_C), "resolve_virtual_call"); - _resolve_static_call_blob = generate_resolve_blob(CAST_FROM_FN_PTR(address, SharedRuntime::resolve_static_call_C), "resolve_static_call"); + _wrong_method_blob = + generate_resolve_blob(SharedStubId::wrong_method_id, + CAST_FROM_FN_PTR(address, SharedRuntime::handle_wrong_method)); + _wrong_method_abstract_blob = + generate_resolve_blob(SharedStubId::wrong_method_abstract_id, + CAST_FROM_FN_PTR(address, SharedRuntime::handle_wrong_method_abstract)); + _ic_miss_blob = + generate_resolve_blob(SharedStubId::ic_miss_id, + CAST_FROM_FN_PTR(address, SharedRuntime::handle_wrong_method_ic_miss)); + _resolve_opt_virtual_call_blob = + generate_resolve_blob(SharedStubId::resolve_opt_virtual_call_id, + CAST_FROM_FN_PTR(address, SharedRuntime::resolve_opt_virtual_call_C)); + _resolve_virtual_call_blob = + generate_resolve_blob(SharedStubId::resolve_virtual_call_id, + CAST_FROM_FN_PTR(address, SharedRuntime::resolve_virtual_call_C)); + _resolve_static_call_blob = + generate_resolve_blob(SharedStubId::resolve_static_call_id, + CAST_FROM_FN_PTR(address, SharedRuntime::resolve_static_call_C)); _throw_delayed_StackOverflowError_blob = - generate_throw_exception("delayed StackOverflowError throw_exception", + generate_throw_exception(SharedStubId::throw_delayed_StackOverflowError_id, CAST_FROM_FN_PTR(address, SharedRuntime::throw_delayed_StackOverflowError)); _throw_AbstractMethodError_blob = - generate_throw_exception("AbstractMethodError throw_exception", + generate_throw_exception(SharedStubId::throw_AbstractMethodError_id, CAST_FROM_FN_PTR(address, SharedRuntime::throw_AbstractMethodError)); _throw_IncompatibleClassChangeError_blob = - generate_throw_exception("IncompatibleClassChangeError throw_exception", + generate_throw_exception(SharedStubId::throw_IncompatibleClassChangeError_id, CAST_FROM_FN_PTR(address, SharedRuntime::throw_IncompatibleClassChangeError)); _throw_NullPointerException_at_call_blob = - generate_throw_exception("NullPointerException at call throw_exception", + generate_throw_exception(SharedStubId::throw_NullPointerException_at_call_id, CAST_FROM_FN_PTR(address, SharedRuntime::throw_NullPointerException_at_call)); AdapterHandlerLibrary::initialize(); @@ -155,11 +155,17 @@ void SharedRuntime::generate_stubs() { // Vectors are generated only by C2 and JVMCI. bool support_wide = is_wide_vector(MaxVectorSize); if (support_wide) { - _polling_page_vectors_safepoint_handler_blob = generate_handler_blob(CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception), POLL_AT_VECTOR_LOOP); + _polling_page_vectors_safepoint_handler_blob = + generate_handler_blob(SharedStubId::polling_page_vectors_safepoint_handler_id, + CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception)); } #endif // COMPILER2_OR_JVMCI - _polling_page_safepoint_handler_blob = generate_handler_blob(CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception), POLL_AT_LOOP); - _polling_page_return_handler_blob = generate_handler_blob(CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception), POLL_AT_RETURN); + _polling_page_safepoint_handler_blob = + generate_handler_blob(SharedStubId::polling_page_safepoint_handler_id, + CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception)); + _polling_page_return_handler_blob = + generate_handler_blob(SharedStubId::polling_page_return_handler_id, + CAST_FROM_FN_PTR(address, SafepointSynchronize::handle_polling_page_exception)); generate_deopt_blob(); } diff --git a/src/hotspot/share/runtime/sharedRuntime.hpp b/src/hotspot/share/runtime/sharedRuntime.hpp index 00a72f5db1e..f530c0ad2a8 100644 --- a/src/hotspot/share/runtime/sharedRuntime.hpp +++ b/src/hotspot/share/runtime/sharedRuntime.hpp @@ -30,6 +30,7 @@ #include "interpreter/linkResolver.hpp" #include "memory/allStatic.hpp" #include "memory/resourceArea.hpp" +#include "runtime/stubDeclarations.hpp" #include "utilities/macros.hpp" class AdapterHandlerEntry; @@ -42,37 +43,57 @@ class vframeStream; // Java exceptions), locking/unlocking mechanisms, statistical // information, etc. +// define SharedStubId enum tags: wrong_method_id, etc + +#define SHARED_STUB_ID_ENUM_DECLARE(name, type) STUB_ID_NAME(name), +enum class SharedStubId :int { + NO_STUBID = -1, + SHARED_STUBS_DO(SHARED_STUB_ID_ENUM_DECLARE) + NUM_STUBIDS +}; +#undef SHARED_STUB_ID_ENUM_DECLARE + class SharedRuntime: AllStatic { friend class VMStructs; private: - // Shared stub locations + // Declare shared stub fields +#define SHARED_STUB_FIELD_DECLARE(name, type) \ + static type BLOB_FIELD_NAME(name); + SHARED_STUBS_DO(SHARED_STUB_FIELD_DECLARE) +#undef SHARED_STUB_FIELD_DECLARE - static RuntimeStub* _wrong_method_blob; - static RuntimeStub* _wrong_method_abstract_blob; - static RuntimeStub* _ic_miss_blob; - static RuntimeStub* _resolve_opt_virtual_call_blob; - static RuntimeStub* _resolve_virtual_call_blob; - static RuntimeStub* _resolve_static_call_blob; - - static DeoptimizationBlob* _deopt_blob; - - static SafepointBlob* _polling_page_vectors_safepoint_handler_blob; - static SafepointBlob* _polling_page_safepoint_handler_blob; - static SafepointBlob* _polling_page_return_handler_blob; +#ifdef ASSERT + static bool is_resolve_id(SharedStubId id) { + return (id == SharedStubId::wrong_method_id || + id == SharedStubId::wrong_method_abstract_id || + id == SharedStubId::ic_miss_id || + id == SharedStubId::resolve_opt_virtual_call_id || + id == SharedStubId::resolve_virtual_call_id || + id == SharedStubId::resolve_static_call_id); + } + static bool is_polling_page_id(SharedStubId id) { + return (id == SharedStubId::polling_page_vectors_safepoint_handler_id || + id == SharedStubId::polling_page_safepoint_handler_id || + id == SharedStubId::polling_page_return_handler_id); + } + static bool is_throw_id(SharedStubId id) { + return (id == SharedStubId::throw_AbstractMethodError_id || + id == SharedStubId::throw_IncompatibleClassChangeError_id || + id == SharedStubId::throw_NullPointerException_at_call_id || + id == SharedStubId::throw_StackOverflowError_id || + id == SharedStubId::throw_delayed_StackOverflowError_id); + } +#endif + // cont_doYieldStub is not yet folded into the general model for + // shared stub/blob handling. It is actually a specially generated + // native wrapper for a specific native method, as also is it's + // counterpart the continuation do_enter method. static nmethod* _cont_doYield_stub; - static RuntimeStub* _throw_AbstractMethodError_blob; - static RuntimeStub* _throw_IncompatibleClassChangeError_blob; - static RuntimeStub* _throw_NullPointerException_at_call_blob; - static RuntimeStub* _throw_StackOverflowError_blob; - static RuntimeStub* _throw_delayed_StackOverflowError_blob; - -#if INCLUDE_JFR - static RuntimeStub* _jfr_write_checkpoint_blob; - static RuntimeStub* _jfr_return_lease_blob; -#endif + // Stub names indexed by SharedStubId + static const char *_stub_names[]; #ifndef PRODUCT // Counters @@ -80,10 +101,9 @@ class SharedRuntime: AllStatic { #endif // !PRODUCT private: - enum { POLL_AT_RETURN, POLL_AT_LOOP, POLL_AT_VECTOR_LOOP }; - static SafepointBlob* generate_handler_blob(address call_ptr, int poll_type); - static RuntimeStub* generate_resolve_blob(address destination, const char* name); - static RuntimeStub* generate_throw_exception(const char* name, address runtime_entry); + static SafepointBlob* generate_handler_blob(SharedStubId id, address call_ptr); + static RuntimeStub* generate_resolve_blob(SharedStubId id, address destination); + static RuntimeStub* generate_throw_exception(SharedStubId id, address runtime_entry); public: static void generate_initial_stubs(void); static void generate_stubs(void); @@ -97,6 +117,11 @@ class SharedRuntime: AllStatic { static RuntimeStub* generate_jfr_return_lease(); #endif + static const char *stub_name(SharedStubId id) { + assert(id > SharedStubId::NO_STUBID && id < SharedStubId::NUM_STUBIDS, "stub id out of range"); + return _stub_names[(int)id]; + } + // max bytes for each dtrace string parameter enum { max_dtrace_string_size = 256 }; diff --git a/src/hotspot/share/runtime/stubDeclarations.hpp b/src/hotspot/share/runtime/stubDeclarations.hpp new file mode 100644 index 00000000000..c9e946906f7 --- /dev/null +++ b/src/hotspot/share/runtime/stubDeclarations.hpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Red Hat, Inc. 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. + * + */ + +#ifndef SHARE_RUNTIME_STUBDECLARATIONS_HPP +#define SHARE_RUNTIME_STUBDECLARATIONS_HPP + +#include "utilities/macros.hpp" + +// macros for generating definitions and declarations for shared, c1 +// and opto blob fields and associated stub ids + +// Different shared stubs can have different blob types and may +// include some JFR stubs +// +// n.b resolve, handler and throw stubs must remain grouped in the +// same order to allow id values to be range checked + +#if INCLUDE_JFR +// do_blob(name, type) +#define SHARED_JFR_STUBS_DO(do_blob) \ + do_blob(jfr_write_checkpoint, RuntimeStub*) \ + do_blob(jfr_return_lease, RuntimeStub*) \ + +#else +#define SHARED_JFR_STUBS_DO(do_blob) +#endif + +// do_blob(name, type) +#define SHARED_STUBS_DO(do_blob) \ + do_blob(deopt, DeoptimizationBlob*) \ + /* resolve stubs */ \ + do_blob(wrong_method, RuntimeStub*) \ + do_blob(wrong_method_abstract, RuntimeStub*) \ + do_blob(ic_miss, RuntimeStub*) \ + do_blob(resolve_opt_virtual_call, RuntimeStub*) \ + do_blob(resolve_virtual_call, RuntimeStub*) \ + do_blob(resolve_static_call, RuntimeStub*) \ + /* handler stubs */ \ + do_blob(polling_page_vectors_safepoint_handler, SafepointBlob*) \ + do_blob(polling_page_safepoint_handler, SafepointBlob*) \ + do_blob(polling_page_return_handler, SafepointBlob*) \ + /* throw stubs */ \ + do_blob(throw_AbstractMethodError, RuntimeStub*) \ + do_blob(throw_IncompatibleClassChangeError, RuntimeStub*) \ + do_blob(throw_NullPointerException_at_call, RuntimeStub*) \ + do_blob(throw_StackOverflowError, RuntimeStub*) \ + do_blob(throw_delayed_StackOverflowError, RuntimeStub*) \ + /* other stubs */ \ + SHARED_JFR_STUBS_DO(do_blob) \ + +// generate a stub id enum tag from a name + +#define STUB_ID_NAME(base) base##_id + +// generate a blob id enum tag from a name + +#define BLOB_ID_NAME(base) base##_id + +// generate a blob field name + +#define BLOB_FIELD_NAME(base) _##base##_blob + +#endif // SHARE_RUNTIME_STUBDECLARATIONS_HPP + diff --git a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java index 7e58a862211..806d3339f5f 100644 --- a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java +++ b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java @@ -57,6 +57,8 @@ import static java.lang.invoke.MethodHandleNatives.Constants.*; import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; +import static jdk.internal.constant.ConstantUtils.concat; +import static jdk.internal.constant.ConstantUtils.validateInternalClassName; /** * Code generation backend for LambdaForm. @@ -67,6 +69,7 @@ class InvokerBytecodeGenerator { /** Define class names for convenience. */ private static final ClassDesc CD_CasesHolder = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/MethodHandleImpl$CasesHolder;"); private static final ClassDesc CD_DirectMethodHandle = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/DirectMethodHandle;"); + private static final ClassDesc CD_MemberName = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/MemberName;"); private static final ClassDesc CD_MethodHandleImpl = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/MethodHandleImpl;"); private static final ClassDesc CD_LambdaForm = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/LambdaForm;"); private static final ClassDesc CD_LambdaForm_Name = ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/LambdaForm$Name;"); @@ -126,7 +129,8 @@ private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize, } this.name = name; this.className = CLASS_PREFIX.concat(name); - this.classDesc = ClassDesc.ofInternalName(className); + validateInternalClassName(name); + this.classDesc = ReferenceClassDescImpl.ofValidated(concat("L", className, ";")); this.lambdaForm = lambdaForm; this.invokerName = invokerName; this.invokerType = invokerType; @@ -142,8 +146,8 @@ private InvokerBytecodeGenerator(String name, String invokerName, MethodType inv // Create an array to map name indexes to locals indexes. localsMap[0] = 0; // localsMap has at least one element for (int i = 1, index = 0; i < localsMap.length; i++) { - Wrapper w = Wrapper.forBasicType(mt.parameterType(i - 1)); - index += w.stackSlots(); + Class cl = mt.parameterType(i - 1); + index += (cl == long.class || cl == double.class) ? 2 : 1; localsMap[i] = index; } } @@ -223,6 +227,7 @@ String classData(Object arg) { // unique static variable name String name; + List classData = this.classData; if (dumper().isEnabled()) { Class c = arg.getClass(); while (c.isArray()) { @@ -232,8 +237,7 @@ String classData(Object arg) { } else { name = "_D_" + classData.size(); } - ClassData cd = new ClassData(name, desc, arg); - classData.add(cd); + classData.add(new ClassData(name, desc, arg)); return name; } @@ -292,15 +296,16 @@ private void methodSetup(ClassBuilder clb, Consumer confi */ private Object classDataValues() { final List cd = classData; - return switch (cd.size()) { + int size = cd.size(); + return switch (size) { case 0 -> null; // special case (classData is not used by ) case 1 -> cd.get(0).value; // special case (single object) case 2 -> List.of(cd.get(0).value, cd.get(1).value); case 3 -> List.of(cd.get(0).value, cd.get(1).value, cd.get(2).value); case 4 -> List.of(cd.get(0).value, cd.get(1).value, cd.get(2).value, cd.get(3).value); default -> { - Object[] data = new Object[classData.size()]; - for (int i = 0; i < classData.size(); i++) { + Object[] data = new Object[size]; + for (int i = 0; i < size; i++) { data[i] = classData.get(i).value; } yield List.of(data); @@ -316,18 +321,17 @@ static void clinit(ClassBuilder clb, ClassDesc classDesc, List classD if (classData.isEmpty()) return; - for (ClassData p : classData) { - // add the static field - clb.withField(p.name, p.desc, ACC_STATIC | ACC_FINAL); - } - clb.withMethodBody(CLASS_INIT_NAME, MTD_void, ACC_STATIC, new Consumer<>() { @Override public void accept(CodeBuilder cob) { cob.loadConstant(classDesc) .invokestatic(CD_MethodHandles, "classData", MTD_Object_Class); - if (classData.size() == 1) { + int size = classData.size(); + if (size == 1) { ClassData p = classData.get(0); + // add the static field + clb.withField(p.name, p.desc, ACC_STATIC | ACC_FINAL); + cob.checkcast(p.desc) .putstatic(classDesc, p.name, p.desc); } else { @@ -335,7 +339,10 @@ public void accept(CodeBuilder cob) { .astore(0); int index = 0; var listGet = cob.constantPool().interfaceMethodRefEntry(CD_List, "get", MTD_Object_int); - for (ClassData p : classData) { + for (int i = 0; i < size; i++) { + ClassData p = classData.get(i); + // add the static field + clb.withField(p.name, p.desc, ACC_STATIC | ACC_FINAL); // initialize the static field cob.aload(0) .loadConstant(index++) @@ -538,6 +545,11 @@ private boolean checkActualReceiver(CodeBuilder cob) { static final Annotation INJECTEDPROFILE = Annotation.of(ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/InjectedProfile;")); static final Annotation LF_COMPILED = Annotation.of(ReferenceClassDescImpl.ofValidated("Ljava/lang/invoke/LambdaForm$Compiled;")); + // Suppress method in backtraces displayed to the user, mark this method as + // a compiled LambdaForm, then either force or prohibit inlining. + public static final RuntimeVisibleAnnotationsAttribute LF_DONTINLINE_ANNOTATIONS = RuntimeVisibleAnnotationsAttribute.of(HIDDEN, LF_COMPILED, DONTINLINE); + public static final RuntimeVisibleAnnotationsAttribute LF_FORCEINLINE_ANNOTATIONS = RuntimeVisibleAnnotationsAttribute.of(HIDDEN, LF_COMPILED, FORCEINLINE); + /** * Generate an invoker method for the passed {@link LambdaForm}. */ @@ -558,21 +570,11 @@ void addMethod(ClassBuilder clb) { @Override public void accept(MethodBuilder mb) { - List annotations = new ArrayList<>(3); - - // Suppress this method in backtraces displayed to the user. - annotations.add(HIDDEN); - - // Mark this method as a compiled LambdaForm - annotations.add(LF_COMPILED); - if (lambdaForm.forceInline) { - // Force inlining of this invoker method. - annotations.add(FORCEINLINE); + mb.accept(LF_FORCEINLINE_ANNOTATIONS); } else { - annotations.add(DONTINLINE); + mb.accept(LF_DONTINLINE_ANNOTATIONS); } - mb.accept(RuntimeVisibleAnnotationsAttribute.of(annotations)); classData(lambdaForm); // keep LambdaForm instance & its compiled form lifetime tightly coupled. @@ -1674,10 +1676,12 @@ public void accept(CodeBuilder cob) { static ClassDesc classDesc(Class cls) { // assert(VerifyAccess.isTypeVisible(cls, Object.class)) : cls.getName(); - return cls.isPrimitive() ? Wrapper.forPrimitiveType(cls).basicClassDescriptor() - : cls == MethodHandle.class ? CD_MethodHandle + return cls == MethodHandle.class ? CD_MethodHandle : cls == DirectMethodHandle.class ? CD_DirectMethodHandle : cls == Object.class ? CD_Object + : cls == MemberName.class ? CD_MemberName + : cls == MethodType.class ? CD_MethodType + : cls.isPrimitive() ? Wrapper.forPrimitiveType(cls).basicClassDescriptor() : ReferenceClassDescImpl.ofValidated(cls.descriptorString()); } diff --git a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java index 342cbbe77e5..0132cc6edf5 100644 --- a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java +++ b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java @@ -1555,6 +1555,7 @@ private static boolean typesMatch(BasicType parameterType, Object object) { * Return -1 if the name is not used. */ int lastUseIndex(Name n) { + Object[] arguments = this.arguments; if (arguments == null) return -1; for (int i = arguments.length; --i >= 0; ) { if (arguments[i] == n) return i; @@ -1562,21 +1563,6 @@ int lastUseIndex(Name n) { return -1; } - /** Return the number of occurrences of n in the argument array. - * Return 0 if the name is not used. - */ - int useCount(Name n) { - int count = 0; - if (arguments != null) { - for (Object argument : arguments) { - if (argument == n) { - count++; - } - } - } - return count; - } - public boolean equals(Name that) { if (this == that) return true; if (isParam()) @@ -1618,8 +1604,16 @@ int lastUseIndex(Name n) { int useCount(Name n) { int count = (result == n.index) ? 1 : 0; int i = Math.max(n.index + 1, arity); + Name[] names = this.names; while (i < names.length) { - count += names[i++].useCount(n); + Object[] arguments = names[i++].arguments; + if (arguments != null) { + for (Object argument : arguments) { + if (argument == n) { + count++; + } + } + } } return count; } diff --git a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java index 44a4ef7b020..8486ede3a27 100644 --- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java +++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java @@ -1242,7 +1242,7 @@ private static MethodHandle generate(Lookup lookup, MethodType args, String[] co lookup = STR_LOOKUP; final MethodType concatArgs = erasedArgs(args); - // 1 argment use built-in method + // 1 argument use built-in method if (args.parameterCount() == 1) { Object concat1 = JLA.stringConcat1(constants); var handle = lookup.findVirtual(concat1.getClass(), METHOD_NAME, concatArgs); @@ -1254,7 +1254,7 @@ private static MethodHandle generate(Lookup lookup, MethodType args, String[] co MethodHandlePair handlePair = weakConstructorHandle.get(); if (handlePair != null) { try { - var instance = handlePair.constructor.invoke(constants); + var instance = handlePair.constructor.invokeBasic((Object)constants); return handlePair.concatenator.bindTo(instance); } catch (Throwable e) { throw new StringConcatException("Exception while utilizing the hidden class", e); @@ -1331,10 +1331,10 @@ public void accept(MethodBuilder mb) { var hiddenClass = lookup.makeHiddenClassDefiner(CLASS_NAME, classBytes, Set.of(), DUMPER) .defineClass(true, null); var constructor = lookup.findConstructor(hiddenClass, CONSTRUCTOR_METHOD_TYPE); - var concat = lookup.findVirtual(hiddenClass, METHOD_NAME, concatArgs); - CACHE.put(concatArgs, new SoftReference<>(new MethodHandlePair(constructor, concat))); - var instance = hiddenClass.cast(constructor.invoke(constants)); - return concat.bindTo(instance); + var concatenator = lookup.findVirtual(hiddenClass, METHOD_NAME, concatArgs); + CACHE.put(concatArgs, new SoftReference<>(new MethodHandlePair(constructor, concatenator))); + var instance = constructor.invokeBasic((Object)constants); + return concatenator.bindTo(instance); } catch (Throwable e) { throw new StringConcatException("Exception while spinning the class", e); } diff --git a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java index 6cfd7f9bb44..51ee21e46b3 100644 --- a/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java +++ b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java @@ -31,8 +31,6 @@ import java.lang.invoke.MethodType; import java.lang.invoke.StringConcatFactory; import java.lang.invoke.TypeDescriptor; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -56,20 +54,14 @@ private ObjectMethods() { } private static final int MAX_STRING_CONCAT_SLOTS = 20; - private static final MethodType DESCRIPTOR_MT = MethodType.methodType(MethodType.class); - private static final MethodType NAMES_MT = MethodType.methodType(List.class); - private static final MethodHandle FALSE = MethodHandles.constant(boolean.class, false); + private static final MethodHandle FALSE = MethodHandles.zero(boolean.class); private static final MethodHandle TRUE = MethodHandles.constant(boolean.class, true); - private static final MethodHandle ZERO = MethodHandles.constant(int.class, 0); + private static final MethodHandle ZERO = MethodHandles.zero(int.class); private static final MethodHandle CLASS_IS_INSTANCE; - private static final MethodHandle OBJECT_EQUALS; private static final MethodHandle OBJECTS_EQUALS; private static final MethodHandle OBJECTS_HASHCODE; private static final MethodHandle OBJECTS_TOSTRING; private static final MethodHandle OBJECT_EQ; - private static final MethodHandle OBJECT_HASHCODE; - private static final MethodHandle OBJECT_TO_STRING; - private static final MethodHandle STRING_FORMAT; private static final MethodHandle HASH_COMBINER; private static final HashMap, MethodHandle> primitiveEquals = new HashMap<>(); @@ -82,21 +74,8 @@ private ObjectMethods() { } MethodHandles.Lookup publicLookup = MethodHandles.publicLookup(); MethodHandles.Lookup lookup = MethodHandles.lookup(); - @SuppressWarnings("removal") - ClassLoader loader = AccessController.doPrivileged(new PrivilegedAction() { - @Override public ClassLoader run() { return ClassLoader.getPlatformClassLoader(); } - }); - CLASS_IS_INSTANCE = publicLookup.findVirtual(Class.class, "isInstance", MethodType.methodType(boolean.class, Object.class)); - OBJECT_EQUALS = publicLookup.findVirtual(Object.class, "equals", - MethodType.methodType(boolean.class, Object.class)); - OBJECT_HASHCODE = publicLookup.findVirtual(Object.class, "hashCode", - MethodType.fromMethodDescriptorString("()I", loader)); - OBJECT_TO_STRING = publicLookup.findVirtual(Object.class, "toString", - MethodType.methodType(String.class)); - STRING_FORMAT = publicLookup.findStatic(String.class, "format", - MethodType.methodType(String.class, String.class, Object[].class)); OBJECTS_EQUALS = publicLookup.findStatic(Objects.class, "equals", MethodType.methodType(boolean.class, Object.class, Object.class)); OBJECTS_HASHCODE = publicLookup.findStatic(Objects.class, "hashCode", @@ -107,41 +86,41 @@ private ObjectMethods() { } OBJECT_EQ = lookup.findStatic(OBJECT_METHODS_CLASS, "eq", MethodType.methodType(boolean.class, Object.class, Object.class)); HASH_COMBINER = lookup.findStatic(OBJECT_METHODS_CLASS, "hashCombiner", - MethodType.fromMethodDescriptorString("(II)I", loader)); + MethodType.methodType(int.class, int.class, int.class)); primitiveEquals.put(byte.class, lookup.findStatic(OBJECT_METHODS_CLASS, "eq", - MethodType.fromMethodDescriptorString("(BB)Z", loader))); + MethodType.methodType(boolean.class, byte.class, byte.class))); primitiveEquals.put(short.class, lookup.findStatic(OBJECT_METHODS_CLASS, "eq", - MethodType.fromMethodDescriptorString("(SS)Z", loader))); + MethodType.methodType(boolean.class, short.class, short.class))); primitiveEquals.put(char.class, lookup.findStatic(OBJECT_METHODS_CLASS, "eq", - MethodType.fromMethodDescriptorString("(CC)Z", loader))); + MethodType.methodType(boolean.class, char.class, char.class))); primitiveEquals.put(int.class, lookup.findStatic(OBJECT_METHODS_CLASS, "eq", - MethodType.fromMethodDescriptorString("(II)Z", loader))); + MethodType.methodType(boolean.class, int.class, int.class))); primitiveEquals.put(long.class, lookup.findStatic(OBJECT_METHODS_CLASS, "eq", - MethodType.fromMethodDescriptorString("(JJ)Z", loader))); + MethodType.methodType(boolean.class, long.class, long.class))); primitiveEquals.put(float.class, lookup.findStatic(OBJECT_METHODS_CLASS, "eq", - MethodType.fromMethodDescriptorString("(FF)Z", loader))); + MethodType.methodType(boolean.class, float.class, float.class))); primitiveEquals.put(double.class, lookup.findStatic(OBJECT_METHODS_CLASS, "eq", - MethodType.fromMethodDescriptorString("(DD)Z", loader))); + MethodType.methodType(boolean.class, double.class, double.class))); primitiveEquals.put(boolean.class, lookup.findStatic(OBJECT_METHODS_CLASS, "eq", - MethodType.fromMethodDescriptorString("(ZZ)Z", loader))); + MethodType.methodType(boolean.class, boolean.class, boolean.class))); primitiveHashers.put(byte.class, lookup.findStatic(Byte.class, "hashCode", - MethodType.fromMethodDescriptorString("(B)I", loader))); + MethodType.methodType(int.class, byte.class))); primitiveHashers.put(short.class, lookup.findStatic(Short.class, "hashCode", - MethodType.fromMethodDescriptorString("(S)I", loader))); + MethodType.methodType(int.class, short.class))); primitiveHashers.put(char.class, lookup.findStatic(Character.class, "hashCode", - MethodType.fromMethodDescriptorString("(C)I", loader))); + MethodType.methodType(int.class, char.class))); primitiveHashers.put(int.class, lookup.findStatic(Integer.class, "hashCode", - MethodType.fromMethodDescriptorString("(I)I", loader))); + MethodType.methodType(int.class, int.class))); primitiveHashers.put(long.class, lookup.findStatic(Long.class, "hashCode", - MethodType.fromMethodDescriptorString("(J)I", loader))); + MethodType.methodType(int.class, long.class))); primitiveHashers.put(float.class, lookup.findStatic(Float.class, "hashCode", - MethodType.fromMethodDescriptorString("(F)I", loader))); + MethodType.methodType(int.class, float.class))); primitiveHashers.put(double.class, lookup.findStatic(Double.class, "hashCode", - MethodType.fromMethodDescriptorString("(D)I", loader))); + MethodType.methodType(int.class, double.class))); primitiveHashers.put(boolean.class, lookup.findStatic(Boolean.class, "hashCode", - MethodType.fromMethodDescriptorString("(Z)I", loader))); + MethodType.methodType(int.class, boolean.class))); primitiveToString.put(byte.class, lookup.findStatic(Byte.class, "toString", MethodType.methodType(String.class, byte.class))); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractAttributeMapper.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractAttributeMapper.java index 8be167cd119..50c0bdc2a9f 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractAttributeMapper.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractAttributeMapper.java @@ -62,13 +62,13 @@ public final String name() { } @Override - public final void writeAttribute(BufWriter buf, T attr) { + public final void writeAttribute(BufWriter writer, T attr) { + BufWriterImpl buf = (BufWriterImpl) writer; buf.writeIndex(buf.constantPool().utf8Entry(name)); - buf.writeInt(0); - int start = buf.size(); + int lengthIndex = buf.skip(4); writeBody(buf, attr); - int written = buf.size() - start; - buf.patchInt(start - 4, 4, written); + int written = buf.size() - lengthIndex - 4; + buf.patchInt(lengthIndex, written); } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java index 10c9cf0a5e1..48c0cb76857 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractInstruction.java @@ -94,7 +94,6 @@ public abstract sealed class AbstractInstruction FMT_Discontinued = "Discontinued[OP=%s]"; final Opcode op; - final int size; @Override public Opcode opcode() { @@ -103,12 +102,12 @@ public Opcode opcode() { @Override public int sizeInBytes() { - return size; + // Note: only lookupswitch and tableswitch have variable sizes + return op.sizeIfFixed(); } - public AbstractInstruction(Opcode op, int size) { + AbstractInstruction(Opcode op) { this.op = op; - this.size = size; } @Override @@ -118,8 +117,8 @@ public abstract static sealed class BoundInstruction extends AbstractInstruction final CodeImpl code; final int pos; - protected BoundInstruction(Opcode op, int size, CodeImpl code, int pos) { - super(op, size); + protected BoundInstruction(Opcode op, CodeImpl code, int pos) { + super(op); this.code = code; this.pos = pos; } @@ -131,7 +130,7 @@ protected Label offsetToLabel(int offset) { @Override public void writeTo(DirectCodeBuilder writer) { // Override this if the instruction has any CP references or labels! - code.classReader.copyBytesTo(writer.bytecodesBufWriter, pos, size); + code.classReader.copyBytesTo(writer.bytecodesBufWriter, pos, op.sizeIfFixed()); } } @@ -139,7 +138,7 @@ public static final class BoundLoadInstruction extends BoundInstruction implements LoadInstruction { public BoundLoadInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @@ -155,7 +154,7 @@ public String toString() { @Override public int slot() { - return switch (size) { + return switch (sizeInBytes()) { case 2 -> code.classReader.readU1(pos + 1); case 4 -> code.classReader.readU2(pos + 2); default -> throw new IllegalArgumentException("Unexpected op size: " + op.sizeIfFixed() + " -- " + op); @@ -168,7 +167,7 @@ public static final class BoundStoreInstruction extends BoundInstruction implements StoreInstruction { public BoundStoreInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -183,10 +182,10 @@ public String toString() { @Override public int slot() { - return switch (size) { + return switch (sizeInBytes()) { case 2 -> code.classReader.readU1(pos + 1); case 4 -> code.classReader.readU2(pos + 2); - default -> throw new IllegalArgumentException("Unexpected op size: " + size + " -- " + op); + default -> throw new IllegalArgumentException("Unexpected op size: " + sizeInBytes() + " -- " + op); }; } @@ -196,17 +195,17 @@ public static final class BoundIncrementInstruction extends BoundInstruction implements IncrementInstruction { public BoundIncrementInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override public int slot() { - return size == 6 ? code.classReader.readU2(pos + 2) : code.classReader.readU1(pos + 1); + return sizeInBytes() == 6 ? code.classReader.readU2(pos + 2) : code.classReader.readU1(pos + 1); } @Override public int constant() { - return size == 6 ? code.classReader.readS2(pos + 4) : (byte) code.classReader.readS1(pos + 2); + return sizeInBytes() == 6 ? code.classReader.readS2(pos + 4) : code.classReader.readS1(pos + 2); } @Override @@ -220,7 +219,7 @@ public static final class BoundBranchInstruction extends BoundInstruction implements BranchInstruction { public BoundBranchInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -229,8 +228,8 @@ public Label target() { } public int branchByteOffset() { - return size == 3 - ? (int) (short) code.classReader.readU2(pos + 1) + return sizeInBytes() == 3 + ? code.classReader.readS2(pos + 1) : code.classReader.readInt(pos + 1); } @@ -256,33 +255,31 @@ public static final class BoundLookupSwitchInstruction // will always need size, cache everything to there private final int afterPad; private final int npairs; + private final int size; BoundLookupSwitchInstruction(Opcode op, CodeImpl code, int pos) { - super(op, size(code, code.codeStart, pos), code, pos); - - this.afterPad = pos + 1 + ((4 - ((pos + 1 - code.codeStart) & 3)) & 3); + super(op, code, pos); + this.afterPad = code.codeStart + RawBytecodeHelper.align(pos + 1 - code.codeStart); this.npairs = code.classReader.readInt(afterPad + 4); if (npairs < 0 || npairs > code.codeLength >> 3) { throw new IllegalArgumentException("Invalid lookupswitch npairs value: " + npairs); } - } - - static int size(CodeImpl code, int codeStart, int pos) { - int afterPad = pos + 1 + ((4 - ((pos + 1 - codeStart) & 3)) & 3); - int pad = afterPad - (pos + 1); - int npairs = code.classReader.readInt(afterPad + 4); - return 1 + pad + 8 + npairs * 8; + this.size = afterPad + 8 + npairs * 8 - pos; } private int defaultOffset() { return code.classReader.readInt(afterPad); } + @Override + public int sizeInBytes() { + return size; + } + @Override public List cases() { var cases = new SwitchCase[npairs]; - for (int i = 0; i < npairs; ++i) { - int z = afterPad + 8 + 8 * i; + for (int i = 0, z = afterPad + 8; i < npairs; ++i, z += 8) { cases[i] = SwitchCase.of(code.classReader.readInt(z), offsetToLabel(code.classReader.readInt(z + 4))); } return List.of(cases); @@ -308,25 +305,26 @@ public String toString() { public static final class BoundTableSwitchInstruction extends BoundInstruction implements TableSwitchInstruction { - BoundTableSwitchInstruction(Opcode op, CodeImpl code, int pos) { - super(op, size(code, code.codeStart, pos), code, pos); - } + private final int afterPad; + private final int low; + private final int high; + private final int size; - static int size(CodeImpl code, int codeStart, int pos) { - int ap = pos + 1 + ((4 - ((pos + 1 - codeStart) & 3)) & 3); - int pad = ap - (pos + 1); - int low = code.classReader.readInt(ap + 4); - int high = code.classReader.readInt(ap + 8); + BoundTableSwitchInstruction(Opcode op, CodeImpl code, int pos) { + super(op, code, pos); + afterPad = code.codeStart + RawBytecodeHelper.align(pos + 1 - code.codeStart); + low = code.classReader.readInt(afterPad + 4); + high = code.classReader.readInt(afterPad + 8); if (high < low || (long)high - low > code.codeLength >> 2) { throw new IllegalArgumentException("Invalid tableswitch values low: " + low + " high: " + high); } int cnt = high - low + 1; - return 1 + pad + 12 + cnt * 4; + size = afterPad + 12 + cnt * 4 - pos; } - private int afterPadding() { - int p = pos; - return p + 1 + ((4 - ((p + 1 - code.codeStart) & 3)) & 3); + @Override + public int sizeInBytes() { + return size; } @Override @@ -336,12 +334,12 @@ public Label defaultTarget() { @Override public int lowValue() { - return code.classReader.readInt(afterPadding() + 4); + return low; } @Override public int highValue() { - return code.classReader.readInt(afterPadding() + 8); + return high; } @Override @@ -350,19 +348,17 @@ public List cases() { int high = highValue(); int defOff = defaultOffset(); var cases = new ArrayList(high - low + 1); - int z = afterPadding() + 12; - for (int i = lowValue(); i <= high; ++i) { + for (int i = low, z = afterPad + 12; i <= high; ++i, z += 4) { int off = code.classReader.readInt(z); if (defOff != off) { cases.add(SwitchCase.of(i, offsetToLabel(off))); } - z += 4; } return Collections.unmodifiableList(cases); } private int defaultOffset() { - return code.classReader.readInt(afterPadding()); + return code.classReader.readInt(afterPad); } @Override @@ -383,7 +379,7 @@ public static final class BoundFieldInstruction private FieldRefEntry fieldEntry; public BoundFieldInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -413,7 +409,7 @@ public static final class BoundInvokeInstruction MemberRefEntry methodEntry; public BoundInvokeInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -453,7 +449,7 @@ public static final class BoundInvokeInterfaceInstruction InterfaceMethodRefEntry methodEntry; public BoundInvokeInterfaceInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -493,7 +489,7 @@ public static final class BoundInvokeDynamicInstruction InvokeDynamicEntry indyEntry; BoundInvokeDynamicInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -523,7 +519,7 @@ public static final class BoundNewObjectInstruction ClassEntry classEntry; BoundNewObjectInstruction(CodeImpl code, int pos) { - super(Opcode.NEW, Opcode.NEW.sizeIfFixed(), code, pos); + super(Opcode.NEW, code, pos); } @Override @@ -552,7 +548,7 @@ public static final class BoundNewPrimitiveArrayInstruction extends BoundInstruction implements NewPrimitiveArrayInstruction { public BoundNewPrimitiveArrayInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -571,7 +567,7 @@ public static final class BoundNewReferenceArrayInstruction extends BoundInstruction implements NewReferenceArrayInstruction { public BoundNewReferenceArrayInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -597,7 +593,7 @@ public static final class BoundNewMultidimensionalArrayInstruction extends BoundInstruction implements NewMultiArrayInstruction { public BoundNewMultidimensionalArrayInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -630,7 +626,7 @@ public static final class BoundTypeCheckInstruction ClassEntry typeEntry; public BoundTypeCheckInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -659,7 +655,7 @@ public static final class BoundArgumentConstantInstruction extends BoundInstruction implements ConstantInstruction.ArgumentConstantInstruction { public BoundArgumentConstantInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -668,7 +664,7 @@ public Integer constantValue() { } public int constantInt() { - return size == 3 ? code.classReader.readS2(pos + 1) : code.classReader.readS1(pos + 1); + return sizeInBytes() == 3 ? code.classReader.readS2(pos + 1) : code.classReader.readS1(pos + 1); } @Override @@ -682,7 +678,7 @@ public static final class BoundLoadConstantInstruction extends BoundInstruction implements ConstantInstruction.LoadConstantInstruction { public BoundLoadConstantInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -717,7 +713,7 @@ public static final class BoundJsrInstruction extends BoundInstruction implements DiscontinuedInstruction.JsrInstruction { public BoundJsrInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -726,7 +722,7 @@ public Label target() { } public int branchByteOffset() { - return size == 3 + return sizeInBytes() == 3 ? code.classReader.readS2(pos + 1) : code.classReader.readInt(pos + 1); } @@ -747,7 +743,7 @@ public static final class BoundRetInstruction extends BoundInstruction implements DiscontinuedInstruction.RetInstruction { public BoundRetInstruction(Opcode op, CodeImpl code, int pos) { - super(op, op.sizeIfFixed(), code, pos); + super(op, code, pos); } @Override @@ -757,7 +753,7 @@ public String toString() { @Override public int slot() { - return switch (size) { + return switch (sizeInBytes()) { case 2 -> code.classReader.readU1(pos + 1); case 4 -> code.classReader.readU2(pos + 2); default -> throw new IllegalArgumentException("Unexpected op size: " + op.sizeIfFixed() + " -- " + op); @@ -769,7 +765,7 @@ public int slot() { public abstract static sealed class UnboundInstruction extends AbstractInstruction { UnboundInstruction(Opcode op) { - super(op, op.sizeIfFixed()); + super(op); } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java index 9f9fcd8d91a..450f6ae1c8c 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractPoolEntry.java @@ -1,6 +1,5 @@ /* * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2024, Alibaba Group Holding Limited. 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 diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java index 0c317065162..15d3c8f5b34 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufWriterImpl.java @@ -26,7 +26,6 @@ package jdk.internal.classfile.impl; -import java.nio.ByteBuffer; import java.util.Arrays; import java.lang.classfile.BufWriter; @@ -218,6 +217,20 @@ public void patchInt(int offset, int size, int value) { this.offset = prevOffset; } + public void patchU2(int offset, int x) { + byte[] elems = this.elems; + elems[offset ] = (byte) (x >> 8); + elems[offset + 1] = (byte) x; + } + + public void patchInt(int offset, int x) { + byte[] elems = this.elems; + elems[offset ] = (byte) (x >> 24); + elems[offset + 1] = (byte) (x >> 16); + elems[offset + 2] = (byte) (x >> 8); + elems[offset + 3] = (byte) x; + } + @Override public void writeIntBytes(int intSize, long intValue) { reserveSpace(intSize); @@ -226,6 +239,18 @@ public void writeIntBytes(int intSize, long intValue) { } } + /** + * Skip a few bytes in the output buffer. The skipped area has undefined value. + * @param bytes number of bytes to skip + * @return the index, for later patching + */ + public int skip(int bytes) { + int now = offset; + reserveSpace(bytes); + offset += bytes; + return now; + } + @Override public void reserveSpace(int freeBytes) { int minCapacity = offset + freeBytes; @@ -247,8 +272,8 @@ public int size() { return offset; } - public ByteBuffer asByteBuffer() { - return ByteBuffer.wrap(elems, 0, offset).slice(); + public RawBytecodeHelper.CodeRange bytecodeView() { + return RawBytecodeHelper.of(elems, offset); } public void copyTo(byte[] array, int bufferOffset) { diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java index 68c93249c1b..3ea8f377f3c 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java @@ -258,6 +258,14 @@ private void inflateJumpTargets() { switch (i) { case BranchInstruction br -> br.target(); case DiscontinuedInstruction.JsrInstruction jsr -> jsr.target(); + case LookupSwitchInstruction ls -> { + ls.defaultTarget(); + ls.cases(); + } + case TableSwitchInstruction ts -> { + ts.defaultTarget(); + ts.cases(); + } default -> {} } pos += i.sizeInBytes(); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java index 0d61895fe9f..59b9c4f70b4 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectClassBuilder.java @@ -192,7 +192,7 @@ else if ((flags & ClassFile.ACC_MODULE) == 0 && !"java/lang/Object".equals(thisC boolean written = constantPool.writeBootstrapMethods(tail); if (written) { // Update attributes count - tail.patchInt(attributesOffset, 2, attributes.size() + 1); + tail.patchU2(attributesOffset, attributes.size() + 1); } // Now we can make the head diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java index 1b950f28d00..be8765d4d45 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java @@ -209,7 +209,7 @@ private void writeExceptionHandlers(BufWriterImpl buf) { } } if (handlersSize < handlers.size()) - buf.patchInt(pos, 2, handlersSize); + buf.patchU2(pos, handlersSize); } private void buildContent() { @@ -246,7 +246,7 @@ public void writeBody(BufWriterImpl b) { } } if (crSize < characterRanges.size()) - b.patchInt(pos, 2, crSize); + b.patchU2(pos, crSize); } }; attributes.withAttribute(a); @@ -269,7 +269,7 @@ public void writeBody(BufWriterImpl b) { } } if (lvSize < localVariables.size()) - b.patchInt(pos, 2, lvSize); + b.patchU2(pos, lvSize); } }; attributes.withAttribute(a); @@ -292,7 +292,7 @@ public void writeBody(BufWriterImpl b) { } } if (lvtSize < localVariableTypes.size()) - b.patchInt(pos, 2, lvtSize); + b.patchU2(pos, lvtSize); } }; attributes.withAttribute(a); @@ -455,8 +455,7 @@ private record DeferredLabel(int labelPc, int size, int instructionPc, Label lab private void writeLabelOffset(int nBytes, int instructionPc, Label label) { int targetBci = labelToBci(label); if (targetBci == -1) { - int pc = curPc(); - bytecodesBufWriter.writeIntBytes(nBytes, 0); + int pc = bytecodesBufWriter.skip(nBytes); if (deferredLabels == null) deferredLabels = new ArrayList<>(); deferredLabels.add(new DeferredLabel(pc, nBytes, instructionPc, label)); @@ -472,8 +471,13 @@ private void processDeferredLabels() { if (deferredLabels != null) { for (DeferredLabel dl : deferredLabels) { int branchOffset = labelToBci(dl.label) - dl.instructionPc; - if (dl.size == 2 && (short)branchOffset != branchOffset) throw new LabelOverflowException(); - bytecodesBufWriter.patchInt(dl.labelPc, dl.size, branchOffset); + if (dl.size == 2) { + if ((short)branchOffset != branchOffset) throw new LabelOverflowException(); + bytecodesBufWriter.patchU2(dl.labelPc, branchOffset); + } else { + assert dl.size == 4; + bytecodesBufWriter.patchInt(dl.labelPc, branchOffset); + } } } } @@ -543,11 +547,11 @@ public void writeLookupSwitch(Label defaultTarget, List cases) { writeBytecode(LOOKUPSWITCH); int pad = 4 - (curPc() % 4); if (pad != 4) - bytecodesBufWriter.writeIntBytes(pad, 0); + bytecodesBufWriter.skip(pad); // padding content can be anything writeLabelOffset(4, instructionPc, defaultTarget); bytecodesBufWriter.writeInt(cases.size()); cases = new ArrayList<>(cases); - cases.sort(new Comparator() { + cases.sort(new Comparator<>() { @Override public int compare(SwitchCase c1, SwitchCase c2) { return Integer.compare(c1.caseValue(), c2.caseValue()); @@ -564,7 +568,7 @@ public void writeTableSwitch(int low, int high, Label defaultTarget, List, IllegalArgumentException> + IAE_FORMATTER = Preconditions.outOfBoundsExceptionFormatter(new Function<>() { + @Override + public IllegalArgumentException apply(String s) { + return new IllegalArgumentException(s); + } + }); + + public record CodeRange(byte[] array, int length) { + public RawBytecodeHelper start() { + return new RawBytecodeHelper(this); + } + } + public static final int ILLEGAL = -1; - private static final byte[] LENGTHS = new byte[] { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3, 3, 2 | (4 << 4), 2 | (4 << 4), 2 | (4 << 4), 2 | (4 << 4), 2 | (4 << 4), 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2 | (4 << 4), 2 | (4 << 4), 2 | (4 << 4), 2 | (4 << 4), 2 | (4 << 4), 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3 | (6 << 4), 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2 | (4 << 4), 0, 0, 1, 1, 1, - 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 5, 5, 3, 2, 3, 1, 1, 3, 3, 1, 1, 0, 4, 3, 3, 5, 5, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 4, 4, 4, 2, 4, 3, 3, 0, 0, 1, 3, 2, 3, 3, 3, 1, 2, 1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + /** + * The length of opcodes, or -1 for no fixed length. + * This is generated as if: + * {@snippet lang=java : + * var lengths = new byte[0x100]; + * Arrays.fill(lengths, (byte) -1); + * for (var op : Opcode.values()) { + * if (!op.isWide()) { + * lengths[op.bytecode()] = (byte) op.sizeIfFixed(); + * } + * } + * } + * Tested in UtilTest::testOpcodeLengthTable. + */ + // Note: Consider distinguishing non-opcode and non-fixed-length opcode + public static final @Stable byte[] LENGTHS = new byte[] { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 3, 2, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, -1, -1, 1, 1, 1, 1, + 1, 1, 3, 3, 3, 3, 3, 3, 3, 5, 5, 3, 2, 3, 1, 1, + 3, 3, 1, 1, -1, 4, 3, 3, 5, 5, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; public static boolean isStoreIntoLocal(int code) { @@ -51,121 +94,200 @@ public static int align(int n) { return (n + 3) & ~3; } - private final ByteBuffer bytecode; - public int bci, nextBci, endBci; - public int rawCode; - public boolean isWide; + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + public final CodeRange code; + private int nextBci; + private int bci; + private int opcode; + private boolean isWide; - public RawBytecodeHelper(ByteBuffer bytecode) { - this.bytecode = bytecode; - this.bci = 0; - this.nextBci = 0; - this.endBci = bytecode.capacity(); + public static CodeRange of(byte[] array) { + return new CodeRange(array, array.length); } - public boolean isLastBytecode() { - return nextBci >= endBci; + public static CodeRange of(byte[] array, int limit) { + return new CodeRange(array, limit); } - public int getShort(int bci) { - return bytecode.getShort(bci); + private RawBytecodeHelper(CodeRange range) { + this.code = range; } - public int dest() { - return bci + getShort(bci + 1); + // immutable states + + /** {@return the end of the code array} */ + public int endBci() { + return code.length; } - public int getInt(int bci) { - return bytecode.getInt(bci); + // setup + + /** + * Sets the starting bci for bytecode reading. Can be set to + * {@link #endBci} to end scanning. Must be followed by a + * {@link #next} before getter access. + */ + public void reset(int nextBci) { + Preconditions.checkIndex(nextBci, endBci() + 1, IAE_FORMATTER); + this.nextBci = nextBci; } - public int destW() { - return bci + getInt(bci + 1); + // getters after transition + + /** + * Returns the current functional opcode, or {@link #ILLEGAL} if + * the next instruction is invalid in format. + * If this returns a valid opcode, that instruction's format must + * be valid and can be accessed unchecked. + */ + public int opcode() { + return opcode; } - public int getIndexU1() { - return bytecode.get(bci + 1) & 0xff; + /** + * Returns whether the current functional opcode is in wide. + */ + public boolean isWide() { + return isWide; } + /** + * Returns the last validated instruction's index. + */ + public int bci() { + return bci; + } + + // general utilities + public int getU1(int bci) { - return bytecode.get(bci) & 0xff; + Preconditions.checkIndex(bci, endBci(), IAE_FORMATTER); + return getU1Unchecked(bci); } - public int rawNext(int jumpTo) { - this.nextBci = jumpTo; - return rawNext(); + public int getU2(int bci) { + Preconditions.checkFromIndexSize(bci, 2, endBci(), IAE_FORMATTER); + return getU2Unchecked(bci); } - public int rawNext() { - bci = nextBci; - int code = bytecode.get(bci) & 0xff; - int len = LENGTHS[code] & 0xf; - if (len > 0 && (bci <= endBci - len)) { - isWide = false; - nextBci += len; - if (nextBci <= bci) { - code = ILLEGAL; - } - rawCode = code; - return code; - } else { - len = switch (bytecode.get(bci) & 0xff) { - case WIDE -> { - if (bci + 1 >= endBci) { - yield -1; - } - yield LENGTHS[bytecode.get(bci + 1) & 0xff] >> 4; - } - case TABLESWITCH -> { - int aligned_bci = align(bci + 1); - if (aligned_bci + 3 * 4 >= endBci) { - yield -1; - } - int lo = bytecode.getInt(aligned_bci + 1 * 4); - int hi = bytecode.getInt(aligned_bci + 2 * 4); - int l = aligned_bci - bci + (3 + hi - lo + 1) * 4; - if (l > 0) yield l; else yield -1; - } - case LOOKUPSWITCH -> { - int aligned_bci = align(bci + 1); - if (aligned_bci + 2 * 4 >= endBci) { - yield -1; - } - int npairs = bytecode.getInt(aligned_bci + 4); - int l = aligned_bci - bci + (2 + 2 * npairs) * 4; - if (l > 0) yield l; else yield -1; - } - default -> - 0; - }; - if (len <= 0 || (bci > endBci - len) || (bci - len >= nextBci)) { - code = ILLEGAL; - } else { - nextBci += len; - isWide = false; - if (code == WIDE) { - if (bci + 1 >= endBci) { - code = ILLEGAL; - } else { - code = bytecode.get(bci + 1) & 0xff; - isWide = true; - } - } - } - rawCode = code; - return code; - } + public int getShort(int bci) { + Preconditions.checkFromIndexSize(bci, 2, endBci(), IAE_FORMATTER); + return getShortUnchecked(bci); + } + + public int getInt(int bci) { + Preconditions.checkFromIndexSize(bci, 4, endBci(), IAE_FORMATTER); + return getIntUnchecked(bci); + } + + // Unchecked accessors: only if opcode() is validated + + public int getU1Unchecked(int bci) { + return Byte.toUnsignedInt(code.array[bci]); + } + + public int getU2Unchecked(int bci) { + return UNSAFE.getCharUnaligned(code.array, (long) Unsafe.ARRAY_BYTE_BASE_OFFSET + bci, true); } + public int getShortUnchecked(int bci) { + return UNSAFE.getShortUnaligned(code.array, (long) Unsafe.ARRAY_BYTE_BASE_OFFSET + bci, true); + } + + // used after switch validation + public int getIntUnchecked(int bci) { + return UNSAFE.getIntUnaligned(code.array, (long) Unsafe.ARRAY_BYTE_BASE_OFFSET + bci, true); + } + + // non-wide branches + public int dest() { + return bci + getShortUnchecked(bci + 1); + } + + // goto_w and jsr_w + public int destW() { + return bci + getIntUnchecked(bci + 1); + } + + // *load, *store, iinc public int getIndex() { - return (isWide) ? getIndexU2Raw(bci + 2) : getIndexU1(); + return isWide ? getU2Unchecked(bci + 2) : getIndexU1(); } + // ldc + public int getIndexU1() { + return getU1Unchecked(bci + 1); + } + + // usually cp entry index public int getIndexU2() { - return getIndexU2Raw(bci + 1); + return getU2Unchecked(bci + 1); } - public int getIndexU2Raw(int bci) { - return bytecode.getShort(bci) & 0xffff; + // Transition methods + + /** + * Transitions to the next instruction and returns whether scanning should + * continue. If the next instruction is malformed, {@link #opcode()} returns + * {@link #ILLEGAL}, so we can perform value access without bound checks if + * we have a valid opcode. + */ + public boolean next() { + var bci = nextBci; + var end = endBci(); + if (bci >= end) { + return false; + } + + int code = getU1Unchecked(bci); + int len = LENGTHS[code & 0xFF]; // & 0xFF eliminates bound check + this.bci = bci; + opcode = code; + isWide = false; + if (len <= 0) { + len = checkSpecialInstruction(bci, end, code); // sets opcode + } + + if (len <= 0 || (nextBci += len) > end) { + opcode = ILLEGAL; + } + + return true; + } + + // Put rarely used code in another method to reduce code size + private int checkSpecialInstruction(int bci, int end, int code) { + if (code == WIDE) { + if (bci + 1 >= end) { + return -1; + } + opcode = code = getIndexU1(); + isWide = true; + // Validated in UtilTest.testOpcodeLengthTable + return LENGTHS[code] * 2; + } + if (code == TABLESWITCH) { + int alignedBci = align(bci + 1); + if (alignedBci + 3 * 4 >= end) { + return -1; + } + int lo = getIntUnchecked(alignedBci + 1 * 4); + int hi = getIntUnchecked(alignedBci + 2 * 4); + long l = alignedBci - bci + (3L + (long) hi - lo + 1L) * 4L; + return l > 0 && ((int) l == l) ? (int) l : -1; + } + if (code == LOOKUPSWITCH) { + int alignedBci = align(bci + 1); + if (alignedBci + 2 * 4 >= end) { + return -1; + } + int npairs = getIntUnchecked(alignedBci + 4); + if (npairs < 0) { + return -1; + } + long l = alignedBci - bci + (2L + 2L * npairs) * 4L; + return l > 0 && ((int) l == l) ? (int) l : -1; + } + return -1; } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java index 777da61c49d..4f48c8b731d 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/SplitConstantPool.java @@ -142,8 +142,8 @@ public boolean writeBootstrapMethods(BufWriterImpl buf) { for (int i = parentBsmSize; i < bsmSize; i++) bootstrapMethodEntry(i).writeTo(buf); int attrLen = buf.size() - pos; - buf.patchInt(pos + 2, 4, attrLen - 6); - buf.patchInt(pos + 6, 2, bsmSize); + buf.patchInt(pos + 2, attrLen - 6); + buf.patchU2(pos + 6, bsmSize); } else { UnboundAttribute a diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java index 861bb509420..783ff4bf106 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackCounter.java @@ -34,7 +34,6 @@ import java.lang.classfile.constantpool.MemberRefEntry; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; -import java.nio.ByteBuffer; import java.util.ArrayDeque; import java.util.BitSet; import java.util.List; @@ -55,7 +54,7 @@ static StackCounter of(DirectCodeBuilder dcb, BufWriterImpl buf) { dcb.methodInfo.methodName().stringValue(), dcb.methodInfo.methodTypeSymbol(), (dcb.methodInfo.methodFlags() & ACC_STATIC) != 0, - dcb.bytecodesBufWriter.asByteBuffer(), + dcb.bytecodesBufWriter.bytecodeView(), dcb.constantPool, dcb.handlers); } @@ -67,7 +66,6 @@ static StackCounter of(DirectCodeBuilder dcb, BufWriterImpl buf) { private final String methodName; private final MethodTypeDesc methodDesc; private final boolean isStatic; - private final ByteBuffer bytecode; private final SplitConstantPool cp; private final Queue targets; private final BitSet visited; @@ -91,12 +89,12 @@ private boolean next() { Target en; while ((en = targets.poll()) != null) { if (!visited.get(en.bci)) { - bcs.nextBci = en.bci; + bcs.reset(en.bci); stack = en.stack; return true; } } - bcs.nextBci = bcs.endBci; + bcs.reset(bcs.endBci()); return false; } @@ -106,14 +104,13 @@ public StackCounter(LabelContext labelContext, String methodName, MethodTypeDesc methodDesc, boolean isStatic, - ByteBuffer bytecode, + RawBytecodeHelper.CodeRange bytecode, SplitConstantPool cp, List handlers) { this.thisClass = thisClass; this.methodName = methodName; this.methodDesc = methodDesc; this.isStatic = isStatic; - this.bytecode = bytecode; this.cp = cp; targets = new ArrayDeque<>(); stack = rets = 0; @@ -132,14 +129,13 @@ public StackCounter(LabelContext labelContext, } maxLocals = isStatic ? 0 : 1; maxLocals += Util.parameterSlots(methodDesc); - bcs = new RawBytecodeHelper(bytecode); - visited = new BitSet(bcs.endBci); + bcs = bytecode.start(); + visited = new BitSet(bcs.endBci()); targets.add(new Target(0, 0)); while (next()) { - while (!bcs.isLastBytecode()) { - bcs.rawNext(); - int opcode = bcs.rawCode; - int bci = bcs.bci; + while (bcs.next()) { + int opcode = bcs.opcode(); + int bci = bcs.bci(); visited.set(bci); switch (opcode) { case NOP, LALOAD, DALOAD, SWAP, INEG, ARRAYLENGTH, INSTANCEOF, LNEG, FNEG, DNEG, I2F, L2D, F2I, D2L, I2B, I2C, I2S, @@ -267,12 +263,12 @@ public StackCounter(LabelContext labelContext, } case TABLESWITCH, LOOKUPSWITCH -> { int alignedBci = RawBytecodeHelper.align(bci + 1); - int defaultOfset = bcs.getInt(alignedBci); + int defaultOffset = bcs.getIntUnchecked(alignedBci); int keys, delta; addStackSlot(-1); - if (bcs.rawCode == TABLESWITCH) { - int low = bcs.getInt(alignedBci + 4); - int high = bcs.getInt(alignedBci + 2 * 4); + if (bcs.opcode() == TABLESWITCH) { + int low = bcs.getIntUnchecked(alignedBci + 4); + int high = bcs.getIntUnchecked(alignedBci + 2 * 4); if (low > high) { throw error("low must be less than or equal to high in tableswitch"); } @@ -282,24 +278,23 @@ public StackCounter(LabelContext labelContext, } delta = 1; } else { - keys = bcs.getInt(alignedBci + 4); + keys = bcs.getIntUnchecked(alignedBci + 4); if (keys < 0) { throw error("number of keys in lookupswitch less than 0"); } delta = 2; for (int i = 0; i < (keys - 1); i++) { - int this_key = bcs.getInt(alignedBci + (2 + 2 * i) * 4); - int next_key = bcs.getInt(alignedBci + (2 + 2 * i + 2) * 4); + int this_key = bcs.getIntUnchecked(alignedBci + (2 + 2 * i) * 4); + int next_key = bcs.getIntUnchecked(alignedBci + (2 + 2 * i + 2) * 4); if (this_key >= next_key) { throw error("Bad lookupswitch instruction"); } } } - int target = bci + defaultOfset; + int target = bci + defaultOffset; jump(target); for (int i = 0; i < keys; i++) { - alignedBci = RawBytecodeHelper.align(bcs.bci + 1); - target = bci + bcs.getInt(alignedBci + (3 + i * delta) * 4); + target = bci + bcs.getIntUnchecked(alignedBci + (3 + i * delta) * 4); jump(target); } next(); @@ -314,7 +309,7 @@ public StackCounter(LabelContext labelContext, } case GETSTATIC, PUTSTATIC, GETFIELD, PUTFIELD -> { var tk = TypeKind.fromDescriptor(cp.entryByIndex(bcs.getIndexU2(), MemberRefEntry.class).nameAndType().type()); - switch (bcs.rawCode) { + switch (bcs.opcode()) { case GETSTATIC -> addStackSlot(tk.slotSize()); case PUTSTATIC -> @@ -337,7 +332,7 @@ public StackCounter(LabelContext labelContext, addStackSlot(delta); } case MULTIANEWARRAY -> - addStackSlot(1 - bcs.getU1(bcs.bci + 3)); + addStackSlot(1 - bcs.getU1Unchecked(bcs.bci() + 3)); case JSR -> { addStackSlot(+1); jump(bcs.dest()); //here we lost track of the exact stack size after return from subroutine @@ -395,10 +390,10 @@ private void processLdc(int index) { private IllegalArgumentException error(String msg) { var sb = new StringBuilder("%s at bytecode offset %d of method %s(%s)".formatted( msg, - bcs.bci, + bcs.bci(), methodName, methodDesc.parameterList().stream().map(ClassDesc::displayName).collect(Collectors.joining(",")))); - Util.dumpMethod(cp, thisClass, methodName, methodDesc, isStatic ? ACC_STATIC : 0, bytecode, sb::append); + Util.dumpMethod(cp, thisClass, methodName, methodDesc, isStatic ? ACC_STATIC : 0, bcs.code, sb::append); return new IllegalArgumentException(sb.toString()); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java index 514c1d32f41..5825eb2f547 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapGenerator.java @@ -38,7 +38,6 @@ import java.lang.classfile.constantpool.MemberRefEntry; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; @@ -46,6 +45,7 @@ import java.util.Objects; import java.util.stream.Collectors; import jdk.internal.constant.ReferenceClassDescImpl; +import jdk.internal.util.Preconditions; import static java.lang.classfile.ClassFile.*; import static java.lang.constant.ConstantDescs.*; @@ -152,7 +152,7 @@ static StackMapGenerator of(DirectCodeBuilder dcb, BufWriterImpl buf) { dcb.methodInfo.methodName().stringValue(), dcb.methodInfo.methodTypeSymbol(), (dcb.methodInfo.methodFlags() & ACC_STATIC) != 0, - dcb.bytecodesBufWriter.asByteBuffer(), + dcb.bytecodesBufWriter.bytecodeView(), dcb.constantPool, dcb.context, dcb.handlers); @@ -188,7 +188,7 @@ static record RawExceptionCatch(int start, int end, int handler, Type catchType) private final Type thisType; private final String methodName; private final MethodTypeDesc methodDesc; - private final ByteBuffer bytecode; + private final RawBytecodeHelper.CodeRange bytecode; private final SplitConstantPool cp; private final boolean isStatic; private final LabelContext labelContext; @@ -222,7 +222,7 @@ public StackMapGenerator(LabelContext labelContext, String methodName, MethodTypeDesc methodDesc, boolean isStatic, - ByteBuffer bytecode, + RawBytecodeHelper.CodeRange bytecode, SplitConstantPool cp, ClassFileImpl context, List handlers) { @@ -289,7 +289,7 @@ private boolean isAnyFrameDirty() { } private void generate() { - exMin = bytecode.capacity(); + exMin = bytecode.length(); exMax = -1; for (var exhandler : handlers) { int start_pc = labelContext.labelToBci(exhandler.tryStart()); @@ -326,15 +326,13 @@ private void generate() { //patch frame frame.pushStack(Type.THROWABLE_TYPE); if (maxStack < 1) maxStack = 1; - int blockSize = (i < framesCount - 1 ? frames.get(i + 1).offset : bytecode.limit()) - frame.offset; + int end = (i < framesCount - 1 ? frames.get(i + 1).offset : bytecode.length()) - 1; //patch bytecode - bytecode.position(frame.offset); - for (int n=1; n bcs.bci) { + if (ncf && thisOffset > bcs.bci()) { throw generatorError("Expecting a stack map frame"); } - if (thisOffset == bcs.bci) { + if (thisOffset == bcs.bci()) { if (!ncf) { currentFrame.checkAssignableTo(frames.get(stackmapIndex)); } @@ -426,11 +423,12 @@ private void processMethod() { if (stackmapIndex == frames.size()) return; //skip the rest of this round nextFrame = frames.get(stackmapIndex++); } - bcs.rawNext(nextFrame.offset); //skip code up-to the next frame - currentFrame.offset = bcs.bci; + bcs.reset(nextFrame.offset); //skip code up-to the next frame + bcs.next(); + currentFrame.offset = bcs.bci(); currentFrame.copyFrom(nextFrame); nextFrame.dirty = false; - } else if (thisOffset < bcs.bci) { + } else if (thisOffset < bcs.bci()) { throw new ClassFormatError(String.format("Bad stack map offset %d", thisOffset)); } } else if (ncf) { @@ -441,11 +439,11 @@ private void processMethod() { } private boolean processBlock(RawBytecodeHelper bcs) { - int opcode = bcs.rawCode; + int opcode = bcs.opcode(); boolean ncf = false; boolean this_uninit = false; boolean verified_exc_handlers = false; - int bci = bcs.bci; + int bci = bcs.bci(); Type type1, type2, type3, type4; if (RawBytecodeHelper.isStoreIntoLocal(opcode) && bci >= exMin && bci < exMax) { processExceptionHandlerTargets(bci, this_uninit); @@ -651,7 +649,7 @@ private boolean processBlock(RawBytecodeHelper bcs) { currentFrame.decStack(1).pushStack(cpIndexToType(bcs.getIndexU2(), cp)); case MULTIANEWARRAY -> { type1 = cpIndexToType(bcs.getIndexU2(), cp); - int dim = bcs.getU1(bcs.bci + 3); + int dim = bcs.getU1Unchecked(bcs.bci() + 3); for (int i = 0; i < dim; i++) { currentFrame.popStack(); } @@ -708,14 +706,14 @@ private void processLdc(int index) { } private void processSwitch(RawBytecodeHelper bcs) { - int bci = bcs.bci; + int bci = bcs.bci(); int alignedBci = RawBytecodeHelper.align(bci + 1); - int defaultOfset = bcs.getInt(alignedBci); + int defaultOffset = bcs.getIntUnchecked(alignedBci); int keys, delta; currentFrame.popStack(); - if (bcs.rawCode == TABLESWITCH) { - int low = bcs.getInt(alignedBci + 4); - int high = bcs.getInt(alignedBci + 2 * 4); + if (bcs.opcode() == TABLESWITCH) { + int low = bcs.getIntUnchecked(alignedBci + 4); + int high = bcs.getIntUnchecked(alignedBci + 2 * 4); if (low > high) { throw generatorError("low must be less than or equal to high in tableswitch"); } @@ -725,31 +723,30 @@ private void processSwitch(RawBytecodeHelper bcs) { } delta = 1; } else { - keys = bcs.getInt(alignedBci + 4); + keys = bcs.getIntUnchecked(alignedBci + 4); if (keys < 0) { throw generatorError("number of keys in lookupswitch less than 0"); } delta = 2; for (int i = 0; i < (keys - 1); i++) { - int this_key = bcs.getInt(alignedBci + (2 + 2 * i) * 4); - int next_key = bcs.getInt(alignedBci + (2 + 2 * i + 2) * 4); + int this_key = bcs.getIntUnchecked(alignedBci + (2 + 2 * i) * 4); + int next_key = bcs.getIntUnchecked(alignedBci + (2 + 2 * i + 2) * 4); if (this_key >= next_key) { throw generatorError("Bad lookupswitch instruction"); } } } - int target = bci + defaultOfset; + int target = bci + defaultOffset; checkJumpTarget(currentFrame, target); for (int i = 0; i < keys; i++) { - alignedBci = RawBytecodeHelper.align(bcs.bci + 1); - target = bci + bcs.getInt(alignedBci + (3 + i * delta) * 4); + target = bci + bcs.getIntUnchecked(alignedBci + (3 + i * delta) * 4); checkJumpTarget(currentFrame, target); } } private void processFieldInstructions(RawBytecodeHelper bcs) { var desc = Util.fieldTypeSymbol(cp.entryByIndex(bcs.getIndexU2(), MemberRefEntry.class).nameAndType()); - switch (bcs.rawCode) { + switch (bcs.opcode()) { case GETSTATIC -> currentFrame.pushStack(desc); case PUTSTATIC -> { @@ -771,13 +768,13 @@ private void processFieldInstructions(RawBytecodeHelper bcs) { private boolean processInvokeInstructions(RawBytecodeHelper bcs, boolean inTryBlock, boolean thisUninit) { int index = bcs.getIndexU2(); - int opcode = bcs.rawCode; + int opcode = bcs.opcode(); var nameAndType = opcode == INVOKEDYNAMIC ? cp.entryByIndex(index, InvokeDynamicEntry.class).nameAndType() : cp.entryByIndex(index, MemberRefEntry.class).nameAndType(); String invokeMethodName = nameAndType.name().stringValue(); var mDesc = Util.methodTypeSymbol(nameAndType); - int bci = bcs.bci; + int bci = bcs.bci(); currentFrame.decStack(Util.parameterSlots(mDesc)); if (opcode != INVOKESTATIC && opcode != INVOKEDYNAMIC) { if (OBJECT_INITIALIZER_NAME.equals(invokeMethodName)) { @@ -790,7 +787,7 @@ private boolean processInvokeInstructions(RawBytecodeHelper bcs, boolean inTryBl thisUninit = true; } else if (type.tag == ITEM_UNINITIALIZED) { int new_offset = type.bci; - int new_class_index = bcs.getIndexU2Raw(new_offset + 1); + int new_class_index = bcs.getU2(new_offset + 1); Type new_class_type = cpIndexToType(new_class_index, cp); if (inTryBlock) { processExceptionHandlerTargets(bci, thisUninit); @@ -849,16 +846,16 @@ private BitSet detectFrameOffsets() { var offsets = new BitSet() { @Override public void set(int i) { - if (i < 0 || i >= bytecode.capacity()) throw new IllegalArgumentException(); + Preconditions.checkIndex(i, bytecode.length(), RawBytecodeHelper.IAE_FORMATTER); super.set(i); } }; - RawBytecodeHelper bcs = new RawBytecodeHelper(bytecode); + var bcs = bytecode.start(); boolean no_control_flow = false; int opcode, bci = 0; - while (!bcs.isLastBytecode()) try { - opcode = bcs.rawNext(); - bci = bcs.bci; + while (bcs.next()) try { + opcode = bcs.opcode(); + bci = bcs.bci(); if (no_control_flow) { offsets.set(bci); } @@ -880,20 +877,20 @@ public void set(int i) { } case TABLESWITCH, LOOKUPSWITCH -> { int aligned_bci = RawBytecodeHelper.align(bci + 1); - int default_ofset = bcs.getInt(aligned_bci); + int default_ofset = bcs.getIntUnchecked(aligned_bci); int keys, delta; - if (bcs.rawCode == TABLESWITCH) { - int low = bcs.getInt(aligned_bci + 4); - int high = bcs.getInt(aligned_bci + 2 * 4); + if (bcs.opcode() == TABLESWITCH) { + int low = bcs.getIntUnchecked(aligned_bci + 4); + int high = bcs.getIntUnchecked(aligned_bci + 2 * 4); keys = high - low + 1; delta = 1; } else { - keys = bcs.getInt(aligned_bci + 4); + keys = bcs.getIntUnchecked(aligned_bci + 4); delta = 2; } offsets.set(bci + default_ofset); for (int i = 0; i < keys; i++) { - offsets.set(bci + bcs.getInt(aligned_bci + (3 + i * delta) * 4)); + offsets.set(bci + bcs.getIntUnchecked(aligned_bci + (3 + i * delta) * 4)); } yield true; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java b/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java index 5ee3759bba3..3119cfa0f49 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java @@ -849,11 +849,10 @@ public AdHocAttribute(AttributeMapper mapper) { @Override public void writeTo(BufWriterImpl b) { b.writeIndex(b.constantPool().utf8Entry(mapper.name())); - b.writeInt(0); - int start = b.size(); + int lengthIndex = b.skip(4); writeBody(b); - int written = b.size() - start; - b.patchInt(start - 4, 4, written); + int written = b.size() - lengthIndex - 4; + b.patchInt(lengthIndex, written); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java b/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java index 2d9e27b8d74..d4d91d9aab1 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/Util.java @@ -53,9 +53,12 @@ import static java.lang.classfile.ClassFile.ACC_STATIC; import java.lang.classfile.attribute.CodeAttribute; import java.lang.classfile.components.ClassPrinter; -import java.nio.ByteBuffer; import java.util.function.Consumer; +import static jdk.internal.constant.PrimitiveClassDescImpl.CD_double; +import static jdk.internal.constant.PrimitiveClassDescImpl.CD_long; +import static jdk.internal.constant.PrimitiveClassDescImpl.CD_void; + /** * Helper to create and manipulate type descriptors, where type descriptors are * represented as JVM type descriptor strings and symbols are represented as @@ -106,9 +109,11 @@ public static boolean isAttributeAllowed(final Attribute attr, } public static int parameterSlots(MethodTypeDesc mDesc) { - int count = 0; - for (int i = 0; i < mDesc.parameterCount(); i++) { - count += slotSize(mDesc.parameterType(i)); + int count = mDesc.parameterCount(); + for (int i = count - 1; i >= 0; i--) { + if (isDoubleSlot(mDesc.parameterType(i))) { + count++; + } } return count; } @@ -118,17 +123,13 @@ public static int[] parseParameterSlots(int flags, MethodTypeDesc mDesc) { int count = ((flags & ACC_STATIC) != 0) ? 0 : 1; for (int i = 0; i < result.length; i++) { result[i] = count; - count += slotSize(mDesc.parameterType(i)); + count += paramSlotSize(mDesc.parameterType(i)); } return result; } public static int maxLocals(int flags, MethodTypeDesc mDesc) { - int count = ((flags & ACC_STATIC) != 0) ? 0 : 1; - for (int i = 0; i < mDesc.parameterCount(); i++) { - count += slotSize(mDesc.parameterType(i)); - } - return count; + return parameterSlots(mDesc) + ((flags & ACC_STATIC) == 0 ? 1 : 0) ; } /** @@ -253,16 +254,15 @@ static void writeList(BufWriterImpl buf, List list) { } public static int slotSize(ClassDesc desc) { - return switch (desc.descriptorString().charAt(0)) { - case 'V' -> 0; - case 'D','J' -> 2; - default -> 1; - }; + return desc == CD_void ? 0 : isDoubleSlot(desc) ? 2 : 1; + } + + public static int paramSlotSize(ClassDesc desc) { + return isDoubleSlot(desc) ? 2 : 1; } public static boolean isDoubleSlot(ClassDesc desc) { - char ch = desc.descriptorString().charAt(0); - return ch == 'D' || ch == 'J'; + return desc == CD_double || desc == CD_long; } public static void dumpMethod(SplitConstantPool cp, @@ -270,7 +270,7 @@ public static void dumpMethod(SplitConstantPool cp, String methodName, MethodTypeDesc methodDesc, int acc, - ByteBuffer bytecode, + RawBytecodeHelper.CodeRange bytecode, Consumer dump) { // try to dump debug info about corrupted bytecode @@ -283,8 +283,8 @@ public static void dumpMethod(SplitConstantPool cp, public void writeBody(BufWriterImpl b) { b.writeU2(-1);//max stack b.writeU2(-1);//max locals - b.writeInt(bytecode.limit()); - b.writeBytes(bytecode.array(), 0, bytecode.limit()); + b.writeInt(bytecode.length()); + b.writeBytes(bytecode.array(), 0, bytecode.length()); b.writeU2(0);//exception handlers b.writeU2(0);//attributes } @@ -292,13 +292,16 @@ public void writeBody(BufWriterImpl b) { ClassPrinter.toYaml(clm.methods().get(0).code().get(), ClassPrinter.Verbosity.TRACE_ALL, dump); } catch (Error | Exception _) { // fallback to bytecode hex dump - bytecode.rewind(); - while (bytecode.position() < bytecode.limit()) { - dump.accept("%n%04x:".formatted(bytecode.position())); - for (int i = 0; i < 16 && bytecode.position() < bytecode.limit(); i++) { - dump.accept(" %02x".formatted(bytecode.get())); - } + dumpBytesHex(dump, bytecode.array(), bytecode.length()); + } + } + + public static void dumpBytesHex(Consumer dump, byte[] bytes, int length) { + for (int i = 0; i < length; i++) { + if (i % 16 == 0) { + dump.accept("%n%04x:".formatted(i)); } + dump.accept(" %02x".formatted(bytes[i])); } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationBytecodes.java b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationBytecodes.java index c9b076aa1f0..de97730aff5 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationBytecodes.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerificationBytecodes.java @@ -24,9 +24,8 @@ */ package jdk.internal.classfile.impl.verifier; -import java.nio.ByteBuffer; - import java.lang.classfile.ClassFile; + import jdk.internal.classfile.impl.verifier.VerificationSignature.BasicType; import static jdk.internal.classfile.impl.verifier.VerificationSignature.BasicType.*; @@ -83,48 +82,12 @@ static boolean is_valid(int code) { return 0 <= code && code < number_of_codes; } - static int wide_length_for(int code) { - return is_valid(code) ? _lengths[code] >> 4 : -1; - } - static boolean is_store_into_local(int code) { return (ClassFile.ISTORE <= code && code <= ClassFile.ASTORE_3); } static final int _lengths[] = new int[number_of_codes]; - static int special_length_at(int code, byte bytecode[], int bci, int end) { - switch (code) { - case ClassFile.WIDE: - if (bci + 1 >= end) { - return -1; - } - return wide_length_for(bytecode[bci + 1] & 0xff); - case ClassFile.TABLESWITCH: - int aligned_bci = align(bci + 1); - if (aligned_bci + 3 * 4 >= end) { - return -1; - } - ByteBuffer bb = ByteBuffer.wrap(bytecode, aligned_bci + 1 * 4, 2 * 4); - int lo = bb.getInt(); - int hi = bb.getInt(); - int len = aligned_bci - bci + (3 + hi - lo + 1) * 4; - return len > 0 ? len : -1; - case ClassFile.LOOKUPSWITCH: - case _fast_binaryswitch: - case _fast_linearswitch: - aligned_bci = align(bci + 1); - if (aligned_bci + 2 * 4 >= end) { - return -1; - } - int npairs = ByteBuffer.wrap(bytecode, aligned_bci + 4, 4).getInt(); - len = aligned_bci - bci + (2 + 2 * npairs) * 4; - return len > 0 ? len : -1; - default: - return 0; - } - } - static int align(int n) { return (n + 3) & ~3; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java index 74cdb881a74..9a0e781d145 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java @@ -24,7 +24,6 @@ */ package jdk.internal.classfile.impl.verifier; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -316,20 +315,20 @@ void verify_method(VerificationWrapper.MethodWrapper m) { if (code_length < 1 || code_length > MAX_CODE_SIZE) { verifyError(String.format("Invalid method Code length %d", code_length)); } - var code = ByteBuffer.wrap(codeArray, 0, _method.codeLength()); - byte[] code_data = generate_code_data(code, code_length); + var code = RawBytecodeHelper.of(codeArray); + byte[] code_data = generate_code_data(code); int ex_minmax[] = new int[] {code_length, -1}; verify_exception_handler_table(code_length, code_data, ex_minmax); verify_local_variable_table(code_length, code_data); VerificationTable stackmap_table = new VerificationTable(stackmap_data, current_frame, max_locals, max_stack, code_data, code_length, cp, this); - var bcs = new RawBytecodeHelper(code); + var bcs = code.start(); boolean no_control_flow = false; int opcode; - while (!bcs.isLastBytecode()) { - opcode = bcs.rawNext(); - bci = bcs.bci; + while (bcs.next()) { + opcode = bcs.opcode(); + bci = bcs.bci(); current_frame.set_offset(bci); current_frame.set_mark(); stackmap_index = verify_stackmap_table(stackmap_index, bci, current_frame, stackmap_table, no_control_flow); @@ -340,7 +339,7 @@ void verify_method(VerificationWrapper.MethodWrapper m) { int target; VerificationType type, type2 = null; VerificationType atype; - if (bcs.isWide) { + if (bcs.isWide()) { if (opcode != ClassFile.IINC && opcode != ClassFile.ILOAD && opcode != ClassFile.ALOAD && opcode != ClassFile.LLOAD && opcode != ClassFile.ISTORE && opcode != ClassFile.ASTORE @@ -1195,7 +1194,7 @@ void verify_method(VerificationWrapper.MethodWrapper m) { case ClassFile.MULTIANEWARRAY : { index = bcs.getIndexU2(); - int dim = _method.codeArray()[bcs.bci+3] & 0xff; + int dim = _method.codeArray()[bcs.bci() +3] & 0xff; verify_cp_class_type(bci, index, cp); VerificationType new_array_type = cp_index_to_type(index, cp); @@ -1230,13 +1229,13 @@ void verify_method(VerificationWrapper.MethodWrapper m) { } } - private byte[] generate_code_data(ByteBuffer code, int code_length) { - byte code_data[] = new byte[code_length]; - var bcs = new RawBytecodeHelper(code); - while (!bcs.isLastBytecode()) { - if (bcs.rawNext() != ILLEGAL) { - int bci = bcs.bci; - if (bcs.rawCode == ClassFile.NEW) { + private byte[] generate_code_data(RawBytecodeHelper.CodeRange code) { + byte[] code_data = new byte[code.length()]; + var bcs = code.start(); + while (bcs.next()) { + if (bcs.opcode() != ILLEGAL) { + int bci = bcs.bci(); + if (bcs.opcode() == ClassFile.NEW) { code_data[bci] = NEW_OFFSET; } else { code_data[bci] = BYTECODE_OFFSET; @@ -1410,7 +1409,7 @@ void verify_ldc(int opcode, int index, VerificationFrame current_frame, Constant } void verify_switch(RawBytecodeHelper bcs, int code_length, byte[] code_data, VerificationFrame current_frame, VerificationTable stackmap_table) { - int bci = bcs.bci; + int bci = bcs.bci(); int aligned_bci = VerificationBytecodes.align(bci + 1); // 4639449 & 4647081: padding bytes must be 0 if (_klass.majorVersion() < NONZERO_PADDING_BYTES_IN_SWITCH_MAJOR_VERSION) { @@ -1422,12 +1421,12 @@ void verify_switch(RawBytecodeHelper bcs, int code_length, byte[] code_data, Ver padding_offset++; } } - int default_ofset = bcs.getInt(aligned_bci); + int default_offset = bcs.getIntUnchecked(aligned_bci); int keys, delta; current_frame.pop_stack(VerificationType.integer_type); - if (bcs.rawCode == ClassFile.TABLESWITCH) { - int low = bcs.getInt(aligned_bci + 4); - int high = bcs.getInt(aligned_bci + 2*4); + if (bcs.opcode() == ClassFile.TABLESWITCH) { + int low = bcs.getIntUnchecked(aligned_bci + 4); + int high = bcs.getIntUnchecked(aligned_bci + 2*4); if (low > high) { verifyError("low must be less than or equal to high in tableswitch"); } @@ -1438,31 +1437,31 @@ void verify_switch(RawBytecodeHelper bcs, int code_length, byte[] code_data, Ver delta = 1; } else { // Make sure that the lookupswitch items are sorted - keys = bcs.getInt(aligned_bci + 4); + keys = bcs.getIntUnchecked(aligned_bci + 4); if (keys < 0) { verifyError("number of keys in lookupswitch less than 0"); } delta = 2; for (int i = 0; i < (keys - 1); i++) { - int this_key = bcs.getInt(aligned_bci + (2+2*i)*4); - int next_key = bcs.getInt(aligned_bci + (2+2*i+2)*4); + int this_key = bcs.getIntUnchecked(aligned_bci + (2+2*i)*4); + int next_key = bcs.getIntUnchecked(aligned_bci + (2+2*i+2)*4); if (this_key >= next_key) { verifyError("Bad lookupswitch instruction"); } } } - int target = bci + default_ofset; + int target = bci + default_offset; stackmap_table.check_jump_target(current_frame, target); for (int i = 0; i < keys; i++) { - aligned_bci = VerificationBytecodes.align(bcs.bci + 1); - target = bci + bcs.getInt(aligned_bci + (3+i*delta)*4); + aligned_bci = VerificationBytecodes.align(bcs.bci() + 1); + target = bci + bcs.getIntUnchecked(aligned_bci + (3+i*delta)*4); stackmap_table.check_jump_target(current_frame, target); } } void verify_field_instructions(RawBytecodeHelper bcs, VerificationFrame current_frame, ConstantPoolWrapper cp, boolean allow_arrays) { int index = bcs.getIndexU2(); - verify_cp_type(bcs.bci, index, cp, 1 << JVM_CONSTANT_Fieldref); + verify_cp_type(bcs.bci(), index, cp, 1 << JVM_CONSTANT_Fieldref); String field_name = cp.refNameAt(index); String field_sig = cp.refSignatureAt(index); if (!VerificationSignature.isValidTypeSignature(field_sig)) verifyError("Invalid field signature"); @@ -1477,7 +1476,7 @@ void verify_field_instructions(RawBytecodeHelper bcs, VerificationFrame current_ VerificationType stack_object_type = null; int n = change_sig_to_verificationType(sig_stream, field_type, 0); boolean is_assignable; - switch (bcs.rawCode) { + switch (bcs.opcode()) { case ClassFile.GETSTATIC -> { for (int i = 0; i < n; i++) { current_frame.push_stack(field_type[i]); @@ -1524,7 +1523,7 @@ boolean ends_in_athrow(int start_bc_offset) { boolean verify_invoke_init(RawBytecodeHelper bcs, int ref_class_index, VerificationType ref_class_type, VerificationFrame current_frame, int code_length, boolean in_try_block, boolean this_uninit, ConstantPoolWrapper cp, VerificationTable stackmap_table) { - int bci = bcs.bci; + int bci = bcs.bci(); VerificationType type = current_frame.pop_stack(VerificationType.reference_check); if (type.is_uninitialized_this(this)) { String superk_name = current_class().superclassName(); @@ -1552,7 +1551,7 @@ boolean verify_invoke_init(RawBytecodeHelper bcs, int ref_class_index, Verificat if (new_offset > (code_length - 3) || (_method.codeArray()[new_offset] & 0xff) != ClassFile.NEW) { verifyError("Expecting new instruction"); } - int new_class_index = bcs.getIndexU2Raw(new_offset + 1); + int new_class_index = bcs.getU2(new_offset + 1); verify_cp_class_type(bci, new_class_index, cp); VerificationType new_class_type = cp_index_to_type( new_class_index, cp); @@ -1583,7 +1582,7 @@ static boolean is_same_or_direct_interface(VerificationWrapper klass, Verificati boolean verify_invoke_instructions(RawBytecodeHelper bcs, int code_length, VerificationFrame current_frame, boolean in_try_block, boolean this_uninit, VerificationType return_type, ConstantPoolWrapper cp, VerificationTable stackmap_table) { // Make sure the constant pool item is the right type int index = bcs.getIndexU2(); - int opcode = bcs.rawCode; + int opcode = bcs.opcode(); int types = 0; switch (opcode) { case ClassFile.INVOKEINTERFACE: @@ -1601,7 +1600,7 @@ boolean verify_invoke_instructions(RawBytecodeHelper bcs, int code_length, Verif default: types = 1 << JVM_CONSTANT_Methodref; } - verify_cp_type(bcs.bci, index, cp, types); + verify_cp_type(bcs.bci(), index, cp, types); String method_name = cp.refNameAt(index); String method_sig = cp.refSignatureAt(index); if (!VerificationSignature.isValidMethodSignature(method_sig)) verifyError("Invalid method signature"); @@ -1619,7 +1618,7 @@ boolean verify_invoke_instructions(RawBytecodeHelper bcs, int code_length, Verif mth_sig_verif_types = new sig_as_verification_types(verif_types); create_method_sig_entry(mth_sig_verif_types, sig); int nargs = mth_sig_verif_types.num_args(); - int bci = bcs.bci; + int bci = bcs.bci(); if (opcode == ClassFile.INVOKEINTERFACE) { if ((_method.codeArray()[bci+3] & 0xff) != (nargs+1)) { verifyError("Inconsistent args count operand in invokeinterface"); diff --git a/test/jdk/jdk/classfile/OneToOneTest.java b/test/jdk/jdk/classfile/OneToOneTest.java index 4e21cbebbe3..779e357ff9b 100644 --- a/test/jdk/jdk/classfile/OneToOneTest.java +++ b/test/jdk/jdk/classfile/OneToOneTest.java @@ -24,16 +24,11 @@ /* * @test * @summary Testing ClassFile class writing and reading. + * @bug 8339368 * @run junit OneToOneTest */ import java.lang.constant.ClassDesc; - -import static java.lang.classfile.ClassFile.ACC_PUBLIC; -import static java.lang.classfile.ClassFile.ACC_STATIC; -import static java.lang.constant.ConstantDescs.*; import java.lang.constant.MethodTypeDesc; -import java.util.List; - import java.lang.reflect.AccessFlag; import java.lang.classfile.ClassModel; import java.lang.classfile.ClassFile; @@ -41,22 +36,20 @@ import java.lang.classfile.Label; import java.lang.classfile.MethodModel; import java.lang.classfile.attribute.SourceFileAttribute; -import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.Test; +import java.lang.classfile.instruction.*; +import java.util.List; -import java.lang.classfile.instruction.ConstantInstruction; -import java.lang.classfile.instruction.StoreInstruction; -import java.lang.classfile.instruction.BranchInstruction; -import java.lang.classfile.instruction.LoadInstruction; -import java.lang.classfile.instruction.OperatorInstruction; -import java.lang.classfile.instruction.FieldInstruction; -import java.lang.classfile.instruction.InvokeInstruction; +import org.junit.jupiter.api.Test; +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; +import static java.lang.classfile.Opcode.*; +import static java.lang.constant.ConstantDescs.*; import static helpers.TestConstants.CD_PrintStream; import static helpers.TestConstants.CD_System; import static helpers.TestConstants.MTD_INT_VOID; import static helpers.TestConstants.MTD_VOID; -import static java.lang.classfile.Opcode.*; +import static org.junit.jupiter.api.Assertions.*; class OneToOneTest { @@ -156,4 +149,36 @@ void testClassWriteRead() { } assertTrue(found); } + + @Test + void testJava5ClassWriteRead() { + MethodModel mm = ClassFile.of().parse(ClassFile.of().build(ClassDesc.of("MyClass"), clb -> clb + .withVersion(ClassFile.JAVA_5_VERSION, 0) + .withMethodBody("switches", MTD_void, ACC_STATIC, cob -> { + Label l1 = cob.newLabel(), l2 = cob.newLabel(), l3 = cob.newLabel(), l4 = cob.newLabel(); + cob.iconst_0() + .tableswitch(l1, List.of(SwitchCase.of(0, l2))) + .labelBinding(l1) + .nop() + .labelBinding(l2) + .iconst_0() + .lookupswitch(l3, List.of(SwitchCase.of(0, l4))) + .labelBinding(l3) + .nop() + .labelBinding(l4) + .return_(); + }))).methods().getFirst(); + var it = mm.code().orElseThrow().iterator(); + while (!(it.next() instanceof ConstantInstruction)); + assertTrue(it.next() instanceof TableSwitchInstruction tsi + && it.next() instanceof LabelTarget lt1 && lt1.label().equals(tsi.defaultTarget()) + && it.next() instanceof NopInstruction + && it.next() instanceof LabelTarget lt2 && lt2.label().equals(tsi.cases().getFirst().target()) + && it.next() instanceof ConstantInstruction + && it.next() instanceof LookupSwitchInstruction lsi + && it.next() instanceof LabelTarget lt3 && lt3.label().equals(lsi.defaultTarget()) + && it.next() instanceof NopInstruction + && it.next() instanceof LabelTarget lt4 && lt4.label().equals(lsi.cases().getFirst().target()), + () -> mm.code().get().elementList().toString()); + } } diff --git a/test/jdk/jdk/classfile/UtilTest.java b/test/jdk/jdk/classfile/UtilTest.java index 5ed04180919..d9d8240ae91 100644 --- a/test/jdk/jdk/classfile/UtilTest.java +++ b/test/jdk/jdk/classfile/UtilTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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 @@ -26,7 +26,14 @@ * @summary Testing ClassFile Util. * @run junit UtilTest */ +import java.lang.classfile.ClassFile; +import java.lang.classfile.Opcode; import java.lang.constant.MethodTypeDesc; +import java.lang.invoke.MethodHandles; +import java.util.Arrays; +import java.util.BitSet; + +import jdk.internal.classfile.impl.RawBytecodeHelper; import jdk.internal.classfile.impl.Util; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -76,4 +83,21 @@ void testParameterSlots() { private void assertSlots(String methodDesc, int slots) { assertEquals(Util.parameterSlots(MethodTypeDesc.ofDescriptor(methodDesc)), slots); } + + @Test + void testOpcodeLengthTable() { + var lengths = new byte[0x100]; + Arrays.fill(lengths, (byte) -1); + for (var op : Opcode.values()) { + if (!op.isWide()) { + lengths[op.bytecode()] = (byte) op.sizeIfFixed(); + } else { + // Wide pseudo-opcodes have double the length as normal variants + // Must match logic in checkSpecialInstruction() + assertEquals(op.sizeIfFixed(), lengths[op.bytecode() & 0xFF] * 2, op + " size"); + } + } + + assertArrayEquals(lengths, RawBytecodeHelper.LENGTHS); + } } diff --git a/test/micro/org/openjdk/bench/jdk/classfile/CodeAttributeTools.java b/test/micro/org/openjdk/bench/jdk/classfile/CodeAttributeTools.java index 4cf578889d1..4239f70504b 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/CodeAttributeTools.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/CodeAttributeTools.java @@ -23,24 +23,21 @@ package org.openjdk.bench.jdk.classfile; import java.io.IOException; +import java.lang.classfile.Attributes; import java.lang.constant.ClassDesc; import java.net.URI; -import java.nio.ByteBuffer; import java.nio.file.FileSystems; import java.nio.file.Files; -import java.util.Iterator; import java.util.ArrayList; import java.util.List; import java.lang.classfile.ClassFile; import java.lang.classfile.ClassReader; -import java.lang.classfile.MethodModel; -import java.lang.classfile.constantpool.ConstantPool; import java.lang.classfile.constantpool.ConstantPoolBuilder; import java.lang.constant.MethodTypeDesc; import jdk.internal.classfile.impl.AbstractPseudoInstruction; -import jdk.internal.classfile.impl.CodeImpl; import jdk.internal.classfile.impl.LabelContext; import jdk.internal.classfile.impl.ClassFileImpl; +import jdk.internal.classfile.impl.RawBytecodeHelper; import jdk.internal.classfile.impl.SplitConstantPool; import jdk.internal.classfile.impl.StackCounter; import jdk.internal.classfile.impl.StackMapGenerator; @@ -70,7 +67,7 @@ record GenData(LabelContext labelContext, String methodName, MethodTypeDesc methodDesc, boolean isStatic, - ByteBuffer bytecode, + RawBytecodeHelper.CodeRange bytecode, ConstantPoolBuilder constantPool, List handlers) {} @@ -85,15 +82,14 @@ public void setup() throws IOException { var thisCls = clm.thisClass().asSymbol(); var cp = new SplitConstantPool((ClassReader)clm.constantPool()); for (var m : clm.methods()) { - m.code().ifPresent(com -> { - var bb = ByteBuffer.wrap(((CodeImpl)com).contents()); + m.findAttribute(Attributes.code()).ifPresent(com -> { data.add(new GenData( (LabelContext)com, thisCls, m.methodName().stringValue(), m.methodTypeSymbol(), (m.flags().flagsMask() & ClassFile.ACC_STATIC) != 0, - bb.slice(8, bb.getInt(4)), + RawBytecodeHelper.of(com.codeArray()), cp, com.exceptionHandlers().stream().map(eh -> (AbstractPseudoInstruction.ExceptionCatchImpl)eh).toList())); }); @@ -112,7 +108,7 @@ public void benchmarkStackMapsGenerator(Blackhole bh) { d.methodName(), d.methodDesc(), d.isStatic(), - d.bytecode().rewind(), + d.bytecode(), (SplitConstantPool)d.constantPool(), (ClassFileImpl)ClassFile.of(), d.handlers())); @@ -127,7 +123,7 @@ public void benchmarkStackCounter(Blackhole bh) { d.methodName(), d.methodDesc(), d.isStatic(), - d.bytecode().rewind(), + d.bytecode(), (SplitConstantPool)d.constantPool(), d.handlers())); }