/* * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2022 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. *
*/
// Move 32-bit register if destination and source are different. void MacroAssembler::lr_if_needed(Register rd, Register rs) { if (rs != rd) { z_lr(rd, rs); }
}
// Move register if destination and source are different. void MacroAssembler::lgr_if_needed(Register rd, Register rs) { if (rs != rd) { z_lgr(rd, rs); }
}
// Zero-extend 32-bit register into 64-bit register if destination and source are different. void MacroAssembler::llgfr_if_needed(Register rd, Register rs) { if (rs != rd) { z_llgfr(rd, rs); }
}
// Move float register if destination and source are different. void MacroAssembler::ldr_if_needed(FloatRegister rd, FloatRegister rs) { if (rs != rd) { z_ldr(rd, rs); }
}
// Move integer register if destination and source are different. // It is assumed that shorter-than-int types are already // appropriately sign-extended. void MacroAssembler::move_reg_if_needed(Register dst, BasicType dst_type, Register src,
BasicType src_type) {
assert((dst_type != T_FLOAT) && (dst_type != T_DOUBLE), "use move_freg for float types");
assert((src_type != T_FLOAT) && (src_type != T_DOUBLE), "use move_freg for float types");
if (dst_type == src_type) {
lgr_if_needed(dst, src); // Just move all 64 bits. return;
}
switch (dst_type) { // Do not support these types for now. // case T_BOOLEAN: case T_BYTE: // signed byte switch (src_type) { case T_INT:
z_lgbr(dst, src); break; default:
ShouldNotReachHere();
} return;
case T_CHAR: case T_SHORT: switch (src_type) { case T_INT: if (dst_type == T_CHAR) {
z_llghr(dst, src);
} else {
z_lghr(dst, src);
} break; default:
ShouldNotReachHere();
} return;
case T_INT: switch (src_type) { case T_BOOLEAN: case T_BYTE: case T_CHAR: case T_SHORT: case T_INT: case T_LONG: case T_OBJECT: case T_ARRAY: case T_VOID: case T_ADDRESS:
lr_if_needed(dst, src); // llgfr_if_needed(dst, src); // zero-extend (in case we need to find a bug). return;
default:
assert(false, "non-integer src type"); return;
} case T_LONG: switch (src_type) { case T_BOOLEAN: case T_BYTE: case T_CHAR: case T_SHORT: case T_INT:
z_lgfr(dst, src); // sign extension return;
case T_LONG: case T_OBJECT: case T_ARRAY: case T_VOID: case T_ADDRESS:
lgr_if_needed(dst, src); return;
default:
assert(false, "non-integer src type"); return;
} return; case T_OBJECT: case T_ARRAY: case T_VOID: case T_ADDRESS: switch (src_type) { // These types don't make sense to be converted to pointers: // case T_BOOLEAN: // case T_BYTE: // case T_CHAR: // case T_SHORT:
case T_INT:
z_llgfr(dst, src); // zero extension return;
case T_LONG: case T_OBJECT: case T_ARRAY: case T_VOID: case T_ADDRESS:
lgr_if_needed(dst, src); return;
void MacroAssembler::and_imm(Register r, long mask, Register tmp /* = Z_R0 */, bool wide /* = false */) {
assert(wide || Immediate::is_simm32(mask), "mask value too large");
if (!wide) {
z_nilf(r, mask); return;
}
assert(r != tmp, " need a different temporary register !");
load_const_optimized(tmp, mask);
z_ngr(r, tmp);
}
// Calculate the 1's complement. // Note: The condition code is neither preserved nor correctly set by this code!!! // Note: (wide == false) does not protect the high order half of the target register // from alteration. It only serves as optimization hint for 32-bit results. void MacroAssembler::not_(Register r1, Register r2, bool wide) {
if ((r2 == noreg) || (r2 == r1)) { // Calc 1's complement in place.
z_xilf(r1, -1); if (wide) {
z_xihf(r1, -1);
}
} else { // Distinct src and dst registers.
load_const_optimized(r1, -1);
z_xgr(r1, r2);
}
}
unsignedlong MacroAssembler::create_mask(int lBitPos, int rBitPos) {
assert(lBitPos >= 0, "zero is leftmost bit position");
assert(rBitPos <= 63, "63 is rightmost bit position");
assert(lBitPos <= rBitPos, "inverted selection interval"); return (lBitPos == 0 ? (unsignedlong)(-1L) : ((1UL<<(63-lBitPos+1))-1)) & (~((1UL<<(63-rBitPos))-1));
}
// Helper function for the "Rotate_then_<logicalOP>" emitters. // Rotate src, then mask register contents such that only bits in range survive. // For oneBits == false, all bits not in range are set to 0. Useful for deleting all bits outside range. // For oneBits == true, all bits not in range are set to 1. Useful for preserving all bits outside range. // The caller must ensure that the selected range only contains bits with defined value. void MacroAssembler::rotate_then_mask(Register dst, Register src, int lBitPos, int rBitPos, int nRotate, bool src32bit, bool dst32bit, bool oneBits) {
assert(!(dst32bit && lBitPos < 32), "selection interval out of range for int destination"); bool sll4rll = (nRotate >= 0) && (nRotate <= (63-rBitPos)); // Substitute SLL(G) for RLL(G). bool srl4rll = (nRotate < 0) && (-nRotate <= lBitPos); // Substitute SRL(G) for RLL(G). // Pre-determine which parts of dst will be zero after shift/rotate. bool llZero = sll4rll && (nRotate >= 16); bool lhZero = (sll4rll && (nRotate >= 32)) || (srl4rll && (nRotate <= -48)); bool lfZero = llZero && lhZero; bool hlZero = (sll4rll && (nRotate >= 48)) || (srl4rll && (nRotate <= -32)); bool hhZero = (srl4rll && (nRotate <= -16)); bool hfZero = hlZero && hhZero;
// rotate then mask src operand. // if oneBits == true, all bits outside selected range are 1s. // if oneBits == false, all bits outside selected range are 0s. if (src32bit) { // There might be garbage in the upper 32 bits which will get masked away. if (dst32bit) {
z_rll(dst, src, nRotate); // Copy and rotate, upper half of reg remains undisturbed.
} else { if (sll4rll) { z_sllg(dst, src, nRotate); } elseif (srl4rll) { z_srlg(dst, src, -nRotate); } else { z_rllg(dst, src, nRotate); }
}
} else { if (sll4rll) { z_sllg(dst, src, nRotate); } elseif (srl4rll) { z_srlg(dst, src, -nRotate); } else { z_rllg(dst, src, nRotate); }
}
unsignedlong range_mask = create_mask(lBitPos, rBitPos); unsignedint range_mask_h = (unsignedint)(range_mask >> 32); unsignedint range_mask_l = (unsignedint)range_mask; unsignedshort range_mask_hh = (unsignedshort)(range_mask >> 48); unsignedshort range_mask_hl = (unsignedshort)(range_mask >> 32); unsignedshort range_mask_lh = (unsignedshort)(range_mask >> 16); unsignedshort range_mask_ll = (unsignedshort)range_mask; // Works for z9 and newer H/W. if (oneBits) { if ((~range_mask_l) != 0) { z_oilf(dst, ~range_mask_l); } // All bits outside range become 1s. if (((~range_mask_h) != 0) && !dst32bit) { z_oihf(dst, ~range_mask_h); }
} else { // All bits outside range become 0s if (((~range_mask_l) != 0) && !lfZero) {
z_nilf(dst, range_mask_l);
} if (((~range_mask_h) != 0) && !dst32bit && !hfZero) {
z_nihf(dst, range_mask_h);
}
}
}
// Rotate src, then insert selected range from rotated src into dst. // Clear dst before, if requested. void MacroAssembler::rotate_then_insert(Register dst, Register src, int lBitPos, int rBitPos, int nRotate, bool clear_dst) { // This version does not depend on src being zero-extended int2long.
nRotate &= 0x003f; // For risbg, pretend it's an unsigned value.
z_risbg(dst, src, lBitPos, rBitPos, nRotate, clear_dst); // Rotate, then insert selected, clear the rest.
}
// Rotate src, then and selected range from rotated src into dst. // Set condition code only if so requested. Otherwise it is unpredictable. // See performance note in macroAssembler_s390.hpp for important information. void MacroAssembler::rotate_then_and(Register dst, Register src, int lBitPos, int rBitPos, int nRotate, bool test_only) {
guarantee(!test_only, "Emitter not fit for test_only instruction variant."); // This version does not depend on src being zero-extended int2long.
nRotate &= 0x003f; // For risbg, pretend it's an unsigned value.
z_rxsbg(dst, src, lBitPos, rBitPos, nRotate, test_only); // Rotate, then xor selected.
}
// Rotate src, then or selected range from rotated src into dst. // Set condition code only if so requested. Otherwise it is unpredictable. // See performance note in macroAssembler_s390.hpp for important information. void MacroAssembler::rotate_then_or(Register dst, Register src, int lBitPos, int rBitPos, int nRotate, bool test_only) {
guarantee(!test_only, "Emitter not fit for test_only instruction variant."); // This version does not depend on src being zero-extended int2long.
nRotate &= 0x003f; // For risbg, pretend it's an unsigned value.
z_rosbg(dst, src, lBitPos, rBitPos, nRotate, test_only); // Rotate, then xor selected.
}
// Rotate src, then xor selected range from rotated src into dst. // Set condition code only if so requested. Otherwise it is unpredictable. // See performance note in macroAssembler_s390.hpp for important information. void MacroAssembler::rotate_then_xor(Register dst, Register src, int lBitPos, int rBitPos, int nRotate, bool test_only) {
guarantee(!test_only, "Emitter not fit for test_only instruction variant."); // This version does not depend on src being zero-extended int2long.
nRotate &= 0x003f; // For risbg, pretend it's an unsigned value.
z_rxsbg(dst, src, lBitPos, rBitPos, nRotate, test_only); // Rotate, then xor selected.
}
void MacroAssembler::add64(Register r1, RegisterOrConstant inc) { if (inc.is_register()) {
z_agr(r1, inc.as_register());
} else { // constant
intptr_t imm = inc.as_constant();
add2reg(r1, imm);
}
} // Helper function to multiply the 64bit contents of a register by a 16bit constant. // The optimization tries to avoid the mghi instruction, since it uses the FPU for // calculation and is thus rather slow. // // There is no handling for special cases, e.g. cval==0 or cval==1. // // Returns len of generated code block. unsignedint MacroAssembler::mul_reg64_const16(Register rval, Register work, int cval) { int block_start = offset();
int bit1 = cval & -cval; if (bit1 == cval) {
z_sllg(rval, rval, exact_log2(bit1)); if (sign_flip) { z_lcgr(rval, rval); }
} else { int bit2 = (cval-bit1) & -(cval-bit1); if ((bit1+bit2) == cval) {
z_sllg(work, rval, exact_log2(bit1));
z_sllg(rval, rval, exact_log2(bit2));
z_agr(rval, work); if (sign_flip) { z_lcgr(rval, rval); }
} else { if (sign_flip) { z_mghi(rval, -cval); } else { z_mghi(rval, cval); }
}
}
BLOCK_COMMENT("} Reg64*Con16");
int block_end = offset(); return block_end - block_start;
}
// Generic operation r1 := r2 + imm. // // Should produce the best code for each supported CPU version. // r2 == noreg yields r1 := r1 + imm // imm == 0 emits either no instruction or r1 := r2 ! // NOTES: 1) Don't use this function where fixed sized // instruction sequences are required!!! // 2) Don't use this function if condition code // setting is required! // 3) Despite being declared as int64_t, the parameter imm // must be a simm_32 value (= signed 32-bit integer). void MacroAssembler::add2reg(Register r1, int64_t imm, Register r2) {
assert(Immediate::is_simm32(imm), "probably an implicit conversion went wrong");
if (r2 == noreg) { r2 = r1; }
// Handle special case imm == 0. if (imm == 0) {
lgr_if_needed(r1, r2); // Nothing else to do. return;
}
// Can we encode imm in 16 bits signed? if (Immediate::is_simm16(imm)) { if (r1 == r2) {
z_aghi(r1, imm); return;
} if (distinctOpnds) {
z_aghik(r1, r2, imm); return;
}
z_lgr(r1, r2);
z_aghi(r1, imm); return;
}
} else { // Can we encode imm in 12 bits unsigned? if (Displacement::is_shortDisp(imm)) {
z_la(r1, imm, r2); return;
} // Can we encode imm in 20 bits signed? if (Displacement::is_validDisp(imm)) { // Always use LAY instruction, so we don't need the tmp register.
z_lay(r1, imm, r2); return;
}
}
// Can handle it (all possible values) with long immediates.
lgr_if_needed(r1, r2);
z_agfi(r1, imm);
}
// Generic operation r := b + x + d // // Addition of several operands with address generation semantics - sort of: // - no restriction on the registers. Any register will do for any operand. // - x == noreg: operand will be disregarded. // - b == noreg: will use (contents of) result reg as operand (r := r + d). // - x == Z_R0: just disregard // - b == Z_R0: use as operand. This is not address generation semantics!!! // // The same restrictions as on add2reg() are valid!!! void MacroAssembler::add2reg_with_index(Register r, int64_t d, Register x, Register b) {
assert(Immediate::is_simm32(d), "probably an implicit conversion went wrong");
if (x == noreg) { x = Z_R0; } if (b == noreg) { b = r; }
// Handle special case x == R0. if (x == Z_R0) { // Can simply add the immediate value to the base register.
add2reg(r, d, b); return;
}
if (!PreferLAoverADD || (b == Z_R0)) { bool distinctOpnds = VM_Version::has_DistinctOpnds(); // Handle special case d == 0. if (d == 0) { if (b == x) { z_sllg(r, b, 1); return; } if (r == x) { z_agr(r, b); return; } if (r == b) { z_agr(r, x); return; } if (distinctOpnds) { z_agrk(r, x, b); return; }
z_lgr(r, b);
z_agr(r, x);
} else { if (x == b) { z_sllg(r, x, 1); } elseif (r == x) { z_agr(r, b); } elseif (r == b) { z_agr(r, x); } elseif (distinctOpnds) { z_agrk(r, x, b); } else {
z_lgr(r, b);
z_agr(r, x);
}
add2reg(r, d);
}
} else { // Can we encode imm in 12 bits unsigned? if (Displacement::is_shortDisp(d)) {
z_la(r, d, x, b); return;
} // Can we encode imm in 20 bits signed? if (Displacement::is_validDisp(d)) {
z_lay(r, d, x, b); return;
}
z_la(r, 0, x, b);
add2reg(r, d);
}
}
// Generic emitter (32bit) for direct memory increment. // For optimal code, do not specify Z_R0 as temp register. void MacroAssembler::add2mem_32(const Address &a, int64_t imm, Register tmp) { if (VM_Version::has_MemWithImmALUOps() && Immediate::is_simm8(imm)) {
z_asi(a, imm);
} else {
z_lgf(tmp, a);
add2reg(tmp, imm);
z_st(tmp, a);
}
}
void MacroAssembler::store_sized_value(Register src, Address dst, size_t size_in_bytes) { switch (size_in_bytes) { case 8: z_stg(src, dst); break; case 4: z_st(src, dst); break; case 2: z_sth(src, dst); break; case 1: z_stc(src, dst); break; default: ShouldNotReachHere();
}
}
// Split a si20 offset (20bit, signed) into an ui12 offset (12bit, unsigned) and // a high-order summand in register tmp. // // return value: < 0: No split required, si20 actually has property uimm12. // >= 0: Split performed. Use return value as uimm12 displacement and // tmp as index register. int MacroAssembler::split_largeoffset(int64_t si20_offset, Register tmp, bool fixed_codelen, bool accumulate) {
assert(Immediate::is_simm20(si20_offset), "sanity"); int lg_off = (int)si20_offset & 0x0fff; // Punch out low-order 12 bits, always positive. int ll_off = (int)si20_offset & ~0x0fff; // Force low-order 12 bits to zero.
assert((Displacement::is_shortDisp(si20_offset) && (ll_off == 0)) ||
!Displacement::is_shortDisp(si20_offset), "unexpected offset values");
assert((lg_off+ll_off) == si20_offset, "offset splitup error");
Register work = accumulate? Z_R0 : tmp;
if (fixed_codelen) { // Len of code = 10 = 4 + 6.
z_lghi(work, ll_off>>12); // Implicit sign extension.
z_slag(work, work, 12);
} else { // Len of code = 0..10. if (ll_off == 0) { return -1; } // ll_off has 8 significant bits (at most) plus sign. if ((ll_off & 0x0000f000) == 0) { // Non-zero bits only in upper halfbyte.
z_llilh(work, ll_off >> 16); if (ll_off < 0) { // Sign-extension required.
z_lgfr(work, work);
}
} else { if ((ll_off & 0x000f0000) == 0) { // Non-zero bits only in lower halfbyte.
z_llill(work, ll_off);
} else { // Non-zero bits in both halfbytes.
z_lghi(work, ll_off>>12); // Implicit sign extension.
z_slag(work, work, 12);
}
}
} if (accumulate) { z_algr(tmp, work); } // len of code += 4 return lg_off;
}
void MacroAssembler::load_float_largeoffset(FloatRegister t, int64_t si20, Register a, Register tmp) { if (Displacement::is_validDisp(si20)) {
z_ley(t, si20, a);
} else { // Fixed_codelen = true is a simple way to ensure that the size of load_float_largeoffset // does not depend on si20 (scratch buffer emit size == code buffer emit size for constant // pool loads). bool accumulate = true; bool fixed_codelen = true; Register work;
if (fixed_codelen) {
z_lgr(tmp, a); // Lgr_if_needed not applicable due to fixed_codelen.
} else {
accumulate = (a == tmp);
}
work = tmp;
void MacroAssembler::load_double_largeoffset(FloatRegister t, int64_t si20, Register a, Register tmp) { if (Displacement::is_validDisp(si20)) {
z_ldy(t, si20, a);
} else { // Fixed_codelen = true is a simple way to ensure that the size of load_double_largeoffset // does not depend on si20 (scratch buffer emit size == code buffer emit size for constant // pool loads). bool accumulate = true; bool fixed_codelen = true; Register work;
if (fixed_codelen) {
z_lgr(tmp, a); // Lgr_if_needed not applicable due to fixed_codelen.
} else {
accumulate = (a == tmp);
}
work = tmp;
// PCrelative TOC access. // Returns distance (in bytes) from current position to start of consts section. // Returns 0 (zero) if no consts section exists or if it has size zero. long MacroAssembler::toc_distance() {
CodeSection* cs = code()->consts(); return (long)((cs != NULL) ? cs->start()-pc() : 0);
}
// Implementation on x86/sparc assumes that constant and instruction section are // adjacent, but this doesn't hold. Two special situations may occur, that we must // be able to handle: // 1. const section may be located apart from the inst section. // 2. const section may be empty // In both cases, we use the const section's start address to compute the "TOC", // this seems to occur only temporarily; in the final step we always seem to end up // with the pc-relatice variant. // // PC-relative offset could be +/-2**32 -> use long for disp // Furthermore: makes no sense to have special code for // adjacent const and inst sections. void MacroAssembler::load_toc(Register Rtoc) { // Simply use distance from start of const section (should be patched in the end). long disp = toc_distance();
RelocationHolder rspec = internal_word_Relocation::spec(pc() + disp);
relocate(rspec);
z_larl(Rtoc, RelAddr::pcrel_off32(disp)); // Offset is in halfwords.
}
// Generic operation: load a value from memory and test. // CondCode indicates the sign (<0, ==0, >0) of the loaded value. void MacroAssembler::load_and_test_byte(Register dst, const Address &a) {
z_lb(dst, a);
z_ltr(dst, dst);
}
// Test a bit in a register. Result is reflected in CC. void MacroAssembler::testbit(Register r, unsignedint bitPos) { if (bitPos < 16) {
z_tmll(r, 1U<<bitPos);
} elseif (bitPos < 32) {
z_tmlh(r, 1U<<(bitPos-16));
} elseif (bitPos < 48) {
z_tmhl(r, 1U<<(bitPos-32));
} elseif (bitPos < 64) {
z_tmhh(r, 1U<<(bitPos-48));
} else {
ShouldNotReachHere();
}
}
void MacroAssembler::prefetch_read(Address a) {
z_pfd(1, a.disp20(), a.indexOrR0(), a.base());
} void MacroAssembler::prefetch_update(Address a) {
z_pfd(2, a.disp20(), a.indexOrR0(), a.base());
}
// Clear a register, i.e. load const zero into reg. // Return len (in bytes) of generated instruction(s). // whole_reg: Clear 64 bits if true, 32 bits otherwise. // set_cc: Use instruction that sets the condition code, if true. int MacroAssembler::clear_reg(Register r, bool whole_reg, bool set_cc) { unsignedint start_off = offset(); if (whole_reg) {
set_cc ? z_xgr(r, r) : z_laz(r, 0, Z_R0);
} else { // Only 32bit register.
set_cc ? z_xr(r, r) : z_lhi(r, 0);
} return offset() - start_off;
}
#ifdef ASSERT int MacroAssembler::preset_reg(Register r, unsignedlong pattern, int pattern_len) { switch (pattern_len) { case 1:
pattern = (pattern & 0x000000ff) | ((pattern & 0x000000ff)<<8); case 2:
pattern = (pattern & 0x0000ffff) | ((pattern & 0x0000ffff)<<16); case 4:
pattern = (pattern & 0xffffffffL) | ((pattern & 0xffffffffL)<<32); case 8: return load_const_optimized_rtn_len(r, pattern, true); break; default:
guarantee(false, "preset_reg: bad len");
} return 0;
} #endif
// addr: Address descriptor of memory to clear index register will not be used ! // size: Number of bytes to clear. // !!! DO NOT USE THEM FOR ATOMIC MEMORY CLEARING !!! // !!! Use store_const() instead !!! void MacroAssembler::clear_mem(const Address& addr, unsigned size) {
guarantee(size <= 256, "MacroAssembler::clear_mem: size too large");
if (size == 1) {
z_mvi(addr, 0); return;
}
switch (size) { case 2: z_mvhhi(addr, 0); return; case 4: z_mvhi(addr, 0); return; case 8: z_mvghi(addr, 0); return; default: ; // Fallthru to xc.
}
// Special version for non-relocateable code if required alignment // is larger than CodeEntryAlignment. void MacroAssembler::align_address(int modulus) { while ((uintptr_t)pc() % modulus != 0) z_nop();
}
Address MacroAssembler::argument_address(RegisterOrConstant arg_slot, Register temp_reg,
int64_t extra_slot_offset) { // On Z, we can have index and disp in an Address. So don't call argument_offset, // which issues an unnecessary add instruction. int stackElementSize = Interpreter::stackElementSize;
int64_t offset = extra_slot_offset * stackElementSize; constRegister argbase = Z_esp; if (arg_slot.is_constant()) {
offset += arg_slot.as_constant() * stackElementSize; return Address(argbase, offset);
} // else
assert(temp_reg != noreg, "must specify");
assert(temp_reg != Z_ARG1, "base and index are conflicting");
z_sllg(temp_reg, arg_slot.as_register(), exact_log2(stackElementSize)); // tempreg = arg_slot << 3 return Address(argbase, temp_reg, offset);
}
//=================================================================== //=== START C O N S T A N T S I N C O D E S T R E A M === //=================================================================== //=== P A T CH A B L E C O N S T A N T S === //===================================================================
//--------------------------------------------------- // Load (patchable) constant into register //---------------------------------------------------
// Load absolute address (and try to optimize). // Note: This method is usable only for position-fixed code, // referring to a position-fixed target location. // If not so, relocations and patching must be used. void MacroAssembler::load_absolute_address(Register d, address addr) {
assert(addr != NULL, "should not happen");
BLOCK_COMMENT("load_absolute_address:"); if (addr == NULL) {
z_larl(d, pc()); // Dummy emit for size calc. return;
}
if (RelAddr::is_in_range_of_RelAddr32(addr, pc())) {
z_larl(d, addr); return;
}
load_const_optimized(d, (long)addr);
}
// Load a 64bit constant. // Patchable code sequence, but not atomically patchable. // Make sure to keep code size constant -> no value-dependent optimizations. // Do not kill condition code. void MacroAssembler::load_const(Register t, long x) { // Note: Right shift is only cleanly defined for unsigned types // or for signed types with nonnegative values.
Assembler::z_iihf(t, (long)((unsignedlong)x >> 32));
Assembler::z_iilf(t, (long)((unsignedlong)x & 0xffffffffUL));
}
// Load a 32bit constant into a 64bit register, sign-extend or zero-extend. // Patchable code sequence, but not atomically patchable. // Make sure to keep code size constant -> no value-dependent optimizations. // Do not kill condition code. void MacroAssembler::load_const_32to64(Register t, int64_t x, bool sign_extend) { if (sign_extend) { Assembler::z_lgfi(t, x); } else { Assembler::z_llilf(t, x); }
}
// Load narrow oop constant, no decompression. void MacroAssembler::load_narrow_oop(Register t, narrowOop a) {
assert(UseCompressedOops, "must be on to call this method");
load_const_32to64(t, CompressedOops::narrow_oop_value(a), false/*sign_extend*/);
}
// Load narrow klass constant, compression required. void MacroAssembler::load_narrow_klass(Register t, Klass* k) {
assert(UseCompressedClassPointers, "must be on to call this method");
narrowKlass encoded_k = CompressedKlassPointers::encode(k);
load_const_32to64(t, encoded_k, false/*sign_extend*/);
}
//------------------------------------------------------ // Compare (patchable) constant with register. //------------------------------------------------------
// Compare narrow oop in reg with narrow oop constant, no decompression. void MacroAssembler::compare_immediate_narrow_oop(Register oop1, narrowOop oop2) {
assert(UseCompressedOops, "must be on to call this method");
// Compare narrow oop in reg with narrow oop constant, no decompression. void MacroAssembler::compare_immediate_narrow_klass(Register klass1, Klass* klass2) {
assert(UseCompressedClassPointers, "must be on to call this method");
narrowKlass encoded_k = CompressedKlassPointers::encode(klass2);
Assembler::z_clfi(klass1, encoded_k);
}
//---------------------------------------------------------- // Check which kind of load_constant we have here. //----------------------------------------------------------
// Detection of CPU version dependent load_const sequence. // The detection is valid only for code sequences generated by load_const, // not load_const_optimized. bool MacroAssembler::is_load_const(address a) { unsignedlong inst1, inst2; unsignedint len1, len2;
// Detection of CPU version dependent load_const_32to64 sequence. // Mostly used for narrow oops and narrow Klass pointers. // The detection is valid only for code sequences generated by load_const_32to64. bool MacroAssembler::is_load_const_32to64(address pos) { unsignedlong inst1, inst2; unsignedint len1;
// Detection of compare_immediate_narrow sequence. // The detection is valid only for code sequences generated by compare_immediate_narrow_oop. bool MacroAssembler::is_compare_immediate32(address pos) { return is_equal(pos, CLFI_ZOPC, RIL_MASK);
}
// Detection of compare_immediate_narrow sequence. // The detection is valid only for code sequences generated by compare_immediate_narrow_oop. bool MacroAssembler::is_compare_immediate_narrow_oop(address pos) { return is_compare_immediate32(pos);
}
// Detection of compare_immediate_narrow sequence. // The detection is valid only for code sequences generated by compare_immediate_narrow_klass. bool MacroAssembler::is_compare_immediate_narrow_klass(address pos) { return is_compare_immediate32(pos);
}
//----------------------------------- // patch the load_constant //-----------------------------------
// CPU-version dependent patching of load_const. void MacroAssembler::patch_const(address a, long x) {
assert(is_load_const(a), "not a load of a constant"); // Note: Right shift is only cleanly defined for unsigned types // or for signed types with nonnegative values.
set_imm32((address)a, (long)((unsignedlong)x >> 32));
set_imm32((address)(a + 6), (long)((unsignedlong)x & 0xffffffffUL));
}
// Patching the value of CPU version dependent load_const_32to64 sequence. // The passed ptr MUST be in compressed format! int MacroAssembler::patch_load_const_32to64(address pos, int64_t np) {
assert(is_load_const_32to64(pos), "not a load of a narrow ptr (oop or klass)");
set_imm32(pos, np); return 6;
}
// Patching the value of CPU version dependent compare_immediate_narrow sequence. // The passed ptr MUST be in compressed format! int MacroAssembler::patch_compare_immediate_32(address pos, int64_t np) {
assert(is_compare_immediate32(pos), "not a compressed ptr compare");
set_imm32(pos, np); return 6;
}
// Patching the immediate value of CPU version dependent load_narrow_oop sequence. // The passed ptr must NOT be in compressed format! int MacroAssembler::patch_load_narrow_oop(address pos, oop o) {
assert(UseCompressedOops, "Can only patch compressed oops"); return patch_load_const_32to64(pos, CompressedOops::narrow_oop_value(o));
}
// Patching the immediate value of CPU version dependent load_narrow_klass sequence. // The passed ptr must NOT be in compressed format! int MacroAssembler::patch_load_narrow_klass(address pos, Klass* k) {
assert(UseCompressedClassPointers, "Can only patch compressed klass pointers");
narrowKlass nk = CompressedKlassPointers::encode(k); return patch_load_const_32to64(pos, nk);
}
// Patching the immediate value of CPU version dependent compare_immediate_narrow_oop sequence. // The passed ptr must NOT be in compressed format! int MacroAssembler::patch_compare_immediate_narrow_oop(address pos, oop o) {
assert(UseCompressedOops, "Can only patch compressed oops"); return patch_compare_immediate_32(pos, CompressedOops::narrow_oop_value(o));
}
// Patching the immediate value of CPU version dependent compare_immediate_narrow_klass sequence. // The passed ptr must NOT be in compressed format! int MacroAssembler::patch_compare_immediate_narrow_klass(address pos, Klass* k) {
assert(UseCompressedClassPointers, "Can only patch compressed klass pointers");
narrowKlass nk = CompressedKlassPointers::encode(k); return patch_compare_immediate_32(pos, nk);
}
//------------------------------------------------------------------------ // Extract the constant from a load_constant instruction stream. //------------------------------------------------------------------------
// Get constant from a load_const sequence. long MacroAssembler::get_const(address a) {
assert(is_load_const(a), "not a load of a constant"); unsignedlong x;
x = (((unsignedlong) (get_imm32(a,0) & 0xffffffff)) << 32);
x |= (((unsignedlong) (get_imm32(a,1) & 0xffffffff))); return (long) x;
}
//-------------------------------------- // Store a constant in memory. //--------------------------------------
// General emitter to move a constant to memory. // The store is atomic. // o Address must be given in RS format (no index register) // o Displacement should be 12bit unsigned for efficiency. 20bit signed also supported. // o Constant can be 1, 2, 4, or 8 bytes, signed or unsigned. // o Memory slot can be 1, 2, 4, or 8 bytes, signed or unsigned. // o Memory slot must be at least as wide as constant, will assert otherwise. // o Signed constants will sign-extend, unsigned constants will zero-extend to slot width. int MacroAssembler::store_const(const Address &dest, long imm, unsignedint lm, unsignedint lc, Register scratch) {
int64_t disp = dest.disp(); Register base = dest.base();
assert(!dest.has_index(), "not supported");
assert((lm==1)||(lm==2)||(lm==4)||(lm==8), "memory length not supported");
assert((lc==1)||(lc==2)||(lc==4)||(lc==8), "constant length not supported");
assert(lm>=lc, "memory slot too small");
assert(lc==8 || Immediate::is_simm(imm, lc*8), "const out of range");
assert(Displacement::is_validDisp(disp), "displacement out of range");
bool is_shortDisp = Displacement::is_shortDisp(disp); int store_offset = -1;
// For target len == 1 it's easy. if (lm == 1) {
store_offset = offset(); if (is_shortDisp) {
z_mvi(disp, base, imm); return store_offset;
} else {
z_mviy(disp, base, imm); return store_offset;
}
}
// All the "good stuff" takes an unsigned displacement. if (is_shortDisp) { // NOTE: Cannot use clear_mem for imm==0, because it is not atomic.
store_offset = offset(); switch (lm) { case 2: // Lc == 1 handled correctly here, even for unsigned. Instruction does no widening.
z_mvhhi(disp, base, imm); return store_offset; case 4: if (Immediate::is_simm16(imm)) {
z_mvhi(disp, base, imm); return store_offset;
} break; case 8: if (Immediate::is_simm16(imm)) {
z_mvghi(disp, base, imm); return store_offset;
} break; default:
ShouldNotReachHere(); break;
}
}
// Can't optimize, so load value and store it.
guarantee(scratch != noreg, " need a scratch register here !"); if (imm != 0) {
load_const_optimized(scratch, imm); // Preserves CC anyway.
} else { // Leave CC alone!!
(void) clear_reg(scratch, true, false); // Indicate unused result.
}
store_offset = offset(); if (is_shortDisp) { switch (lm) { case 2:
z_sth(scratch, disp, Z_R0, base); return store_offset; case 4:
z_st(scratch, disp, Z_R0, base); return store_offset; case 8:
z_stg(scratch, disp, Z_R0, base); return store_offset; default:
ShouldNotReachHere(); break;
}
} else { switch (lm) { case 2:
z_sthy(scratch, disp, Z_R0, base); return store_offset; case 4:
z_sty(scratch, disp, Z_R0, base); return store_offset; case 8:
z_stg(scratch, disp, Z_R0, base); return store_offset; default:
ShouldNotReachHere(); break;
}
} return -1; // should not reach here
}
//=================================================================== //=== N O T P A T CH A B L E C O N S T A N T S === //===================================================================
// Load constant x into register t with a fast instruction sequence // depending on the bits in x. Preserves CC under all circumstances. int MacroAssembler::load_const_optimized_rtn_len(Register t, long x, bool emit) { if (x == 0) { int len; if (emit) {
len = clear_reg(t, true, false);
} else {
len = 4;
} return len;
}
if (Immediate::is_simm16(x)) { if (emit) { z_lghi(t, x); } return 4;
}
// 64 bit value: | part1 | part2 | part3 | part4 | // At least one part is not zero! // Note: Right shift is only cleanly defined for unsigned types // or for signed types with nonnegative values. int part1 = (int)((unsignedlong)x >> 48) & 0x0000ffff; int part2 = (int)((unsignedlong)x >> 32) & 0x0000ffff; int part3 = (int)((unsignedlong)x >> 16) & 0x0000ffff; int part4 = (int)x & 0x0000ffff; int part12 = (int)((unsignedlong)x >> 32); int part34 = (int)x;
// Lower word only (unsigned). if (part12 == 0) { if (part3 == 0) { if (emit) z_llill(t, part4); return 4;
} if (part4 == 0) { if (emit) z_llilh(t, part3); return 4;
} if (emit) z_llilf(t, part34); return 6;
}
// Upper word only. if (part34 == 0) { if (part1 == 0) { if (emit) z_llihl(t, part2); return 4;
} if (part2 == 0) { if (emit) z_llihh(t, part1); return 4;
} if (emit) z_llihf(t, part12); return 6;
}
// Lower word only (signed). if ((part1 == 0x0000ffff) && (part2 == 0x0000ffff) && ((part3 & 0x00008000) != 0)) { if (emit) z_lgfi(t, part34); return 6;
}
int len = 0;
if ((part1 == 0) || (part2 == 0)) { if (part1 == 0) { if (emit) z_llihl(t, part2);
len += 4;
} else { if (emit) z_llihh(t, part1);
len += 4;
}
} else { if (emit) z_llihf(t, part12);
len += 6;
}
if ((part3 == 0) || (part4 == 0)) { if (part3 == 0) { if (emit) z_iill(t, part4);
len += 4;
} else { if (emit) z_iilh(t, part3);
len += 4;
}
} else { if (emit) z_iilf(t, part34);
len += 6;
} return len;
}
//===================================================================== //=== H I G H E R L E V E L B R A N C H E M I T T E R S === //=====================================================================
// Note: In the worst case, one of the scratch registers is destroyed!!! void MacroAssembler::compare32_and_branch(Register r1, RegisterOrConstant x2, branch_condition cond, Label& lbl) { // Right operand is constant. if (x2.is_constant()) {
jlong value = x2.as_constant();
compare_and_branch_optimized(r1, value, cond, lbl, /*len64=*/false, /*has_sign=*/true); return;
}
// Right operand is in register.
compare_and_branch_optimized(r1, x2.as_register(), cond, lbl, /*len64=*/false, /*has_sign=*/true);
}
// Note: In the worst case, one of the scratch registers is destroyed!!! void MacroAssembler::compareU32_and_branch(Register r1, RegisterOrConstant x2, branch_condition cond, Label& lbl) { // Right operand is constant. if (x2.is_constant()) {
jlong value = x2.as_constant();
compare_and_branch_optimized(r1, value, cond, lbl, /*len64=*/false, /*has_sign=*/false); return;
}
// Right operand is in register.
compare_and_branch_optimized(r1, x2.as_register(), cond, lbl, /*len64=*/false, /*has_sign=*/false);
}
// Note: In the worst case, one of the scratch registers is destroyed!!! void MacroAssembler::compare64_and_branch(Register r1, RegisterOrConstant x2, branch_condition cond, Label& lbl) { // Right operand is constant. if (x2.is_constant()) {
jlong value = x2.as_constant();
compare_and_branch_optimized(r1, value, cond, lbl, /*len64=*/true, /*has_sign=*/true); return;
}
// Right operand is in register.
compare_and_branch_optimized(r1, x2.as_register(), cond, lbl, /*len64=*/true, /*has_sign=*/true);
}
void MacroAssembler::compareU64_and_branch(Register r1, RegisterOrConstant x2, branch_condition cond, Label& lbl) { // Right operand is constant. if (x2.is_constant()) {
jlong value = x2.as_constant();
compare_and_branch_optimized(r1, value, cond, lbl, /*len64=*/true, /*has_sign=*/false); return;
}
// Right operand is in register.
compare_and_branch_optimized(r1, x2.as_register(), cond, lbl, /*len64=*/true, /*has_sign=*/false);
}
// Generate an optimal branch to the branch target. // Optimal means that a relative branch (brc or brcl) is used if the // branch distance is short enough. Loading the target address into a // register and branching via reg is used as fallback only. // // Used registers: // Z_R1 - work reg. Holds branch target address. // Used in fallback case only. // // This version of branch_optimized is good for cases where the target address is known // and constant, i.e. is never changed (no relocation, no patching). void MacroAssembler::branch_optimized(Assembler::branch_condition cond, address branch_addr) {
address branch_origin = pc();
if (RelAddr::is_in_range_of_RelAddr16(branch_addr, branch_origin)) {
z_brc(cond, branch_addr);
} elseif (RelAddr::is_in_range_of_RelAddr32(branch_addr, branch_origin)) {
z_brcl(cond, branch_addr);
} else {
load_const_optimized(Z_R1, branch_addr); // CC must not get killed by load_const_optimized.
z_bcr(cond, Z_R1);
}
}
// This version of branch_optimized is good for cases where the target address // is potentially not yet known at the time the code is emitted. // // One very common case is a branch to an unbound label which is handled here. // The caller might know (or hope) that the branch distance is short enough // to be encoded in a 16bit relative address. In this case he will pass a // NearLabel branch_target. // Care must be taken with unbound labels. Each call to target(label) creates // an entry in the patch queue for that label to patch all references of the label // once it gets bound. Those recorded patch locations must be patchable. Otherwise, // an assertion fires at patch time. void MacroAssembler::branch_optimized(Assembler::branch_condition cond, Label& branch_target) { if (branch_target.is_bound()) {
address branch_addr = target(branch_target);
branch_optimized(cond, branch_addr);
} elseif (branch_target.is_near()) {
z_brc(cond, branch_target); // Caller assures that the target will be in range for z_brc.
} else {
z_brcl(cond, branch_target); // Let's hope target is in range. Otherwise, we will abort at patch time.
}
}
// Generate an optimal compare and branch to the branch target. // Optimal means that a relative branch (clgrj, brc or brcl) is used if the // branch distance is short enough. Loading the target address into a // register and branching via reg is used as fallback only. // // Input: // r1 - left compare operand // r2 - right compare operand void MacroAssembler::compare_and_branch_optimized(Register r1, Register r2,
Assembler::branch_condition cond,
address branch_addr, bool len64, bool has_sign) { unsignedint casenum = (len64?2:0)+(has_sign?0:1);
address branch_origin = pc(); if (VM_Version::has_CompareBranch() && RelAddr::is_in_range_of_RelAddr16(branch_addr, branch_origin)) { switch (casenum) { case 0: z_crj( r1, r2, cond, branch_addr); break; case 1: z_clrj (r1, r2, cond, branch_addr); break; case 2: z_cgrj(r1, r2, cond, branch_addr); break; case 3: z_clgrj(r1, r2, cond, branch_addr); break; default: ShouldNotReachHere(); break;
}
} else { switch (casenum) { case 0: z_cr( r1, r2); break; case 1: z_clr(r1, r2); break; case 2: z_cgr(r1, r2); break; case 3: z_clgr(r1, r2); break; default: ShouldNotReachHere(); break;
}
branch_optimized(cond, branch_addr);
}
}
// Generate an optimal compare and branch to the branch target. // Optimal means that a relative branch (clgij, brc or brcl) is used if the // branch distance is short enough. Loading the target address into a // register and branching via reg is used as fallback only. // // Input: // r1 - left compare operand (in register) // x2 - right compare operand (immediate) void MacroAssembler::compare_and_branch_optimized(Register r1,
jlong x2,
Assembler::branch_condition cond,
Label& branch_target, bool len64, bool has_sign) {
address branch_origin = pc(); bool x2_imm8 = (has_sign && Immediate::is_simm8(x2)) || (!has_sign && Immediate::is_uimm8(x2)); bool is_RelAddr16 = branch_target.is_near() ||
(branch_target.is_bound() &&
RelAddr::is_in_range_of_RelAddr16(target(branch_target), branch_origin)); unsignedint casenum = (len64?2:0)+(has_sign?0:1);
if (VM_Version::has_CompareBranch() && is_RelAddr16 && x2_imm8) { switch (casenum) { case 0: z_cij( r1, x2, cond, branch_target); break; case 1: z_clij(r1, x2, cond, branch_target); break; case 2: z_cgij(r1, x2, cond, branch_target); break; case 3: z_clgij(r1, x2, cond, branch_target); break; default: ShouldNotReachHere(); break;
} return;
}
if (x2 == 0) { switch (casenum) { case 0: z_ltr(r1, r1); break; case 1: z_ltr(r1, r1); break; // Caution: unsigned test only provides zero/notZero indication! case 2: z_ltgr(r1, r1); break; case 3: z_ltgr(r1, r1); break; // Caution: unsigned test only provides zero/notZero indication! default: ShouldNotReachHere(); break;
}
} else { if ((has_sign && Immediate::is_simm16(x2)) || (!has_sign && Immediate::is_uimm(x2, 15))) { switch (casenum) { case 0: z_chi(r1, x2); break; case 1: z_chi(r1, x2); break; // positive immediate < 2**15 case 2: z_cghi(r1, x2); break; case 3: z_cghi(r1, x2); break; // positive immediate < 2**15 default: break;
}
} elseif ( (has_sign && Immediate::is_simm32(x2)) || (!has_sign && Immediate::is_uimm32(x2)) ) { switch (casenum) { case 0: z_cfi( r1, x2); break; case 1: z_clfi(r1, x2); break; case 2: z_cgfi(r1, x2); break; case 3: z_clgfi(r1, x2); break; default: ShouldNotReachHere(); break;
}
} else { // No instruction with immediate operand possible, so load into register. Register scratch = (r1 != Z_R0) ? Z_R0 : Z_R1;
load_const_optimized(scratch, x2); switch (casenum) { case 0: z_cr( r1, scratch); break; case 1: z_clr(r1, scratch); break; case 2: z_cgr(r1, scratch); break; case 3: z_clgr(r1, scratch); break; default: ShouldNotReachHere(); break;
}
}
}
branch_optimized(cond, branch_target);
}
// Generate an optimal compare and branch to the branch target. // Optimal means that a relative branch (clgrj, brc or brcl) is used if the // branch distance is short enough. Loading the target address into a // register and branching via reg is used as fallback only. // // Input: // r1 - left compare operand // r2 - right compare operand void MacroAssembler::compare_and_branch_optimized(Register r1, Register r2,
Assembler::branch_condition cond,
Label& branch_target, bool len64, bool has_sign) { unsignedint casenum = (len64 ? 2 : 0) + (has_sign ? 0 : 1);
if (branch_target.is_bound()) {
address branch_addr = target(branch_target);
compare_and_branch_optimized(r1, r2, cond, branch_addr, len64, has_sign);
} else { if (VM_Version::has_CompareBranch() && branch_target.is_near()) { switch (casenum) { case 0: z_crj( r1, r2, cond, branch_target); break; case 1: z_clrj( r1, r2, cond, branch_target); break; case 2: z_cgrj( r1, r2, cond, branch_target); break; case 3: z_clgrj(r1, r2, cond, branch_target); break; default: ShouldNotReachHere(); break;
}
} else { switch (casenum) { case 0: z_cr( r1, r2); break; case 1: z_clr(r1, r2); break; case 2: z_cgr(r1, r2); break;
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.66 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.