| /* | |
| * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. | |
| * Copyright (c) 2012, 2025 SAP SE. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| * | |
| */ | |
| #include "asm/macroAssembler.inline.hpp" | |
| #include "compiler/oopMap.hpp" | |
| #include "gc/shared/barrierSet.hpp" | |
| #include "gc/shared/barrierSetAssembler.hpp" | |
| #include "gc/shared/barrierSetNMethod.hpp" | |
| #include "interpreter/interpreter.hpp" | |
| #include "nativeInst_ppc.hpp" | |
| #include "oops/instanceOop.hpp" | |
| #include "oops/method.hpp" | |
| #include "oops/objArrayKlass.hpp" | |
| #include "oops/oop.inline.hpp" | |
| #include "prims/methodHandles.hpp" | |
| #include "prims/upcallLinker.hpp" | |
| #include "runtime/continuation.hpp" | |
| #include "runtime/continuationEntry.inline.hpp" | |
| #include "runtime/frame.inline.hpp" | |
| #include "runtime/handles.inline.hpp" | |
| #include "runtime/javaThread.hpp" | |
| #include "runtime/sharedRuntime.hpp" | |
| #include "runtime/stubCodeGenerator.hpp" | |
| #include "runtime/stubRoutines.hpp" | |
| #include "runtime/vm_version.hpp" | |
| #include "utilities/align.hpp" | |
| #include "utilities/powerOfTwo.hpp" | |
| #if INCLUDE_ZGC | |
| #include "gc/z/zBarrierSetAssembler.hpp" | |
| #endif | |
| // Declaration and definition of StubGenerator (no .hpp file). | |
| // For a more detailed description of the stub routine structure | |
| // see the comment in stubRoutines.hpp. | |
| #define __ _masm-> | |
| #ifdef PRODUCT | |
| #define BLOCK_COMMENT(str) // nothing | |
| #else | |
| #define BLOCK_COMMENT(str) __ block_comment(str) | |
| #endif | |
| #if defined(ABI_ELFv2) | |
| #define STUB_ENTRY(name) StubRoutines::name | |
| #else | |
| #define STUB_ENTRY(name) ((FunctionDescriptor*)StubRoutines::name)->entry() | |
| #endif | |
| class StubGenerator: public StubCodeGenerator { | |
| private: | |
| // Call stubs are used to call Java from C | |
| // | |
| // Arguments: | |
| // | |
| // R3 - call wrapper address : address | |
| // R4 - result : intptr_t* | |
| // R5 - result type : BasicType | |
| // R6 - method : Method | |
| // R7 - frame mgr entry point : address | |
| // R8 - parameter block : intptr_t* | |
| // R9 - parameter count in words : int | |
| // R10 - thread : Thread* | |
| // | |
| address generate_call_stub(address& return_address) { | |
| // Setup a new c frame, copy java arguments, call template interpreter or | |
| // native_entry, and process result. | |
| StubGenStubId stub_id = StubGenStubId::call_stub_id; | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| int save_nonvolatile_registers_size = __ save_nonvolatile_registers_size(true, SuperwordUseVSX); | |
| // some sanity checks | |
| STATIC_ASSERT(StackAlignmentInBytes == 16); | |
| assert((sizeof(frame::native_abi_minframe) % 16) == 0, "unaligned"); | |
| assert((sizeof(frame::native_abi_reg_args) % 16) == 0, "unaligned"); | |
| assert((save_nonvolatile_registers_size % 16) == 0, "unaligned"); | |
| assert((sizeof(frame::parent_ijava_frame_abi) % 16) == 0, "unaligned"); | |
| assert((sizeof(frame::entry_frame_locals) % 16) == 0, "unaligned"); | |
| Register r_arg_call_wrapper_addr = R3; | |
| Register r_arg_result_addr = R4; | |
| Register r_arg_result_type = R5; | |
| Register r_arg_method = R6; | |
| Register r_arg_entry = R7; | |
| Register r_arg_argument_addr = R8; | |
| Register r_arg_argument_count = R9; | |
| Register r_arg_thread = R10; | |
| Register r_entryframe_fp = R2; // volatile | |
| Register r_argument_size = R11_scratch1; // volatile | |
| Register r_top_of_arguments_addr = R21_tmp1; | |
| { | |
| // Stack on entry to call_stub: | |
| // | |
| // F1 [C_FRAME] | |
| // ... | |
| Register r_frame_size = R12_scratch2; // volatile | |
| Label arguments_copied; | |
| // Save LR/CR to caller's C_FRAME. | |
| __ save_LR_CR(R0); | |
| // Keep copy of our frame pointer (caller's SP). | |
| __ mr(r_entryframe_fp, R1_SP); | |
| // calculate frame size | |
| STATIC_ASSERT(Interpreter::logStackElementSize == 3); | |
| // space for arguments aligned up: ((arg_count + 1) * 8) &~ 15 | |
| __ addi(r_frame_size, r_arg_argument_count, 1); | |
| __ rldicr(r_frame_size, r_frame_size, 3, 63 - 4); | |
| // this is the pure space for arguments (excluding alignment padding) | |
| __ sldi(r_argument_size, r_arg_argument_count, 3); | |
| __ addi(r_frame_size, r_frame_size, | |
| save_nonvolatile_registers_size + frame::entry_frame_locals_size + frame::top_ijava_frame_abi_size); | |
| // push ENTRY_FRAME | |
| __ push_frame(r_frame_size, R0); | |
| // Save non-volatiles registers to ENTRY_FRAME. | |
| __ save_nonvolatile_registers(r_entryframe_fp, -(frame::entry_frame_locals_size + save_nonvolatile_registers_size), | |
| true, SuperwordUseVSX); | |
| BLOCK_COMMENT("Push ENTRY_FRAME including arguments"); | |
| // Push ENTRY_FRAME including arguments: | |
| // | |
| // F0 [TOP_IJAVA_FRAME_ABI] | |
| // alignment (optional) | |
| // [outgoing Java arguments] | |
| // [non-volatiles] | |
| // [ENTRY_FRAME_LOCALS] | |
| // F1 [C_FRAME] | |
| // ... | |
| // initialize call_stub locals (step 1) | |
| __ std(r_arg_call_wrapper_addr, _entry_frame_locals_neg(call_wrapper_address), r_entryframe_fp); | |
| __ std(r_arg_result_addr, _entry_frame_locals_neg(result_address), r_entryframe_fp); | |
| __ std(r_arg_result_type, _entry_frame_locals_neg(result_type), r_entryframe_fp); | |
| // we will save arguments_tos_address later | |
| BLOCK_COMMENT("Copy Java arguments"); | |
| // copy Java arguments | |
| // Calculate top_of_arguments_addr which will be R17_tos (not prepushed) later. | |
| __ addi(r_top_of_arguments_addr, r_entryframe_fp, | |
| -(save_nonvolatile_registers_size + frame::entry_frame_locals_size)); | |
| __ sub(r_top_of_arguments_addr, r_top_of_arguments_addr, r_argument_size); | |
| // any arguments to copy? | |
| __ cmpdi(CR0, r_arg_argument_count, 0); | |
| __ beq(CR0, arguments_copied); | |
| // prepare loop and copy arguments in reverse order | |
| { | |
| Register r_argument_addr = R22_tmp2; | |
| Register r_argumentcopy_addr = R23_tmp3; | |
| // init CTR with arg_argument_count | |
| __ mtctr(r_arg_argument_count); | |
| // let r_argumentcopy_addr point to last outgoing Java arguments P | |
| __ mr(r_argumentcopy_addr, r_top_of_arguments_addr); | |
| // let r_argument_addr point to last incoming java argument | |
| __ add(r_argument_addr, r_arg_argument_addr, r_argument_size); | |
| __ addi(r_argument_addr, r_argument_addr, -BytesPerWord); | |
| // now loop while CTR > 0 and copy arguments | |
| { | |
| Label next_argument; | |
| __ bind(next_argument); | |
| __ ld(R0, 0, r_argument_addr); | |
| // argument_addr--; | |
| __ addi(r_argument_addr, r_argument_addr, -BytesPerWord); | |
| __ std(R0, 0, r_argumentcopy_addr); | |
| // argumentcopy_addr++; | |
| __ addi(r_argumentcopy_addr, r_argumentcopy_addr, BytesPerWord); | |
| __ bdnz(next_argument); | |
| } | |
| } | |
| // Arguments copied, continue. | |
| __ bind(arguments_copied); | |
| } | |
| { | |
| BLOCK_COMMENT("Call template interpreter or native entry."); | |
| assert_different_registers(r_arg_entry, r_top_of_arguments_addr, r_arg_method, r_arg_thread); | |
| // Register state on entry to template interpreter / native entry: | |
| // | |
| // tos - intptr_t* sender tos (prepushed) Lesp = (SP) + copied_arguments_offset - 8 | |
| // R19_method - Method | |
| // R16_thread - JavaThread* | |
| // Tos must point to last argument - element_size. | |
| const Register tos = R15_esp; | |
| __ addi(tos, r_top_of_arguments_addr, -Interpreter::stackElementSize); | |
| // initialize call_stub locals (step 2) | |
| // now save tos as arguments_tos_address | |
| __ std(tos, _entry_frame_locals_neg(arguments_tos_address), r_entryframe_fp); | |
| // load argument registers for call | |
| __ mr(R19_method, r_arg_method); | |
| __ mr(R16_thread, r_arg_thread); | |
| assert(tos != r_arg_method, "trashed r_arg_method"); | |
| assert(tos != r_arg_thread && R19_method != r_arg_thread, "trashed r_arg_thread"); | |
| // Set R15_prev_state to 0 for simplifying checks in callee. | |
| __ load_const_optimized(R25_templateTableBase, (address)Interpreter::dispatch_table((TosState)0), R0); | |
| // Stack on entry to template interpreter / native entry: | |
| // | |
| // F0 [TOP_IJAVA_FRAME_ABI] | |
| // alignment (optional) | |
| // [outgoing Java arguments] | |
| // [non-volatiles] | |
| // [ENTRY_FRAME_LOCALS] | |
| // F1 [C_FRAME] | |
| // ... | |
| // | |
| // global toc register | |
| __ load_const_optimized(R29_TOC, MacroAssembler::global_toc(), R0); | |
| // Remember the senderSP so we interpreter can pop c2i arguments off of the stack | |
| // when called via a c2i. | |
| // Pass initial_caller_sp to framemanager. | |
| __ mr(R21_sender_SP, R1_SP); | |
| // Do a light-weight C-call here, r_arg_entry holds the address | |
| // of the interpreter entry point (template interpreter or native entry) | |
| // and save runtime-value of LR in return_address. | |
| assert(r_arg_entry != tos && r_arg_entry != R19_method && r_arg_entry != R16_thread, | |
| "trashed r_arg_entry"); | |
| return_address = __ call_stub(r_arg_entry); | |
| } | |
| { | |
| BLOCK_COMMENT("Returned from template interpreter or native entry."); | |
| // Now pop frame, process result, and return to caller. | |
| // Stack on exit from template interpreter / native entry: | |
| // | |
| // F0 [ABI] | |
| // ... | |
| // [non-volatiles] | |
| // [ENTRY_FRAME_LOCALS] | |
| // F1 [C_FRAME] | |
| // ... | |
| // | |
| // Just pop the topmost frame ... | |
| // | |
| Label ret_is_object; | |
| Label ret_is_long; | |
| Label ret_is_float; | |
| Label ret_is_double; | |
| Register r_lr = R11_scratch1; | |
| Register r_cr = R12_scratch2; | |
| // Reload some volatile registers which we've spilled before the call | |
| // to template interpreter / native entry. | |
| // Access all locals via frame pointer, because we know nothing about | |
| // the topmost frame's size. | |
| __ ld(r_entryframe_fp, _abi0(callers_sp), R1_SP); // restore after call | |
| assert_different_registers(r_entryframe_fp, R3_RET, r_arg_result_addr, r_arg_result_type, r_cr, r_lr); | |
| __ ld(r_arg_result_addr, _entry_frame_locals_neg(result_address), r_entryframe_fp); | |
| __ ld(r_arg_result_type, _entry_frame_locals_neg(result_type), r_entryframe_fp); | |
| __ ld(r_cr, _abi0(cr), r_entryframe_fp); | |
| __ ld(r_lr, _abi0(lr), r_entryframe_fp); | |
| __ mtcr(r_cr); // restore CR | |
| __ mtlr(r_lr); // restore LR | |
| // Store result depending on type. Everything that is not | |
| // T_OBJECT, T_LONG, T_FLOAT, or T_DOUBLE is treated as T_INT. | |
| // Using volatile CRs. | |
| __ cmpwi(CR1, r_arg_result_type, T_OBJECT); | |
| __ cmpwi(CR5, r_arg_result_type, T_LONG); | |
| __ cmpwi(CR6, r_arg_result_type, T_FLOAT); | |
| __ cmpwi(CR7, r_arg_result_type, T_DOUBLE); | |
| __ pop_cont_fastpath(); // kills CR0, uses R16_thread | |
| // restore non-volatile registers | |
| __ restore_nonvolatile_registers(r_entryframe_fp, -(frame::entry_frame_locals_size + save_nonvolatile_registers_size), | |
| true, SuperwordUseVSX); | |
| // pop frame | |
| __ mr(R1_SP, r_entryframe_fp); | |
| // Stack on exit from call_stub: | |
| // | |
| // 0 [C_FRAME] | |
| // ... | |
| // | |
| // no call_stub frames left. | |
| __ beq(CR1, ret_is_object); | |
| __ beq(CR5, ret_is_long); | |
| __ beq(CR6, ret_is_float); | |
| __ beq(CR7, ret_is_double); | |
| // default: | |
| __ stw(R3_RET, 0, r_arg_result_addr); | |
| __ blr(); // return to caller | |
| // case T_OBJECT: | |
| // case T_LONG: | |
| __ bind(ret_is_object); | |
| __ bind(ret_is_long); | |
| __ std(R3_RET, 0, r_arg_result_addr); | |
| __ blr(); // return to caller | |
| // case T_FLOAT: | |
| __ bind(ret_is_float); | |
| __ stfs(F1_RET, 0, r_arg_result_addr); | |
| __ blr(); // return to caller | |
| // case T_DOUBLE: | |
| __ bind(ret_is_double); | |
| __ stfd(F1_RET, 0, r_arg_result_addr); | |
| __ blr(); // return to caller | |
| } | |
| return start; | |
| } | |
| // Return point for a Java call if there's an exception thrown in | |
| // Java code. The exception is caught and transformed into a | |
| // pending exception stored in JavaThread that can be tested from | |
| // within the VM. | |
| // | |
| address generate_catch_exception() { | |
| StubGenStubId stub_id = StubGenStubId::catch_exception_id; | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ pc(); | |
| // Registers alive | |
| // | |
| // R16_thread | |
| // R3_ARG1 - address of pending exception | |
| // R4_ARG2 - return address in call stub | |
| const Register exception_file = R21_tmp1; | |
| const Register exception_line = R22_tmp2; | |
| __ load_const(exception_file, (void*)__FILE__); | |
| __ load_const(exception_line, (void*)__LINE__); | |
| __ std(R3_ARG1, in_bytes(JavaThread::pending_exception_offset()), R16_thread); | |
| // store into `char *' | |
| __ std(exception_file, in_bytes(JavaThread::exception_file_offset()), R16_thread); | |
| // store into `int' | |
| __ stw(exception_line, in_bytes(JavaThread::exception_line_offset()), R16_thread); | |
| // complete return to VM | |
| assert(StubRoutines::_call_stub_return_address != nullptr, "must have been generated before"); | |
| __ mtlr(R4_ARG2); | |
| // continue in call stub | |
| __ blr(); | |
| return start; | |
| } | |
| // Continuation point for runtime calls returning with a pending | |
| // exception. The pending exception check happened in the runtime | |
| // or native call stub. The pending exception in Thread is | |
| // converted into a Java-level exception. | |
| // | |
| // Read: | |
| // | |
| // LR: The pc the runtime library callee wants to return to. | |
| // Since the exception occurred in the callee, the return pc | |
| // from the point of view of Java is the exception pc. | |
| // thread: Needed for method handles. | |
| // | |
| // Invalidate: | |
| // | |
| // volatile registers (except below). | |
| // | |
| // Update: | |
| // | |
| // R4_ARG2: exception | |
| // | |
| // (LR is unchanged and is live out). | |
| // | |
| address generate_forward_exception() { | |
| StubGenStubId stub_id = StubGenStubId::forward_exception_id; | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ pc(); | |
| if (VerifyOops) { | |
| // Get pending exception oop. | |
| __ ld(R3_ARG1, | |
| in_bytes(Thread::pending_exception_offset()), | |
| R16_thread); | |
| // Make sure that this code is only executed if there is a pending exception. | |
| { | |
| Label L; | |
| __ cmpdi(CR0, R3_ARG1, 0); | |
| __ bne(CR0, L); | |
| __ stop("StubRoutines::forward exception: no pending exception (1)"); | |
| __ bind(L); | |
| } | |
| __ verify_oop(R3_ARG1, "StubRoutines::forward exception: not an oop"); | |
| } | |
| // Save LR/CR and copy exception pc (LR) into R4_ARG2. | |
| __ save_LR(R4_ARG2); | |
| __ push_frame_reg_args(0, R0); | |
| // Find exception handler. | |
| __ call_VM_leaf(CAST_FROM_FN_PTR(address, | |
| SharedRuntime::exception_handler_for_return_address), | |
| R16_thread, | |
| R4_ARG2); | |
| // Copy handler's address. | |
| __ mtctr(R3_RET); | |
| __ pop_frame(); | |
| __ restore_LR(R0); | |
| // Set up the arguments for the exception handler: | |
| // - R3_ARG1: exception oop | |
| // - R4_ARG2: exception pc. | |
| // Load pending exception oop. | |
| __ ld(R3_ARG1, | |
| in_bytes(Thread::pending_exception_offset()), | |
| R16_thread); | |
| // The exception pc is the return address in the caller. | |
| // Must load it into R4_ARG2. | |
| __ mflr(R4_ARG2); | |
| #ifdef ASSERT | |
| // Make sure exception is set. | |
| { | |
| Label L; | |
| __ cmpdi(CR0, R3_ARG1, 0); | |
| __ bne(CR0, L); | |
| __ stop("StubRoutines::forward exception: no pending exception (2)"); | |
| __ bind(L); | |
| } | |
| #endif | |
| // Clear the pending exception. | |
| __ li(R0, 0); | |
| __ std(R0, | |
| in_bytes(Thread::pending_exception_offset()), | |
| R16_thread); | |
| // Jump to exception handler. | |
| __ bctr(); | |
| return start; | |
| } | |
| #undef __ | |
| #define __ _masm-> | |
| #if !defined(PRODUCT) | |
| // Wrapper which calls oopDesc::is_oop_or_null() | |
| // Only called by MacroAssembler::verify_oop | |
| static void verify_oop_helper(const char* message, oopDesc* o) { | |
| if (!oopDesc::is_oop_or_null(o)) { | |
| fatal("%s. oop: " PTR_FORMAT, message, p2i(o)); | |
| } | |
| ++ StubRoutines::_verify_oop_count; | |
| } | |
| #endif | |
| // Return address of code to be called from code generated by | |
| // MacroAssembler::verify_oop. | |
| // | |
| // Don't generate, rather use C++ code. | |
| address generate_verify_oop() { | |
| // this is actually a `FunctionDescriptor*'. | |
| address start = nullptr; | |
| #if !defined(PRODUCT) | |
| start = CAST_FROM_FN_PTR(address, verify_oop_helper); | |
| #endif | |
| return start; | |
| } | |
| // Computes the Galois/Counter Mode (GCM) product and reduction. | |
| // | |
| // This function performs polynomial multiplication of the subkey H with | |
| // the current GHASH state using vectorized polynomial multiplication (`vpmsumd`). | |
| // The subkey H is divided into lower, middle, and higher halves. | |
| // The multiplication results are reduced using `vConstC2` to stay within GF(2^128). | |
| // The final computed value is stored back into `vState`. | |
| static void computeGCMProduct(MacroAssembler* _masm, | |
| VectorRegister vLowerH, VectorRegister vH, VectorRegister vHigherH, | |
| VectorRegister vConstC2, VectorRegister vZero, VectorRegister vState, | |
| VectorRegister vLowProduct, VectorRegister vMidProduct, VectorRegister vHighProduct, | |
| VectorRegister vReducedLow, VectorRegister vTmp8, VectorRegister vTmp9, | |
| VectorRegister vCombinedResult, VectorRegister vSwappedH) { | |
| __ vxor(vH, vH, vState); | |
| __ vpmsumd(vLowProduct, vLowerH, vH); // L : Lower Half of subkey H | |
| __ vpmsumd(vMidProduct, vSwappedH, vH); // M : Combined halves of subkey H | |
| __ vpmsumd(vHighProduct, vHigherH, vH); // H : Higher Half of subkey H | |
| __ vpmsumd(vReducedLow, vLowProduct, vConstC2); // Reduction | |
| __ vsldoi(vTmp8, vMidProduct, vZero, 8); // mL : Extract the lower 64 bits of M | |
| __ vsldoi(vTmp9, vZero, vMidProduct, 8); // mH : Extract the higher 64 bits of M | |
| __ vxor(vLowProduct, vLowProduct, vTmp8); // LL + mL : Partial result for lower half | |
| __ vxor(vHighProduct, vHighProduct, vTmp9); // HH + mH : Partial result for upper half | |
| __ vsldoi(vLowProduct, vLowProduct, vLowProduct, 8); // Swap | |
| __ vxor(vLowProduct, vLowProduct, vReducedLow); | |
| __ vsldoi(vCombinedResult, vLowProduct, vLowProduct, 8); // Swap | |
| __ vpmsumd(vLowProduct, vLowProduct, vConstC2); // Reduction using constant | |
| __ vxor(vCombinedResult, vCombinedResult, vHighProduct); // Combine reduced Low & High products | |
| __ vxor(vState, vLowProduct, vCombinedResult); | |
| } | |
| // Generate stub for ghash process blocks. | |
| // | |
| // Arguments for generated stub: | |
| // state: R3_ARG1 (long[] state) | |
| // subkeyH: R4_ARG2 (long[] subH) | |
| // data: R5_ARG3 (byte[] data) | |
| // blocks: R6_ARG4 (number of 16-byte blocks to process) | |
| // | |
| // The polynomials are processed in bit-reflected order for efficiency reasons. | |
| // This optimization leverages the structure of the Galois field arithmetic | |
| // to minimize the number of bit manipulations required during multiplication. | |
| // For an explanation of how this works, refer : | |
| // Vinodh Gopal, Erdinc Ozturk, Wajdi Feghali, Jim Guilford, Gil Wolrich, | |
| // Martin Dixon. "Optimized Galois-Counter-Mode Implementation on Intel® | |
| // Architecture Processor" | |
| // http://web.archive.org/web/20130609111954/http://www.intel.com/content/dam/www/public/us/en/documents/white-papers/communications-ia-galois-counter-mode-paper.pdf | |
| // | |
| // | |
| address generate_ghash_processBlocks() { | |
| StubCodeMark mark(this, "StubRoutines", "ghash"); | |
| address start = __ function_entry(); | |
| // Registers for parameters | |
| Register state = R3_ARG1; // long[] state | |
| Register subkeyH = R4_ARG2; // long[] subH | |
| Register data = R5_ARG3; // byte[] data | |
| Register blocks = R6_ARG4; | |
| Register temp1 = R8; | |
| // Vector Registers | |
| VectorRegister vZero = VR0; | |
| VectorRegister vH = VR1; | |
| VectorRegister vLowerH = VR2; | |
| VectorRegister vHigherH = VR3; | |
| VectorRegister vLowProduct = VR4; | |
| VectorRegister vMidProduct = VR5; | |
| VectorRegister vHighProduct = VR6; | |
| VectorRegister vReducedLow = VR7; | |
| VectorRegister vTmp8 = VR8; | |
| VectorRegister vTmp9 = VR9; | |
| VectorRegister vTmp10 = VR10; | |
| VectorRegister vSwappedH = VR11; | |
| VectorRegister vTmp12 = VR12; | |
| VectorRegister loadOrder = VR13; | |
| VectorRegister vHigh = VR14; | |
| VectorRegister vLow = VR15; | |
| VectorRegister vState = VR16; | |
| VectorRegister vPerm = VR17; | |
| VectorRegister vCombinedResult = VR18; | |
| VectorRegister vConstC2 = VR19; | |
| __ li(temp1, 0xc2); | |
| __ sldi(temp1, temp1, 56); | |
| __ vspltisb(vZero, 0); | |
| __ mtvrd(vConstC2, temp1); | |
| __ lxvd2x(vH->to_vsr(), subkeyH); | |
| __ lxvd2x(vState->to_vsr(), state); | |
| // Operations to obtain lower and higher bytes of subkey H. | |
| __ vspltisb(vReducedLow, 1); | |
| __ vspltisb(vTmp10, 7); | |
| __ vsldoi(vTmp8, vZero, vReducedLow, 1); // 0x1 | |
| __ vor(vTmp8, vConstC2, vTmp8); // 0xC2...1 | |
| __ vsplt(vTmp9, 0, vH); // MSB of H | |
| __ vsl(vH, vH, vReducedLow); // Carry = H<<7 | |
| __ vsrab(vTmp9, vTmp9, vTmp10); | |
| __ vand(vTmp9, vTmp9, vTmp8); // Carry | |
| __ vxor(vTmp10, vH, vTmp9); | |
| __ vsldoi(vConstC2, vZero, vConstC2, 8); | |
| __ vsldoi(vSwappedH, vTmp10, vTmp10, 8); // swap Lower and Higher Halves of subkey H | |
| __ vsldoi(vLowerH, vZero, vSwappedH, 8); // H.L | |
| __ vsldoi(vHigherH, vSwappedH, vZero, 8); // H.H | |
| #ifdef ASSERT | |
| __ cmpwi(CR0, blocks, 0); // Compare 'blocks' (R6_ARG4) with zero | |
| __ asm_assert_ne("blocks should NOT be zero"); | |
| #endif | |
| __ clrldi(blocks, blocks, 32); | |
| __ mtctr(blocks); | |
| __ lvsl(loadOrder, temp1); | |
| #ifdef VM_LITTLE_ENDIAN | |
| __ vspltisb(vTmp12, 0xf); | |
| __ vxor(loadOrder, loadOrder, vTmp12); | |
| #define LE_swap_bytes(x) __ vec_perm(x, x, x, loadOrder) | |
| #else | |
| #define LE_swap_bytes(x) | |
| #endif | |
| // This code performs Karatsuba multiplication in Galois fields to compute the GHASH operation. | |
| // | |
| // The Karatsuba method breaks the multiplication of two 128-bit numbers into smaller parts, | |
| // performing three 128-bit multiplications and combining the results efficiently. | |
| // | |
| // (C1:C0) = A1*B1, (D1:D0) = A0*B0, (E1:E0) = (A0+A1)(B0+B1) | |
| // (A1:A0)(B1:B0) = C1:(C0+C1+D1+E1):(D1+C0+D0+E0):D0 | |
| // | |
| // Inputs: | |
| // - vH: The data vector (state), containing both B0 (lower half) and B1 (higher half). | |
| // - vLowerH: Lower half of the subkey H (A0). | |
| // - vHigherH: Higher half of the subkey H (A1). | |
| // - vConstC2: Constant used for reduction (for final processing). | |
| // | |
| // References: | |
| // Shay Gueron, Michael E. Kounavis. | |
| // "Intel® Carry-Less Multiplication Instruction and its Usage for Computing the GCM Mode" | |
| // https://web.archive.org/web/20110609115824/https://software.intel.com/file/24918 | |
| // | |
| Label L_aligned_loop, L_store, L_unaligned_loop, L_initialize_unaligned_loop; | |
| __ andi(temp1, data, 15); | |
| __ cmpwi(CR0, temp1, 0); | |
| __ bne(CR0, L_initialize_unaligned_loop); | |
| __ bind(L_aligned_loop); | |
| __ lvx(vH, temp1, data); | |
| LE_swap_bytes(vH); | |
| computeGCMProduct(_masm, vLowerH, vH, vHigherH, vConstC2, vZero, vState, | |
| vLowProduct, vMidProduct, vHighProduct, vReducedLow, vTmp8, vTmp9, vCombinedResult, vSwappedH); | |
| __ addi(data, data, 16); | |
| __ bdnz(L_aligned_loop); | |
| __ b(L_store); | |
| __ bind(L_initialize_unaligned_loop); | |
| __ li(temp1, 0); | |
| __ lvsl(vPerm, temp1, data); | |
| __ lvx(vHigh, temp1, data); | |
| #ifdef VM_LITTLE_ENDIAN | |
| __ vspltisb(vTmp12, -1); | |
| __ vxor(vPerm, vPerm, vTmp12); | |
| #endif | |
| __ bind(L_unaligned_loop); | |
| __ addi(data, data, 16); | |
| __ lvx(vLow, temp1, data); | |
| __ vec_perm(vH, vHigh, vLow, vPerm); | |
| computeGCMProduct(_masm, vLowerH, vH, vHigherH, vConstC2, vZero, vState, | |
| vLowProduct, vMidProduct, vHighProduct, vReducedLow, vTmp8, vTmp9, vCombinedResult, vSwappedH); | |
| __ vmr(vHigh, vLow); | |
| __ bdnz(L_unaligned_loop); | |
| __ bind(L_store); | |
| __ stxvd2x(vState->to_vsr(), state); | |
| __ blr(); | |
| return start; | |
| } | |
| // -XX:+OptimizeFill : convert fill/copy loops into intrinsic | |
| // | |
| // The code is implemented(ported from sparc) as we believe it benefits JVM98, however | |
| // tracing(-XX:+TraceOptimizeFill) shows the intrinsic replacement doesn't happen at all! | |
| // | |
| // Source code in function is_range_check_if() shows that OptimizeFill relaxed the condition | |
| // for turning on loop predication optimization, and hence the behavior of "array range check" | |
| // and "loop invariant check" could be influenced, which potentially boosted JVM98. | |
| // | |
| // Generate stub for disjoint short fill. If "aligned" is true, the | |
| // "to" address is assumed to be heapword aligned. | |
| // | |
| // Arguments for generated stub: | |
| // to: R3_ARG1 | |
| // value: R4_ARG2 | |
| // count: R5_ARG3 treated as signed | |
| // | |
| address generate_fill(StubGenStubId stub_id) { | |
| BasicType t; | |
| bool aligned; | |
| switch (stub_id) { | |
| case jbyte_fill_id: | |
| t = T_BYTE; | |
| aligned = false; | |
| break; | |
| case jshort_fill_id: | |
| t = T_SHORT; | |
| aligned = false; | |
| break; | |
| case jint_fill_id: | |
| t = T_INT; | |
| aligned = false; | |
| break; | |
| case arrayof_jbyte_fill_id: | |
| t = T_BYTE; | |
| aligned = true; | |
| break; | |
| case arrayof_jshort_fill_id: | |
| t = T_SHORT; | |
| aligned = true; | |
| break; | |
| case arrayof_jint_fill_id: | |
| t = T_INT; | |
| aligned = true; | |
| break; | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| const Register to = R3_ARG1; // source array address | |
| const Register value = R4_ARG2; // fill value | |
| const Register count = R5_ARG3; // elements count | |
| const Register temp = R6_ARG4; // temp register | |
| //assert_clean_int(count, O3); // Make sure 'count' is clean int. | |
| Label L_exit, L_skip_align1, L_skip_align2, L_fill_byte; | |
| Label L_fill_2_bytes, L_fill_4_bytes, L_fill_elements, L_fill_32_bytes; | |
| int shift = -1; | |
| switch (t) { | |
| case T_BYTE: | |
| shift = 2; | |
| // Clone bytes (zero extend not needed because store instructions below ignore high order bytes). | |
| __ rldimi(value, value, 8, 48); // 8 bit -> 16 bit | |
| __ cmpdi(CR0, count, 2<<shift); // Short arrays (< 8 bytes) fill by element. | |
| __ blt(CR0, L_fill_elements); | |
| __ rldimi(value, value, 16, 32); // 16 bit -> 32 bit | |
| break; | |
| case T_SHORT: | |
| shift = 1; | |
| // Clone bytes (zero extend not needed because store instructions below ignore high order bytes). | |
| __ rldimi(value, value, 16, 32); // 16 bit -> 32 bit | |
| __ cmpdi(CR0, count, 2<<shift); // Short arrays (< 8 bytes) fill by element. | |
| __ blt(CR0, L_fill_elements); | |
| break; | |
| case T_INT: | |
| shift = 0; | |
| __ cmpdi(CR0, count, 2<<shift); // Short arrays (< 8 bytes) fill by element. | |
| __ blt(CR0, L_fill_4_bytes); | |
| break; | |
| default: ShouldNotReachHere(); | |
| } | |
| if (!aligned && (t == T_BYTE || t == T_SHORT)) { | |
| // Align source address at 4 bytes address boundary. | |
| if (t == T_BYTE) { | |
| // One byte misalignment happens only for byte arrays. | |
| __ andi_(temp, to, 1); | |
| __ beq(CR0, L_skip_align1); | |
| __ stb(value, 0, to); | |
| __ addi(to, to, 1); | |
| __ addi(count, count, -1); | |
| __ bind(L_skip_align1); | |
| } | |
| // Two bytes misalignment happens only for byte and short (char) arrays. | |
| __ andi_(temp, to, 2); | |
| __ beq(CR0, L_skip_align2); | |
| __ sth(value, 0, to); | |
| __ addi(to, to, 2); | |
| __ addi(count, count, -(1 << (shift - 1))); | |
| __ bind(L_skip_align2); | |
| } | |
| if (!aligned) { | |
| // Align to 8 bytes, we know we are 4 byte aligned to start. | |
| __ andi_(temp, to, 7); | |
| __ beq(CR0, L_fill_32_bytes); | |
| __ stw(value, 0, to); | |
| __ addi(to, to, 4); | |
| __ addi(count, count, -(1 << shift)); | |
| __ bind(L_fill_32_bytes); | |
| } | |
| __ li(temp, 8<<shift); // Prepare for 32 byte loop. | |
| // Clone bytes int->long as above. | |
| __ rldimi(value, value, 32, 0); // 32 bit -> 64 bit | |
| Label L_check_fill_8_bytes; | |
| // Fill 32-byte chunks. | |
| __ subf_(count, temp, count); | |
| __ blt(CR0, L_check_fill_8_bytes); | |
| Label L_fill_32_bytes_loop; | |
| __ align(32); | |
| __ bind(L_fill_32_bytes_loop); | |
| __ std(value, 0, to); | |
| __ std(value, 8, to); | |
| __ subf_(count, temp, count); // Update count. | |
| __ std(value, 16, to); | |
| __ std(value, 24, to); | |
| __ addi(to, to, 32); | |
| __ bge(CR0, L_fill_32_bytes_loop); | |
| __ bind(L_check_fill_8_bytes); | |
| __ add_(count, temp, count); | |
| __ beq(CR0, L_exit); | |
| __ addic_(count, count, -(2 << shift)); | |
| __ blt(CR0, L_fill_4_bytes); | |
| // | |
| // Length is too short, just fill 8 bytes at a time. | |
| // | |
| Label L_fill_8_bytes_loop; | |
| __ bind(L_fill_8_bytes_loop); | |
| __ std(value, 0, to); | |
| __ addic_(count, count, -(2 << shift)); | |
| __ addi(to, to, 8); | |
| __ bge(CR0, L_fill_8_bytes_loop); | |
| // Fill trailing 4 bytes. | |
| __ bind(L_fill_4_bytes); | |
| __ andi_(temp, count, 1<<shift); | |
| __ beq(CR0, L_fill_2_bytes); | |
| __ stw(value, 0, to); | |
| if (t == T_BYTE || t == T_SHORT) { | |
| __ addi(to, to, 4); | |
| // Fill trailing 2 bytes. | |
| __ bind(L_fill_2_bytes); | |
| __ andi_(temp, count, 1<<(shift-1)); | |
| __ beq(CR0, L_fill_byte); | |
| __ sth(value, 0, to); | |
| if (t == T_BYTE) { | |
| __ addi(to, to, 2); | |
| // Fill trailing byte. | |
| __ bind(L_fill_byte); | |
| __ andi_(count, count, 1); | |
| __ beq(CR0, L_exit); | |
| __ stb(value, 0, to); | |
| } else { | |
| __ bind(L_fill_byte); | |
| } | |
| } else { | |
| __ bind(L_fill_2_bytes); | |
| } | |
| __ bind(L_exit); | |
| __ blr(); | |
| // Handle copies less than 8 bytes. Int is handled elsewhere. | |
| if (t == T_BYTE) { | |
| __ bind(L_fill_elements); | |
| Label L_fill_2, L_fill_4; | |
| __ andi_(temp, count, 1); | |
| __ beq(CR0, L_fill_2); | |
| __ stb(value, 0, to); | |
| __ addi(to, to, 1); | |
| __ bind(L_fill_2); | |
| __ andi_(temp, count, 2); | |
| __ beq(CR0, L_fill_4); | |
| __ stb(value, 0, to); | |
| __ stb(value, 0, to); | |
| __ addi(to, to, 2); | |
| __ bind(L_fill_4); | |
| __ andi_(temp, count, 4); | |
| __ beq(CR0, L_exit); | |
| __ stb(value, 0, to); | |
| __ stb(value, 1, to); | |
| __ stb(value, 2, to); | |
| __ stb(value, 3, to); | |
| __ blr(); | |
| } | |
| if (t == T_SHORT) { | |
| Label L_fill_2; | |
| __ bind(L_fill_elements); | |
| __ andi_(temp, count, 1); | |
| __ beq(CR0, L_fill_2); | |
| __ sth(value, 0, to); | |
| __ addi(to, to, 2); | |
| __ bind(L_fill_2); | |
| __ andi_(temp, count, 2); | |
| __ beq(CR0, L_exit); | |
| __ sth(value, 0, to); | |
| __ sth(value, 2, to); | |
| __ blr(); | |
| } | |
| return start; | |
| } | |
| inline void assert_positive_int(Register count) { | |
| #ifdef ASSERT | |
| __ srdi_(R0, count, 31); | |
| __ asm_assert_eq("missing zero extend"); | |
| #endif | |
| } | |
| // Generate overlap test for array copy stubs. | |
| // | |
| // Input: | |
| // R3_ARG1 - from | |
| // R4_ARG2 - to | |
| // R5_ARG3 - element count | |
| // | |
| void array_overlap_test(address no_overlap_target, int log2_elem_size) { | |
| Register tmp1 = R6_ARG4; | |
| Register tmp2 = R7_ARG5; | |
| assert_positive_int(R5_ARG3); | |
| __ subf(tmp1, R3_ARG1, R4_ARG2); // distance in bytes | |
| __ sldi(tmp2, R5_ARG3, log2_elem_size); // size in bytes | |
| __ cmpld(CR0, R3_ARG1, R4_ARG2); // Use unsigned comparison! | |
| __ cmpld(CR1, tmp1, tmp2); | |
| __ crnand(CR0, Assembler::less, CR1, Assembler::less); | |
| // Overlaps if Src before dst and distance smaller than size. | |
| // Branch to forward copy routine otherwise (within range of 32kB). | |
| __ bc(Assembler::bcondCRbiIs1, Assembler::bi0(CR0, Assembler::less), no_overlap_target); | |
| // need to copy backwards | |
| } | |
| // This is common errorexit stub for UnsafeMemoryAccess. | |
| address generate_unsafecopy_common_error_exit() { | |
| address start_pc = __ pc(); | |
| Register tmp1 = R6_ARG4; | |
| // probably copy stub would have changed value reset it. | |
| if (VM_Version::has_mfdscr()) { | |
| __ load_const_optimized(tmp1, VM_Version::_dscr_val); | |
| __ mtdscr(tmp1); | |
| } | |
| __ li(R3_RET, 0); // return 0 | |
| __ blr(); | |
| return start_pc; | |
| } | |
| // The guideline in the implementations of generate_disjoint_xxx_copy | |
| // (xxx=byte,short,int,long,oop) is to copy as many elements as possible with | |
| // single instructions, but to avoid alignment interrupts (see subsequent | |
| // comment). Furthermore, we try to minimize misaligned access, even | |
| // though they cause no alignment interrupt. | |
| // | |
| // In Big-Endian mode, the PowerPC architecture requires implementations to | |
| // handle automatically misaligned integer halfword and word accesses, | |
| // word-aligned integer doubleword accesses, and word-aligned floating-point | |
| // accesses. Other accesses may or may not generate an Alignment interrupt | |
| // depending on the implementation. | |
| // Alignment interrupt handling may require on the order of hundreds of cycles, | |
| // so every effort should be made to avoid misaligned memory values. | |
| // | |
| // | |
| // Generate stub for disjoint byte copy. If "aligned" is true, the | |
| // "from" and "to" addresses are assumed to be heapword aligned. | |
| // | |
| // Arguments for generated stub: | |
| // from: R3_ARG1 | |
| // to: R4_ARG2 | |
| // count: R5_ARG3 treated as signed | |
| // | |
| address generate_disjoint_byte_copy(StubGenStubId stub_id) { | |
| bool aligned; | |
| switch (stub_id) { | |
| case jbyte_disjoint_arraycopy_id: | |
| aligned = false; | |
| break; | |
| case arrayof_jbyte_disjoint_arraycopy_id: | |
| aligned = true; | |
| break; | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| assert_positive_int(R5_ARG3); | |
| Register tmp1 = R6_ARG4; | |
| Register tmp2 = R7_ARG5; | |
| Register tmp3 = R8_ARG6; | |
| Register tmp4 = R9_ARG7; | |
| VectorSRegister tmp_vsr1 = VSR1; | |
| VectorSRegister tmp_vsr2 = VSR2; | |
| Label l_1, l_2, l_3, l_4, l_5, l_6, l_7, l_8, l_9, l_10; | |
| { | |
| // UnsafeMemoryAccess page error: continue at UnsafeMemoryAccess common_error_exit | |
| UnsafeMemoryAccessMark umam(this, !aligned, false); | |
| // Don't try anything fancy if arrays don't have many elements. | |
| __ li(tmp3, 0); | |
| __ cmpwi(CR0, R5_ARG3, 17); | |
| __ ble(CR0, l_6); // copy 4 at a time | |
| if (!aligned) { | |
| __ xorr(tmp1, R3_ARG1, R4_ARG2); | |
| __ andi_(tmp1, tmp1, 3); | |
| __ bne(CR0, l_6); // If arrays don't have the same alignment mod 4, do 4 element copy. | |
| // Copy elements if necessary to align to 4 bytes. | |
| __ neg(tmp1, R3_ARG1); // Compute distance to alignment boundary. | |
| __ andi_(tmp1, tmp1, 3); | |
| __ beq(CR0, l_2); | |
| __ subf(R5_ARG3, tmp1, R5_ARG3); | |
| __ bind(l_9); | |
| __ lbz(tmp2, 0, R3_ARG1); | |
| __ addic_(tmp1, tmp1, -1); | |
| __ stb(tmp2, 0, R4_ARG2); | |
| __ addi(R3_ARG1, R3_ARG1, 1); | |
| __ addi(R4_ARG2, R4_ARG2, 1); | |
| __ bne(CR0, l_9); | |
| __ bind(l_2); | |
| } | |
| // copy 8 elements at a time | |
| __ xorr(tmp2, R3_ARG1, R4_ARG2); // skip if src & dest have differing alignment mod 8 | |
| __ andi_(tmp1, tmp2, 7); | |
| __ bne(CR0, l_7); // not same alignment -> to or from is aligned -> copy 8 | |
| // copy a 2-element word if necessary to align to 8 bytes | |
| __ andi_(R0, R3_ARG1, 7); | |
| __ beq(CR0, l_7); | |
| __ lwzx(tmp2, R3_ARG1, tmp3); | |
| __ addi(R5_ARG3, R5_ARG3, -4); | |
| __ stwx(tmp2, R4_ARG2, tmp3); | |
| { // FasterArrayCopy | |
| __ addi(R3_ARG1, R3_ARG1, 4); | |
| __ addi(R4_ARG2, R4_ARG2, 4); | |
| } | |
| __ bind(l_7); | |
| { // FasterArrayCopy | |
| __ cmpwi(CR0, R5_ARG3, 31); | |
| __ ble(CR0, l_6); // copy 2 at a time if less than 32 elements remain | |
| __ srdi(tmp1, R5_ARG3, 5); | |
| __ andi_(R5_ARG3, R5_ARG3, 31); | |
| __ mtctr(tmp1); | |
| // Prefetch the data into the L2 cache. | |
| __ dcbt(R3_ARG1, 0); | |
| // If supported set DSCR pre-fetch to deepest. | |
| if (VM_Version::has_mfdscr()) { | |
| __ load_const_optimized(tmp2, VM_Version::_dscr_val | 7); | |
| __ mtdscr(tmp2); | |
| } | |
| __ li(tmp1, 16); | |
| // Backbranch target aligned to 32-byte. Not 16-byte align as | |
| // loop contains < 8 instructions that fit inside a single | |
| // i-cache sector. | |
| __ align(32); | |
| __ bind(l_10); | |
| // Use loop with VSX load/store instructions to | |
| // copy 32 elements a time. | |
| __ lxvd2x(tmp_vsr1, R3_ARG1); // Load src | |
| __ stxvd2x(tmp_vsr1, R4_ARG2); // Store to dst | |
| __ lxvd2x(tmp_vsr2, tmp1, R3_ARG1); // Load src + 16 | |
| __ stxvd2x(tmp_vsr2, tmp1, R4_ARG2); // Store to dst + 16 | |
| __ addi(R3_ARG1, R3_ARG1, 32); // Update src+=32 | |
| __ addi(R4_ARG2, R4_ARG2, 32); // Update dsc+=32 | |
| __ bdnz(l_10); // Dec CTR and loop if not zero. | |
| // Restore DSCR pre-fetch value. | |
| if (VM_Version::has_mfdscr()) { | |
| __ load_const_optimized(tmp2, VM_Version::_dscr_val); | |
| __ mtdscr(tmp2); | |
| } | |
| } // FasterArrayCopy | |
| __ bind(l_6); | |
| // copy 4 elements at a time | |
| __ cmpwi(CR0, R5_ARG3, 4); | |
| __ blt(CR0, l_1); | |
| __ srdi(tmp1, R5_ARG3, 2); | |
| __ mtctr(tmp1); // is > 0 | |
| __ andi_(R5_ARG3, R5_ARG3, 3); | |
| { // FasterArrayCopy | |
| __ addi(R3_ARG1, R3_ARG1, -4); | |
| __ addi(R4_ARG2, R4_ARG2, -4); | |
| __ bind(l_3); | |
| __ lwzu(tmp2, 4, R3_ARG1); | |
| __ stwu(tmp2, 4, R4_ARG2); | |
| __ bdnz(l_3); | |
| __ addi(R3_ARG1, R3_ARG1, 4); | |
| __ addi(R4_ARG2, R4_ARG2, 4); | |
| } | |
| // do single element copy | |
| __ bind(l_1); | |
| __ cmpwi(CR0, R5_ARG3, 0); | |
| __ beq(CR0, l_4); | |
| { // FasterArrayCopy | |
| __ mtctr(R5_ARG3); | |
| __ addi(R3_ARG1, R3_ARG1, -1); | |
| __ addi(R4_ARG2, R4_ARG2, -1); | |
| __ bind(l_5); | |
| __ lbzu(tmp2, 1, R3_ARG1); | |
| __ stbu(tmp2, 1, R4_ARG2); | |
| __ bdnz(l_5); | |
| } | |
| } | |
| __ bind(l_4); | |
| __ li(R3_RET, 0); // return 0 | |
| __ blr(); | |
| return start; | |
| } | |
| // Generate stub for conjoint byte copy. If "aligned" is true, the | |
| // "from" and "to" addresses are assumed to be heapword aligned. | |
| // | |
| // Arguments for generated stub: | |
| // from: R3_ARG1 | |
| // to: R4_ARG2 | |
| // count: R5_ARG3 treated as signed | |
| // | |
| address generate_conjoint_byte_copy(StubGenStubId stub_id) { | |
| bool aligned; | |
| switch (stub_id) { | |
| case jbyte_arraycopy_id: | |
| aligned = false; | |
| break; | |
| case arrayof_jbyte_arraycopy_id: | |
| aligned = true; | |
| break; | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| assert_positive_int(R5_ARG3); | |
| Register tmp1 = R6_ARG4; | |
| Register tmp2 = R7_ARG5; | |
| Register tmp3 = R8_ARG6; | |
| address nooverlap_target = aligned ? | |
| STUB_ENTRY(arrayof_jbyte_disjoint_arraycopy()) : | |
| STUB_ENTRY(jbyte_disjoint_arraycopy()); | |
| array_overlap_test(nooverlap_target, 0); | |
| // Do reverse copy. We assume the case of actual overlap is rare enough | |
| // that we don't have to optimize it. | |
| Label l_1, l_2; | |
| { | |
| // UnsafeMemoryAccess page error: continue at UnsafeMemoryAccess common_error_exit | |
| UnsafeMemoryAccessMark umam(this, !aligned, false); | |
| __ b(l_2); | |
| __ bind(l_1); | |
| __ stbx(tmp1, R4_ARG2, R5_ARG3); | |
| __ bind(l_2); | |
| __ addic_(R5_ARG3, R5_ARG3, -1); | |
| __ lbzx(tmp1, R3_ARG1, R5_ARG3); | |
| __ bge(CR0, l_1); | |
| } | |
| __ li(R3_RET, 0); // return 0 | |
| __ blr(); | |
| return start; | |
| } | |
| // Generate stub for disjoint short copy. If "aligned" is true, the | |
| // "from" and "to" addresses are assumed to be heapword aligned. | |
| // | |
| // Arguments for generated stub: | |
| // from: R3_ARG1 | |
| // to: R4_ARG2 | |
| // elm.count: R5_ARG3 treated as signed | |
| // | |
| // Strategy for aligned==true: | |
| // | |
| // If length <= 9: | |
| // 1. copy 2 elements at a time (l_6) | |
| // 2. copy last element if original element count was odd (l_1) | |
| // | |
| // If length > 9: | |
| // 1. copy 4 elements at a time until less than 4 elements are left (l_7) | |
| // 2. copy 2 elements at a time until less than 2 elements are left (l_6) | |
| // 3. copy last element if one was left in step 2. (l_1) | |
| // | |
| // | |
| // Strategy for aligned==false: | |
| // | |
| // If length <= 9: same as aligned==true case, but NOTE: load/stores | |
| // can be unaligned (see comment below) | |
| // | |
| // If length > 9: | |
| // 1. continue with step 6. if the alignment of from and to mod 4 | |
| // is different. | |
| // 2. align from and to to 4 bytes by copying 1 element if necessary | |
| // 3. at l_2 from and to are 4 byte aligned; continue with | |
| // 5. if they cannot be aligned to 8 bytes because they have | |
| // got different alignment mod 8. | |
| // 4. at this point we know that both, from and to, have the same | |
| // alignment mod 8, now copy one element if necessary to get | |
| // 8 byte alignment of from and to. | |
| // 5. copy 4 elements at a time until less than 4 elements are | |
| // left; depending on step 3. all load/stores are aligned or | |
| // either all loads or all stores are unaligned. | |
| // 6. copy 2 elements at a time until less than 2 elements are | |
| // left (l_6); arriving here from step 1., there is a chance | |
| // that all accesses are unaligned. | |
| // 7. copy last element if one was left in step 6. (l_1) | |
| // | |
| // There are unaligned data accesses using integer load/store | |
| // instructions in this stub. POWER allows such accesses. | |
| // | |
| // According to the manuals (PowerISA_V2.06_PUBLIC, Book II, | |
| // Chapter 2: Effect of Operand Placement on Performance) unaligned | |
| // integer load/stores have good performance. Only unaligned | |
| // floating point load/stores can have poor performance. | |
| // | |
| // TODO: | |
| // | |
| // 1. check if aligning the backbranch target of loops is beneficial | |
| // | |
| address generate_disjoint_short_copy(StubGenStubId stub_id) { | |
| bool aligned; | |
| switch (stub_id) { | |
| case jshort_disjoint_arraycopy_id: | |
| aligned = false; | |
| break; | |
| case arrayof_jshort_disjoint_arraycopy_id: | |
| aligned = true; | |
| break; | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| StubCodeMark mark(this, stub_id); | |
| Register tmp1 = R6_ARG4; | |
| Register tmp2 = R7_ARG5; | |
| Register tmp3 = R8_ARG6; | |
| Register tmp4 = R9_ARG7; | |
| VectorSRegister tmp_vsr1 = VSR1; | |
| VectorSRegister tmp_vsr2 = VSR2; | |
| address start = __ function_entry(); | |
| assert_positive_int(R5_ARG3); | |
| Label l_1, l_2, l_3, l_4, l_5, l_6, l_7, l_8, l_9; | |
| { | |
| // UnsafeMemoryAccess page error: continue at UnsafeMemoryAccess common_error_exit | |
| UnsafeMemoryAccessMark umam(this, !aligned, false); | |
| // don't try anything fancy if arrays don't have many elements | |
| __ li(tmp3, 0); | |
| __ cmpwi(CR0, R5_ARG3, 9); | |
| __ ble(CR0, l_6); // copy 2 at a time | |
| if (!aligned) { | |
| __ xorr(tmp1, R3_ARG1, R4_ARG2); | |
| __ andi_(tmp1, tmp1, 3); | |
| __ bne(CR0, l_6); // if arrays don't have the same alignment mod 4, do 2 element copy | |
| // At this point it is guaranteed that both, from and to have the same alignment mod 4. | |
| // Copy 1 element if necessary to align to 4 bytes. | |
| __ andi_(tmp1, R3_ARG1, 3); | |
| __ beq(CR0, l_2); | |
| __ lhz(tmp2, 0, R3_ARG1); | |
| __ addi(R3_ARG1, R3_ARG1, 2); | |
| __ sth(tmp2, 0, R4_ARG2); | |
| __ addi(R4_ARG2, R4_ARG2, 2); | |
| __ addi(R5_ARG3, R5_ARG3, -1); | |
| __ bind(l_2); | |
| // At this point the positions of both, from and to, are at least 4 byte aligned. | |
| // Copy 4 elements at a time. | |
| // Align to 8 bytes, but only if both, from and to, have same alignment mod 8. | |
| __ xorr(tmp2, R3_ARG1, R4_ARG2); | |
| __ andi_(tmp1, tmp2, 7); | |
| __ bne(CR0, l_7); // not same alignment mod 8 -> copy 4, either from or to will be unaligned | |
| // Copy a 2-element word if necessary to align to 8 bytes. | |
| __ andi_(R0, R3_ARG1, 7); | |
| __ beq(CR0, l_7); | |
| __ lwzx(tmp2, R3_ARG1, tmp3); | |
| __ addi(R5_ARG3, R5_ARG3, -2); | |
| __ stwx(tmp2, R4_ARG2, tmp3); | |
| { // FasterArrayCopy | |
| __ addi(R3_ARG1, R3_ARG1, 4); | |
| __ addi(R4_ARG2, R4_ARG2, 4); | |
| } | |
| } | |
| __ bind(l_7); | |
| // Copy 4 elements at a time; either the loads or the stores can | |
| // be unaligned if aligned == false. | |
| { // FasterArrayCopy | |
| __ cmpwi(CR0, R5_ARG3, 15); | |
| __ ble(CR0, l_6); // copy 2 at a time if less than 16 elements remain | |
| __ srdi(tmp1, R5_ARG3, 4); | |
| __ andi_(R5_ARG3, R5_ARG3, 15); | |
| __ mtctr(tmp1); | |
| // Processor supports VSX, so use it to mass copy. | |
| // Prefetch src data into L2 cache. | |
| __ dcbt(R3_ARG1, 0); | |
| // If supported set DSCR pre-fetch to deepest. | |
| if (VM_Version::has_mfdscr()) { | |
| __ load_const_optimized(tmp2, VM_Version::_dscr_val | 7); | |
| __ mtdscr(tmp2); | |
| } | |
| __ li(tmp1, 16); | |
| // Backbranch target aligned to 32-byte. It's not aligned 16-byte | |
| // as loop contains < 8 instructions that fit inside a single | |
| // i-cache sector. | |
| __ align(32); | |
| __ bind(l_9); | |
| // Use loop with VSX load/store instructions to | |
| // copy 16 elements a time. | |
| __ lxvd2x(tmp_vsr1, R3_ARG1); // Load from src. | |
| __ stxvd2x(tmp_vsr1, R4_ARG2); // Store to dst. | |
| __ lxvd2x(tmp_vsr2, R3_ARG1, tmp1); // Load from src + 16. | |
| __ stxvd2x(tmp_vsr2, R4_ARG2, tmp1); // Store to dst + 16. | |
| __ addi(R3_ARG1, R3_ARG1, 32); // Update src+=32. | |
| __ addi(R4_ARG2, R4_ARG2, 32); // Update dsc+=32. | |
| __ bdnz(l_9); // Dec CTR and loop if not zero. | |
| // Restore DSCR pre-fetch value. | |
| if (VM_Version::has_mfdscr()) { | |
| __ load_const_optimized(tmp2, VM_Version::_dscr_val); | |
| __ mtdscr(tmp2); | |
| } | |
| } // FasterArrayCopy | |
| __ bind(l_6); | |
| // copy 2 elements at a time | |
| { // FasterArrayCopy | |
| __ cmpwi(CR0, R5_ARG3, 2); | |
| __ blt(CR0, l_1); | |
| __ srdi(tmp1, R5_ARG3, 1); | |
| __ andi_(R5_ARG3, R5_ARG3, 1); | |
| __ addi(R3_ARG1, R3_ARG1, -4); | |
| __ addi(R4_ARG2, R4_ARG2, -4); | |
| __ mtctr(tmp1); | |
| __ bind(l_3); | |
| __ lwzu(tmp2, 4, R3_ARG1); | |
| __ stwu(tmp2, 4, R4_ARG2); | |
| __ bdnz(l_3); | |
| __ addi(R3_ARG1, R3_ARG1, 4); | |
| __ addi(R4_ARG2, R4_ARG2, 4); | |
| } | |
| // do single element copy | |
| __ bind(l_1); | |
| __ cmpwi(CR0, R5_ARG3, 0); | |
| __ beq(CR0, l_4); | |
| { // FasterArrayCopy | |
| __ mtctr(R5_ARG3); | |
| __ addi(R3_ARG1, R3_ARG1, -2); | |
| __ addi(R4_ARG2, R4_ARG2, -2); | |
| __ bind(l_5); | |
| __ lhzu(tmp2, 2, R3_ARG1); | |
| __ sthu(tmp2, 2, R4_ARG2); | |
| __ bdnz(l_5); | |
| } | |
| } | |
| __ bind(l_4); | |
| __ li(R3_RET, 0); // return 0 | |
| __ blr(); | |
| return start; | |
| } | |
| // Generate stub for conjoint short copy. If "aligned" is true, the | |
| // "from" and "to" addresses are assumed to be heapword aligned. | |
| // | |
| // Arguments for generated stub: | |
| // from: R3_ARG1 | |
| // to: R4_ARG2 | |
| // count: R5_ARG3 treated as signed | |
| // | |
| address generate_conjoint_short_copy(StubGenStubId stub_id) { | |
| bool aligned; | |
| switch (stub_id) { | |
| case jshort_arraycopy_id: | |
| aligned = false; | |
| break; | |
| case arrayof_jshort_arraycopy_id: | |
| aligned = true; | |
| break; | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| assert_positive_int(R5_ARG3); | |
| Register tmp1 = R6_ARG4; | |
| Register tmp2 = R7_ARG5; | |
| Register tmp3 = R8_ARG6; | |
| address nooverlap_target = aligned ? | |
| STUB_ENTRY(arrayof_jshort_disjoint_arraycopy()) : | |
| STUB_ENTRY(jshort_disjoint_arraycopy()); | |
| array_overlap_test(nooverlap_target, 1); | |
| Label l_1, l_2; | |
| { | |
| // UnsafeMemoryAccess page error: continue at UnsafeMemoryAccess common_error_exit | |
| UnsafeMemoryAccessMark umam(this, !aligned, false); | |
| __ sldi(tmp1, R5_ARG3, 1); | |
| __ b(l_2); | |
| __ bind(l_1); | |
| __ sthx(tmp2, R4_ARG2, tmp1); | |
| __ bind(l_2); | |
| __ addic_(tmp1, tmp1, -2); | |
| __ lhzx(tmp2, R3_ARG1, tmp1); | |
| __ bge(CR0, l_1); | |
| } | |
| __ li(R3_RET, 0); // return 0 | |
| __ blr(); | |
| return start; | |
| } | |
| // Generate core code for disjoint int copy (and oop copy on 32-bit). If "aligned" | |
| // is true, the "from" and "to" addresses are assumed to be heapword aligned. | |
| // | |
| // Arguments: | |
| // from: R3_ARG1 | |
| // to: R4_ARG2 | |
| // count: R5_ARG3 treated as signed | |
| // | |
| void generate_disjoint_int_copy_core(bool aligned) { | |
| Register tmp1 = R6_ARG4; | |
| Register tmp2 = R7_ARG5; | |
| Register tmp3 = R8_ARG6; | |
| Register tmp4 = R0; | |
| VectorSRegister tmp_vsr1 = VSR1; | |
| VectorSRegister tmp_vsr2 = VSR2; | |
| Label l_1, l_2, l_3, l_4, l_5, l_6, l_7; | |
| // for short arrays, just do single element copy | |
| __ li(tmp3, 0); | |
| __ cmpwi(CR0, R5_ARG3, 5); | |
| __ ble(CR0, l_2); | |
| if (!aligned) { | |
| // check if arrays have same alignment mod 8. | |
| __ xorr(tmp1, R3_ARG1, R4_ARG2); | |
| __ andi_(R0, tmp1, 7); | |
| // Not the same alignment, but ld and std just need to be 4 byte aligned. | |
| __ bne(CR0, l_4); // to OR from is 8 byte aligned -> copy 2 at a time | |
| // copy 1 element to align to and from on an 8 byte boundary | |
| __ andi_(R0, R3_ARG1, 7); | |
| __ beq(CR0, l_4); | |
| __ lwzx(tmp2, R3_ARG1, tmp3); | |
| __ addi(R5_ARG3, R5_ARG3, -1); | |
| __ stwx(tmp2, R4_ARG2, tmp3); | |
| { // FasterArrayCopy | |
| __ addi(R3_ARG1, R3_ARG1, 4); | |
| __ addi(R4_ARG2, R4_ARG2, 4); | |
| } | |
| __ bind(l_4); | |
| } | |
| { // FasterArrayCopy | |
| __ cmpwi(CR0, R5_ARG3, 7); | |
| __ ble(CR0, l_2); // copy 1 at a time if less than 8 elements remain | |
| __ srdi(tmp1, R5_ARG3, 3); | |
| __ andi_(R5_ARG3, R5_ARG3, 7); | |
| __ mtctr(tmp1); | |
| // Processor supports VSX, so use it to mass copy. | |
| // Prefetch the data into the L2 cache. | |
| __ dcbt(R3_ARG1, 0); | |
| // Set DSCR pre-fetch to deepest. | |
| if (VM_Version::has_mfdscr()) { | |
| __ load_const_optimized(tmp2, VM_Version::_dscr_val | 7); | |
| __ mtdscr(tmp2); | |
| } | |
| __ li(tmp1, 16); | |
| // Backbranch target aligned to 32-byte. Not 16-byte align as | |
| // loop contains < 8 instructions that fit inside a single | |
| // i-cache sector. | |
| __ align(32); | |
| __ bind(l_7); | |
| // Use loop with VSX load/store instructions to | |
| // copy 8 elements a time. | |
| __ lxvd2x(tmp_vsr1, R3_ARG1); // Load src | |
| __ stxvd2x(tmp_vsr1, R4_ARG2); // Store to dst | |
| __ lxvd2x(tmp_vsr2, tmp1, R3_ARG1); // Load src + 16 | |
| __ stxvd2x(tmp_vsr2, tmp1, R4_ARG2); // Store to dst + 16 | |
| __ addi(R3_ARG1, R3_ARG1, 32); // Update src+=32 | |
| __ addi(R4_ARG2, R4_ARG2, 32); // Update dsc+=32 | |
| __ bdnz(l_7); // Dec CTR and loop if not zero. | |
| // Restore DSCR pre-fetch value. | |
| if (VM_Version::has_mfdscr()) { | |
| __ load_const_optimized(tmp2, VM_Version::_dscr_val); | |
| __ mtdscr(tmp2); | |
| } | |
| } // FasterArrayCopy | |
| // copy 1 element at a time | |
| __ bind(l_2); | |
| __ cmpwi(CR0, R5_ARG3, 0); | |
| __ beq(CR0, l_1); | |
| { // FasterArrayCopy | |
| __ mtctr(R5_ARG3); | |
| __ addi(R3_ARG1, R3_ARG1, -4); | |
| __ addi(R4_ARG2, R4_ARG2, -4); | |
| __ bind(l_3); | |
| __ lwzu(tmp2, 4, R3_ARG1); | |
| __ stwu(tmp2, 4, R4_ARG2); | |
| __ bdnz(l_3); | |
| } | |
| __ bind(l_1); | |
| return; | |
| } | |
| // Generate stub for disjoint int copy. If "aligned" is true, the | |
| // "from" and "to" addresses are assumed to be heapword aligned. | |
| // | |
| // Arguments for generated stub: | |
| // from: R3_ARG1 | |
| // to: R4_ARG2 | |
| // count: R5_ARG3 treated as signed | |
| // | |
| address generate_disjoint_int_copy(StubGenStubId stub_id) { | |
| bool aligned; | |
| switch (stub_id) { | |
| case jint_disjoint_arraycopy_id: | |
| aligned = false; | |
| break; | |
| case arrayof_jint_disjoint_arraycopy_id: | |
| aligned = true; | |
| break; | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| assert_positive_int(R5_ARG3); | |
| { | |
| // UnsafeMemoryAccess page error: continue at UnsafeMemoryAccess common_error_exit | |
| UnsafeMemoryAccessMark umam(this, !aligned, false); | |
| generate_disjoint_int_copy_core(aligned); | |
| } | |
| __ li(R3_RET, 0); // return 0 | |
| __ blr(); | |
| return start; | |
| } | |
| // Generate core code for conjoint int copy (and oop copy on | |
| // 32-bit). If "aligned" is true, the "from" and "to" addresses | |
| // are assumed to be heapword aligned. | |
| // | |
| // Arguments: | |
| // from: R3_ARG1 | |
| // to: R4_ARG2 | |
| // count: R5_ARG3 treated as signed | |
| // | |
| void generate_conjoint_int_copy_core(bool aligned) { | |
| // Do reverse copy. We assume the case of actual overlap is rare enough | |
| // that we don't have to optimize it. | |
| Label l_1, l_2, l_3, l_4, l_5, l_6, l_7; | |
| Register tmp1 = R6_ARG4; | |
| Register tmp2 = R7_ARG5; | |
| Register tmp3 = R8_ARG6; | |
| Register tmp4 = R0; | |
| VectorSRegister tmp_vsr1 = VSR1; | |
| VectorSRegister tmp_vsr2 = VSR2; | |
| { // FasterArrayCopy | |
| __ cmpwi(CR0, R5_ARG3, 0); | |
| __ beq(CR0, l_6); | |
| __ sldi(R5_ARG3, R5_ARG3, 2); | |
| __ add(R3_ARG1, R3_ARG1, R5_ARG3); | |
| __ add(R4_ARG2, R4_ARG2, R5_ARG3); | |
| __ srdi(R5_ARG3, R5_ARG3, 2); | |
| if (!aligned) { | |
| // check if arrays have same alignment mod 8. | |
| __ xorr(tmp1, R3_ARG1, R4_ARG2); | |
| __ andi_(R0, tmp1, 7); | |
| // Not the same alignment, but ld and std just need to be 4 byte aligned. | |
| __ bne(CR0, l_7); // to OR from is 8 byte aligned -> copy 2 at a time | |
| // copy 1 element to align to and from on an 8 byte boundary | |
| __ andi_(R0, R3_ARG1, 7); | |
| __ beq(CR0, l_7); | |
| __ addi(R3_ARG1, R3_ARG1, -4); | |
| __ addi(R4_ARG2, R4_ARG2, -4); | |
| __ addi(R5_ARG3, R5_ARG3, -1); | |
| __ lwzx(tmp2, R3_ARG1); | |
| __ stwx(tmp2, R4_ARG2); | |
| __ bind(l_7); | |
| } | |
| __ cmpwi(CR0, R5_ARG3, 7); | |
| __ ble(CR0, l_5); // copy 1 at a time if less than 8 elements remain | |
| __ srdi(tmp1, R5_ARG3, 3); | |
| __ andi(R5_ARG3, R5_ARG3, 7); | |
| __ mtctr(tmp1); | |
| // Processor supports VSX, so use it to mass copy. | |
| // Prefetch the data into the L2 cache. | |
| __ dcbt(R3_ARG1, 0); | |
| // Set DSCR pre-fetch to deepest. | |
| if (VM_Version::has_mfdscr()) { | |
| __ load_const_optimized(tmp2, VM_Version::_dscr_val | 7); | |
| __ mtdscr(tmp2); | |
| } | |
| __ li(tmp1, 16); | |
| // Backbranch target aligned to 32-byte. Not 16-byte align as | |
| // loop contains < 8 instructions that fit inside a single | |
| // i-cache sector. | |
| __ align(32); | |
| __ bind(l_4); | |
| // Use loop with VSX load/store instructions to | |
| // copy 8 elements a time. | |
| __ addi(R3_ARG1, R3_ARG1, -32); // Update src-=32 | |
| __ addi(R4_ARG2, R4_ARG2, -32); // Update dsc-=32 | |
| __ lxvd2x(tmp_vsr2, tmp1, R3_ARG1); // Load src+16 | |
| __ lxvd2x(tmp_vsr1, R3_ARG1); // Load src | |
| __ stxvd2x(tmp_vsr2, tmp1, R4_ARG2); // Store to dst+16 | |
| __ stxvd2x(tmp_vsr1, R4_ARG2); // Store to dst | |
| __ bdnz(l_4); | |
| // Restore DSCR pre-fetch value. | |
| if (VM_Version::has_mfdscr()) { | |
| __ load_const_optimized(tmp2, VM_Version::_dscr_val); | |
| __ mtdscr(tmp2); | |
| } | |
| __ cmpwi(CR0, R5_ARG3, 0); | |
| __ beq(CR0, l_6); | |
| __ bind(l_5); | |
| __ mtctr(R5_ARG3); | |
| __ bind(l_3); | |
| __ lwz(R0, -4, R3_ARG1); | |
| __ stw(R0, -4, R4_ARG2); | |
| __ addi(R3_ARG1, R3_ARG1, -4); | |
| __ addi(R4_ARG2, R4_ARG2, -4); | |
| __ bdnz(l_3); | |
| __ bind(l_6); | |
| } | |
| } | |
| // Generate stub for conjoint int copy. If "aligned" is true, the | |
| // "from" and "to" addresses are assumed to be heapword aligned. | |
| // | |
| // Arguments for generated stub: | |
| // from: R3_ARG1 | |
| // to: R4_ARG2 | |
| // count: R5_ARG3 treated as signed | |
| // | |
| address generate_conjoint_int_copy(StubGenStubId stub_id) { | |
| bool aligned; | |
| switch (stub_id) { | |
| case jint_arraycopy_id: | |
| aligned = false; | |
| break; | |
| case arrayof_jint_arraycopy_id: | |
| aligned = true; | |
| break; | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| assert_positive_int(R5_ARG3); | |
| address nooverlap_target = aligned ? | |
| STUB_ENTRY(arrayof_jint_disjoint_arraycopy()) : | |
| STUB_ENTRY(jint_disjoint_arraycopy()); | |
| array_overlap_test(nooverlap_target, 2); | |
| { | |
| // UnsafeMemoryAccess page error: continue at UnsafeMemoryAccess common_error_exit | |
| UnsafeMemoryAccessMark umam(this, !aligned, false); | |
| generate_conjoint_int_copy_core(aligned); | |
| } | |
| __ li(R3_RET, 0); // return 0 | |
| __ blr(); | |
| return start; | |
| } | |
| // Generate core code for disjoint long copy (and oop copy on | |
| // 64-bit). If "aligned" is true, the "from" and "to" addresses | |
| // are assumed to be heapword aligned. | |
| // | |
| // Arguments: | |
| // from: R3_ARG1 | |
| // to: R4_ARG2 | |
| // count: R5_ARG3 treated as signed | |
| // | |
| void generate_disjoint_long_copy_core(bool aligned) { | |
| Register tmp1 = R6_ARG4; | |
| Register tmp2 = R7_ARG5; | |
| Register tmp3 = R8_ARG6; | |
| Register tmp4 = R0; | |
| Label l_1, l_2, l_3, l_4, l_5; | |
| VectorSRegister tmp_vsr1 = VSR1; | |
| VectorSRegister tmp_vsr2 = VSR2; | |
| { // FasterArrayCopy | |
| __ cmpwi(CR0, R5_ARG3, 3); | |
| __ ble(CR0, l_3); // copy 1 at a time if less than 4 elements remain | |
| __ srdi(tmp1, R5_ARG3, 2); | |
| __ andi_(R5_ARG3, R5_ARG3, 3); | |
| __ mtctr(tmp1); | |
| // Processor supports VSX, so use it to mass copy. | |
| // Prefetch the data into the L2 cache. | |
| __ dcbt(R3_ARG1, 0); | |
| // Set DSCR pre-fetch to deepest. | |
| if (VM_Version::has_mfdscr()) { | |
| __ load_const_optimized(tmp2, VM_Version::_dscr_val | 7); | |
| __ mtdscr(tmp2); | |
| } | |
| __ li(tmp1, 16); | |
| // Backbranch target aligned to 32-byte. Not 16-byte align as | |
| // loop contains < 8 instructions that fit inside a single | |
| // i-cache sector. | |
| __ align(32); | |
| __ bind(l_5); | |
| // Use loop with VSX load/store instructions to | |
| // copy 4 elements a time. | |
| __ lxvd2x(tmp_vsr1, R3_ARG1); // Load src | |
| __ stxvd2x(tmp_vsr1, R4_ARG2); // Store to dst | |
| __ lxvd2x(tmp_vsr2, tmp1, R3_ARG1); // Load src + 16 | |
| __ stxvd2x(tmp_vsr2, tmp1, R4_ARG2); // Store to dst + 16 | |
| __ addi(R3_ARG1, R3_ARG1, 32); // Update src+=32 | |
| __ addi(R4_ARG2, R4_ARG2, 32); // Update dsc+=32 | |
| __ bdnz(l_5); // Dec CTR and loop if not zero. | |
| // Restore DSCR pre-fetch value. | |
| if (VM_Version::has_mfdscr()) { | |
| __ load_const_optimized(tmp2, VM_Version::_dscr_val); | |
| __ mtdscr(tmp2); | |
| } | |
| } // FasterArrayCopy | |
| // copy 1 element at a time | |
| __ bind(l_3); | |
| __ cmpwi(CR0, R5_ARG3, 0); | |
| __ beq(CR0, l_1); | |
| { // FasterArrayCopy | |
| __ mtctr(R5_ARG3); | |
| __ addi(R3_ARG1, R3_ARG1, -8); | |
| __ addi(R4_ARG2, R4_ARG2, -8); | |
| __ bind(l_2); | |
| __ ldu(R0, 8, R3_ARG1); | |
| __ stdu(R0, 8, R4_ARG2); | |
| __ bdnz(l_2); | |
| } | |
| __ bind(l_1); | |
| } | |
| // Generate stub for disjoint long copy. If "aligned" is true, the | |
| // "from" and "to" addresses are assumed to be heapword aligned. | |
| // | |
| // Arguments for generated stub: | |
| // from: R3_ARG1 | |
| // to: R4_ARG2 | |
| // count: R5_ARG3 treated as signed | |
| // | |
| address generate_disjoint_long_copy(StubGenStubId stub_id) { | |
| bool aligned; | |
| switch (stub_id) { | |
| case jlong_disjoint_arraycopy_id: | |
| aligned = false; | |
| break; | |
| case arrayof_jlong_disjoint_arraycopy_id: | |
| aligned = true; | |
| break; | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| assert_positive_int(R5_ARG3); | |
| { | |
| // UnsafeMemoryAccess page error: continue at UnsafeMemoryAccess common_error_exit | |
| UnsafeMemoryAccessMark umam(this, !aligned, false); | |
| generate_disjoint_long_copy_core(aligned); | |
| } | |
| __ li(R3_RET, 0); // return 0 | |
| __ blr(); | |
| return start; | |
| } | |
| // Generate core code for conjoint long copy (and oop copy on | |
| // 64-bit). If "aligned" is true, the "from" and "to" addresses | |
| // are assumed to be heapword aligned. | |
| // | |
| // Arguments: | |
| // from: R3_ARG1 | |
| // to: R4_ARG2 | |
| // count: R5_ARG3 treated as signed | |
| // | |
| void generate_conjoint_long_copy_core(bool aligned) { | |
| Register tmp1 = R6_ARG4; | |
| Register tmp2 = R7_ARG5; | |
| Register tmp3 = R8_ARG6; | |
| Register tmp4 = R0; | |
| VectorSRegister tmp_vsr1 = VSR1; | |
| VectorSRegister tmp_vsr2 = VSR2; | |
| Label l_1, l_2, l_3, l_4, l_5; | |
| __ cmpwi(CR0, R5_ARG3, 0); | |
| __ beq(CR0, l_1); | |
| { // FasterArrayCopy | |
| __ sldi(R5_ARG3, R5_ARG3, 3); | |
| __ add(R3_ARG1, R3_ARG1, R5_ARG3); | |
| __ add(R4_ARG2, R4_ARG2, R5_ARG3); | |
| __ srdi(R5_ARG3, R5_ARG3, 3); | |
| __ cmpwi(CR0, R5_ARG3, 3); | |
| __ ble(CR0, l_5); // copy 1 at a time if less than 4 elements remain | |
| __ srdi(tmp1, R5_ARG3, 2); | |
| __ andi(R5_ARG3, R5_ARG3, 3); | |
| __ mtctr(tmp1); | |
| // Processor supports VSX, so use it to mass copy. | |
| // Prefetch the data into the L2 cache. | |
| __ dcbt(R3_ARG1, 0); | |
| // Set DSCR pre-fetch to deepest. | |
| if (VM_Version::has_mfdscr()) { | |
| __ load_const_optimized(tmp2, VM_Version::_dscr_val | 7); | |
| __ mtdscr(tmp2); | |
| } | |
| __ li(tmp1, 16); | |
| // Backbranch target aligned to 32-byte. Not 16-byte align as | |
| // loop contains < 8 instructions that fit inside a single | |
| // i-cache sector. | |
| __ align(32); | |
| __ bind(l_4); | |
| // Use loop with VSX load/store instructions to | |
| // copy 4 elements a time. | |
| __ addi(R3_ARG1, R3_ARG1, -32); // Update src-=32 | |
| __ addi(R4_ARG2, R4_ARG2, -32); // Update dsc-=32 | |
| __ lxvd2x(tmp_vsr2, tmp1, R3_ARG1); // Load src+16 | |
| __ lxvd2x(tmp_vsr1, R3_ARG1); // Load src | |
| __ stxvd2x(tmp_vsr2, tmp1, R4_ARG2); // Store to dst+16 | |
| __ stxvd2x(tmp_vsr1, R4_ARG2); // Store to dst | |
| __ bdnz(l_4); | |
| // Restore DSCR pre-fetch value. | |
| if (VM_Version::has_mfdscr()) { | |
| __ load_const_optimized(tmp2, VM_Version::_dscr_val); | |
| __ mtdscr(tmp2); | |
| } | |
| __ cmpwi(CR0, R5_ARG3, 0); | |
| __ beq(CR0, l_1); | |
| __ bind(l_5); | |
| __ mtctr(R5_ARG3); | |
| __ bind(l_3); | |
| __ ld(R0, -8, R3_ARG1); | |
| __ std(R0, -8, R4_ARG2); | |
| __ addi(R3_ARG1, R3_ARG1, -8); | |
| __ addi(R4_ARG2, R4_ARG2, -8); | |
| __ bdnz(l_3); | |
| } | |
| __ bind(l_1); | |
| } | |
| // Generate stub for conjoint long copy. If "aligned" is true, the | |
| // "from" and "to" addresses are assumed to be heapword aligned. | |
| // | |
| // Arguments for generated stub: | |
| // from: R3_ARG1 | |
| // to: R4_ARG2 | |
| // count: R5_ARG3 treated as signed | |
| // | |
| address generate_conjoint_long_copy(StubGenStubId stub_id) { | |
| bool aligned; | |
| switch (stub_id) { | |
| case jlong_arraycopy_id: | |
| aligned = false; | |
| break; | |
| case arrayof_jlong_arraycopy_id: | |
| aligned = true; | |
| break; | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| assert_positive_int(R5_ARG3); | |
| address nooverlap_target = aligned ? | |
| STUB_ENTRY(arrayof_jlong_disjoint_arraycopy()) : | |
| STUB_ENTRY(jlong_disjoint_arraycopy()); | |
| array_overlap_test(nooverlap_target, 3); | |
| { | |
| // UnsafeMemoryAccess page error: continue at UnsafeMemoryAccess common_error_exit | |
| UnsafeMemoryAccessMark umam(this, !aligned, false); | |
| generate_conjoint_long_copy_core(aligned); | |
| } | |
| __ li(R3_RET, 0); // return 0 | |
| __ blr(); | |
| return start; | |
| } | |
| // Generate stub for conjoint oop copy. If "aligned" is true, the | |
| // "from" and "to" addresses are assumed to be heapword aligned. | |
| // | |
| // Arguments for generated stub: | |
| // from: R3_ARG1 | |
| // to: R4_ARG2 | |
| // count: R5_ARG3 treated as signed | |
| // dest_uninitialized: G1 support | |
| // | |
| address generate_conjoint_oop_copy(StubGenStubId stub_id) { | |
| bool aligned; | |
| bool dest_uninitialized; | |
| switch (stub_id) { | |
| case oop_arraycopy_id: | |
| aligned = false; | |
| dest_uninitialized = false; | |
| break; | |
| case arrayof_oop_arraycopy_id: | |
| aligned = true; | |
| dest_uninitialized = false; | |
| break; | |
| case oop_arraycopy_uninit_id: | |
| aligned = false; | |
| dest_uninitialized = true; | |
| break; | |
| case arrayof_oop_arraycopy_uninit_id: | |
| aligned = true; | |
| dest_uninitialized = true; | |
| break; | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| assert_positive_int(R5_ARG3); | |
| address nooverlap_target = aligned ? | |
| STUB_ENTRY(arrayof_oop_disjoint_arraycopy(dest_uninitialized)) : | |
| STUB_ENTRY(oop_disjoint_arraycopy(dest_uninitialized)); | |
| array_overlap_test(nooverlap_target, UseCompressedOops ? 2 : 3); | |
| DecoratorSet decorators = IN_HEAP | IS_ARRAY; | |
| if (dest_uninitialized) { | |
| decorators |= IS_DEST_UNINITIALIZED; | |
| } | |
| if (aligned) { | |
| decorators |= ARRAYCOPY_ALIGNED; | |
| } | |
| BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler(); | |
| bs->arraycopy_prologue(_masm, decorators, T_OBJECT, R3_ARG1, R4_ARG2, R5_ARG3, noreg, noreg); | |
| if (UseCompressedOops) { | |
| generate_conjoint_int_copy_core(aligned); | |
| } else { | |
| #if INCLUDE_ZGC | |
| if (UseZGC) { | |
| ZBarrierSetAssembler *zbs = (ZBarrierSetAssembler*)bs; | |
| zbs->generate_conjoint_oop_copy(_masm, dest_uninitialized); | |
| } else | |
| #endif | |
| generate_conjoint_long_copy_core(aligned); | |
| } | |
| bs->arraycopy_epilogue(_masm, decorators, T_OBJECT, R4_ARG2, R5_ARG3, noreg); | |
| __ li(R3_RET, 0); // return 0 | |
| __ blr(); | |
| return start; | |
| } | |
| // Generate stub for disjoint oop copy. If "aligned" is true, the | |
| // "from" and "to" addresses are assumed to be heapword aligned. | |
| // | |
| // Arguments for generated stub: | |
| // from: R3_ARG1 | |
| // to: R4_ARG2 | |
| // count: R5_ARG3 treated as signed | |
| // dest_uninitialized: G1 support | |
| // | |
| address generate_disjoint_oop_copy(StubGenStubId stub_id) { | |
| bool aligned; | |
| bool dest_uninitialized; | |
| switch (stub_id) { | |
| case oop_disjoint_arraycopy_id: | |
| aligned = false; | |
| dest_uninitialized = false; | |
| break; | |
| case arrayof_oop_disjoint_arraycopy_id: | |
| aligned = true; | |
| dest_uninitialized = false; | |
| break; | |
| case oop_disjoint_arraycopy_uninit_id: | |
| aligned = false; | |
| dest_uninitialized = true; | |
| break; | |
| case arrayof_oop_disjoint_arraycopy_uninit_id: | |
| aligned = true; | |
| dest_uninitialized = true; | |
| break; | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| assert_positive_int(R5_ARG3); | |
| DecoratorSet decorators = IN_HEAP | IS_ARRAY | ARRAYCOPY_DISJOINT; | |
| if (dest_uninitialized) { | |
| decorators |= IS_DEST_UNINITIALIZED; | |
| } | |
| if (aligned) { | |
| decorators |= ARRAYCOPY_ALIGNED; | |
| } | |
| BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler(); | |
| bs->arraycopy_prologue(_masm, decorators, T_OBJECT, R3_ARG1, R4_ARG2, R5_ARG3, noreg, noreg); | |
| if (UseCompressedOops) { | |
| generate_disjoint_int_copy_core(aligned); | |
| } else { | |
| #if INCLUDE_ZGC | |
| if (UseZGC) { | |
| ZBarrierSetAssembler *zbs = (ZBarrierSetAssembler*)bs; | |
| zbs->generate_disjoint_oop_copy(_masm, dest_uninitialized); | |
| } else | |
| #endif | |
| generate_disjoint_long_copy_core(aligned); | |
| } | |
| bs->arraycopy_epilogue(_masm, decorators, T_OBJECT, R4_ARG2, R5_ARG3, noreg); | |
| __ li(R3_RET, 0); // return 0 | |
| __ blr(); | |
| return start; | |
| } | |
| // Helper for generating a dynamic type check. | |
| // Smashes only the given temp registers. | |
| void generate_type_check(Register sub_klass, | |
| Register super_check_offset, | |
| Register super_klass, | |
| Register temp1, | |
| Register temp2, | |
| Label& L_success) { | |
| assert_different_registers(sub_klass, super_check_offset, super_klass); | |
| BLOCK_COMMENT("type_check:"); | |
| Label L_miss; | |
| __ check_klass_subtype_fast_path(sub_klass, super_klass, temp1, temp2, &L_success, &L_miss, nullptr, | |
| super_check_offset); | |
| __ check_klass_subtype_slow_path(sub_klass, super_klass, temp1, temp2, &L_success); | |
| // Fall through on failure! | |
| __ bind(L_miss); | |
| } | |
| // Generate stub for checked oop copy. | |
| // | |
| // Arguments for generated stub: | |
| // from: R3 | |
| // to: R4 | |
| // count: R5 treated as signed | |
| // ckoff: R6 (super_check_offset) | |
| // ckval: R7 (super_klass) | |
| // ret: R3 zero for success; (-1^K) where K is partial transfer count | |
| // | |
| address generate_checkcast_copy(StubGenStubId stub_id) { | |
| const Register R3_from = R3_ARG1; // source array address | |
| const Register R4_to = R4_ARG2; // destination array address | |
| const Register R5_count = R5_ARG3; // elements count | |
| const Register R6_ckoff = R6_ARG4; // super_check_offset | |
| const Register R7_ckval = R7_ARG5; // super_klass | |
| const Register R8_offset = R8_ARG6; // loop var, with stride wordSize | |
| const Register R9_remain = R9_ARG7; // loop var, with stride -1 | |
| const Register R10_oop = R10_ARG8; // actual oop copied | |
| const Register R11_klass = R11_scratch1; // oop._klass | |
| const Register R12_tmp = R12_scratch2; | |
| const Register R2_tmp = R2; | |
| bool dest_uninitialized; | |
| switch (stub_id) { | |
| case checkcast_arraycopy_id: | |
| dest_uninitialized = false; | |
| break; | |
| case checkcast_arraycopy_uninit_id: | |
| dest_uninitialized = true; | |
| break; | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| //__ align(CodeEntryAlignment); | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| // Assert that int is 64 bit sign extended and arrays are not conjoint. | |
| #ifdef ASSERT | |
| { | |
| assert_positive_int(R5_ARG3); | |
| const Register tmp1 = R11_scratch1, tmp2 = R12_scratch2; | |
| Label no_overlap; | |
| __ subf(tmp1, R3_ARG1, R4_ARG2); // distance in bytes | |
| __ sldi(tmp2, R5_ARG3, LogBytesPerHeapOop); // size in bytes | |
| __ cmpld(CR0, R3_ARG1, R4_ARG2); // Use unsigned comparison! | |
| __ cmpld(CR1, tmp1, tmp2); | |
| __ crnand(CR0, Assembler::less, CR1, Assembler::less); | |
| // Overlaps if Src before dst and distance smaller than size. | |
| // Branch to forward copy routine otherwise. | |
| __ blt(CR0, no_overlap); | |
| __ stop("overlap in checkcast_copy"); | |
| __ bind(no_overlap); | |
| } | |
| #endif | |
| DecoratorSet decorators = IN_HEAP | IS_ARRAY | ARRAYCOPY_CHECKCAST; | |
| if (dest_uninitialized) { | |
| decorators |= IS_DEST_UNINITIALIZED; | |
| } | |
| BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler(); | |
| bs->arraycopy_prologue(_masm, decorators, T_OBJECT, R3_from, R4_to, R5_count, /* preserve: */ R6_ckoff, R7_ckval); | |
| //inc_counter_np(SharedRuntime::_checkcast_array_copy_ctr, R12_tmp, R3_RET); | |
| Label load_element, store_element, store_null, success, do_epilogue; | |
| __ or_(R9_remain, R5_count, R5_count); // Initialize loop index, and test it. | |
| __ li(R8_offset, 0); // Offset from start of arrays. | |
| __ bne(CR0, load_element); | |
| // Empty array: Nothing to do. | |
| __ li(R3_RET, 0); // Return 0 on (trivial) success. | |
| __ blr(); | |
| // ======== begin loop ======== | |
| // (Entry is load_element.) | |
| __ align(OptoLoopAlignment); | |
| __ bind(store_element); | |
| if (UseCompressedOops) { | |
| __ encode_heap_oop_not_null(R10_oop); | |
| __ bind(store_null); | |
| __ stw(R10_oop, R8_offset, R4_to); | |
| } else { | |
| __ bind(store_null); | |
| #if INCLUDE_ZGC | |
| if (UseZGC) { | |
| __ store_heap_oop(R10_oop, R8_offset, R4_to, R11_scratch1, R12_tmp, noreg, | |
| MacroAssembler::PRESERVATION_FRAME_LR_GP_REGS, | |
| dest_uninitialized ? IS_DEST_UNINITIALIZED : 0); | |
| } else | |
| #endif | |
| __ std(R10_oop, R8_offset, R4_to); | |
| } | |
| __ addi(R8_offset, R8_offset, heapOopSize); // Step to next offset. | |
| __ addic_(R9_remain, R9_remain, -1); // Decrement the count. | |
| __ beq(CR0, success); | |
| // ======== loop entry is here ======== | |
| __ bind(load_element); | |
| #if INCLUDE_ZGC | |
| if (UseZGC) { | |
| __ load_heap_oop(R10_oop, R8_offset, R3_from, | |
| R11_scratch1, R12_tmp, | |
| MacroAssembler::PRESERVATION_FRAME_LR_GP_REGS, | |
| 0, &store_null); | |
| } else | |
| #endif | |
| __ load_heap_oop(R10_oop, R8_offset, R3_from, | |
| R11_scratch1, R12_tmp, | |
| MacroAssembler::PRESERVATION_FRAME_LR_GP_REGS, | |
| AS_RAW, &store_null); | |
| __ load_klass(R11_klass, R10_oop); // Query the object klass. | |
| generate_type_check(R11_klass, R6_ckoff, R7_ckval, R12_tmp, R2_tmp, | |
| // Branch to this on success: | |
| store_element); | |
| // ======== end loop ======== | |
| // It was a real error; we must depend on the caller to finish the job. | |
| // Register R9_remain has number of *remaining* oops, R5_count number of *total* oops. | |
| // Emit GC store barriers for the oops we have copied (R5_count minus R9_remain), | |
| // and report their number to the caller. | |
| __ subf_(R5_count, R9_remain, R5_count); | |
| __ nand(R3_RET, R5_count, R5_count); // report (-1^K) to caller | |
| __ bne(CR0, do_epilogue); | |
| __ blr(); | |
| __ bind(success); | |
| __ li(R3_RET, 0); | |
| __ bind(do_epilogue); | |
| bs->arraycopy_epilogue(_masm, decorators, T_OBJECT, R4_to, R5_count, /* preserve */ R3_RET); | |
| __ blr(); | |
| return start; | |
| } | |
| // Generate 'unsafe' array copy stub. | |
| // Though just as safe as the other stubs, it takes an unscaled | |
| // size_t argument instead of an element count. | |
| // | |
| // Arguments for generated stub: | |
| // from: R3 | |
| // to: R4 | |
| // count: R5 byte count, treated as ssize_t, can be zero | |
| // | |
| // Examines the alignment of the operands and dispatches | |
| // to a long, int, short, or byte copy loop. | |
| // | |
| address generate_unsafe_copy(address byte_copy_entry, | |
| address short_copy_entry, | |
| address int_copy_entry, | |
| address long_copy_entry) { | |
| const Register R3_from = R3_ARG1; // source array address | |
| const Register R4_to = R4_ARG2; // destination array address | |
| const Register R5_count = R5_ARG3; // elements count (as long on PPC64) | |
| const Register R6_bits = R6_ARG4; // test copy of low bits | |
| const Register R7_tmp = R7_ARG5; | |
| //__ align(CodeEntryAlignment); | |
| StubGenStubId stub_id = StubGenStubId::unsafe_arraycopy_id; | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| // Bump this on entry, not on exit: | |
| //inc_counter_np(SharedRuntime::_unsafe_array_copy_ctr, R6_bits, R7_tmp); | |
| Label short_copy, int_copy, long_copy; | |
| __ orr(R6_bits, R3_from, R4_to); | |
| __ orr(R6_bits, R6_bits, R5_count); | |
| __ andi_(R0, R6_bits, (BytesPerLong-1)); | |
| __ beq(CR0, long_copy); | |
| __ andi_(R0, R6_bits, (BytesPerInt-1)); | |
| __ beq(CR0, int_copy); | |
| __ andi_(R0, R6_bits, (BytesPerShort-1)); | |
| __ beq(CR0, short_copy); | |
| // byte_copy: | |
| __ b(byte_copy_entry); | |
| __ bind(short_copy); | |
| __ srwi(R5_count, R5_count, LogBytesPerShort); | |
| __ b(short_copy_entry); | |
| __ bind(int_copy); | |
| __ srwi(R5_count, R5_count, LogBytesPerInt); | |
| __ b(int_copy_entry); | |
| __ bind(long_copy); | |
| __ srwi(R5_count, R5_count, LogBytesPerLong); | |
| __ b(long_copy_entry); | |
| return start; | |
| } | |
| // Perform range checks on the proposed arraycopy. | |
| // Kills the two temps, but nothing else. | |
| // Also, clean the sign bits of src_pos and dst_pos. | |
| void arraycopy_range_checks(Register src, // source array oop | |
| Register src_pos, // source position | |
| Register dst, // destination array oop | |
| Register dst_pos, // destination position | |
| Register length, // length of copy | |
| Register temp1, Register temp2, | |
| Label& L_failed) { | |
| BLOCK_COMMENT("arraycopy_range_checks:"); | |
| const Register array_length = temp1; // scratch | |
| const Register end_pos = temp2; // scratch | |
| // if (src_pos + length > arrayOop(src)->length() ) FAIL; | |
| __ lwa(array_length, arrayOopDesc::length_offset_in_bytes(), src); | |
| __ add(end_pos, src_pos, length); // src_pos + length | |
| __ cmpd(CR0, end_pos, array_length); | |
| __ bgt(CR0, L_failed); | |
| // if (dst_pos + length > arrayOop(dst)->length() ) FAIL; | |
| __ lwa(array_length, arrayOopDesc::length_offset_in_bytes(), dst); | |
| __ add(end_pos, dst_pos, length); // src_pos + length | |
| __ cmpd(CR0, end_pos, array_length); | |
| __ bgt(CR0, L_failed); | |
| BLOCK_COMMENT("arraycopy_range_checks done"); | |
| } | |
| // Helper for generate_unsafe_setmemory | |
| // | |
| // Atomically fill an array of memory using 1-, 2-, 4-, or 8-byte chunks and return. | |
| static void do_setmemory_atomic_loop(int elem_size, Register dest, Register size, Register byteVal, | |
| MacroAssembler *_masm) { | |
| Label L_Loop, L_Tail; // 2x unrolled loop | |
| // Propagate byte to required width | |
| if (elem_size > 1) __ rldimi(byteVal, byteVal, 8, 64 - 2 * 8); | |
| if (elem_size > 2) __ rldimi(byteVal, byteVal, 16, 64 - 2 * 16); | |
| if (elem_size > 4) __ rldimi(byteVal, byteVal, 32, 64 - 2 * 32); | |
| __ srwi_(R0, size, exact_log2(2 * elem_size)); // size is a 32 bit value | |
| __ beq(CR0, L_Tail); | |
| __ mtctr(R0); | |
| __ align(32); // loop alignment | |
| __ bind(L_Loop); | |
| __ store_sized_value(byteVal, 0, dest, elem_size); | |
| __ store_sized_value(byteVal, elem_size, dest, elem_size); | |
| __ addi(dest, dest, 2 * elem_size); | |
| __ bdnz(L_Loop); | |
| __ bind(L_Tail); | |
| __ andi_(R0, size, elem_size); | |
| __ bclr(Assembler::bcondCRbiIs1, Assembler::bi0(CR0, Assembler::equal), Assembler::bhintbhBCLRisReturn); | |
| __ store_sized_value(byteVal, 0, dest, elem_size); | |
| __ blr(); | |
| } | |
| // | |
| // Generate 'unsafe' set memory stub | |
| // Though just as safe as the other stubs, it takes an unscaled | |
| // size_t (# bytes) argument instead of an element count. | |
| // | |
| // Input: | |
| // R3_ARG1 - destination array address | |
| // R4_ARG2 - byte count (size_t) | |
| // R5_ARG3 - byte value | |
| // | |
| address generate_unsafe_setmemory(address unsafe_byte_fill) { | |
| __ align(CodeEntryAlignment); | |
| StubCodeMark mark(this, StubGenStubId::unsafe_setmemory_id); | |
| address start = __ function_entry(); | |
| // bump this on entry, not on exit: | |
| // inc_counter_np(SharedRuntime::_unsafe_set_memory_ctr); | |
| { | |
| Label L_fill8Bytes, L_fill4Bytes, L_fillBytes; | |
| const Register dest = R3_ARG1; | |
| const Register size = R4_ARG2; | |
| const Register byteVal = R5_ARG3; | |
| const Register rScratch1 = R6; | |
| // fill_to_memory_atomic(unsigned char*, unsigned long, unsigned char) | |
| // Check for pointer & size alignment | |
| __ orr(rScratch1, dest, size); | |
| __ andi_(R0, rScratch1, 7); | |
| __ beq(CR0, L_fill8Bytes); | |
| __ andi_(R0, rScratch1, 3); | |
| __ beq(CR0, L_fill4Bytes); | |
| __ andi_(R0, rScratch1, 1); | |
| __ bne(CR0, L_fillBytes); | |
| // Mark remaining code as such which performs Unsafe accesses. | |
| UnsafeMemoryAccessMark umam(this, true, false); | |
| // At this point, we know the lower bit of size is zero and a | |
| // multiple of 2 | |
| do_setmemory_atomic_loop(2, dest, size, byteVal, _masm); | |
| __ align(32); | |
| __ bind(L_fill8Bytes); | |
| // At this point, we know the lower 3 bits of size are zero and a | |
| // multiple of 8 | |
| do_setmemory_atomic_loop(8, dest, size, byteVal, _masm); | |
| __ align(32); | |
| __ bind(L_fill4Bytes); | |
| // At this point, we know the lower 2 bits of size are zero and a | |
| // multiple of 4 | |
| do_setmemory_atomic_loop(4, dest, size, byteVal, _masm); | |
| __ align(32); | |
| __ bind(L_fillBytes); | |
| do_setmemory_atomic_loop(1, dest, size, byteVal, _masm); | |
| } | |
| return start; | |
| } | |
| // | |
| // Generate generic array copy stubs | |
| // | |
| // Input: | |
| // R3 - src oop | |
| // R4 - src_pos | |
| // R5 - dst oop | |
| // R6 - dst_pos | |
| // R7 - element count | |
| // | |
| // Output: | |
| // R3 == 0 - success | |
| // R3 == -1 - need to call System.arraycopy | |
| // | |
| address generate_generic_copy(address entry_jbyte_arraycopy, | |
| address entry_jshort_arraycopy, | |
| address entry_jint_arraycopy, | |
| address entry_oop_arraycopy, | |
| address entry_disjoint_oop_arraycopy, | |
| address entry_jlong_arraycopy, | |
| address entry_checkcast_arraycopy) { | |
| Label L_failed, L_objArray; | |
| // Input registers | |
| const Register src = R3_ARG1; // source array oop | |
| const Register src_pos = R4_ARG2; // source position | |
| const Register dst = R5_ARG3; // destination array oop | |
| const Register dst_pos = R6_ARG4; // destination position | |
| const Register length = R7_ARG5; // elements count | |
| // registers used as temp | |
| const Register src_klass = R8_ARG6; // source array klass | |
| const Register dst_klass = R9_ARG7; // destination array klass | |
| const Register lh = R10_ARG8; // layout handler | |
| const Register temp = R2; | |
| //__ align(CodeEntryAlignment); | |
| StubGenStubId stub_id = StubGenStubId::generic_arraycopy_id; | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| // Bump this on entry, not on exit: | |
| //inc_counter_np(SharedRuntime::_generic_array_copy_ctr, lh, temp); | |
| // In principle, the int arguments could be dirty. | |
| //----------------------------------------------------------------------- | |
| // Assembler stubs will be used for this call to arraycopy | |
| // if the following conditions are met: | |
| // | |
| // (1) src and dst must not be null. | |
| // (2) src_pos must not be negative. | |
| // (3) dst_pos must not be negative. | |
| // (4) length must not be negative. | |
| // (5) src klass and dst klass should be the same and not null. | |
| // (6) src and dst should be arrays. | |
| // (7) src_pos + length must not exceed length of src. | |
| // (8) dst_pos + length must not exceed length of dst. | |
| BLOCK_COMMENT("arraycopy initial argument checks"); | |
| __ cmpdi(CR1, src, 0); // if (src == nullptr) return -1; | |
| __ extsw_(src_pos, src_pos); // if (src_pos < 0) return -1; | |
| __ cmpdi(CR5, dst, 0); // if (dst == nullptr) return -1; | |
| __ cror(CR1, Assembler::equal, CR0, Assembler::less); | |
| __ extsw_(dst_pos, dst_pos); // if (src_pos < 0) return -1; | |
| __ cror(CR5, Assembler::equal, CR0, Assembler::less); | |
| __ extsw_(length, length); // if (length < 0) return -1; | |
| __ cror(CR1, Assembler::equal, CR5, Assembler::equal); | |
| __ cror(CR1, Assembler::equal, CR0, Assembler::less); | |
| __ beq(CR1, L_failed); | |
| BLOCK_COMMENT("arraycopy argument klass checks"); | |
| __ load_klass(src_klass, src); | |
| __ load_klass(dst_klass, dst); | |
| // Load layout helper | |
| // | |
| // |array_tag| | header_size | element_type | |log2_element_size| | |
| // 32 30 24 16 8 2 0 | |
| // | |
| // array_tag: typeArray = 0x3, objArray = 0x2, non-array = 0x0 | |
| // | |
| int lh_offset = in_bytes(Klass::layout_helper_offset()); | |
| // Load 32-bits signed value. Use br() instruction with it to check icc. | |
| __ lwz(lh, lh_offset, src_klass); | |
| // Handle objArrays completely differently... | |
| jint objArray_lh = Klass::array_layout_helper(T_OBJECT); | |
| __ load_const_optimized(temp, objArray_lh, R0); | |
| __ cmpw(CR0, lh, temp); | |
| __ beq(CR0, L_objArray); | |
| __ cmpd(CR5, src_klass, dst_klass); // if (src->klass() != dst->klass()) return -1; | |
| __ cmpwi(CR6, lh, Klass::_lh_neutral_value); // if (!src->is_Array()) return -1; | |
| __ crnand(CR5, Assembler::equal, CR6, Assembler::less); | |
| __ beq(CR5, L_failed); | |
| // At this point, it is known to be a typeArray (array_tag 0x3). | |
| #ifdef ASSERT | |
| { Label L; | |
| jint lh_prim_tag_in_place = (Klass::_lh_array_tag_type_value << Klass::_lh_array_tag_shift); | |
| __ load_const_optimized(temp, lh_prim_tag_in_place, R0); | |
| __ cmpw(CR0, lh, temp); | |
| __ bge(CR0, L); | |
| __ stop("must be a primitive array"); | |
| __ bind(L); | |
| } | |
| #endif | |
| arraycopy_range_checks(src, src_pos, dst, dst_pos, length, | |
| temp, dst_klass, L_failed); | |
| // TypeArrayKlass | |
| // | |
| // src_addr = (src + array_header_in_bytes()) + (src_pos << log2elemsize); | |
| // dst_addr = (dst + array_header_in_bytes()) + (dst_pos << log2elemsize); | |
| // | |
| const Register offset = dst_klass; // array offset | |
| const Register elsize = src_klass; // log2 element size | |
| __ rldicl(offset, lh, 64 - Klass::_lh_header_size_shift, 64 - exact_log2(Klass::_lh_header_size_mask + 1)); | |
| __ andi(elsize, lh, Klass::_lh_log2_element_size_mask); | |
| __ add(src, offset, src); // src array offset | |
| __ add(dst, offset, dst); // dst array offset | |
| // Next registers should be set before the jump to corresponding stub. | |
| const Register from = R3_ARG1; // source array address | |
| const Register to = R4_ARG2; // destination array address | |
| const Register count = R5_ARG3; // elements count | |
| // 'from', 'to', 'count' registers should be set in this order | |
| // since they are the same as 'src', 'src_pos', 'dst'. | |
| BLOCK_COMMENT("scale indexes to element size"); | |
| __ sld(src_pos, src_pos, elsize); | |
| __ sld(dst_pos, dst_pos, elsize); | |
| __ add(from, src_pos, src); // src_addr | |
| __ add(to, dst_pos, dst); // dst_addr | |
| __ mr(count, length); // length | |
| BLOCK_COMMENT("choose copy loop based on element size"); | |
| // Using conditional branches with range 32kB. | |
| const int bo = Assembler::bcondCRbiIs1, bi = Assembler::bi0(CR0, Assembler::equal); | |
| __ cmpwi(CR0, elsize, 0); | |
| __ bc(bo, bi, entry_jbyte_arraycopy); | |
| __ cmpwi(CR0, elsize, LogBytesPerShort); | |
| __ bc(bo, bi, entry_jshort_arraycopy); | |
| __ cmpwi(CR0, elsize, LogBytesPerInt); | |
| __ bc(bo, bi, entry_jint_arraycopy); | |
| #ifdef ASSERT | |
| { Label L; | |
| __ cmpwi(CR0, elsize, LogBytesPerLong); | |
| __ beq(CR0, L); | |
| __ stop("must be long copy, but elsize is wrong"); | |
| __ bind(L); | |
| } | |
| #endif | |
| __ b(entry_jlong_arraycopy); | |
| // ObjArrayKlass | |
| __ bind(L_objArray); | |
| // live at this point: src_klass, dst_klass, src[_pos], dst[_pos], length | |
| Label L_disjoint_plain_copy, L_checkcast_copy; | |
| // test array classes for subtyping | |
| __ cmpd(CR0, src_klass, dst_klass); // usual case is exact equality | |
| __ bne(CR0, L_checkcast_copy); | |
| // Identically typed arrays can be copied without element-wise checks. | |
| arraycopy_range_checks(src, src_pos, dst, dst_pos, length, | |
| temp, lh, L_failed); | |
| __ addi(src, src, arrayOopDesc::base_offset_in_bytes(T_OBJECT)); //src offset | |
| __ addi(dst, dst, arrayOopDesc::base_offset_in_bytes(T_OBJECT)); //dst offset | |
| __ sldi(src_pos, src_pos, LogBytesPerHeapOop); | |
| __ sldi(dst_pos, dst_pos, LogBytesPerHeapOop); | |
| __ add(from, src_pos, src); // src_addr | |
| __ add(to, dst_pos, dst); // dst_addr | |
| __ mr(count, length); // length | |
| __ b(entry_oop_arraycopy); | |
| __ bind(L_checkcast_copy); | |
| // live at this point: src_klass, dst_klass | |
| { | |
| // Before looking at dst.length, make sure dst is also an objArray. | |
| __ lwz(temp, lh_offset, dst_klass); | |
| __ cmpw(CR0, lh, temp); | |
| __ bne(CR0, L_failed); | |
| // It is safe to examine both src.length and dst.length. | |
| arraycopy_range_checks(src, src_pos, dst, dst_pos, length, | |
| temp, lh, L_failed); | |
| // Marshal the base address arguments now, freeing registers. | |
| __ addi(src, src, arrayOopDesc::base_offset_in_bytes(T_OBJECT)); //src offset | |
| __ addi(dst, dst, arrayOopDesc::base_offset_in_bytes(T_OBJECT)); //dst offset | |
| __ sldi(src_pos, src_pos, LogBytesPerHeapOop); | |
| __ sldi(dst_pos, dst_pos, LogBytesPerHeapOop); | |
| __ add(from, src_pos, src); // src_addr | |
| __ add(to, dst_pos, dst); // dst_addr | |
| __ mr(count, length); // length | |
| Register sco_temp = R6_ARG4; // This register is free now. | |
| assert_different_registers(from, to, count, sco_temp, | |
| dst_klass, src_klass); | |
| // Generate the type check. | |
| int sco_offset = in_bytes(Klass::super_check_offset_offset()); | |
| __ lwz(sco_temp, sco_offset, dst_klass); | |
| generate_type_check(src_klass, sco_temp, dst_klass, | |
| temp, /* temp */ R10_ARG8, L_disjoint_plain_copy); | |
| // Fetch destination element klass from the ObjArrayKlass header. | |
| int ek_offset = in_bytes(ObjArrayKlass::element_klass_offset()); | |
| // The checkcast_copy loop needs two extra arguments: | |
| __ ld(R7_ARG5, ek_offset, dst_klass); // dest elem klass | |
| __ lwz(R6_ARG4, sco_offset, R7_ARG5); // sco of elem klass | |
| __ b(entry_checkcast_arraycopy); | |
| } | |
| __ bind(L_disjoint_plain_copy); | |
| __ b(entry_disjoint_oop_arraycopy); | |
| __ bind(L_failed); | |
| __ li(R3_RET, -1); // return -1 | |
| __ blr(); | |
| return start; | |
| } | |
| // Arguments for generated stub: | |
| // R3_ARG1 - source byte array address | |
| // R4_ARG2 - destination byte array address | |
| // R5_ARG3 - round key array | |
| address generate_aescrypt_encryptBlock() { | |
| assert(UseAES, "need AES instructions and misaligned SSE support"); | |
| StubGenStubId stub_id = StubGenStubId::aescrypt_encryptBlock_id; | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| Label L_doLast, L_error; | |
| Register from = R3_ARG1; // source array address | |
| Register to = R4_ARG2; // destination array address | |
| Register key = R5_ARG3; // round key array | |
| Register keylen = R8; | |
| Register temp = R9; | |
| Register keypos = R10; | |
| Register fifteen = R12; | |
| VectorRegister vRet = VR0; | |
| VectorRegister vKey1 = VR1; | |
| VectorRegister vKey2 = VR2; | |
| VectorRegister vKey3 = VR3; | |
| VectorRegister vKey4 = VR4; | |
| VectorRegister fromPerm = VR5; | |
| VectorRegister keyPerm = VR6; | |
| VectorRegister toPerm = VR7; | |
| VectorRegister fSplt = VR8; | |
| VectorRegister vTmp1 = VR9; | |
| VectorRegister vTmp2 = VR10; | |
| VectorRegister vTmp3 = VR11; | |
| VectorRegister vTmp4 = VR12; | |
| __ li (fifteen, 15); | |
| // load unaligned from[0-15] to vRet | |
| __ lvx (vRet, from); | |
| __ lvx (vTmp1, fifteen, from); | |
| __ lvsl (fromPerm, from); | |
| #ifdef VM_LITTLE_ENDIAN | |
| __ vspltisb (fSplt, 0x0f); | |
| __ vxor (fromPerm, fromPerm, fSplt); | |
| #endif | |
| __ vperm (vRet, vRet, vTmp1, fromPerm); | |
| // load keylen (44 or 52 or 60) | |
| __ lwz (keylen, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT), key); | |
| // to load keys | |
| __ load_perm (keyPerm, key); | |
| #ifdef VM_LITTLE_ENDIAN | |
| __ vspltisb (vTmp2, -16); | |
| __ vrld (keyPerm, keyPerm, vTmp2); | |
| __ vrld (keyPerm, keyPerm, vTmp2); | |
| __ vsldoi (keyPerm, keyPerm, keyPerm, 8); | |
| #endif | |
| // load the 1st round key to vTmp1 | |
| __ lvx (vTmp1, key); | |
| __ li (keypos, 16); | |
| __ lvx (vKey1, keypos, key); | |
| __ vec_perm (vTmp1, vKey1, keyPerm); | |
| // 1st round | |
| __ vxor (vRet, vRet, vTmp1); | |
| // load the 2nd round key to vKey1 | |
| __ li (keypos, 32); | |
| __ lvx (vKey2, keypos, key); | |
| __ vec_perm (vKey1, vKey2, keyPerm); | |
| // load the 3rd round key to vKey2 | |
| __ li (keypos, 48); | |
| __ lvx (vKey3, keypos, key); | |
| __ vec_perm (vKey2, vKey3, keyPerm); | |
| // load the 4th round key to vKey3 | |
| __ li (keypos, 64); | |
| __ lvx (vKey4, keypos, key); | |
| __ vec_perm (vKey3, vKey4, keyPerm); | |
| // load the 5th round key to vKey4 | |
| __ li (keypos, 80); | |
| __ lvx (vTmp1, keypos, key); | |
| __ vec_perm (vKey4, vTmp1, keyPerm); | |
| // 2nd - 5th rounds | |
| __ vcipher (vRet, vRet, vKey1); | |
| __ vcipher (vRet, vRet, vKey2); | |
| __ vcipher (vRet, vRet, vKey3); | |
| __ vcipher (vRet, vRet, vKey4); | |
| // load the 6th round key to vKey1 | |
| __ li (keypos, 96); | |
| __ lvx (vKey2, keypos, key); | |
| __ vec_perm (vKey1, vTmp1, vKey2, keyPerm); | |
| // load the 7th round key to vKey2 | |
| __ li (keypos, 112); | |
| __ lvx (vKey3, keypos, key); | |
| __ vec_perm (vKey2, vKey3, keyPerm); | |
| // load the 8th round key to vKey3 | |
| __ li (keypos, 128); | |
| __ lvx (vKey4, keypos, key); | |
| __ vec_perm (vKey3, vKey4, keyPerm); | |
| // load the 9th round key to vKey4 | |
| __ li (keypos, 144); | |
| __ lvx (vTmp1, keypos, key); | |
| __ vec_perm (vKey4, vTmp1, keyPerm); | |
| // 6th - 9th rounds | |
| __ vcipher (vRet, vRet, vKey1); | |
| __ vcipher (vRet, vRet, vKey2); | |
| __ vcipher (vRet, vRet, vKey3); | |
| __ vcipher (vRet, vRet, vKey4); | |
| // load the 10th round key to vKey1 | |
| __ li (keypos, 160); | |
| __ lvx (vKey2, keypos, key); | |
| __ vec_perm (vKey1, vTmp1, vKey2, keyPerm); | |
| // load the 11th round key to vKey2 | |
| __ li (keypos, 176); | |
| __ lvx (vTmp1, keypos, key); | |
| __ vec_perm (vKey2, vTmp1, keyPerm); | |
| // if all round keys are loaded, skip next 4 rounds | |
| __ cmpwi (CR0, keylen, 44); | |
| __ beq (CR0, L_doLast); | |
| // 10th - 11th rounds | |
| __ vcipher (vRet, vRet, vKey1); | |
| __ vcipher (vRet, vRet, vKey2); | |
| // load the 12th round key to vKey1 | |
| __ li (keypos, 192); | |
| __ lvx (vKey2, keypos, key); | |
| __ vec_perm (vKey1, vTmp1, vKey2, keyPerm); | |
| // load the 13th round key to vKey2 | |
| __ li (keypos, 208); | |
| __ lvx (vTmp1, keypos, key); | |
| __ vec_perm (vKey2, vTmp1, keyPerm); | |
| // if all round keys are loaded, skip next 2 rounds | |
| __ cmpwi (CR0, keylen, 52); | |
| __ beq (CR0, L_doLast); | |
| #ifdef ASSERT | |
| __ cmpwi (CR0, keylen, 60); | |
| __ bne (CR0, L_error); | |
| #endif | |
| // 12th - 13th rounds | |
| __ vcipher (vRet, vRet, vKey1); | |
| __ vcipher (vRet, vRet, vKey2); | |
| // load the 14th round key to vKey1 | |
| __ li (keypos, 224); | |
| __ lvx (vKey2, keypos, key); | |
| __ vec_perm (vKey1, vTmp1, vKey2, keyPerm); | |
| // load the 15th round key to vKey2 | |
| __ li (keypos, 240); | |
| __ lvx (vTmp1, keypos, key); | |
| __ vec_perm (vKey2, vTmp1, keyPerm); | |
| __ bind(L_doLast); | |
| // last two rounds | |
| __ vcipher (vRet, vRet, vKey1); | |
| __ vcipherlast (vRet, vRet, vKey2); | |
| #ifdef VM_LITTLE_ENDIAN | |
| // toPerm = 0x0F0E0D0C0B0A09080706050403020100 | |
| __ lvsl (toPerm, keypos); // keypos is a multiple of 16 | |
| __ vxor (toPerm, toPerm, fSplt); | |
| // Swap Bytes | |
| __ vperm (vRet, vRet, vRet, toPerm); | |
| #endif | |
| // store result (unaligned) | |
| // Note: We can't use a read-modify-write sequence which touches additional Bytes. | |
| Register lo = temp, hi = fifteen; // Reuse | |
| __ vsldoi (vTmp1, vRet, vRet, 8); | |
| __ mfvrd (hi, vRet); | |
| __ mfvrd (lo, vTmp1); | |
| __ std (hi, 0 LITTLE_ENDIAN_ONLY(+ 8), to); | |
| __ std (lo, 0 BIG_ENDIAN_ONLY(+ 8), to); | |
| __ blr(); | |
| #ifdef ASSERT | |
| __ bind(L_error); | |
| __ stop("aescrypt_encryptBlock: invalid key length"); | |
| #endif | |
| return start; | |
| } | |
| // Arguments for generated stub: | |
| // R3_ARG1 - source byte array address | |
| // R4_ARG2 - destination byte array address | |
| // R5_ARG3 - K (key) in little endian int array | |
| address generate_aescrypt_decryptBlock() { | |
| assert(UseAES, "need AES instructions and misaligned SSE support"); | |
| StubGenStubId stub_id = StubGenStubId::aescrypt_decryptBlock_id; | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| Label L_doLast, L_do44, L_do52, L_error; | |
| Register from = R3_ARG1; // source array address | |
| Register to = R4_ARG2; // destination array address | |
| Register key = R5_ARG3; // round key array | |
| Register keylen = R8; | |
| Register temp = R9; | |
| Register keypos = R10; | |
| Register fifteen = R12; | |
| VectorRegister vRet = VR0; | |
| VectorRegister vKey1 = VR1; | |
| VectorRegister vKey2 = VR2; | |
| VectorRegister vKey3 = VR3; | |
| VectorRegister vKey4 = VR4; | |
| VectorRegister vKey5 = VR5; | |
| VectorRegister fromPerm = VR6; | |
| VectorRegister keyPerm = VR7; | |
| VectorRegister toPerm = VR8; | |
| VectorRegister fSplt = VR9; | |
| VectorRegister vTmp1 = VR10; | |
| VectorRegister vTmp2 = VR11; | |
| VectorRegister vTmp3 = VR12; | |
| VectorRegister vTmp4 = VR13; | |
| __ li (fifteen, 15); | |
| // load unaligned from[0-15] to vRet | |
| __ lvx (vRet, from); | |
| __ lvx (vTmp1, fifteen, from); | |
| __ lvsl (fromPerm, from); | |
| #ifdef VM_LITTLE_ENDIAN | |
| __ vspltisb (fSplt, 0x0f); | |
| __ vxor (fromPerm, fromPerm, fSplt); | |
| #endif | |
| __ vperm (vRet, vRet, vTmp1, fromPerm); // align [and byte swap in LE] | |
| // load keylen (44 or 52 or 60) | |
| __ lwz (keylen, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT), key); | |
| // to load keys | |
| __ load_perm (keyPerm, key); | |
| #ifdef VM_LITTLE_ENDIAN | |
| __ vxor (vTmp2, vTmp2, vTmp2); | |
| __ vspltisb (vTmp2, -16); | |
| __ vrld (keyPerm, keyPerm, vTmp2); | |
| __ vrld (keyPerm, keyPerm, vTmp2); | |
| __ vsldoi (keyPerm, keyPerm, keyPerm, 8); | |
| #endif | |
| __ cmpwi (CR0, keylen, 44); | |
| __ beq (CR0, L_do44); | |
| __ cmpwi (CR0, keylen, 52); | |
| __ beq (CR0, L_do52); | |
| #ifdef ASSERT | |
| __ cmpwi (CR0, keylen, 60); | |
| __ bne (CR0, L_error); | |
| #endif | |
| // load the 15th round key to vKey1 | |
| __ li (keypos, 240); | |
| __ lvx (vKey1, keypos, key); | |
| __ li (keypos, 224); | |
| __ lvx (vKey2, keypos, key); | |
| __ vec_perm (vKey1, vKey2, vKey1, keyPerm); | |
| // load the 14th round key to vKey2 | |
| __ li (keypos, 208); | |
| __ lvx (vKey3, keypos, key); | |
| __ vec_perm (vKey2, vKey3, vKey2, keyPerm); | |
| // load the 13th round key to vKey3 | |
| __ li (keypos, 192); | |
| __ lvx (vKey4, keypos, key); | |
| __ vec_perm (vKey3, vKey4, vKey3, keyPerm); | |
| // load the 12th round key to vKey4 | |
| __ li (keypos, 176); | |
| __ lvx (vKey5, keypos, key); | |
| __ vec_perm (vKey4, vKey5, vKey4, keyPerm); | |
| // load the 11th round key to vKey5 | |
| __ li (keypos, 160); | |
| __ lvx (vTmp1, keypos, key); | |
| __ vec_perm (vKey5, vTmp1, vKey5, keyPerm); | |
| // 1st - 5th rounds | |
| __ vxor (vRet, vRet, vKey1); | |
| __ vncipher (vRet, vRet, vKey2); | |
| __ vncipher (vRet, vRet, vKey3); | |
| __ vncipher (vRet, vRet, vKey4); | |
| __ vncipher (vRet, vRet, vKey5); | |
| __ b (L_doLast); | |
| __ align(32); | |
| __ bind (L_do52); | |
| // load the 13th round key to vKey1 | |
| __ li (keypos, 208); | |
| __ lvx (vKey1, keypos, key); | |
| __ li (keypos, 192); | |
| __ lvx (vKey2, keypos, key); | |
| __ vec_perm (vKey1, vKey2, vKey1, keyPerm); | |
| // load the 12th round key to vKey2 | |
| __ li (keypos, 176); | |
| __ lvx (vKey3, keypos, key); | |
| __ vec_perm (vKey2, vKey3, vKey2, keyPerm); | |
| // load the 11th round key to vKey3 | |
| __ li (keypos, 160); | |
| __ lvx (vTmp1, keypos, key); | |
| __ vec_perm (vKey3, vTmp1, vKey3, keyPerm); | |
| // 1st - 3rd rounds | |
| __ vxor (vRet, vRet, vKey1); | |
| __ vncipher (vRet, vRet, vKey2); | |
| __ vncipher (vRet, vRet, vKey3); | |
| __ b (L_doLast); | |
| __ align(32); | |
| __ bind (L_do44); | |
| // load the 11th round key to vKey1 | |
| __ li (keypos, 176); | |
| __ lvx (vKey1, keypos, key); | |
| __ li (keypos, 160); | |
| __ lvx (vTmp1, keypos, key); | |
| __ vec_perm (vKey1, vTmp1, vKey1, keyPerm); | |
| // 1st round | |
| __ vxor (vRet, vRet, vKey1); | |
| __ bind (L_doLast); | |
| // load the 10th round key to vKey1 | |
| __ li (keypos, 144); | |
| __ lvx (vKey2, keypos, key); | |
| __ vec_perm (vKey1, vKey2, vTmp1, keyPerm); | |
| // load the 9th round key to vKey2 | |
| __ li (keypos, 128); | |
| __ lvx (vKey3, keypos, key); | |
| __ vec_perm (vKey2, vKey3, vKey2, keyPerm); | |
| // load the 8th round key to vKey3 | |
| __ li (keypos, 112); | |
| __ lvx (vKey4, keypos, key); | |
| __ vec_perm (vKey3, vKey4, vKey3, keyPerm); | |
| // load the 7th round key to vKey4 | |
| __ li (keypos, 96); | |
| __ lvx (vKey5, keypos, key); | |
| __ vec_perm (vKey4, vKey5, vKey4, keyPerm); | |
| // load the 6th round key to vKey5 | |
| __ li (keypos, 80); | |
| __ lvx (vTmp1, keypos, key); | |
| __ vec_perm (vKey5, vTmp1, vKey5, keyPerm); | |
| // last 10th - 6th rounds | |
| __ vncipher (vRet, vRet, vKey1); | |
| __ vncipher (vRet, vRet, vKey2); | |
| __ vncipher (vRet, vRet, vKey3); | |
| __ vncipher (vRet, vRet, vKey4); | |
| __ vncipher (vRet, vRet, vKey5); | |
| // load the 5th round key to vKey1 | |
| __ li (keypos, 64); | |
| __ lvx (vKey2, keypos, key); | |
| __ vec_perm (vKey1, vKey2, vTmp1, keyPerm); | |
| // load the 4th round key to vKey2 | |
| __ li (keypos, 48); | |
| __ lvx (vKey3, keypos, key); | |
| __ vec_perm (vKey2, vKey3, vKey2, keyPerm); | |
| // load the 3rd round key to vKey3 | |
| __ li (keypos, 32); | |
| __ lvx (vKey4, keypos, key); | |
| __ vec_perm (vKey3, vKey4, vKey3, keyPerm); | |
| // load the 2nd round key to vKey4 | |
| __ li (keypos, 16); | |
| __ lvx (vKey5, keypos, key); | |
| __ vec_perm (vKey4, vKey5, vKey4, keyPerm); | |
| // load the 1st round key to vKey5 | |
| __ lvx (vTmp1, key); | |
| __ vec_perm (vKey5, vTmp1, vKey5, keyPerm); | |
| // last 5th - 1th rounds | |
| __ vncipher (vRet, vRet, vKey1); | |
| __ vncipher (vRet, vRet, vKey2); | |
| __ vncipher (vRet, vRet, vKey3); | |
| __ vncipher (vRet, vRet, vKey4); | |
| __ vncipherlast (vRet, vRet, vKey5); | |
| #ifdef VM_LITTLE_ENDIAN | |
| // toPerm = 0x0F0E0D0C0B0A09080706050403020100 | |
| __ lvsl (toPerm, keypos); // keypos is a multiple of 16 | |
| __ vxor (toPerm, toPerm, fSplt); | |
| // Swap Bytes | |
| __ vperm (vRet, vRet, vRet, toPerm); | |
| #endif | |
| // store result (unaligned) | |
| // Note: We can't use a read-modify-write sequence which touches additional Bytes. | |
| Register lo = temp, hi = fifteen; // Reuse | |
| __ vsldoi (vTmp1, vRet, vRet, 8); | |
| __ mfvrd (hi, vRet); | |
| __ mfvrd (lo, vTmp1); | |
| __ std (hi, 0 LITTLE_ENDIAN_ONLY(+ 8), to); | |
| __ std (lo, 0 BIG_ENDIAN_ONLY(+ 8), to); | |
| __ blr(); | |
| #ifdef ASSERT | |
| __ bind(L_error); | |
| __ stop("aescrypt_decryptBlock: invalid key length"); | |
| #endif | |
| return start; | |
| } | |
| address generate_sha256_implCompress(StubGenStubId stub_id) { | |
| assert(UseSHA, "need SHA instructions"); | |
| bool multi_block; | |
| switch (stub_id) { | |
| case sha256_implCompress_id: | |
| multi_block = false; | |
| break; | |
| case sha256_implCompressMB_id: | |
| multi_block = true; | |
| break; | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| __ sha256 (multi_block); | |
| __ blr(); | |
| return start; | |
| } | |
| address generate_sha512_implCompress(StubGenStubId stub_id) { | |
| assert(UseSHA, "need SHA instructions"); | |
| bool multi_block; | |
| switch (stub_id) { | |
| case sha512_implCompress_id: | |
| multi_block = false; | |
| break; | |
| case sha512_implCompressMB_id: | |
| multi_block = true; | |
| break; | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| __ sha512 (multi_block); | |
| __ blr(); | |
| return start; | |
| } | |
| address generate_data_cache_writeback() { | |
| const Register cacheline = R3_ARG1; | |
| StubGenStubId stub_id = StubGenStubId::data_cache_writeback_id; | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ pc(); | |
| __ cache_wb(Address(cacheline)); | |
| __ blr(); | |
| return start; | |
| } | |
| address generate_data_cache_writeback_sync() { | |
| const Register is_presync = R3_ARG1; | |
| Register temp = R4; | |
| Label SKIP; | |
| StubGenStubId stub_id = StubGenStubId::data_cache_writeback_sync_id; | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ pc(); | |
| __ andi_(temp, is_presync, 1); | |
| __ bne(CR0, SKIP); | |
| __ cache_wbsync(false); // post sync => emit 'sync' | |
| __ bind(SKIP); // pre sync => emit nothing | |
| __ blr(); | |
| return start; | |
| } | |
| void generate_arraycopy_stubs() { | |
| // Note: the disjoint stubs must be generated first, some of | |
| // the conjoint stubs use them. | |
| address ucm_common_error_exit = generate_unsafecopy_common_error_exit(); | |
| UnsafeMemoryAccess::set_common_exit_stub_pc(ucm_common_error_exit); | |
| // non-aligned disjoint versions | |
| StubRoutines::_jbyte_disjoint_arraycopy = generate_disjoint_byte_copy(StubGenStubId::jbyte_disjoint_arraycopy_id); | |
| StubRoutines::_jshort_disjoint_arraycopy = generate_disjoint_short_copy(StubGenStubId::jshort_disjoint_arraycopy_id); | |
| StubRoutines::_jint_disjoint_arraycopy = generate_disjoint_int_copy(StubGenStubId::jint_disjoint_arraycopy_id); | |
| StubRoutines::_jlong_disjoint_arraycopy = generate_disjoint_long_copy(StubGenStubId::jlong_disjoint_arraycopy_id); | |
| StubRoutines::_oop_disjoint_arraycopy = generate_disjoint_oop_copy(StubGenStubId::oop_disjoint_arraycopy_id); | |
| StubRoutines::_oop_disjoint_arraycopy_uninit = generate_disjoint_oop_copy(StubGenStubId::oop_disjoint_arraycopy_uninit_id); | |
| // aligned disjoint versions | |
| StubRoutines::_arrayof_jbyte_disjoint_arraycopy = generate_disjoint_byte_copy(StubGenStubId::arrayof_jbyte_disjoint_arraycopy_id); | |
| StubRoutines::_arrayof_jshort_disjoint_arraycopy = generate_disjoint_short_copy(StubGenStubId::arrayof_jshort_disjoint_arraycopy_id); | |
| StubRoutines::_arrayof_jint_disjoint_arraycopy = generate_disjoint_int_copy(StubGenStubId::arrayof_jint_disjoint_arraycopy_id); | |
| StubRoutines::_arrayof_jlong_disjoint_arraycopy = generate_disjoint_long_copy(StubGenStubId::arrayof_jlong_disjoint_arraycopy_id); | |
| StubRoutines::_arrayof_oop_disjoint_arraycopy = generate_disjoint_oop_copy(StubGenStubId::arrayof_oop_disjoint_arraycopy_id); | |
| StubRoutines::_arrayof_oop_disjoint_arraycopy_uninit = generate_disjoint_oop_copy(StubGenStubId::oop_disjoint_arraycopy_uninit_id); | |
| // non-aligned conjoint versions | |
| StubRoutines::_jbyte_arraycopy = generate_conjoint_byte_copy(StubGenStubId::jbyte_arraycopy_id); | |
| StubRoutines::_jshort_arraycopy = generate_conjoint_short_copy(StubGenStubId::jshort_arraycopy_id); | |
| StubRoutines::_jint_arraycopy = generate_conjoint_int_copy(StubGenStubId::jint_arraycopy_id); | |
| StubRoutines::_jlong_arraycopy = generate_conjoint_long_copy(StubGenStubId::jlong_arraycopy_id); | |
| StubRoutines::_oop_arraycopy = generate_conjoint_oop_copy(StubGenStubId::oop_arraycopy_id); | |
| StubRoutines::_oop_arraycopy_uninit = generate_conjoint_oop_copy(StubGenStubId::oop_arraycopy_uninit_id); | |
| // aligned conjoint versions | |
| StubRoutines::_arrayof_jbyte_arraycopy = generate_conjoint_byte_copy(StubGenStubId::arrayof_jbyte_arraycopy_id); | |
| StubRoutines::_arrayof_jshort_arraycopy = generate_conjoint_short_copy(StubGenStubId::arrayof_jshort_arraycopy_id); | |
| StubRoutines::_arrayof_jint_arraycopy = generate_conjoint_int_copy(StubGenStubId::arrayof_jint_arraycopy_id); | |
| StubRoutines::_arrayof_jlong_arraycopy = generate_conjoint_long_copy(StubGenStubId::arrayof_jlong_arraycopy_id); | |
| StubRoutines::_arrayof_oop_arraycopy = generate_conjoint_oop_copy(StubGenStubId::arrayof_oop_arraycopy_id); | |
| StubRoutines::_arrayof_oop_arraycopy_uninit = generate_conjoint_oop_copy(StubGenStubId::arrayof_oop_arraycopy_id); | |
| // special/generic versions | |
| StubRoutines::_checkcast_arraycopy = generate_checkcast_copy(StubGenStubId::checkcast_arraycopy_id); | |
| StubRoutines::_checkcast_arraycopy_uninit = generate_checkcast_copy(StubGenStubId::checkcast_arraycopy_uninit_id); | |
| StubRoutines::_unsafe_arraycopy = generate_unsafe_copy(STUB_ENTRY(jbyte_arraycopy()), | |
| STUB_ENTRY(jshort_arraycopy()), | |
| STUB_ENTRY(jint_arraycopy()), | |
| STUB_ENTRY(jlong_arraycopy())); | |
| StubRoutines::_generic_arraycopy = generate_generic_copy(STUB_ENTRY(jbyte_arraycopy()), | |
| STUB_ENTRY(jshort_arraycopy()), | |
| STUB_ENTRY(jint_arraycopy()), | |
| STUB_ENTRY(oop_arraycopy()), | |
| STUB_ENTRY(oop_disjoint_arraycopy()), | |
| STUB_ENTRY(jlong_arraycopy()), | |
| STUB_ENTRY(checkcast_arraycopy())); | |
| // fill routines | |
| #ifdef COMPILER2 | |
| if (OptimizeFill) { | |
| StubRoutines::_jbyte_fill = generate_fill(StubGenStubId::jbyte_fill_id); | |
| StubRoutines::_jshort_fill = generate_fill(StubGenStubId::jshort_fill_id); | |
| StubRoutines::_jint_fill = generate_fill(StubGenStubId::jint_fill_id); | |
| StubRoutines::_arrayof_jbyte_fill = generate_fill(StubGenStubId::arrayof_jbyte_fill_id); | |
| StubRoutines::_arrayof_jshort_fill = generate_fill(StubGenStubId::arrayof_jshort_fill_id); | |
| StubRoutines::_arrayof_jint_fill = generate_fill(StubGenStubId::arrayof_jint_fill_id); | |
| } | |
| StubRoutines::_unsafe_setmemory = generate_unsafe_setmemory(StubRoutines::_jbyte_fill); | |
| #endif | |
| } | |
| // Stub for BigInteger::multiplyToLen() | |
| // | |
| // Arguments: | |
| // | |
| // Input: | |
| // R3 - x address | |
| // R4 - x length | |
| // R5 - y address | |
| // R6 - y length | |
| // R7 - z address | |
| // | |
| address generate_multiplyToLen() { | |
| StubGenStubId stub_id = StubGenStubId::multiplyToLen_id; | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| const Register x = R3; | |
| const Register xlen = R4; | |
| const Register y = R5; | |
| const Register ylen = R6; | |
| const Register z = R7; | |
| const Register tmp1 = R2; // TOC not used. | |
| const Register tmp2 = R9; | |
| const Register tmp3 = R10; | |
| const Register tmp4 = R11; | |
| const Register tmp5 = R12; | |
| // non-volatile regs | |
| const Register tmp6 = R31; | |
| const Register tmp7 = R30; | |
| const Register tmp8 = R29; | |
| const Register tmp9 = R28; | |
| const Register tmp10 = R27; | |
| const Register tmp11 = R26; | |
| const Register tmp12 = R25; | |
| const Register tmp13 = R24; | |
| BLOCK_COMMENT("Entry:"); | |
| // C2 does not respect int to long conversion for stub calls. | |
| __ clrldi(xlen, xlen, 32); | |
| __ clrldi(ylen, ylen, 32); | |
| // Save non-volatile regs (frameless). | |
| int current_offs = 8; | |
| __ std(R24, -current_offs, R1_SP); current_offs += 8; | |
| __ std(R25, -current_offs, R1_SP); current_offs += 8; | |
| __ std(R26, -current_offs, R1_SP); current_offs += 8; | |
| __ std(R27, -current_offs, R1_SP); current_offs += 8; | |
| __ std(R28, -current_offs, R1_SP); current_offs += 8; | |
| __ std(R29, -current_offs, R1_SP); current_offs += 8; | |
| __ std(R30, -current_offs, R1_SP); current_offs += 8; | |
| __ std(R31, -current_offs, R1_SP); | |
| __ multiply_to_len(x, xlen, y, ylen, z, tmp1, tmp2, tmp3, tmp4, tmp5, | |
| tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13); | |
| // Restore non-volatile regs. | |
| current_offs = 8; | |
| __ ld(R24, -current_offs, R1_SP); current_offs += 8; | |
| __ ld(R25, -current_offs, R1_SP); current_offs += 8; | |
| __ ld(R26, -current_offs, R1_SP); current_offs += 8; | |
| __ ld(R27, -current_offs, R1_SP); current_offs += 8; | |
| __ ld(R28, -current_offs, R1_SP); current_offs += 8; | |
| __ ld(R29, -current_offs, R1_SP); current_offs += 8; | |
| __ ld(R30, -current_offs, R1_SP); current_offs += 8; | |
| __ ld(R31, -current_offs, R1_SP); | |
| __ blr(); // Return to caller. | |
| return start; | |
| } | |
| /** | |
| * Arguments: | |
| * | |
| * Input: | |
| * R3_ARG1 - out address | |
| * R4_ARG2 - in address | |
| * R5_ARG3 - offset | |
| * R6_ARG4 - len | |
| * R7_ARG5 - k | |
| * Output: | |
| * R3_RET - carry | |
| */ | |
| address generate_mulAdd() { | |
| __ align(CodeEntryAlignment); | |
| StubGenStubId stub_id = StubGenStubId::mulAdd_id; | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| // C2 does not sign extend signed parameters to full 64 bits registers: | |
| __ rldic (R5_ARG3, R5_ARG3, 2, 32); // always positive | |
| __ clrldi(R6_ARG4, R6_ARG4, 32); // force zero bits on higher word | |
| __ clrldi(R7_ARG5, R7_ARG5, 32); // force zero bits on higher word | |
| __ muladd(R3_ARG1, R4_ARG2, R5_ARG3, R6_ARG4, R7_ARG5, R8, R9, R10); | |
| // Moves output carry to return register | |
| __ mr (R3_RET, R10); | |
| __ blr(); | |
| return start; | |
| } | |
| /** | |
| * Arguments: | |
| * | |
| * Input: | |
| * R3_ARG1 - in address | |
| * R4_ARG2 - in length | |
| * R5_ARG3 - out address | |
| * R6_ARG4 - out length | |
| */ | |
| address generate_squareToLen() { | |
| __ align(CodeEntryAlignment); | |
| StubGenStubId stub_id = StubGenStubId::squareToLen_id; | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| // args - higher word is cleaned (unsignedly) due to int to long casting | |
| const Register in = R3_ARG1; | |
| const Register in_len = R4_ARG2; | |
| __ clrldi(in_len, in_len, 32); | |
| const Register out = R5_ARG3; | |
| const Register out_len = R6_ARG4; | |
| __ clrldi(out_len, out_len, 32); | |
| // output | |
| const Register ret = R3_RET; | |
| // temporaries | |
| const Register lplw_s = R7; | |
| const Register in_aux = R8; | |
| const Register out_aux = R9; | |
| const Register piece = R10; | |
| const Register product = R14; | |
| const Register lplw = R15; | |
| const Register i_minus1 = R16; | |
| const Register carry = R17; | |
| const Register offset = R18; | |
| const Register off_aux = R19; | |
| const Register t = R20; | |
| const Register mlen = R21; | |
| const Register len = R22; | |
| const Register a = R23; | |
| const Register b = R24; | |
| const Register i = R25; | |
| const Register c = R26; | |
| const Register cs = R27; | |
| // Labels | |
| Label SKIP_LSHIFT, SKIP_DIAGONAL_SUM, SKIP_ADDONE, SKIP_LOOP_SQUARE; | |
| Label LOOP_LSHIFT, LOOP_DIAGONAL_SUM, LOOP_ADDONE, LOOP_SQUARE; | |
| // Save non-volatile regs (frameless). | |
| int current_offs = -8; | |
| __ std(R28, current_offs, R1_SP); current_offs -= 8; | |
| __ std(R27, current_offs, R1_SP); current_offs -= 8; | |
| __ std(R26, current_offs, R1_SP); current_offs -= 8; | |
| __ std(R25, current_offs, R1_SP); current_offs -= 8; | |
| __ std(R24, current_offs, R1_SP); current_offs -= 8; | |
| __ std(R23, current_offs, R1_SP); current_offs -= 8; | |
| __ std(R22, current_offs, R1_SP); current_offs -= 8; | |
| __ std(R21, current_offs, R1_SP); current_offs -= 8; | |
| __ std(R20, current_offs, R1_SP); current_offs -= 8; | |
| __ std(R19, current_offs, R1_SP); current_offs -= 8; | |
| __ std(R18, current_offs, R1_SP); current_offs -= 8; | |
| __ std(R17, current_offs, R1_SP); current_offs -= 8; | |
| __ std(R16, current_offs, R1_SP); current_offs -= 8; | |
| __ std(R15, current_offs, R1_SP); current_offs -= 8; | |
| __ std(R14, current_offs, R1_SP); | |
| // Store the squares, right shifted one bit (i.e., divided by 2) | |
| __ subi (out_aux, out, 8); | |
| __ subi (in_aux, in, 4); | |
| __ cmpwi (CR0, in_len, 0); | |
| // Initialize lplw outside of the loop | |
| __ xorr (lplw, lplw, lplw); | |
| __ ble (CR0, SKIP_LOOP_SQUARE); // in_len <= 0 | |
| __ mtctr (in_len); | |
| __ bind(LOOP_SQUARE); | |
| __ lwzu (piece, 4, in_aux); | |
| __ mulld (product, piece, piece); | |
| // shift left 63 bits and only keep the MSB | |
| __ rldic (lplw_s, lplw, 63, 0); | |
| __ mr (lplw, product); | |
| // shift right 1 bit without sign extension | |
| __ srdi (product, product, 1); | |
| // join them to the same register and store it | |
| __ orr (product, lplw_s, product); | |
| #ifdef VM_LITTLE_ENDIAN | |
| // Swap low and high words for little endian | |
| __ rldicl (product, product, 32, 0); | |
| #endif | |
| __ stdu (product, 8, out_aux); | |
| __ bdnz (LOOP_SQUARE); | |
| __ bind(SKIP_LOOP_SQUARE); | |
| // Add in off-diagonal sums | |
| __ cmpwi (CR0, in_len, 0); | |
| __ ble (CR0, SKIP_DIAGONAL_SUM); | |
| // Avoid CTR usage here in order to use it at mulAdd | |
| __ subi (i_minus1, in_len, 1); | |
| __ li (offset, 4); | |
| __ bind(LOOP_DIAGONAL_SUM); | |
| __ sldi (off_aux, out_len, 2); | |
| __ sub (off_aux, off_aux, offset); | |
| __ mr (len, i_minus1); | |
| __ sldi (mlen, i_minus1, 2); | |
| __ lwzx (t, in, mlen); | |
| __ muladd (out, in, off_aux, len, t, a, b, carry); | |
| // begin<addOne> | |
| // off_aux = out_len*4 - 4 - mlen - offset*4 - 4; | |
| __ addi (mlen, mlen, 4); | |
| __ sldi (a, out_len, 2); | |
| __ subi (a, a, 4); | |
| __ sub (a, a, mlen); | |
| __ subi (off_aux, offset, 4); | |
| __ sub (off_aux, a, off_aux); | |
| __ lwzx (b, off_aux, out); | |
| __ add (b, b, carry); | |
| __ stwx (b, off_aux, out); | |
| // if (((uint64_t)s >> 32) != 0) { | |
| __ srdi_ (a, b, 32); | |
| __ beq (CR0, SKIP_ADDONE); | |
| // while (--mlen >= 0) { | |
| __ bind(LOOP_ADDONE); | |
| __ subi (mlen, mlen, 4); | |
| __ cmpwi (CR0, mlen, 0); | |
| __ beq (CR0, SKIP_ADDONE); | |
| // if (--offset_aux < 0) { // Carry out of number | |
| __ subi (off_aux, off_aux, 4); | |
| __ cmpwi (CR0, off_aux, 0); | |
| __ blt (CR0, SKIP_ADDONE); | |
| // } else { | |
| __ lwzx (b, off_aux, out); | |
| __ addi (b, b, 1); | |
| __ stwx (b, off_aux, out); | |
| __ cmpwi (CR0, b, 0); | |
| __ bne (CR0, SKIP_ADDONE); | |
| __ b (LOOP_ADDONE); | |
| __ bind(SKIP_ADDONE); | |
| // } } } end<addOne> | |
| __ addi (offset, offset, 8); | |
| __ subi (i_minus1, i_minus1, 1); | |
| __ cmpwi (CR0, i_minus1, 0); | |
| __ bge (CR0, LOOP_DIAGONAL_SUM); | |
| __ bind(SKIP_DIAGONAL_SUM); | |
| // Shift back up and set low bit | |
| // Shifts 1 bit left up to len positions. Assumes no leading zeros | |
| // begin<primitiveLeftShift> | |
| __ cmpwi (CR0, out_len, 0); | |
| __ ble (CR0, SKIP_LSHIFT); | |
| __ li (i, 0); | |
| __ lwz (c, 0, out); | |
| __ subi (b, out_len, 1); | |
| __ mtctr (b); | |
| __ bind(LOOP_LSHIFT); | |
| __ mr (b, c); | |
| __ addi (cs, i, 4); | |
| __ lwzx (c, out, cs); | |
| __ sldi (b, b, 1); | |
| __ srwi (cs, c, 31); | |
| __ orr (b, b, cs); | |
| __ stwx (b, i, out); | |
| __ addi (i, i, 4); | |
| __ bdnz (LOOP_LSHIFT); | |
| __ sldi (c, out_len, 2); | |
| __ subi (c, c, 4); | |
| __ lwzx (b, out, c); | |
| __ sldi (b, b, 1); | |
| __ stwx (b, out, c); | |
| __ bind(SKIP_LSHIFT); | |
| // end<primitiveLeftShift> | |
| // Set low bit | |
| __ sldi (i, in_len, 2); | |
| __ subi (i, i, 4); | |
| __ lwzx (i, in, i); | |
| __ sldi (c, out_len, 2); | |
| __ subi (c, c, 4); | |
| __ lwzx (b, out, c); | |
| __ andi (i, i, 1); | |
| __ orr (i, b, i); | |
| __ stwx (i, out, c); | |
| // Restore non-volatile regs. | |
| current_offs = -8; | |
| __ ld(R28, current_offs, R1_SP); current_offs -= 8; | |
| __ ld(R27, current_offs, R1_SP); current_offs -= 8; | |
| __ ld(R26, current_offs, R1_SP); current_offs -= 8; | |
| __ ld(R25, current_offs, R1_SP); current_offs -= 8; | |
| __ ld(R24, current_offs, R1_SP); current_offs -= 8; | |
| __ ld(R23, current_offs, R1_SP); current_offs -= 8; | |
| __ ld(R22, current_offs, R1_SP); current_offs -= 8; | |
| __ ld(R21, current_offs, R1_SP); current_offs -= 8; | |
| __ ld(R20, current_offs, R1_SP); current_offs -= 8; | |
| __ ld(R19, current_offs, R1_SP); current_offs -= 8; | |
| __ ld(R18, current_offs, R1_SP); current_offs -= 8; | |
| __ ld(R17, current_offs, R1_SP); current_offs -= 8; | |
| __ ld(R16, current_offs, R1_SP); current_offs -= 8; | |
| __ ld(R15, current_offs, R1_SP); current_offs -= 8; | |
| __ ld(R14, current_offs, R1_SP); | |
| __ mr(ret, out); | |
| __ blr(); | |
| return start; | |
| } | |
| /** | |
| * Arguments: | |
| * | |
| * Inputs: | |
| * R3_ARG1 - int crc | |
| * R4_ARG2 - byte* buf | |
| * R5_ARG3 - int length (of buffer) | |
| * | |
| * scratch: | |
| * R2, R6-R12 | |
| * | |
| * Output: | |
| * R3_RET - int crc result | |
| */ | |
| // Compute CRC32 function. | |
| address generate_CRC32_updateBytes(StubGenStubId stub_id) { | |
| bool is_crc32c; | |
| switch (stub_id) { | |
| case updateBytesCRC32_id: | |
| is_crc32c = false; | |
| break; | |
| case updateBytesCRC32C_id: | |
| is_crc32c = true; | |
| break; | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| __ align(CodeEntryAlignment); | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); // Remember stub start address (is rtn value). | |
| __ crc32(R3_ARG1, R4_ARG2, R5_ARG3, R2, R6, R7, R8, R9, R10, R11, R12, is_crc32c); | |
| __ blr(); | |
| return start; | |
| } | |
| address generate_floatToFloat16() { | |
| __ align(CodeEntryAlignment); | |
| StubCodeMark mark(this, "StubRoutines", "floatToFloat16"); | |
| address start = __ function_entry(); | |
| __ f2hf(R3_RET, F1_ARG1, F0); | |
| __ blr(); | |
| return start; | |
| } | |
| address generate_float16ToFloat() { | |
| __ align(CodeEntryAlignment); | |
| StubCodeMark mark(this, "StubRoutines", "float16ToFloat"); | |
| address start = __ function_entry(); | |
| __ hf2f(F1_RET, R3_ARG1); | |
| __ blr(); | |
| return start; | |
| } | |
| address generate_method_entry_barrier() { | |
| __ align(CodeEntryAlignment); | |
| StubGenStubId stub_id = StubGenStubId::method_entry_barrier_id; | |
| StubCodeMark mark(this, stub_id); | |
| address stub_address = __ pc(); | |
| int nbytes_save = MacroAssembler::num_volatile_regs * BytesPerWord; | |
| __ save_volatile_gprs(R1_SP, -nbytes_save, true); | |
| // Link register points to instruction in prologue of the guarded nmethod. | |
| // As the stub requires one layer of indirection (argument is of type address* and not address), | |
| // passing the link register's value directly doesn't work. | |
| // Since we have to save the link register on the stack anyway, we calculate the corresponding stack address | |
| // and pass that one instead. | |
| __ addi(R3_ARG1, R1_SP, _abi0(lr)); | |
| __ save_LR(R0); | |
| __ push_frame_reg_args(nbytes_save, R0); | |
| __ call_VM_leaf(CAST_FROM_FN_PTR(address, BarrierSetNMethod::nmethod_stub_entry_barrier)); | |
| __ mr(R0, R3_RET); | |
| __ pop_frame(); | |
| __ restore_LR(R3_RET /* used as tmp register */); | |
| __ restore_volatile_gprs(R1_SP, -nbytes_save, true); | |
| __ cmpdi(CR0, R0, 0); | |
| // Return to prologue if no deoptimization is required (bnelr) | |
| __ bclr(Assembler::bcondCRbiIs1, Assembler::bi0(CR0, Assembler::equal), Assembler::bhintIsTaken); | |
| // Deoptimization required. | |
| // For actually handling the deoptimization, the 'wrong method stub' is invoked. | |
| __ load_const_optimized(R0, SharedRuntime::get_handle_wrong_method_stub()); | |
| __ mtctr(R0); | |
| // Pop the frame built in the prologue. | |
| __ pop_frame(); | |
| // Restore link register. Required as the 'wrong method stub' needs the caller's frame | |
| // to properly deoptimize this method (e.g. by re-resolving the call site for compiled methods). | |
| // This method's prologue is aborted. | |
| __ restore_LR(R0); | |
| __ bctr(); | |
| return stub_address; | |
| } | |
| #ifdef VM_LITTLE_ENDIAN | |
| // The following Base64 decode intrinsic is based on an algorithm outlined | |
| // in here: | |
| // http://0x80.pl/notesen/2016-01-17-sse-base64-decoding.html | |
| // in the section titled "Vector lookup (pshufb with bitmask)" | |
| // | |
| // This implementation differs in the following ways: | |
| // * Instead of Intel SSE instructions, Power AltiVec VMX and VSX instructions | |
| // are used instead. It turns out that some of the vector operations | |
| // needed in the algorithm require fewer AltiVec instructions. | |
| // * The algorithm in the above mentioned paper doesn't handle the | |
| // Base64-URL variant in RFC 4648. Adjustments to both the code and to two | |
| // lookup tables are needed for this. | |
| // * The "Pack" section of the code is a complete rewrite for Power because we | |
| // can utilize better instructions for this step. | |
| // | |
| // Offsets per group of Base64 characters | |
| // Uppercase | |
| #define UC (signed char)((-'A' + 0) & 0xff) | |
| // Lowercase | |
| #define LC (signed char)((-'a' + 26) & 0xff) | |
| // Digits | |
| #define DIG (signed char)((-'0' + 52) & 0xff) | |
| // Plus sign (URL = 0) | |
| #define PLS (signed char)((-'+' + 62) & 0xff) | |
| // Hyphen (URL = 1) | |
| #define HYP (signed char)((-'-' + 62) & 0xff) | |
| // Slash (URL = 0) | |
| #define SLS (signed char)((-'/' + 63) & 0xff) | |
| // Underscore (URL = 1) | |
| #define US (signed char)((-'_' + 63) & 0xff) | |
| // For P10 (or later) only | |
| #define VALID_B64 0x80 | |
| #define VB64(x) (VALID_B64 | x) | |
| #define BLK_OFFSETOF(x) (offsetof(constant_block, x)) | |
| // In little-endian mode, the lxv instruction loads the element at EA into | |
| // element 15 of the vector register, EA+1 goes into element 14, and so | |
| // on. | |
| // | |
| // To make a look-up table easier to read, ARRAY_TO_LXV_ORDER reverses the | |
| // order of the elements in a vector initialization. | |
| #define ARRAY_TO_LXV_ORDER(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15) e15, e14, e13, e12, e11, e10, e9, e8, e7, e6, e5, e4, e3, e2, e1, e0 | |
| // | |
| // Base64 decodeBlock intrinsic | |
| address generate_base64_decodeBlock() { | |
| __ align(CodeEntryAlignment); | |
| StubGenStubId stub_id = StubGenStubId::base64_decodeBlock_id; | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| typedef struct { | |
| signed char offsetLUT_val[16]; | |
| signed char offsetLUT_URL_val[16]; | |
| unsigned char maskLUT_val[16]; | |
| unsigned char maskLUT_URL_val[16]; | |
| unsigned char bitposLUT_val[16]; | |
| unsigned char table_32_47_val[16]; | |
| unsigned char table_32_47_URL_val[16]; | |
| unsigned char table_48_63_val[16]; | |
| unsigned char table_64_79_val[16]; | |
| unsigned char table_80_95_val[16]; | |
| unsigned char table_80_95_URL_val[16]; | |
| unsigned char table_96_111_val[16]; | |
| unsigned char table_112_127_val[16]; | |
| unsigned char pack_lshift_val[16]; | |
| unsigned char pack_rshift_val[16]; | |
| unsigned char pack_permute_val[16]; | |
| } constant_block; | |
| alignas(16) static const constant_block const_block = { | |
| .offsetLUT_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| 0, 0, PLS, DIG, UC, UC, LC, LC, | |
| 0, 0, 0, 0, 0, 0, 0, 0 ) }, | |
| .offsetLUT_URL_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| 0, 0, HYP, DIG, UC, UC, LC, LC, | |
| 0, 0, 0, 0, 0, 0, 0, 0 ) }, | |
| .maskLUT_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| /* 0 */ (unsigned char)0b10101000, | |
| /* 1 .. 9 */ (unsigned char)0b11111000, (unsigned char)0b11111000, (unsigned char)0b11111000, (unsigned char)0b11111000, | |
| (unsigned char)0b11111000, (unsigned char)0b11111000, (unsigned char)0b11111000, (unsigned char)0b11111000, | |
| (unsigned char)0b11111000, | |
| /* 10 */ (unsigned char)0b11110000, | |
| /* 11 */ (unsigned char)0b01010100, | |
| /* 12 .. 14 */ (unsigned char)0b01010000, (unsigned char)0b01010000, (unsigned char)0b01010000, | |
| /* 15 */ (unsigned char)0b01010100 ) }, | |
| .maskLUT_URL_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| /* 0 */ (unsigned char)0b10101000, | |
| /* 1 .. 9 */ (unsigned char)0b11111000, (unsigned char)0b11111000, (unsigned char)0b11111000, (unsigned char)0b11111000, | |
| (unsigned char)0b11111000, (unsigned char)0b11111000, (unsigned char)0b11111000, (unsigned char)0b11111000, | |
| (unsigned char)0b11111000, | |
| /* 10 */ (unsigned char)0b11110000, | |
| /* 11 .. 12 */ (unsigned char)0b01010000, (unsigned char)0b01010000, | |
| /* 13 */ (unsigned char)0b01010100, | |
| /* 14 */ (unsigned char)0b01010000, | |
| /* 15 */ (unsigned char)0b01110000 ) }, | |
| .bitposLUT_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, (unsigned char)0x80, | |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ) }, | |
| // In the following table_*_val constants, a 0 value means the | |
| // character is not in the Base64 character set | |
| .table_32_47_val = { | |
| ARRAY_TO_LXV_ORDER ( | |
| /* space .. '*' = 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* '+' = 62 */ VB64(62), /* ',' .. '.' = 0 */ 0, 0, 0, /* '/' = 63 */ VB64(63) ) }, | |
| .table_32_47_URL_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| /* space .. ',' = 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* '-' = 62 */ VB64(62), /* '.' .. '/' */ 0, 0 ) }, | |
| .table_48_63_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| /* '0' .. '9' = 52 .. 61 */ VB64(52), VB64(53), VB64(54), VB64(55), VB64(56), VB64(57), VB64(58), VB64(59), VB64(60), VB64(61), | |
| /* ':' .. '?' = 0 */ 0, 0, 0, 0, 0, 0 ) }, | |
| .table_64_79_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| /* '@' = 0 */ 0, /* 'A' .. 'O' = 0 .. 14 */ VB64(0), VB64(1), VB64(2), VB64(3), VB64(4), VB64(5), VB64(6), VB64(7), VB64(8), | |
| VB64(9), VB64(10), VB64(11), VB64(12), VB64(13), VB64(14) ) }, | |
| .table_80_95_val = { | |
| ARRAY_TO_LXV_ORDER(/* 'P' .. 'Z' = 15 .. 25 */ VB64(15), VB64(16), VB64(17), VB64(18), VB64(19), VB64(20), VB64(21), VB64(22), | |
| VB64(23), VB64(24), VB64(25), /* '[' .. '_' = 0 */ 0, 0, 0, 0, 0 ) }, | |
| .table_80_95_URL_val = { | |
| ARRAY_TO_LXV_ORDER(/* 'P' .. 'Z' = 15 .. 25 */ VB64(15), VB64(16), VB64(17), VB64(18), VB64(19), VB64(20), VB64(21), VB64(22), | |
| VB64(23), VB64(24), VB64(25), /* '[' .. '^' = 0 */ 0, 0, 0, 0, /* '_' = 63 */ VB64(63) ) }, | |
| .table_96_111_val = { | |
| ARRAY_TO_LXV_ORDER(/* '`' = 0 */ 0, /* 'a' .. 'o' = 26 .. 40 */ VB64(26), VB64(27), VB64(28), VB64(29), VB64(30), VB64(31), | |
| VB64(32), VB64(33), VB64(34), VB64(35), VB64(36), VB64(37), VB64(38), VB64(39), VB64(40) ) }, | |
| .table_112_127_val = { | |
| ARRAY_TO_LXV_ORDER(/* 'p' .. 'z' = 41 .. 51 */ VB64(41), VB64(42), VB64(43), VB64(44), VB64(45), VB64(46), VB64(47), VB64(48), | |
| VB64(49), VB64(50), VB64(51), /* '{' .. DEL = 0 */ 0, 0, 0, 0, 0 ) }, | |
| .pack_lshift_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| 0, 6, 4, 2, 0, 6, 4, 2, 0, 6, 4, 2, 0, 6, 4, 2 ) }, | |
| .pack_rshift_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| 0, 2, 4, 0, 0, 2, 4, 0, 0, 2, 4, 0, 0, 2, 4, 0 ) }, | |
| // The first 4 index values are "don't care" because | |
| // we only use the first 12 bytes of the vector, | |
| // which are decoded from 16 bytes of Base64 characters. | |
| .pack_permute_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| 0, 0, 0, 0, | |
| 0, 1, 2, | |
| 4, 5, 6, | |
| 8, 9, 10, | |
| 12, 13, 14 ) } | |
| }; | |
| const unsigned block_size = 16; // number of bytes to process in each pass through the loop | |
| const unsigned block_size_shift = 4; | |
| // According to the ELF V2 ABI, registers r3-r12 are volatile and available for use without save/restore | |
| Register s = R3_ARG1; // source starting address of Base64 characters | |
| Register sp = R4_ARG2; // source offset | |
| Register sl = R5_ARG3; // source length = # of Base64 characters to be processed | |
| Register d = R6_ARG4; // destination address | |
| Register dp = R7_ARG5; // destination offset | |
| Register isURL = R8_ARG6; // boolean, if non-zero indicates use of RFC 4648 base64url encoding | |
| Register isMIME = R9_ARG7; // boolean, if non-zero indicates use of RFC 2045 MIME encoding - not used | |
| // Local variables | |
| Register const_ptr = R9; // used for loading constants | |
| Register tmp_reg = R10; // used for speeding up load_constant_optimized() | |
| // Re-use R9 and R10 to avoid using non-volatile registers (requires save/restore) | |
| Register out = R9; // moving out (destination) pointer | |
| Register in = R10; // moving in (source) pointer | |
| // Volatile VSRS are 0..13, 32..51 (VR0..VR13) | |
| // VR Constants | |
| VectorRegister vec_0s = VR0; | |
| VectorRegister vec_4s = VR1; | |
| VectorRegister vec_8s = VR2; | |
| VectorRegister vec_special_case_char = VR3; | |
| VectorRegister pack_rshift = VR4; | |
| VectorRegister pack_lshift = VR5; | |
| // VSR Constants | |
| VectorSRegister offsetLUT = VSR0; | |
| VectorSRegister maskLUT = VSR1; | |
| VectorSRegister bitposLUT = VSR2; | |
| VectorSRegister vec_0xfs = VSR3; | |
| VectorSRegister vec_special_case_offset = VSR4; | |
| VectorSRegister pack_permute = VSR5; | |
| // P10 (or later) VSR lookup constants | |
| VectorSRegister table_32_47 = VSR0; | |
| VectorSRegister table_48_63 = VSR1; | |
| VectorSRegister table_64_79 = VSR2; | |
| VectorSRegister table_80_95 = VSR3; | |
| VectorSRegister table_96_111 = VSR4; | |
| VectorSRegister table_112_127 = VSR6; | |
| // Data read in and later converted | |
| VectorRegister input = VR6; | |
| // Variable for testing Base64 validity | |
| VectorRegister non_match = VR10; | |
| // P9 VR Variables for lookup | |
| VectorRegister higher_nibble = VR7; | |
| VectorRegister eq_special_case_char = VR8; | |
| VectorRegister offsets = VR9; | |
| // P9 VSR lookup variables | |
| VectorSRegister bit = VSR6; | |
| VectorSRegister lower_nibble = VSR7; | |
| VectorSRegister M = VSR8; | |
| // P10 (or later) VSR lookup variables | |
| VectorSRegister xlate_a = VSR7; | |
| VectorSRegister xlate_b = VSR8; | |
| // Variables for pack | |
| // VR | |
| VectorRegister l = VR7; // reuse higher_nibble's register | |
| VectorRegister r = VR8; // reuse eq_special_case_char's register | |
| VectorRegister gathered = VR10; // reuse non_match's register | |
| Label not_URL, calculate_size, loop_start, loop_exit, return_zero; | |
| // The upper 32 bits of the non-pointer parameter registers are not | |
| // guaranteed to be zero, so mask off those upper bits. | |
| __ clrldi(sp, sp, 32); | |
| __ clrldi(sl, sl, 32); | |
| // Don't handle the last 4 characters of the source, because this | |
| // VSX-based algorithm doesn't handle padding characters. Also the | |
| // vector code will always write 16 bytes of decoded data on each pass, | |
| // but only the first 12 of those 16 bytes are valid data (16 base64 | |
| // characters become 12 bytes of binary data), so for this reason we | |
| // need to subtract an additional 8 bytes from the source length, in | |
| // order not to write past the end of the destination buffer. The | |
| // result of this subtraction implies that a Java function in the | |
| // Base64 class will be used to process the last 12 characters. | |
| __ sub(sl, sl, sp); | |
| __ subi(sl, sl, 12); | |
| // Load CTR with the number of passes through the loop | |
| // = sl >> block_size_shift. After the shift, if sl <= 0, there's too | |
| // little data to be processed by this intrinsic. | |
| __ srawi_(sl, sl, block_size_shift); | |
| __ ble(CR0, return_zero); | |
| __ mtctr(sl); | |
| // Clear the other two parameter registers upper 32 bits. | |
| __ clrldi(isURL, isURL, 32); | |
| __ clrldi(dp, dp, 32); | |
| // Load constant vec registers that need to be loaded from memory | |
| __ load_const_optimized(const_ptr, (address)&const_block, tmp_reg); | |
| __ lxv(bitposLUT, BLK_OFFSETOF(bitposLUT_val), const_ptr); | |
| __ lxv(pack_rshift->to_vsr(), BLK_OFFSETOF(pack_rshift_val), const_ptr); | |
| __ lxv(pack_lshift->to_vsr(), BLK_OFFSETOF(pack_lshift_val), const_ptr); | |
| __ lxv(pack_permute, BLK_OFFSETOF(pack_permute_val), const_ptr); | |
| // Splat the constants that can use xxspltib | |
| __ xxspltib(vec_0s->to_vsr(), 0); | |
| __ xxspltib(vec_8s->to_vsr(), 8); | |
| if (PowerArchitecturePPC64 >= 10) { | |
| // Using VALID_B64 for the offsets effectively strips the upper bit | |
| // of each byte that was selected from the table. Setting the upper | |
| // bit gives us a way to distinguish between the 6-bit value of 0 | |
| // from an error code of 0, which will happen if the character is | |
| // outside the range of the lookup, or is an illegal Base64 | |
| // character, such as %. | |
| __ xxspltib(offsets->to_vsr(), VALID_B64); | |
| __ lxv(table_48_63, BLK_OFFSETOF(table_48_63_val), const_ptr); | |
| __ lxv(table_64_79, BLK_OFFSETOF(table_64_79_val), const_ptr); | |
| __ lxv(table_80_95, BLK_OFFSETOF(table_80_95_val), const_ptr); | |
| __ lxv(table_96_111, BLK_OFFSETOF(table_96_111_val), const_ptr); | |
| __ lxv(table_112_127, BLK_OFFSETOF(table_112_127_val), const_ptr); | |
| } else { | |
| __ xxspltib(vec_4s->to_vsr(), 4); | |
| __ xxspltib(vec_0xfs, 0xf); | |
| __ lxv(bitposLUT, BLK_OFFSETOF(bitposLUT_val), const_ptr); | |
| } | |
| // The rest of the constants use different values depending on the | |
| // setting of isURL | |
| __ cmpwi(CR0, isURL, 0); | |
| __ beq(CR0, not_URL); | |
| // isURL != 0 (true) | |
| if (PowerArchitecturePPC64 >= 10) { | |
| __ lxv(table_32_47, BLK_OFFSETOF(table_32_47_URL_val), const_ptr); | |
| __ lxv(table_80_95, BLK_OFFSETOF(table_80_95_URL_val), const_ptr); | |
| } else { | |
| __ lxv(offsetLUT, BLK_OFFSETOF(offsetLUT_URL_val), const_ptr); | |
| __ lxv(maskLUT, BLK_OFFSETOF(maskLUT_URL_val), const_ptr); | |
| __ xxspltib(vec_special_case_char->to_vsr(), '_'); | |
| __ xxspltib(vec_special_case_offset, (unsigned char)US); | |
| } | |
| __ b(calculate_size); | |
| // isURL = 0 (false) | |
| __ bind(not_URL); | |
| if (PowerArchitecturePPC64 >= 10) { | |
| __ lxv(table_32_47, BLK_OFFSETOF(table_32_47_val), const_ptr); | |
| __ lxv(table_80_95, BLK_OFFSETOF(table_80_95_val), const_ptr); | |
| } else { | |
| __ lxv(offsetLUT, BLK_OFFSETOF(offsetLUT_val), const_ptr); | |
| __ lxv(maskLUT, BLK_OFFSETOF(maskLUT_val), const_ptr); | |
| __ xxspltib(vec_special_case_char->to_vsr(), '/'); | |
| __ xxspltib(vec_special_case_offset, (unsigned char)SLS); | |
| } | |
| __ bind(calculate_size); | |
| // out starts at d + dp | |
| __ add(out, d, dp); | |
| // in starts at s + sp | |
| __ add(in, s, sp); | |
| __ align(32); | |
| __ bind(loop_start); | |
| __ lxv(input->to_vsr(), 0, in); // offset=0 | |
| // | |
| // Lookup | |
| // | |
| if (PowerArchitecturePPC64 >= 10) { | |
| // Use xxpermx to do a lookup of each Base64 character in the | |
| // input vector and translate it to a 6-bit value + 0x80. | |
| // Characters which are not valid Base64 characters will result | |
| // in a zero in the corresponding byte. | |
| // | |
| // Note that due to align(32) call above, the xxpermx instructions do | |
| // not require align_prefix() calls, since the final xxpermx | |
| // prefix+opcode is at byte 24. | |
| __ xxpermx(xlate_a, table_32_47, table_48_63, input->to_vsr(), 1); // offset=4 | |
| __ xxpermx(xlate_b, table_64_79, table_80_95, input->to_vsr(), 2); // offset=12 | |
| __ xxlor(xlate_b, xlate_a, xlate_b); // offset=20 | |
| __ xxpermx(xlate_a, table_96_111, table_112_127, input->to_vsr(), 3); // offset=24 | |
| __ xxlor(input->to_vsr(), xlate_a, xlate_b); | |
| // Check for non-Base64 characters by comparing each byte to zero. | |
| __ vcmpequb_(non_match, input, vec_0s); | |
| } else { | |
| // Isolate the upper 4 bits of each character by shifting it right 4 bits | |
| __ vsrb(higher_nibble, input, vec_4s); | |
| // Isolate the lower 4 bits by masking | |
| __ xxland(lower_nibble, input->to_vsr(), vec_0xfs); | |
| // Get the offset (the value to subtract from the byte) by using | |
| // a lookup table indexed by the upper 4 bits of the character | |
| __ xxperm(offsets->to_vsr(), offsetLUT, higher_nibble->to_vsr()); | |
| // Find out which elements are the special case character (isURL ? '/' : '-') | |
| __ vcmpequb(eq_special_case_char, input, vec_special_case_char); | |
| // For each character in the input which is a special case | |
| // character, replace its offset with one that is special for that | |
| // character. | |
| __ xxsel(offsets->to_vsr(), offsets->to_vsr(), vec_special_case_offset, eq_special_case_char->to_vsr()); | |
| // Use the lower_nibble to select a mask "M" from the lookup table. | |
| __ xxperm(M, maskLUT, lower_nibble); | |
| // "bit" is used to isolate which of the bits in M is relevant. | |
| __ xxperm(bit, bitposLUT, higher_nibble->to_vsr()); | |
| // Each element of non_match correspond to one each of the 16 input | |
| // characters. Those elements that become 0x00 after the xxland | |
| // instruction are invalid Base64 characters. | |
| __ xxland(non_match->to_vsr(), M, bit); | |
| // Compare each element to zero | |
| // | |
| __ vcmpequb_(non_match, non_match, vec_0s); | |
| } | |
| // vmcmpequb_ sets the EQ bit of CR6 if no elements compare equal. | |
| // Any element comparing equal to zero means there is an error in | |
| // that element. Note that the comparison result register | |
| // non_match is not referenced again. Only CR6-EQ matters. | |
| __ bne_predict_not_taken(CR6, loop_exit); | |
| // The Base64 characters had no errors, so add the offsets, which in | |
| // the case of Power10 is a constant vector of all 0x80's (see earlier | |
| // comment where the offsets register is loaded). | |
| __ vaddubm(input, input, offsets); | |
| // Pack | |
| // | |
| // In the tables below, b0, b1, .. b15 are the bytes of decoded | |
| // binary data, the first line of each of the cells (except for | |
| // the constants) uses the bit-field nomenclature from the | |
| // above-linked paper, whereas the second line is more specific | |
| // about which exact bits are present, and is constructed using the | |
| // Power ISA 3.x document style, where: | |
| // | |
| // * The specifier after the colon depicts which bits are there. | |
| // * The bit numbering is big endian style (bit 0 is the most | |
| // significant). | |
| // * || is a concatenate operator. | |
| // * Strings of 0's are a field of zeros with the shown length, and | |
| // likewise for strings of 1's. | |
| // Note that only e12..e15 are shown here because the shifting | |
| // and OR'ing pattern replicates for e8..e11, e4..7, and | |
| // e0..e3. | |
| // | |
| // +======================+=================+======================+======================+=============+ | |
| // | Vector | e12 | e13 | e14 | e15 | | |
| // | Element | | | | | | |
| // +======================+=================+======================+======================+=============+ | |
| // | after vaddubm | 00dddddd | 00cccccc | 00bbbbbb | 00aaaaaa | | |
| // | | 00||b2:2..7 | 00||b1:4..7||b2:0..1 | 00||b0:6..7||b1:0..3 | 00||b0:0..5 | | |
| // +----------------------+-----------------+----------------------+----------------------+-------------+ | |
| // | pack_lshift | | << 6 | << 4 | << 2 | | |
| // +----------------------+-----------------+----------------------+----------------------+-------------+ | |
| // | l after vslb | 00dddddd | cc000000 | bbbb0000 | aaaaaa00 | | |
| // | | 00||b2:2..7 | b2:0..1||000000 | b1:0..3||0000 | b0:0..5||00 | | |
| // +----------------------+-----------------+----------------------+----------------------+-------------+ | |
| // | l after vslo | cc000000 | bbbb0000 | aaaaaa00 | 00000000 | | |
| // | | b2:0..1||000000 | b1:0..3||0000 | b0:0..5||00 | 00000000 | | |
| // +----------------------+-----------------+----------------------+----------------------+-------------+ | |
| // | pack_rshift | | >> 2 | >> 4 | | | |
| // +----------------------+-----------------+----------------------+----------------------+-------------+ | |
| // | r after vsrb | 00dddddd | 0000cccc | 000000bb | 00aaaaaa | | |
| // | | 00||b2:2..7 | 0000||b1:4..7 | 000000||b0:6..7 | 00||b0:0..5 | | |
| // +----------------------+-----------------+----------------------+----------------------+-------------+ | |
| // | gathered after xxlor | ccdddddd | bbbbcccc | aaaaaabb | 00aaaaaa | | |
| // | | b2:0..7 | b1:0..7 | b0:0..7 | 00||b0:0..5 | | |
| // +======================+=================+======================+======================+=============+ | |
| // | |
| // Note: there is a typo in the above-linked paper that shows the result of the gathering process is: | |
| // [ddddddcc|bbbbcccc|aaaaaabb] | |
| // but should be: | |
| // [ccdddddd|bbbbcccc|aaaaaabb] | |
| // | |
| __ vslb(l, input, pack_lshift); | |
| // vslo of vec_8s shifts the vector by one octet toward lower | |
| // element numbers, discarding element 0. This means it actually | |
| // shifts to the right (not left) according to the order of the | |
| // table above. | |
| __ vslo(l, l, vec_8s); | |
| __ vsrb(r, input, pack_rshift); | |
| __ xxlor(gathered->to_vsr(), l->to_vsr(), r->to_vsr()); | |
| // Final rearrangement of bytes into their correct positions. | |
| // +==============+======+======+======+======+=====+=====+====+====+====+====+=====+=====+=====+=====+=====+=====+ | |
| // | Vector | e0 | e1 | e2 | e3 | e4 | e5 | e6 | e7 | e8 | e9 | e10 | e11 | e12 | e13 | e14 | e15 | | |
| // | Elements | | | | | | | | | | | | | | | | | | |
| // +==============+======+======+======+======+=====+=====+====+====+====+====+=====+=====+=====+=====+=====+=====+ | |
| // | after xxlor | b11 | b10 | b9 | xx | b8 | b7 | b6 | xx | b5 | b4 | b3 | xx | b2 | b1 | b0 | xx | | |
| // +--------------+------+------+------+------+-----+-----+----+----+----+----+-----+-----+-----+-----+-----+-----+ | |
| // | pack_permute | 0 | 0 | 0 | 0 | 0 | 1 | 2 | 4 | 5 | 6 | 8 | 9 | 10 | 12 | 13 | 14 | | |
| // +--------------+------+------+------+------+-----+-----+----+----+----+----+-----+-----+-----+-----+-----+-----+ | |
| // | after xxperm | b11* | b11* | b11* | b11* | b11 | b10 | b9 | b8 | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | | |
| // +==============+======+======+======+======+=====+=====+====+====+====+====+=====+=====+=====+=====+=====+=====+ | |
| // xx bytes are not used to form the final data | |
| // b0..b15 are the decoded and reassembled 8-bit bytes of data | |
| // b11 with asterisk is a "don't care", because these bytes will be | |
| // overwritten on the next iteration. | |
| __ xxperm(gathered->to_vsr(), gathered->to_vsr(), pack_permute); | |
| // We cannot use a static displacement on the store, since it's a | |
| // multiple of 12, not 16. Note that this stxv instruction actually | |
| // writes 16 bytes, even though only the first 12 are valid data. | |
| __ stxv(gathered->to_vsr(), 0, out); | |
| __ addi(out, out, 12); | |
| __ addi(in, in, 16); | |
| __ bdnz(loop_start); | |
| __ bind(loop_exit); | |
| // Return the number of out bytes produced, which is (out - (d + dp)) == out - d - dp; | |
| __ sub(R3_RET, out, d); | |
| __ sub(R3_RET, R3_RET, dp); | |
| __ blr(); | |
| __ bind(return_zero); | |
| __ li(R3_RET, 0); | |
| __ blr(); | |
| return start; | |
| } | |
| #undef UC | |
| #undef LC | |
| #undef DIG | |
| #undef PLS | |
| #undef HYP | |
| #undef SLS | |
| #undef US | |
| // This algorithm is based on the methods described in this paper: | |
| // http://0x80.pl/notesen/2016-01-12-sse-base64-encoding.html | |
| // | |
| // The details of this implementation vary from the paper due to the | |
| // difference in the ISA between SSE and AltiVec, especially in the | |
| // splitting bytes section where there is no need on Power to mask after | |
| // the shift because the shift is byte-wise rather than an entire an entire | |
| // 128-bit word. | |
| // | |
| // For the lookup part of the algorithm, different logic is used than | |
| // described in the paper because of the availability of vperm, which can | |
| // do a 64-byte table lookup in four instructions, while preserving the | |
| // branchless nature. | |
| // | |
| // Description of the ENCODE_CORE macro | |
| // | |
| // Expand first 12 x 8-bit data bytes into 16 x 6-bit bytes (upper 2 | |
| // bits of each byte are zeros) | |
| // | |
| // (Note: e7..e0 are not shown because they follow the same pattern as | |
| // e8..e15) | |
| // | |
| // In the table below, b0, b1, .. b15 are the bytes of unencoded | |
| // binary data, the first line of each of the cells (except for | |
| // the constants) uses the bit-field nomenclature from the | |
| // above-linked paper, whereas the second line is more specific | |
| // about which exact bits are present, and is constructed using the | |
| // Power ISA 3.x document style, where: | |
| // | |
| // * The specifier after the colon depicts which bits are there. | |
| // * The bit numbering is big endian style (bit 0 is the most | |
| // significant). | |
| // * || is a concatenate operator. | |
| // * Strings of 0's are a field of zeros with the shown length, and | |
| // likewise for strings of 1's. | |
| // | |
| // +==========================+=============+======================+======================+=============+=============+======================+======================+=============+ | |
| // | Vector | e8 | e9 | e10 | e11 | e12 | e13 | e14 | e15 | | |
| // | Element | | | | | | | | | | |
| // +==========================+=============+======================+======================+=============+=============+======================+======================+=============+ | |
| // | after lxv | jjjjkkkk | iiiiiijj | gghhhhhh | ffffgggg | eeeeeeff | ccdddddd | bbbbcccc | aaaaaabb | | |
| // | | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | | |
| // +--------------------------+-------------+----------------------+----------------------+-------------+-------------+----------------------+----------------------+-------------+ | |
| // | xxperm indexes | 0 | 10 | 11 | 12 | 0 | 13 | 14 | 15 | | |
| // +--------------------------+-------------+----------------------+----------------------+-------------+-------------+----------------------+----------------------+-------------+ | |
| // | (1) after xxperm | | gghhhhhh | ffffgggg | eeeeeeff | | ccdddddd | bbbbcccc | aaaaaabb | | |
| // | | (b15) | b5 | b4 | b3 | (b15) | b2 | b1 | b0 | | |
| // +--------------------------+-------------+----------------------+----------------------+-------------+-------------+----------------------+----------------------+-------------+ | |
| // | rshift_amount | 0 | 6 | 4 | 2 | 0 | 6 | 4 | 2 | | |
| // +--------------------------+-------------+----------------------+----------------------+-------------+-------------+----------------------+----------------------+-------------+ | |
| // | after vsrb | | 000000gg | 0000ffff | 00eeeeee | | 000000cc | 0000bbbb | 00aaaaaa | | |
| // | | (b15) | 000000||b5:0..1 | 0000||b4:0..3 | 00||b3:0..5 | (b15) | 000000||b2:0..1 | 0000||b1:0..3 | 00||b0:0..5 | | |
| // +--------------------------+-------------+----------------------+----------------------+-------------+-------------+----------------------+----------------------+-------------+ | |
| // | rshift_mask | 00000000 | 000000||11 | 0000||1111 | 00||111111 | 00000000 | 000000||11 | 0000||1111 | 00||111111 | | |
| // +--------------------------+-------------+----------------------+----------------------+-------------+-------------+----------------------+----------------------+-------------+ | |
| // | rshift after vand | 00000000 | 000000gg | 0000ffff | 00eeeeee | 00000000 | 000000cc | 0000bbbb | 00aaaaaa | | |
| // | | 00000000 | 000000||b5:0..1 | 0000||b4:0..3 | 00||b3:0..5 | 00000000 | 000000||b2:0..1 | 0000||b1:0..3 | 00||b0:0..5 | | |
| // +--------------------------+-------------+----------------------+----------------------+-------------+-------------+----------------------+----------------------+-------------+ | |
| // | 1 octet lshift (1) | gghhhhhh | ffffgggg | eeeeeeff | | ccdddddd | bbbbcccc | aaaaaabb | 00000000 | | |
| // | | b5 | b4 | b3 | (b15) | b2 | b1 | b0 | 00000000 | | |
| // +--------------------------+-------------+----------------------+----------------------+-------------+-------------+----------------------+----------------------+-------------+ | |
| // | lshift_amount | 0 | 2 | 4 | 0 | 0 | 2 | 4 | 0 | | |
| // +--------------------------+-------------+----------------------+----------------------+-------------+-------------+----------------------+----------------------+-------------+ | |
| // | after vslb | gghhhhhh | ffgggg00 | eeff0000 | | ccdddddd | bbcccc00 | aabb0000 | 00000000 | | |
| // | | b5 | b4:2..7||00 | b3:4..7||0000 | (b15) | b2:0..7 | b1:2..7||00 | b0:4..7||0000 | 00000000 | | |
| // +--------------------------+-------------+----------------------+----------------------+-------------+-------------+----------------------+----------------------+-------------+ | |
| // | lshift_mask | 00||111111 | 00||1111||00 | 00||11||0000 | 00000000 | 00||111111 | 00||1111||00 | 00||11||0000 | 00000000 | | |
| // +--------------------------+-------------+----------------------+----------------------+-------------+-------------+----------------------+----------------------+-------------+ | |
| // | lshift after vand | 00hhhhhh | 00gggg00 | 00ff0000 | 00000000 | 00dddddd | 00cccc00 | 00bb0000 | 00000000 | | |
| // | | 00||b5:2..7 | 00||b4:4..7||00 | 00||b3:6..7||0000 | 00000000 | 00||b2:2..7 | 00||b1:4..7||00 | 00||b0:6..7||0000 | 00000000 | | |
| // +--------------------------+-------------+----------------------+----------------------+-------------+-------------+----------------------+----------------------+-------------+ | |
| // | after vor lshift, rshift | 00hhhhhh | 00gggggg | 00ffffff | 00eeeeee | 00dddddd | 00cccccc | 00bbbbbb | 00aaaaaa | | |
| // | | 00||b5:2..7 | 00||b4:4..7||b5:0..1 | 00||b3:6..7||b4:0..3 | 00||b3:0..5 | 00||b2:2..7 | 00||b1:4..7||b2:0..1 | 00||b0:6..7||b1:0..3 | 00||b0:0..5 | | |
| // +==========================+=============+======================+======================+=============+=============+======================+======================+=============+ | |
| // | |
| // Expand the first 12 bytes into 16 bytes, leaving every 4th byte | |
| // blank for now. | |
| // __ xxperm(input->to_vsr(), input->to_vsr(), expand_permute); | |
| // | |
| // Generate two bit-shifted pieces - rshift and lshift - that will | |
| // later be OR'd together. | |
| // | |
| // First the right-shifted piece | |
| // __ vsrb(rshift, input, expand_rshift); | |
| // __ vand(rshift, rshift, expand_rshift_mask); | |
| // | |
| // Now the left-shifted piece, which is done by octet shifting | |
| // the input one byte to the left, then doing a variable shift, | |
| // followed by a mask operation. | |
| // | |
| // __ vslo(lshift, input, vec_8s); | |
| // __ vslb(lshift, lshift, expand_lshift); | |
| // __ vand(lshift, lshift, expand_lshift_mask); | |
| // | |
| // Combine the two pieces by OR'ing | |
| // __ vor(expanded, rshift, lshift); | |
| // | |
| // At this point, expanded is a vector containing a 6-bit value in each | |
| // byte. These values are used as indexes into a 64-byte lookup table that | |
| // is contained in four vector registers. The lookup operation is done | |
| // using vperm instructions with the same indexes for the lower 32 and | |
| // upper 32 bytes. To figure out which of the two looked-up bytes to use | |
| // at each location, all values in expanded are compared to 31. Using | |
| // vsel, values higher than 31 use the results from the upper 32 bytes of | |
| // the lookup operation, while values less than or equal to 31 use the | |
| // lower 32 bytes of the lookup operation. | |
| // | |
| // Note: it's tempting to use a xxpermx,xxpermx,vor sequence here on | |
| // Power10 (or later), but experiments doing so on Power10 yielded a slight | |
| // performance drop, perhaps due to the need for xxpermx instruction | |
| // prefixes. | |
| #define ENCODE_CORE \ | |
| __ xxperm(input->to_vsr(), input->to_vsr(), expand_permute); \ | |
| __ vsrb(rshift, input, expand_rshift); \ | |
| __ vand(rshift, rshift, expand_rshift_mask); \ | |
| __ vslo(lshift, input, vec_8s); \ | |
| __ vslb(lshift, lshift, expand_lshift); \ | |
| __ vand(lshift, lshift, expand_lshift_mask); \ | |
| __ vor(expanded, rshift, lshift); \ | |
| __ vperm(encoded_00_31, vec_base64_00_15, vec_base64_16_31, expanded); \ | |
| __ vperm(encoded_32_63, vec_base64_32_47, vec_base64_48_63, expanded); \ | |
| __ vcmpgtub(gt_31, expanded, vec_31s); \ | |
| __ vsel(expanded, encoded_00_31, encoded_32_63, gt_31); | |
| // Intrinsic function prototype in Base64.java: | |
| // private void encodeBlock(byte[] src, int sp, int sl, byte[] dst, int dp, boolean isURL) { | |
| address generate_base64_encodeBlock() { | |
| __ align(CodeEntryAlignment); | |
| StubGenStubId stub_id = StubGenStubId::base64_encodeBlock_id; | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ function_entry(); | |
| typedef struct { | |
| unsigned char expand_permute_val[16]; | |
| unsigned char expand_rshift_val[16]; | |
| unsigned char expand_rshift_mask_val[16]; | |
| unsigned char expand_lshift_val[16]; | |
| unsigned char expand_lshift_mask_val[16]; | |
| unsigned char base64_00_15_val[16]; | |
| unsigned char base64_16_31_val[16]; | |
| unsigned char base64_32_47_val[16]; | |
| unsigned char base64_48_63_val[16]; | |
| unsigned char base64_48_63_URL_val[16]; | |
| } constant_block; | |
| alignas(16) static const constant_block const_block = { | |
| .expand_permute_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| 0, 4, 5, 6, | |
| 0, 7, 8, 9, | |
| 0, 10, 11, 12, | |
| 0, 13, 14, 15 ) }, | |
| .expand_rshift_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| 0, 6, 4, 2, | |
| 0, 6, 4, 2, | |
| 0, 6, 4, 2, | |
| 0, 6, 4, 2 ) }, | |
| .expand_rshift_mask_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| 0b00000000, 0b00000011, 0b00001111, 0b00111111, | |
| 0b00000000, 0b00000011, 0b00001111, 0b00111111, | |
| 0b00000000, 0b00000011, 0b00001111, 0b00111111, | |
| 0b00000000, 0b00000011, 0b00001111, 0b00111111 ) }, | |
| .expand_lshift_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| 0, 2, 4, 0, | |
| 0, 2, 4, 0, | |
| 0, 2, 4, 0, | |
| 0, 2, 4, 0 ) }, | |
| .expand_lshift_mask_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| 0b00111111, 0b00111100, 0b00110000, 0b00000000, | |
| 0b00111111, 0b00111100, 0b00110000, 0b00000000, | |
| 0b00111111, 0b00111100, 0b00110000, 0b00000000, | |
| 0b00111111, 0b00111100, 0b00110000, 0b00000000 ) }, | |
| .base64_00_15_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P' ) }, | |
| .base64_16_31_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f' ) }, | |
| .base64_32_47_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v' ) }, | |
| .base64_48_63_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' ) }, | |
| .base64_48_63_URL_val = { | |
| ARRAY_TO_LXV_ORDER( | |
| 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','-','_' ) } | |
| }; | |
| // Number of bytes to process in each pass through the main loop. | |
| // 12 of the 16 bytes from each lxv are encoded to 16 Base64 bytes. | |
| const unsigned block_size = 12; | |
| // According to the ELF V2 ABI, registers r3-r12 are volatile and available for use without save/restore | |
| Register src = R3_ARG1; // source starting address of Base64 characters | |
| Register sp = R4_ARG2; // source starting position | |
| Register sl = R5_ARG3; // total source length of the Base64 characters to be processed | |
| Register dst = R6_ARG4; // destination address | |
| Register dp = R7_ARG5; // destination starting position | |
| Register isURL = R8_ARG6; // boolean, if non-zero indicates use of RFC 4648 base64url encoding | |
| // Local variables | |
| Register const_ptr = R12; // used for loading constants (reuses isURL's register) | |
| Register tmp_reg = R9; // used for speeding up load_constant() | |
| Register size = R9; // number of bytes to process (reuses tmp_reg's register) | |
| Register blocked_size = R10; // number of bytes to process a block at a time | |
| Register block_modulo = R12; // == block_size (reuse const_ptr) | |
| Register remaining = R12; // bytes remaining to process after the blocks are completed (reuse block_modulo's reg) | |
| Register in = R4; // current input (source) pointer (reuse sp's register) | |
| Register num_blocks = R11; // number of blocks to be processed by the loop | |
| Register out = R8; // current output (destination) pointer (reuse const_ptr's register) | |
| Register three = R9; // constant divisor (reuse size's register) | |
| Register bytes_to_write = R10; // number of bytes to write with the stxvl instr (reused blocked_size's register) | |
| Register tmp1 = R7; // temp register for lxvl length (reuse dp's register) | |
| Register modulo_chars = R7; // number of bytes written during the final write % 4 (reuse tmp1's register) | |
| Register pad_char = R6; // literal '=' (reuse dst's register) | |
| // Volatile VSRS are 0..13, 32..51 (VR0..VR13) | |
| // VR Constants | |
| VectorRegister vec_8s = VR0; | |
| VectorRegister vec_31s = VR1; | |
| VectorRegister vec_base64_00_15 = VR2; | |
| VectorRegister vec_base64_16_31 = VR3; | |
| VectorRegister vec_base64_32_47 = VR4; | |
| VectorRegister vec_base64_48_63 = VR5; | |
| VectorRegister expand_rshift = VR6; | |
| VectorRegister expand_rshift_mask = VR7; | |
| VectorRegister expand_lshift = VR8; | |
| VectorRegister expand_lshift_mask = VR9; | |
| // VR variables for expand | |
| VectorRegister input = VR10; | |
| VectorRegister rshift = VR11; | |
| VectorRegister lshift = VR12; | |
| VectorRegister expanded = VR13; | |
| // VR variables for lookup | |
| VectorRegister encoded_00_31 = VR10; // (reuse input) | |
| VectorRegister encoded_32_63 = VR11; // (reuse rshift) | |
| VectorRegister gt_31 = VR12; // (reuse lshift) | |
| // VSR Constants | |
| VectorSRegister expand_permute = VSR0; | |
| Label not_URL, calculate_size, calculate_blocked_size, skip_loop; | |
| Label loop_start, le_16_to_write, no_pad, one_pad_char; | |
| // The upper 32 bits of the non-pointer parameter registers are not | |
| // guaranteed to be zero, so mask off those upper bits. | |
| __ clrldi(sp, sp, 32); | |
| __ clrldi(sl, sl, 32); | |
| __ clrldi(dp, dp, 32); | |
| __ clrldi(isURL, isURL, 32); | |
| // load up the constants | |
| __ load_const_optimized(const_ptr, (address)&const_block, tmp_reg); | |
| __ lxv(expand_permute, BLK_OFFSETOF(expand_permute_val), const_ptr); | |
| __ lxv(expand_rshift->to_vsr(), BLK_OFFSETOF(expand_rshift_val), const_ptr); | |
| __ lxv(expand_rshift_mask->to_vsr(), BLK_OFFSETOF(expand_rshift_mask_val), const_ptr); | |
| __ lxv(expand_lshift->to_vsr(), BLK_OFFSETOF(expand_lshift_val), const_ptr); | |
| __ lxv(expand_lshift_mask->to_vsr(), BLK_OFFSETOF(expand_lshift_mask_val), const_ptr); | |
| __ lxv(vec_base64_00_15->to_vsr(), BLK_OFFSETOF(base64_00_15_val), const_ptr); | |
| __ lxv(vec_base64_16_31->to_vsr(), BLK_OFFSETOF(base64_16_31_val), const_ptr); | |
| __ lxv(vec_base64_32_47->to_vsr(), BLK_OFFSETOF(base64_32_47_val), const_ptr); | |
| // Splat the constants that can use xxspltib | |
| __ xxspltib(vec_8s->to_vsr(), 8); | |
| __ xxspltib(vec_31s->to_vsr(), 31); | |
| // Use a different translation lookup table depending on the | |
| // setting of isURL | |
| __ cmpdi(CR0, isURL, 0); | |
| __ beq(CR0, not_URL); | |
| __ lxv(vec_base64_48_63->to_vsr(), BLK_OFFSETOF(base64_48_63_URL_val), const_ptr); | |
| __ b(calculate_size); | |
| __ bind(not_URL); | |
| __ lxv(vec_base64_48_63->to_vsr(), BLK_OFFSETOF(base64_48_63_val), const_ptr); | |
| __ bind(calculate_size); | |
| // size = sl - sp - 4 (*) | |
| // (*) Don't process the last four bytes in the main loop because | |
| // we don't want the lxv instruction to read past the end of the src | |
| // data, in case those four bytes are on the start of an unmapped or | |
| // otherwise inaccessible page. | |
| // | |
| __ sub(size, sl, sp); | |
| __ subi(size, size, 4); | |
| __ cmpdi(CR7, size, block_size); | |
| __ bgt(CR7, calculate_blocked_size); | |
| __ mr(remaining, size); | |
| // Add the 4 back into remaining again | |
| __ addi(remaining, remaining, 4); | |
| // make "in" point to the beginning of the source data: in = src + sp | |
| __ add(in, src, sp); | |
| // out = dst + dp | |
| __ add(out, dst, dp); | |
| __ b(skip_loop); | |
| __ bind(calculate_blocked_size); | |
| __ li(block_modulo, block_size); | |
| // num_blocks = size / block_modulo | |
| __ divwu(num_blocks, size, block_modulo); | |
| // blocked_size = num_blocks * size | |
| __ mullw(blocked_size, num_blocks, block_modulo); | |
| // remaining = size - blocked_size | |
| __ sub(remaining, size, blocked_size); | |
| __ mtctr(num_blocks); | |
| // Add the 4 back in to remaining again | |
| __ addi(remaining, remaining, 4); | |
| // make "in" point to the beginning of the source data: in = src + sp | |
| __ add(in, src, sp); | |
| // out = dst + dp | |
| __ add(out, dst, dp); | |
| __ align(32); | |
| __ bind(loop_start); | |
| __ lxv(input->to_vsr(), 0, in); | |
| ENCODE_CORE | |
| __ stxv(expanded->to_vsr(), 0, out); | |
| __ addi(in, in, 12); | |
| __ addi(out, out, 16); | |
| __ bdnz(loop_start); | |
| __ bind(skip_loop); | |
| // When there are less than 16 bytes left, we need to be careful not to | |
| // read beyond the end of the src buffer, which might be in an unmapped | |
| // page. | |
| // Load the remaining bytes using lxvl. | |
| __ rldicr(tmp1, remaining, 56, 7); | |
| __ lxvl(input->to_vsr(), in, tmp1); | |
| ENCODE_CORE | |
| // bytes_to_write = ((remaining * 4) + 2) / 3 | |
| __ li(three, 3); | |
| __ rlwinm(bytes_to_write, remaining, 2, 0, 29); // remaining * 4 | |
| __ addi(bytes_to_write, bytes_to_write, 2); | |
| __ divwu(bytes_to_write, bytes_to_write, three); | |
| __ cmpwi(CR7, bytes_to_write, 16); | |
| __ ble_predict_taken(CR7, le_16_to_write); | |
| __ stxv(expanded->to_vsr(), 0, out); | |
| // We've processed 12 of the 13-15 data bytes, so advance the pointers, | |
| // and do one final pass for the remaining 1-3 bytes. | |
| __ addi(in, in, 12); | |
| __ addi(out, out, 16); | |
| __ subi(remaining, remaining, 12); | |
| __ subi(bytes_to_write, bytes_to_write, 16); | |
| __ rldicr(tmp1, bytes_to_write, 56, 7); | |
| __ lxvl(input->to_vsr(), in, tmp1); | |
| ENCODE_CORE | |
| __ bind(le_16_to_write); | |
| // shift bytes_to_write into the upper 8 bits of t1 for use by stxvl | |
| __ rldicr(tmp1, bytes_to_write, 56, 7); | |
| __ stxvl(expanded->to_vsr(), out, tmp1); | |
| __ add(out, out, bytes_to_write); | |
| __ li(pad_char, '='); | |
| __ rlwinm_(modulo_chars, bytes_to_write, 0, 30, 31); // bytes_to_write % 4, set CR0 | |
| // Examples: | |
| // remaining bytes_to_write modulo_chars num pad chars | |
| // 0 0 0 0 | |
| // 1 2 2 2 | |
| // 2 3 3 1 | |
| // 3 4 0 0 | |
| // 4 6 2 2 | |
| // 5 7 3 1 | |
| // ... | |
| // 12 16 0 0 | |
| // 13 18 2 2 | |
| // 14 19 3 1 | |
| // 15 20 0 0 | |
| __ beq(CR0, no_pad); | |
| __ cmpwi(CR7, modulo_chars, 3); | |
| __ beq(CR7, one_pad_char); | |
| // two pad chars | |
| __ stb(pad_char, out); | |
| __ addi(out, out, 1); | |
| __ bind(one_pad_char); | |
| __ stb(pad_char, out); | |
| __ bind(no_pad); | |
| __ blr(); | |
| return start; | |
| } | |
| #endif // VM_LITTLE_ENDIAN | |
| void generate_lookup_secondary_supers_table_stub() { | |
| StubGenStubId stub_id = StubGenStubId::lookup_secondary_supers_table_id; | |
| StubCodeMark mark(this, stub_id); | |
| const Register | |
| r_super_klass = R4_ARG2, | |
| r_array_base = R3_ARG1, | |
| r_array_length = R7_ARG5, | |
| r_array_index = R6_ARG4, | |
| r_sub_klass = R5_ARG3, | |
| r_bitmap = R11_scratch1, | |
| result = R8_ARG6; | |
| for (int slot = 0; slot < Klass::SECONDARY_SUPERS_TABLE_SIZE; slot++) { | |
| StubRoutines::_lookup_secondary_supers_table_stubs[slot] = __ pc(); | |
| __ lookup_secondary_supers_table_const(r_sub_klass, r_super_klass, | |
| r_array_base, r_array_length, r_array_index, | |
| r_bitmap, result, slot); | |
| __ blr(); | |
| } | |
| } | |
| // Slow path implementation for UseSecondarySupersTable. | |
| address generate_lookup_secondary_supers_table_slow_path_stub() { | |
| StubGenStubId stub_id = StubGenStubId::lookup_secondary_supers_table_slow_path_id; | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ pc(); | |
| const Register | |
| r_super_klass = R4_ARG2, | |
| r_array_base = R3_ARG1, | |
| temp1 = R7_ARG5, | |
| r_array_index = R6_ARG4, | |
| r_bitmap = R11_scratch1, | |
| result = R8_ARG6; | |
| __ lookup_secondary_supers_table_slow_path(r_super_klass, r_array_base, r_array_index, r_bitmap, result, temp1); | |
| __ blr(); | |
| return start; | |
| } | |
| address generate_cont_thaw(StubGenStubId stub_id) { | |
| if (!Continuations::enabled()) return nullptr; | |
| Continuation::thaw_kind kind; | |
| bool return_barrier; | |
| bool return_barrier_exception; | |
| switch (stub_id) { | |
| case cont_thaw_id: | |
| kind = Continuation::thaw_top; | |
| return_barrier = false; | |
| return_barrier_exception = false; | |
| break; | |
| case cont_returnBarrier_id: | |
| kind = Continuation::thaw_return_barrier; | |
| return_barrier = true; | |
| return_barrier_exception = false; | |
| break; | |
| case cont_returnBarrierExc_id: | |
| kind = Continuation::thaw_return_barrier_exception; | |
| return_barrier = true; | |
| return_barrier_exception = true; | |
| break; | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| StubCodeMark mark(this, stub_id); | |
| Register tmp1 = R10_ARG8; | |
| Register tmp2 = R9_ARG7; | |
| Register tmp3 = R8_ARG6; | |
| Register nvtmp = R15_esp; // nonvolatile tmp register | |
| FloatRegister nvftmp = F20; // nonvolatile fp tmp register | |
| address start = __ pc(); | |
| if (kind == Continuation::thaw_top) { | |
| __ clobber_nonvolatile_registers(); // Except R16_thread and R29_TOC | |
| } | |
| if (return_barrier) { | |
| __ mr(nvtmp, R3_RET); __ fmr(nvftmp, F1_RET); // preserve possible return value from a method returning to the return barrier | |
| DEBUG_ONLY(__ ld_ptr(tmp1, _abi0(callers_sp), R1_SP);) | |
| __ ld_ptr(R1_SP, JavaThread::cont_entry_offset(), R16_thread); | |
| #ifdef ASSERT | |
| __ ld_ptr(tmp2, _abi0(callers_sp), R1_SP); | |
| __ cmpd(CR0, tmp1, tmp2); | |
| __ asm_assert_eq(FILE_AND_LINE ": callers sp is corrupt"); | |
| #endif | |
| } | |
| #ifdef ASSERT | |
| __ ld_ptr(tmp1, JavaThread::cont_entry_offset(), R16_thread); | |
| __ cmpd(CR0, R1_SP, tmp1); | |
| __ asm_assert_eq(FILE_AND_LINE ": incorrect R1_SP"); | |
| #endif | |
| __ li(R4_ARG2, return_barrier ? 1 : 0); | |
| __ call_VM_leaf(CAST_FROM_FN_PTR(address, Continuation::prepare_thaw), R16_thread, R4_ARG2); | |
| #ifdef ASSERT | |
| DEBUG_ONLY(__ ld_ptr(tmp1, JavaThread::cont_entry_offset(), R16_thread)); | |
| DEBUG_ONLY(__ cmpd(CR0, R1_SP, tmp1)); | |
| __ asm_assert_eq(FILE_AND_LINE ": incorrect R1_SP"); | |
| #endif | |
| // R3_RET contains the size of the frames to thaw, 0 if overflow or no more frames | |
| Label thaw_success; | |
| __ cmpdi(CR0, R3_RET, 0); | |
| __ bne(CR0, thaw_success); | |
| __ load_const_optimized(tmp1, (SharedRuntime::throw_StackOverflowError_entry()), R0); | |
| __ mtctr(tmp1); __ bctr(); | |
| __ bind(thaw_success); | |
| __ addi(R3_RET, R3_RET, frame::native_abi_reg_args_size); // Large abi required for C++ calls. | |
| __ neg(R3_RET, R3_RET); | |
| // align down resulting in a smaller negative offset | |
| __ clrrdi(R3_RET, R3_RET, exact_log2(frame::alignment_in_bytes)); | |
| DEBUG_ONLY(__ mr(tmp1, R1_SP);) | |
| __ resize_frame(R3_RET, tmp2); // make room for the thawed frames | |
| __ li(R4_ARG2, kind); | |
| __ call_VM_leaf(Continuation::thaw_entry(), R16_thread, R4_ARG2); | |
| __ mr(R1_SP, R3_RET); // R3_RET contains the SP of the thawed top frame | |
| if (return_barrier) { | |
| // we're now in the caller of the frame that returned to the barrier | |
| __ mr(R3_RET, nvtmp); __ fmr(F1_RET, nvftmp); // restore return value (no safepoint in the call to thaw, so even an oop return value should be OK) | |
| } else { | |
| // we're now on the yield frame (which is in an address above us b/c rsp has been pushed down) | |
| __ li(R3_RET, 0); // return 0 (success) from doYield | |
| } | |
| if (return_barrier_exception) { | |
| Register ex_pc = R17_tos; // nonvolatile register | |
| __ ld(ex_pc, _abi0(lr), R1_SP); // LR | |
| __ mr(nvtmp, R3_RET); // save return value containing the exception oop | |
| // The thawed top frame has got a frame::java_abi. This is not sufficient for the runtime call. | |
| __ push_frame_reg_args(0, tmp1); | |
| __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), R16_thread, ex_pc); | |
| __ mtlr(R3_RET); // the exception handler | |
| __ pop_frame(); | |
| // See OptoRuntime::generate_exception_blob for register arguments | |
| __ mr(R3_ARG1, nvtmp); // exception oop | |
| __ mr(R4_ARG2, ex_pc); // exception pc | |
| } else { | |
| // We're "returning" into the topmost thawed frame; see Thaw::push_return_frame | |
| __ ld(R0, _abi0(lr), R1_SP); // LR | |
| __ mtlr(R0); | |
| } | |
| __ blr(); | |
| return start; | |
| } | |
| address generate_cont_thaw() { | |
| return generate_cont_thaw(StubGenStubId::cont_thaw_id); | |
| } | |
| // TODO: will probably need multiple return barriers depending on return type | |
| address generate_cont_returnBarrier() { | |
| return generate_cont_thaw(StubGenStubId::cont_returnBarrier_id); | |
| } | |
| address generate_cont_returnBarrier_exception() { | |
| return generate_cont_thaw(StubGenStubId::cont_returnBarrierExc_id); | |
| } | |
| address generate_cont_preempt_stub() { | |
| if (!Continuations::enabled()) return nullptr; | |
| StubGenStubId stub_id = StubGenStubId::cont_preempt_id; | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ pc(); | |
| __ clobber_nonvolatile_registers(); // Except R16_thread and R29_TOC | |
| __ reset_last_Java_frame(false /*check_last_java_sp*/); | |
| // Set sp to enterSpecial frame, i.e. remove all frames copied into the heap. | |
| __ ld_ptr(R1_SP, JavaThread::cont_entry_offset(), R16_thread); | |
| Label preemption_cancelled; | |
| __ lbz(R11_scratch1, in_bytes(JavaThread::preemption_cancelled_offset()), R16_thread); | |
| __ cmpwi(CR0, R11_scratch1, 0); | |
| __ bne(CR0, preemption_cancelled); | |
| // Remove enterSpecial frame from the stack and return to Continuation.run() to unmount. | |
| SharedRuntime::continuation_enter_cleanup(_masm); | |
| __ pop_frame(); | |
| __ restore_LR(R11_scratch1); | |
| __ blr(); | |
| // We acquired the monitor after freezing the frames so call thaw to continue execution. | |
| __ bind(preemption_cancelled); | |
| __ li(R11_scratch1, 0); // false | |
| __ stb(R11_scratch1, in_bytes(JavaThread::preemption_cancelled_offset()), R16_thread); | |
| int simm16_offs = __ load_const_optimized(R11_scratch1, ContinuationEntry::thaw_call_pc_address(), R0, true); | |
| __ ld(R11_scratch1, simm16_offs, R11_scratch1); | |
| __ mtctr(R11_scratch1); | |
| __ bctr(); | |
| return start; | |
| } | |
| // exception handler for upcall stubs | |
| address generate_upcall_stub_exception_handler() { | |
| StubGenStubId stub_id = StubGenStubId::upcall_stub_exception_handler_id; | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ pc(); | |
| // Native caller has no idea how to handle exceptions, | |
| // so we just crash here. Up to callee to catch exceptions. | |
| __ verify_oop(R3_ARG1); | |
| __ load_const_optimized(R12_scratch2, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::handle_uncaught_exception), R0); | |
| __ call_c(R12_scratch2); | |
| __ should_not_reach_here(); | |
| return start; | |
| } | |
| // load Method* target of MethodHandle | |
| // R3_ARG1 = jobject receiver | |
| // R19_method = result Method* | |
| address generate_upcall_stub_load_target() { | |
| StubGenStubId stub_id = StubGenStubId::upcall_stub_load_target_id; | |
| StubCodeMark mark(this, stub_id); | |
| address start = __ pc(); | |
| __ resolve_global_jobject(R3_ARG1, R22_tmp2, R23_tmp3, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS); | |
| // Load target method from receiver | |
| __ load_heap_oop(R19_method, java_lang_invoke_MethodHandle::form_offset(), R3_ARG1, | |
| R22_tmp2, R23_tmp3, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS, IS_NOT_NULL); | |
| __ load_heap_oop(R19_method, java_lang_invoke_LambdaForm::vmentry_offset(), R19_method, | |
| R22_tmp2, R23_tmp3, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS, IS_NOT_NULL); | |
| __ load_heap_oop(R19_method, java_lang_invoke_MemberName::method_offset(), R19_method, | |
| R22_tmp2, R23_tmp3, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS, IS_NOT_NULL); | |
| __ ld(R19_method, java_lang_invoke_ResolvedMethodName::vmtarget_offset(), R19_method); | |
| __ std(R19_method, in_bytes(JavaThread::callee_target_offset()), R16_thread); // just in case callee is deoptimized | |
| __ blr(); | |
| return start; | |
| } | |
| // Initialization | |
| void generate_initial_stubs() { | |
| // Generates all stubs and initializes the entry points | |
| // Entry points that exist in all platforms. | |
| // Note: This is code that could be shared among different platforms - however the | |
| // benefit seems to be smaller than the disadvantage of having a | |
| // much more complicated generator structure. See also comment in | |
| // stubRoutines.hpp. | |
| StubRoutines::_forward_exception_entry = generate_forward_exception(); | |
| StubRoutines::_call_stub_entry = generate_call_stub(StubRoutines::_call_stub_return_address); | |
| StubRoutines::_catch_exception_entry = generate_catch_exception(); | |
| if (UnsafeMemoryAccess::_table == nullptr) { | |
| UnsafeMemoryAccess::create_table(8 + 4); // 8 for copyMemory; 4 for setMemory | |
| } | |
| // CRC32 Intrinsics. | |
| if (UseCRC32Intrinsics) { | |
| StubRoutines::_crc_table_adr = StubRoutines::ppc::generate_crc_constants(REVERSE_CRC32_POLY); | |
| StubRoutines::_updateBytesCRC32 = generate_CRC32_updateBytes(StubGenStubId::updateBytesCRC32_id); | |
| } | |
| // CRC32C Intrinsics. | |
| if (UseCRC32CIntrinsics) { | |
| StubRoutines::_crc32c_table_addr = StubRoutines::ppc::generate_crc_constants(REVERSE_CRC32C_POLY); | |
| StubRoutines::_updateBytesCRC32C = generate_CRC32_updateBytes(StubGenStubId::updateBytesCRC32C_id); | |
| } | |
| if (VM_Version::supports_float16()) { | |
| // For results consistency both intrinsics should be enabled. | |
| StubRoutines::_hf2f = generate_float16ToFloat(); | |
| StubRoutines::_f2hf = generate_floatToFloat16(); | |
| } | |
| } | |
| void generate_continuation_stubs() { | |
| // Continuation stubs: | |
| StubRoutines::_cont_thaw = generate_cont_thaw(); | |
| StubRoutines::_cont_returnBarrier = generate_cont_returnBarrier(); | |
| StubRoutines::_cont_returnBarrierExc = generate_cont_returnBarrier_exception(); | |
| StubRoutines::_cont_preempt_stub = generate_cont_preempt_stub(); | |
| } | |
| void generate_final_stubs() { | |
| // Generates all stubs and initializes the entry points | |
| // support for verify_oop (must happen after universe_init) | |
| StubRoutines::_verify_oop_subroutine_entry = generate_verify_oop(); | |
| // nmethod entry barriers for concurrent class unloading | |
| StubRoutines::_method_entry_barrier = generate_method_entry_barrier(); | |
| // arraycopy stubs used by compilers | |
| generate_arraycopy_stubs(); | |
| #ifdef COMPILER2 | |
| if (UseSecondarySupersTable) { | |
| StubRoutines::_lookup_secondary_supers_table_slow_path_stub = generate_lookup_secondary_supers_table_slow_path_stub(); | |
| if (!InlineSecondarySupersTest) { | |
| generate_lookup_secondary_supers_table_stub(); | |
| } | |
| } | |
| #endif // COMPILER2 | |
| StubRoutines::_upcall_stub_exception_handler = generate_upcall_stub_exception_handler(); | |
| StubRoutines::_upcall_stub_load_target = generate_upcall_stub_load_target(); | |
| } | |
| void generate_compiler_stubs() { | |
| #if COMPILER2_OR_JVMCI | |
| #ifdef COMPILER2 | |
| if (UseMultiplyToLenIntrinsic) { | |
| StubRoutines::_multiplyToLen = generate_multiplyToLen(); | |
| } | |
| if (UseSquareToLenIntrinsic) { | |
| StubRoutines::_squareToLen = generate_squareToLen(); | |
| } | |
| if (UseMulAddIntrinsic) { | |
| StubRoutines::_mulAdd = generate_mulAdd(); | |
| } | |
| if (UseMontgomeryMultiplyIntrinsic) { | |
| StubRoutines::_montgomeryMultiply | |
| = CAST_FROM_FN_PTR(address, SharedRuntime::montgomery_multiply); | |
| } | |
| if (UseMontgomerySquareIntrinsic) { | |
| StubRoutines::_montgomerySquare | |
| = CAST_FROM_FN_PTR(address, SharedRuntime::montgomery_square); | |
| } | |
| #endif | |
| // data cache line writeback | |
| if (VM_Version::supports_data_cache_line_flush()) { | |
| StubRoutines::_data_cache_writeback = generate_data_cache_writeback(); | |
| StubRoutines::_data_cache_writeback_sync = generate_data_cache_writeback_sync(); | |
| } | |
| if (UseGHASHIntrinsics) { | |
| StubRoutines::_ghash_processBlocks = generate_ghash_processBlocks(); | |
| } | |
| if (UseAESIntrinsics) { | |
| StubRoutines::_aescrypt_encryptBlock = generate_aescrypt_encryptBlock(); | |
| StubRoutines::_aescrypt_decryptBlock = generate_aescrypt_decryptBlock(); | |
| } | |
| if (UseSHA256Intrinsics) { | |
| StubRoutines::_sha256_implCompress = generate_sha256_implCompress(StubGenStubId::sha256_implCompress_id); | |
| StubRoutines::_sha256_implCompressMB = generate_sha256_implCompress(StubGenStubId::sha256_implCompressMB_id); | |
| } | |
| if (UseSHA512Intrinsics) { | |
| StubRoutines::_sha512_implCompress = generate_sha512_implCompress(StubGenStubId::sha512_implCompress_id); | |
| StubRoutines::_sha512_implCompressMB = generate_sha512_implCompress(StubGenStubId::sha512_implCompressMB_id); | |
| } | |
| #ifdef VM_LITTLE_ENDIAN | |
| // Currently supported on PPC64LE only | |
| if (UseBASE64Intrinsics) { | |
| StubRoutines::_base64_decodeBlock = generate_base64_decodeBlock(); | |
| StubRoutines::_base64_encodeBlock = generate_base64_encodeBlock(); | |
| } | |
| #endif | |
| #endif // COMPILER2_OR_JVMCI | |
| } | |
| public: | |
| StubGenerator(CodeBuffer* code, StubGenBlobId blob_id) : StubCodeGenerator(code, blob_id) { | |
| switch(blob_id) { | |
| case initial_id: | |
| generate_initial_stubs(); | |
| break; | |
| case continuation_id: | |
| generate_continuation_stubs(); | |
| break; | |
| case compiler_id: | |
| generate_compiler_stubs(); | |
| break; | |
| case final_id: | |
| generate_final_stubs(); | |
| break; | |
| default: | |
| fatal("unexpected blob id: %d", blob_id); | |
| break; | |
| }; | |
| } | |
| }; | |
| void StubGenerator_generate(CodeBuffer* code, StubGenBlobId blob_id) { | |
| StubGenerator g(code, blob_id); | |
| } | |